diff -uNr --exclude=CVS linux-2.4.5-ac18/MAINTAINERS linuxppc64_2_4/MAINTAINERS --- linux-2.4.5-ac18/MAINTAINERS Tue Jun 26 22:04:26 2001 +++ linuxppc64_2_4/MAINTAINERS Wed Jun 27 13:48:14 2001 @@ -863,6 +863,19 @@ L: linuxppc-dev@lists.linuxppc.org S: Maintained +LINUX FOR 64BIT POWERPC +P: +M: +P: Tom Gall +M: tom_gall@vnet.ibm.com +P: Dwayne McConnell +M: dwayne@austin.ibm.com +P: David Engebretsen +M: engebret@us.ibm.com +W: http://linuxppc64.org +L: linuxppc64-dev@lists.linuxppc.porg +S: Maintained + LOGICAL VOLUME MANAGER P: Heinz Mauelshagen M: linux-LVM@EZ-Darmstadt.Telekom.de diff -uNr --exclude=CVS linux-2.4.5-ac18/Makefile linuxppc64_2_4/Makefile --- linux-2.4.5-ac18/Makefile Tue Jun 26 22:04:26 2001 +++ linuxppc64_2_4/Makefile Wed Aug 1 14:05:31 2001 @@ -8,11 +8,13 @@ # SUBARCH tells the usermode build what the underlying arch is. That is set # first, and if a usermode build is happening, the "ARCH=um" on the command # line overrides the setting of ARCH below. If a native build is happening, -# then ARCH is assigned, getting whatever value it gets normally, and +# then ARCH is assigned, getting whatever value it gets normally, and # SUBARCH is subsequently ignored. SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) -ARCH := $(SUBARCH) +#ARCH := $(SUBARCH) + +ARCH := ppc64 CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \ else if [ -x /bin/bash ]; then echo /bin/bash; \ @@ -25,7 +27,7 @@ HOSTCC = gcc HOSTCFLAGS = -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -CROSS_COMPILE = +CROSS_COMPILE = /usr/local/ppc64-current/powerpc-elf64/bin/ # # Include the make variables (CC, etc...) diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/Makefile linuxppc64_2_4/arch/ppc64/Makefile --- linux-2.4.5-ac18/arch/ppc64/Makefile Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/Makefile Wed Jun 6 03:33:21 2001 @@ -0,0 +1,101 @@ +# 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 +# this architecture +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# +# Copyright (C) 1994 by Linus Torvalds +# Changes for PPC by Gary Thomas +# Rewritten by Cort Dougan and Paul Mackerras +# Adjusted for PPC64 by Tom Gall +# + +KERNELLOAD =0xc000000000000000 + +ifeq ($(shell uname -m),ppc64) +CHECKS = checks +endif + +ASFLAGS = +LINKFLAGS = -T arch/ppc64/vmlinux.lds -Ttext $(KERNELLOAD) -Bstatic +CPPFLAGS := $(CPPFLAGS) -D__powerpc__ -include $(TOPDIR)/arch/ppc64/mymacros.h +CFLAGS := $(CFLAGS) -D__linux__ -D__powerpc__ -fsigned-char -Wa,-Saix \ + -msoft-float -pipe -Wno-uninitialized $(PRINTK) \ + -include $(TOPDIR)/arch/ppc64/mymacros.h -mminimal-toc +CPP = $(CC) -E $(CFLAGS) + + +HEAD := arch/ppc64/kernel/head.o + +ARCH_SUBDIRS = arch/ppc64/kernel arch/ppc64/mm arch/ppc64/lib +SUBDIRS := $(SUBDIRS) $(ARCH_SUBDIRS) +ARCHIVES := arch/ppc64/kernel/kernel.o arch/ppc64/mm/mm.o arch/ppc64/lib/lib.o $(ARCHIVES) +CORE_FILES := arch/ppc64/kernel/kernel.o arch/ppc64/mm/mm.o arch/ppc64/lib/lib.o $(CORE_FILES) + +ifdef CONFIG_XMON +SUBDIRS += arch/ppc64/xmon +CORE_FILES += arch/ppc64/xmon/x.o +endif +ifdef CONFIG_KDB +SUBDIRS += arch/ppc64/kdb +CORE_FILES += arch/ppc64/kdb/kdba.o +endif + +MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot + +checks: + @$(MAKE) -C arch/$(ARCH)/kernel checks + +BOOT_TARGETS = zImage znetboot.initrd zImage.initrd + +ifdef CONFIG_POWER3 +ifndef CONFIG_8260 +$(BOOT_TARGETS): $(CHECKS) vmlinux + @$(MAKEBOOT) $@ + +znetboot: $(CHECKS) vmlinux +ifdef CONFIG_ALL_PPC +ifdef CONFIG_SMP + cp -f vmlinux /tftpboot/vmlinux.smp +else + cp -f vmlinux /tftpboot/vmlinux +endif +endif + @$(MAKEBOOT) $@ +else +# 8260 is custom 6xx +$(BOOT_TARGETS): $(CHECKS) vmlinux + @$(MAKEMBXBOOT) $@ +endif +endif + +ifdef CONFIG_PPC64BRIDGE +$(BOOT_TARGETS): $(CHECKS) vmlinux + @$(MAKEBOOT) $@ + +znetboot: $(CHECKS) vmlinux + cp -f vmlinux /tftpboot/vmlinux.64 + @$(MAKEBOOT) $@ +endif + +.PHONY: clean_config +clean_config: + rm -f .config arch/ppc64/defconfig + +chrp_config: clean_config + cp -f arch/ppc64/configs/chrp_defconfig arch/ppc64/defconfig + +common_config: clean_config + cp -f arch/ppc64/configs/common_defconfig arch/ppc64/defconfig + +archclean: + rm -f arch/ppc64/kernel/{ppc_defs.h,checks,mk_defs.s mk_defs_out.c mk_defs_tpl} + @$(MAKEBOOT) clean + +archmrproper: + +archdep: + $(MAKEBOOT) fastdep diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/boot/Makefile linuxppc64_2_4/arch/ppc64/boot/Makefile --- linux-2.4.5-ac18/arch/ppc64/boot/Makefile Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/boot/Makefile Fri May 4 17:13:58 2001 @@ -0,0 +1,92 @@ +# Makefile for making ELF bootable images for booting on CHRP +# using Open Firmware. +# +# Geert Uytterhoeven September 1997 +# +# Based on coffboot by Paul Mackerras +# Simplified for ppc64 by Todd Inglett +# +# NOTE: this code is built for 32 bit in ELF32 format even though +# it packages a 64 bit kernel. We do this to simplify the +# bootloader and increase compatibility with OpenFirmware. +# +# To this end we need to define BOOTCC, etc, as the tools +# needed to build the 32 bit image. These are normally HOSTCC, +# but may be a third compiler if, for example, you are cross +# compiling from an intel box. Once the 64bit ppc gcc is +# stable it will probably simply be a compiler switch to +# compile for 32bit mode. + +BOOTCC = $(HOSTCC) +BOOTCFLAGS = $(HOSTCFLAGS) +BOOTLD = ld +BOOTAS = as +BOOTAFLAGS = -D__ASSEMBLY__ $(HOSTCFLAGS) + +.c.o: + $(BOOTCC) $(BOOTCFLAGS) -c -o $*.o $< +.S.o: + $(BOOTCC) $(BOOTAFLAGS) -traditional -c -o $*.o $< + +CFLAGS = $(CPPFLAGS) -O -fno-builtin -DSTDC_HEADERS +LD_ARGS = -Ttext 0x00400000 + +OBJS = crt0.o start.o main.o zlib.o image.o +#LIBS = $(TOPDIR)/lib/lib.a +LIBS = + +ifeq ($(CONFIG_SMP),y) +TFTPIMAGE=/tftpboot/zImage.chrp.smp +else +TFTPIMAGE=/tftpboot/zImage.chrp +endif + +all: $(TOPDIR)/zImage + +znetboot: zImage + cp zImage $(TFTPIMAGE) + +znetboot.initrd: zImage.initrd + cp zImage.initrd $(TFTPIMAGE) + +floppy: zImage + mcopy zImage a:zImage + +piggyback: piggyback.c + $(HOSTCC) $(HOSTCFLAGS) -DKERNELBASE=$(KERNELBASE) -o piggyback piggyback.c + +addnote: addnote.c + $(HOSTCC) $(HOSTCFLAGS) -o addnote addnote.c + +image.o: piggyback vmlinux.gz + ./piggyback image < vmlinux.gz | $(BOOTAS) -o image.o + +sysmap.o: piggyback ../../../System.map + ./piggyback sysmap < ../../../System.map | $(BOOTAS) -o sysmap.o + +initrd.o: ramdisk.image.gz piggyback + ./piggyback initrd < ramdisk.image.gz | $(BOOTAS) -o initrd.o + +zImage: $(OBJS) no_initrd.o addnote + $(BOOTLD) $(LD_ARGS) -o $@ $(OBJS) no_initrd.o $(LIBS) + ./addnote $@ + +zImage.initrd: $(OBJS) initrd.o addnote + $(BOOTLD) $(LD_ARGS) -o $@ $(OBJS) initrd.o $(LIBS) + ./addnote $@ + + +vmlinux.gz: $(TOPDIR)/vmlinux + $(OBJCOPY) -S -O binary $(TOPDIR)/vmlinux vmlinux + gzip -vf9 vmlinux + + +clean: + rm -f piggyback note addnote $(OBJS) zImage vmlinux.gz no_initrd.o + +fastdep: + $(TOPDIR)/scripts/mkdep *.[Sch] > .depend + +dep: + $(CPP) $(CPPFLAGS) -M *.S *.c > .depend + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/boot/addnote.c linuxppc64_2_4/arch/ppc64/boot/addnote.c --- linux-2.4.5-ac18/arch/ppc64/boot/addnote.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/boot/addnote.c Fri May 4 17:13:58 2001 @@ -0,0 +1,164 @@ +/* + * Program to hack in a PT_NOTE program header entry in an ELF file. + * This is needed for OF on RS/6000s to load an image correctly. + * Note that OF needs a program header entry for the note, not an + * ELF section. + * + * Copyright 2000 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. + * + * Usage: addnote zImage + */ +#include +#include +#include +#include + +char arch[] = "PowerPC"; + +#define N_DESCR 6 +unsigned int descr[N_DESCR] = { + 0xffffffff, /* real-mode = true */ + 0x00c00000, /* real-base, i.e. where we expect OF to be */ + 0xffffffff, /* real-size */ + 0xffffffff, /* virt-base */ + 0xffffffff, /* virt-size */ + 0x4000, /* load-base */ +}; + +unsigned char buf[512]; + +#define GET_16BE(off) ((buf[off] << 8) + (buf[(off)+1])) +#define GET_32BE(off) ((GET_16BE(off) << 16) + GET_16BE((off)+2)) + +#define PUT_16BE(off, v) (buf[off] = ((v) >> 8) & 0xff, \ + buf[(off) + 1] = (v) & 0xff) +#define PUT_32BE(off, v) (PUT_16BE((off), (v) >> 16), \ + PUT_16BE((off) + 2, (v))) + +/* Structure of an ELF file */ +#define E_IDENT 0 /* ELF header */ +#define E_PHOFF 28 +#define E_PHENTSIZE 42 +#define E_PHNUM 44 +#define E_HSIZE 52 /* size of ELF header */ + +#define EI_MAGIC 0 /* offsets in E_IDENT area */ +#define EI_CLASS 4 +#define EI_DATA 5 + +#define PH_TYPE 0 /* ELF program header */ +#define PH_OFFSET 4 +#define PH_FILESZ 16 +#define PH_HSIZE 32 /* size of program header */ + +#define PT_NOTE 4 /* Program header type = note */ + +#define ELFCLASS32 1 +#define ELFDATA2MSB 2 + +unsigned char elf_magic[4] = { 0x7f, 'E', 'L', 'F' }; + +int +main(int ac, char **av) +{ + int fd, n, i; + int ph, ps, np; + int nnote, ns; + + if (ac != 2) { + fprintf(stderr, "Usage: %s elf-file\n", av[0]); + exit(1); + } + fd = open(av[1], O_RDWR); + if (fd < 0) { + perror(av[1]); + exit(1); + } + + nnote = strlen(arch) + 1 + (N_DESCR + 3) * 4; + + n = read(fd, buf, sizeof(buf)); + if (n < 0) { + perror("read"); + exit(1); + } + + if (n < E_HSIZE || memcmp(&buf[E_IDENT+EI_MAGIC], elf_magic, 4) != 0) + goto notelf; + + if (buf[E_IDENT+EI_CLASS] != ELFCLASS32 + || buf[E_IDENT+EI_DATA] != ELFDATA2MSB) { + fprintf(stderr, "%s is not a big-endian 32-bit ELF image\n", + av[1]); + exit(1); + } + + ph = GET_32BE(E_PHOFF); + ps = GET_16BE(E_PHENTSIZE); + np = GET_16BE(E_PHNUM); + if (ph < E_HSIZE || ps < PH_HSIZE || np < 1) + goto notelf; + if (ph + (np + 1) * ps + nnote > n) + goto nospace; + + for (i = 0; i < np; ++i) { + if (GET_32BE(ph + PH_TYPE) == PT_NOTE) { + fprintf(stderr, "%s already has a note entry\n", + av[1]); + exit(0); + } + ph += ps; + } + + /* XXX check that the area we want to use is all zeroes */ + for (i = 0; i < ps + nnote; ++i) + if (buf[ph + i] != 0) + goto nospace; + + /* fill in the program header entry */ + ns = ph + ps; + PUT_32BE(ph + PH_TYPE, PT_NOTE); + PUT_32BE(ph + PH_OFFSET, ns); + PUT_32BE(ph + PH_FILESZ, nnote); + + /* fill in the note area we point to */ + /* XXX we should probably make this a proper section */ + PUT_32BE(ns, strlen(arch) + 1); + PUT_32BE(ns + 4, N_DESCR * 4); + PUT_32BE(ns + 8, 0x1275); + strcpy(&buf[ns + 12], arch); + ns += 12 + strlen(arch) + 1; + for (i = 0; i < N_DESCR; ++i) + PUT_32BE(ns + i * 4, descr[i]); + + /* Update the number of program headers */ + PUT_16BE(E_PHNUM, np + 1); + + /* write back */ + lseek(fd, (long) 0, SEEK_SET); + i = write(fd, buf, n); + if (i < 0) { + perror("write"); + exit(1); + } + if (i < n) { + fprintf(stderr, "%s: write truncated\n", av[1]); + exit(1); + } + + exit(0); + + notelf: + fprintf(stderr, "%s does not appear to be an ELF file\n", av[0]); + exit(1); + + nospace: + fprintf(stderr, "sorry, I can't find space in %s to put the note\n", + av[0]); + exit(1); +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/boot/crt0.S linuxppc64_2_4/arch/ppc64/boot/crt0.S --- linux-2.4.5-ac18/arch/ppc64/boot/crt0.S Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/boot/crt0.S Fri May 4 17:13:58 2001 @@ -0,0 +1,241 @@ +/* + * 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. + * + * NOTE: this code runs in 32 bit mode and is packaged as ELF32. + */ + .text + .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 + + + +/* + * 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 + + +#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 -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/boot/main.c linuxppc64_2_4/arch/ppc64/boot/main.c --- linux-2.4.5-ac18/arch/ppc64/boot/main.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/boot/main.c Thu May 31 02:19:54 2001 @@ -0,0 +1,257 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * Updates for PPC64 by Todd Inglett & Dave Engebretsen. + * + * 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__ +#include "zlib.h" +#include +#include +#include +#include + +#define _ALIGN(addr,size) (((addr)+(size)-1)&(~((size)-1))) + +extern void *finddevice(const char *); +extern int getprop(void *, const char *, void *, int); +extern void printf(const char *fmt, ...); +extern int sprintf(char *buf, const char *fmt, ...); +void gunzip(void *, int, unsigned char *, int *); +void *claim(unsigned int, unsigned int, unsigned int); +void flush_cache(void *, int); +void pause(void); + +#define RAM_START 0x00000000 +#define RAM_END (64<<20) + +#define BOOT_START ((unsigned long)_start) +#define BOOT_END ((unsigned long)(_end + 0xFFF) & ~0xFFF) + +#define RAM_FREE ((unsigned long)(_end+0x1000)&~0xFFF) + +// Value picked to match that used by yaboot +#define PROG_START 0x01400000 + +char *avail_ram; +char *begin_avail, *end_avail; +char *avail_high; +unsigned int heap_use; +unsigned int heap_max; + +extern char _end[]; +extern char image_data[]; +extern int image_len; +extern char initrd_data[]; +extern int initrd_len; +extern char sysmap_data[]; +extern int sysmap_len; + +static char scratch[128<<10]; /* 128kB of scratch space for gunzip */ + +void +chrpboot(int a1, int a2, void *prom) +{ + unsigned sa, len; + void *dst, *claim_addr; + unsigned char *im; + unsigned initrd_start, initrd_size; + extern char _start; + + printf("chrpboot starting: loaded at 0x%x\n\r", (unsigned)&_start); + printf(" initrd_len = 0x%x\n\r", (unsigned)initrd_len); + + if (initrd_len) { + initrd_size = initrd_len; + initrd_start = (RAM_END - initrd_size) & ~0xFFF; + a1 = initrd_start; + a2 = initrd_size; + claim(initrd_start, RAM_END - initrd_start, 0); + printf("initial ramdisk moving 0x%x <- 0x%x (%x bytes)\n\r", + initrd_start, (unsigned)initrd_data, initrd_size); + memcpy((void *)initrd_start, (void *)initrd_data, initrd_size); + } + + im = image_data; + len = image_len; + /* claim 4MB starting at PROG_START */ + + for(claim_addr = PROG_START; + claim_addr <= PROG_START * 8; + claim_addr += 0x100000) { + printf(" trying: 0x%08lx\n\r", claim_addr); + dst = claim(claim_addr, (4<<20), 0); + if (dst != (void *)-1) break; + } + if (dst == (void *)-1) { + printf("claim error, can't allocate kernel memory\n\r"); + return; + } + + if (im[0] == 0x1f && im[1] == 0x8b) { + avail_ram = scratch; + begin_avail = avail_high = avail_ram; + end_avail = scratch + sizeof(scratch); + printf("gunzipping (0x%x <- 0x%x:0x%0x)...", + (unsigned)dst, (unsigned)im, (unsigned)im+len); + gunzip(dst, 0x400000, im, &len); + printf("done %u bytes\n\r", len); + printf("%u bytes of heap consumed, max in use %u\n", + (unsigned)(avail_high - begin_avail), heap_max); + } else { + memmove(dst, im, len); + } + + flush_cache(dst, len); + make_bi_recs((unsigned long)dst + len); + + sa = (unsigned long)dst; + printf("start address = 0x%x\n\r", (unsigned) sa); + + (*(void (*)(int, int, void *))sa)(a1, a2, prom); + + printf("returned?\n\r"); + + pause(); +} + +void make_bi_recs(unsigned long addr) +{ + struct bi_record *rec; + + rec = (struct bi_record *)_ALIGN(addr + (1<<20) -1, (1<<20)); + + rec->tag = BI_FIRST; + rec->size = sizeof(struct bi_record); + rec = (struct bi_record *)((unsigned long)rec + rec->size); + + rec->tag = BI_BOOTLOADER_ID; + sprintf( (char *)rec->data, "chrpboot"); + rec->size = sizeof(struct bi_record) + strlen("chrpboot") + 1; + rec = (struct bi_record *)((unsigned long)rec + rec->size); + + rec->tag = BI_MACHTYPE; + rec->data[0] = _MACH_chrp; + rec->data[1] = 1; + rec->size = sizeof(struct bi_record) + sizeof(unsigned long); + rec = (struct bi_record *)((unsigned long)rec + rec->size); +#if 0 + rec->tag = BI_SYSMAP; + rec->data[0] = (unsigned long)sysmap_data; + rec->data[1] = sysmap_len; + rec->size = sizeof(struct bi_record) + sizeof(unsigned long); + rec = (struct bi_record *)((unsigned long)rec + rec->size); +#endif + rec->tag = BI_LAST; + rec->size = sizeof(struct bi_record); + rec = (struct bi_record *)((unsigned long)rec + rec->size); +} + +struct memchunk { + unsigned int size; + unsigned int pad; + struct memchunk *next; +}; + +static struct memchunk *freechunks; + +void *zalloc(void *x, unsigned items, unsigned size) +{ + void *p; + struct memchunk **mpp, *mp; + + size *= items; + size = _ALIGN(size, sizeof(struct memchunk)); + heap_use += size; + if (heap_use > heap_max) + heap_max = heap_use; + for (mpp = &freechunks; (mp = *mpp) != 0; mpp = &mp->next) { + if (mp->size == size) { + *mpp = mp->next; + return mp; + } + } + p = avail_ram; + avail_ram += size; + if (avail_ram > avail_high) + avail_high = avail_ram; + if (avail_ram > end_avail) { + printf("oops... out of memory\n"); + pause(); + } + return p; +} + +void zfree(void *x, void *addr, unsigned nb) +{ + struct memchunk *mp = addr; + + nb = _ALIGN(nb, sizeof(struct memchunk)); + heap_use -= nb; + if (avail_ram == addr + nb) { + avail_ram = addr; + return; + } + mp->size = nb; + mp->next = freechunks; + freechunks = mp; +} + +#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\r"); + 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\r"); + exit(); + } + + s.zalloc = zalloc; + s.zfree = zfree; + r = inflateInit2(&s, -MAX_WBITS); + if (r != Z_OK) { + printf("inflateInit2 returned %d\n\r", 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 msg: %s\n\r", r, s.msg); + exit(); + } + *lenp = s.next_out - (unsigned char *) dst; + inflateEnd(&s); +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/boot/mknote.c linuxppc64_2_4/arch/ppc64/boot/mknote.c --- linux-2.4.5-ac18/arch/ppc64/boot/mknote.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/boot/mknote.c Fri May 4 17:13:58 2001 @@ -0,0 +1,43 @@ +/* + * Copyright (C) Cort Dougan 1999. + * + * 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. + * + * Generate a note section as per the CHRP specification. + * + */ + +#include + +#define PL(x) printf("%c%c%c%c", ((x)>>24)&0xff, ((x)>>16)&0xff, ((x)>>8)&0xff, (x)&0xff ); + +int main(void) +{ +/* header */ + /* namesz */ + PL(strlen("PowerPC")+1); + /* descrsz */ + PL(6*4); + /* type */ + PL(0x1275); + /* name */ + printf("PowerPC"); printf("%c", 0); + +/* descriptor */ + /* real-mode */ + PL(0xffffffff); + /* real-base */ + PL(0x00c00000); + /* real-size */ + PL(0xffffffff); + /* virt-base */ + PL(0xffffffff); + /* virt-size */ + PL(0xffffffff); + /* load-base */ + PL(0x4000); + return 0; +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/boot/no_initrd.c linuxppc64_2_4/arch/ppc64/boot/no_initrd.c --- linux-2.4.5-ac18/arch/ppc64/boot/no_initrd.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/boot/no_initrd.c Fri May 4 17:13:58 2001 @@ -0,0 +1,2 @@ +char initrd_data[1]; +int initrd_len = 0; diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/boot/piggyback.c linuxppc64_2_4/arch/ppc64/boot/piggyback.c --- linux-2.4.5-ac18/arch/ppc64/boot/piggyback.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/boot/piggyback.c Fri May 4 17:13:58 2001 @@ -0,0 +1,74 @@ +/* + * Copyright 2001 IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include + +extern long ce_exec_config[]; + +int main(int argc, char *argv[]) +{ + int i, cnt, pos, len; + unsigned int cksum, val; + unsigned char *lp; + unsigned char buf[8192]; + if (argc != 2) + { + fprintf(stderr, "usage: %s name out-file\n", + argv[0]); + exit(1); + } + fprintf(stdout, "#\n"); + fprintf(stdout, "# Miscellaneous data structures:\n"); + fprintf(stdout, "# WARNING - this file is automatically generated!\n"); + fprintf(stdout, "#\n"); + fprintf(stdout, "\n"); + fprintf(stdout, "\t.data\n"); + fprintf(stdout, "\t.globl %s_data\n", argv[1]); + fprintf(stdout, "%s_data:\n", argv[1]); + pos = 0; + cksum = 0; + while ((len = read(0, buf, sizeof(buf))) > 0) + { + cnt = 0; + lp = (unsigned char *)buf; + len = (len + 3) & ~3; /* Round up to longwords */ + for (i = 0; i < len; i += 4) + { + if (cnt == 0) + { + fprintf(stdout, "\t.long\t"); + } + fprintf(stdout, "0x%02X%02X%02X%02X", lp[0], lp[1], lp[2], lp[3]); + val = *(unsigned long *)lp; + cksum ^= val; + lp += 4; + if (++cnt == 4) + { + cnt = 0; + fprintf(stdout, " # %x \n", pos+i-12); + fflush(stdout); + } else + { + fprintf(stdout, ","); + } + } + if (cnt) + { + fprintf(stdout, "0\n"); + } + pos += len; + } + fprintf(stdout, "\t.globl %s_len\n", argv[1]); + fprintf(stdout, "%s_len:\t.long\t0x%x\n", argv[1], pos); + fflush(stdout); + fclose(stdout); + fprintf(stderr, "cksum = %x\n", cksum); + exit(0); +} + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/boot/start.c linuxppc64_2_4/arch/ppc64/boot/start.c --- linux-2.4.5-ac18/arch/ppc64/boot/start.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/boot/start.c Fri May 4 17:13:58 2001 @@ -0,0 +1,654 @@ +/* + * 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 +#include +#include +#include + +#include + +int (*prom)(void *); + +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 chrpboot(int a1, int a2, void *prom); /* in main.c */ + +void printk(char *fmt, ...); + +void +start(int a1, int a2, void *promptr) +{ + prom = (int (*)(void *)) 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(); + + chrpboot(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(void) +{ + 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; +} + +void * +claim(unsigned int virt, unsigned int size, unsigned int align) +{ + struct prom_args { + char *service; + int nargs; + int nret; + unsigned int virt; + unsigned int size; + unsigned int align; + void *ret; + } args; + + args.service = "claim"; + args.nargs = 3; + args.nret = 1; + args.virt = virt; + args.size = size; + args.align = align; + (*prom)(&args); + return args.ret; +} + +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(void) +{ + 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(void) +{ + 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++; +} + + + +/* String functions lifted from lib/vsprintf.c and lib/ctype.c */ +unsigned char _ctype[] = { +_C,_C,_C,_C,_C,_C,_C,_C, /* 0-7 */ +_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C, /* 8-15 */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 16-23 */ +_C,_C,_C,_C,_C,_C,_C,_C, /* 24-31 */ +_S|_SP,_P,_P,_P,_P,_P,_P,_P, /* 32-39 */ +_P,_P,_P,_P,_P,_P,_P,_P, /* 40-47 */ +_D,_D,_D,_D,_D,_D,_D,_D, /* 48-55 */ +_D,_D,_P,_P,_P,_P,_P,_P, /* 56-63 */ +_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U, /* 64-71 */ +_U,_U,_U,_U,_U,_U,_U,_U, /* 72-79 */ +_U,_U,_U,_U,_U,_U,_U,_U, /* 80-87 */ +_U,_U,_U,_P,_P,_P,_P,_P, /* 88-95 */ +_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L, /* 96-103 */ +_L,_L,_L,_L,_L,_L,_L,_L, /* 104-111 */ +_L,_L,_L,_L,_L,_L,_L,_L, /* 112-119 */ +_L,_L,_L,_P,_P,_P,_P,_C, /* 120-127 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 128-143 */ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 144-159 */ +_S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 160-175 */ +_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 176-191 */ +_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U, /* 192-207 */ +_U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L, /* 208-223 */ +_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L, /* 224-239 */ +_L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L}; /* 240-255 */ + +size_t strnlen(const char * s, size_t count) +{ + const char *sc; + + for (sc = s; count-- && *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} + +unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base) +{ + unsigned long result = 0,value; + + if (!base) { + base = 10; + if (*cp == '0') { + base = 8; + cp++; + if ((*cp == 'x') && isxdigit(cp[1])) { + cp++; + base = 16; + } + } + } + while (isxdigit(*cp) && + (value = isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) { + result = result*base + value; + cp++; + } + if (endp) + *endp = (char *)cp; + return result; +} + +long simple_strtol(const char *cp,char **endp,unsigned int base) +{ + if(*cp=='-') + return -simple_strtoul(cp+1,endp,base); + return simple_strtoul(cp,endp,base); +} + +static int skip_atoi(const char **s) +{ + int i=0; + + while (isdigit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} + +#define ZEROPAD 1 /* pad with zero */ +#define SIGN 2 /* unsigned/signed long */ +#define PLUS 4 /* show plus */ +#define SPACE 8 /* space if plus */ +#define LEFT 16 /* left justified */ +#define SPECIAL 32 /* 0x */ +#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ + +static char * number(char * str, long long num, int base, int size, int precision, int type) +{ + char c,sign,tmp[66]; + const char *digits="0123456789abcdefghijklmnopqrstuvwxyz"; + int i; + + if (type & LARGE) + digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + if (type & LEFT) + type &= ~ZEROPAD; + if (base < 2 || base > 36) + return 0; + c = (type & ZEROPAD) ? '0' : ' '; + sign = 0; + if (type & SIGN) { + if (num < 0) { + sign = '-'; + num = -num; + size--; + } else if (type & PLUS) { + sign = '+'; + size--; + } else if (type & SPACE) { + sign = ' '; + size--; + } + } + if (type & SPECIAL) { + if (base == 16) + size -= 2; + else if (base == 8) + size--; + } + i = 0; + if (num == 0) + tmp[i++]='0'; + else while (num != 0) + tmp[i++] = digits[do_div(num,base)]; + if (i > precision) + precision = i; + size -= precision; + if (!(type&(ZEROPAD+LEFT))) + while(size-->0) + *str++ = ' '; + if (sign) + *str++ = sign; + if (type & SPECIAL) { + if (base==8) + *str++ = '0'; + else if (base==16) { + *str++ = '0'; + *str++ = digits[33]; + } + } + if (!(type & LEFT)) + while (size-- > 0) + *str++ = c; + while (i < precision--) + *str++ = '0'; + while (i-- > 0) + *str++ = tmp[i]; + while (size-- > 0) + *str++ = ' '; + return str; +} + +/* Forward decl. needed for IP address printing stuff... */ +int sprintf(char * buf, const char *fmt, ...); + +int vsprintf(char *buf, const char *fmt, va_list args) +{ + int len; + unsigned long long num; + int i, base; + char * str; + const char *s; + + int flags; /* flags to number() */ + + int field_width; /* width of output field */ + int precision; /* min. # of digits for integers; max + number of chars for from string */ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + /* 'z' support added 23/7/1999 S.H. */ + /* 'z' changed to 'Z' --davidm 1/25/99 */ + + + for (str=buf ; *fmt ; ++fmt) { + if (*fmt != '%') { + *str++ = *fmt; + continue; + } + + /* process flags */ + flags = 0; + repeat: + ++fmt; /* this also skips first '%' */ + switch (*fmt) { + case '-': flags |= LEFT; goto repeat; + case '+': flags |= PLUS; goto repeat; + case ' ': flags |= SPACE; goto repeat; + case '#': flags |= SPECIAL; goto repeat; + case '0': flags |= ZEROPAD; goto repeat; + } + + /* get field width */ + field_width = -1; + if (isdigit(*fmt)) + field_width = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + field_width = va_arg(args, int); + if (field_width < 0) { + field_width = -field_width; + flags |= LEFT; + } + } + + /* get the precision */ + precision = -1; + if (*fmt == '.') { + ++fmt; + if (isdigit(*fmt)) + precision = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + precision = va_arg(args, int); + } + if (precision < 0) + precision = 0; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt =='Z') { + qualifier = *fmt; + ++fmt; + } + + /* default base */ + base = 10; + + switch (*fmt) { + case 'c': + if (!(flags & LEFT)) + while (--field_width > 0) + *str++ = ' '; + *str++ = (unsigned char) va_arg(args, int); + while (--field_width > 0) + *str++ = ' '; + continue; + + case 's': + s = va_arg(args, char *); + if (!s) + s = ""; + + len = strnlen(s, precision); + + if (!(flags & LEFT)) + while (len < field_width--) + *str++ = ' '; + for (i = 0; i < len; ++i) + *str++ = *s++; + while (len < field_width--) + *str++ = ' '; + continue; + + case 'p': + if (field_width == -1) { + field_width = 2*sizeof(void *); + flags |= ZEROPAD; + } + str = number(str, + (unsigned long) va_arg(args, void *), 16, + field_width, precision, flags); + continue; + + + case 'n': + if (qualifier == 'l') { + long * ip = va_arg(args, long *); + *ip = (str - buf); + } else if (qualifier == 'Z') { + size_t * ip = va_arg(args, size_t *); + *ip = (str - buf); + } else { + int * ip = va_arg(args, int *); + *ip = (str - buf); + } + continue; + + case '%': + *str++ = '%'; + continue; + + /* integer number formats - set up the flags and "break" */ + case 'o': + base = 8; + break; + + case 'X': + flags |= LARGE; + case 'x': + base = 16; + break; + + case 'd': + case 'i': + flags |= SIGN; + case 'u': + break; + + default: + *str++ = '%'; + if (*fmt) + *str++ = *fmt; + else + --fmt; + continue; + } + if (qualifier == 'L') + num = va_arg(args, long long); + else if (qualifier == 'l') { + num = va_arg(args, unsigned long); + if (flags & SIGN) + num = (signed long) num; + } else if (qualifier == 'Z') { + num = va_arg(args, size_t); + } else if (qualifier == 'h') { + num = (unsigned short) va_arg(args, int); + if (flags & SIGN) + num = (signed short) num; + } else { + num = va_arg(args, unsigned int); + if (flags & SIGN) + num = (signed int) num; + } + str = number(str, num, base, field_width, precision, flags); + } + *str = '\0'; + return str-buf; +} + +int sprintf(char * buf, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i=vsprintf(buf,fmt,args); + va_end(args); + return i; +} + +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 -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/boot/zlib.c linuxppc64_2_4/arch/ppc64/boot/zlib.c --- linux-2.4.5-ac18/arch/ppc64/boot/zlib.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/boot/zlib.c Mon Jun 18 13:47:13 2001 @@ -0,0 +1,2170 @@ +/* + * 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 + * + 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 + + * + * + */ + +/*+++++*/ +/* 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 +#define zmemcpy memcpy +#define zmemzero(dest, len) memset(dest, 0, len) + +/* Diagnostic functions */ +#ifdef DEBUG_ZLIB +# include +# 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)<>=(j);k-=(j);} +/* output bytes */ +#define WAVAIL (qread?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} + +/* + * The IBM 150 firmware munges the data right after _etext[]. This + * protects it. -- Cort + */ +local uInt protect_mask[] = {0, 0, 0, 0, 0, 0, 0, 0, 0 ,0 ,0 ,0}; +/* 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)<>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 -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/boot/zlib.h linuxppc64_2_4/arch/ppc64/boot/zlib.h --- linux-2.4.5-ac18/arch/ppc64/boot/zlib.h Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/boot/zlib.h Mon Jun 18 13:47:13 2001 @@ -0,0 +1,432 @@ +/* */ + +/* + * 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 +#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<new(2**30) * 4; # 2**32 without going floating point + +# +# Parse the cmdline options +# +my $arg; +while (defined($arg = shift)) { + if ($arg eq '-objdump') { + $objdump = shift; + } elsif ($arg eq '-o') { + $outfile = shift; + } elsif ($arg eq '-h') { + usage(); + } elsif ($arg eq '-v') { + $verbose = 1; + } elsif ($arg eq '-dummy') { + $dummy = 1; + } elsif ($arg !~ m/^-/) { + unshift @ARGV, $arg; + last; + } else { + die "$me: unknown option $arg\n"; + } +} + +usage() unless ($#ARGV == 0 || $dummy); +$objfile = $ARGV[0]; # typically vmlinux + +if ($verbose) { + print "outfile=$outfile\n"; + print "objdump=$objdump\n"; + print "objfile=$objfile\n"; +} +( $dummy || -r $objfile ) or die "$me: cannot read $objfile: $!\n"; + + +if (!$dummy) { + # + # Read the section table from objdump -h into hash + # %sections. Each entry is [Name, Size, Addr, Name_Off, Sect_idx] and + # the hash is indexed by Name. + # + open(S, "$objdump -h $objfile 2>&1 |") or die "$me: cannot $objdump -h $objfile: $!\n"; + my $sline; + while (defined($sline = )) { + chomp($sline); + $sline =~ s/^\s*//; + next unless $sline =~ m/^[0-9]/; + my ($idx, $name, $size, $vma, $rest) = split(/\s+/, $sline, 5); + print "name=$name, size=$size, vma=$vma\n" if $verbose; + $sectlist[$nextsect] = [$name, $size, $vma, 0, 0]; + $sections{$name} = $sectlist[$nextsect++]; + } + close(S); + print "$me: processed $nextsect sections\n" if $verbose; + + # Sort by start addr for readability. This is not required. + @sectlist = sort { $a->[2] cmp $b->[2] } @sectlist; + for (my $i = 0; $i < $nextsect; $i++) { + $sectlist[$i]->[4] = $i; # Sect_Idx + } + + # + # Now read the symbol table from objdump -t + # + open(S, "$objdump -t $objfile 2>&1 |") or die "$me: cannot $objdump -t $objfile: $!\n"; + while (defined($sline = )) { + last if $sline =~ m/^SYMBOL/; + } + while (defined($sline = )) { + chomp($sline); + my ($addr, $foo, $type, $sect, $val, $name) = split(/\s+/, $sline); + # Now type might be empty. + if (!defined($name)) { + $name = $val; + $val = $sect; + $sect = $type; + $type = " "; + } + # Weed out symbols which are not useful. + next if !$name; + next if $sect eq ".rodata"; # TOC entries, I think. + next if $sect eq "*ABS*"; + next if $name =~ m/^LC?\.\./; + # print "$addr,$type,$sect,$val,$name\n" if $verbose; + my $sec_ref = $sections{$sect}; + $symbols[$nextsym++] = [$name, $sec_ref, $addr, 0]; + } + close(S); + print "$me: processed $nextsym symbols\n" if $verbose; + # Sort by addr for readability. This is not required. + @symbols = sort { $a->[2] cmp $b->[2] } @symbols; + + # + # Now that everything is sorted, we need to assign space for + # each string in the string table. + # + for (my $i = 0; $i < $nextsect; $i++) { + my $len = length($sectlist[$i]->[0]); + $sectlist[$i]->[3] = $strtablen; + $strtablen += $len+1; + } + for (my $i = 0; $i < $nextsym; $i++) { + my $len = length($symbols[$i]->[0]); + $symbols[$i]->[3] = $strtablen; + $strtablen += $len+1; + } +} + +if ($outfile) { + open(OUT, ">$outfile") or die "$me: cannot open $outfile for write: $!\n"; +} else { + open(OUT, ">&STDOUT"); +} + +if ($dummy) { + # Produce dummy output and exit. + print OUT "long __start___kallsyms = 0;\n"; + print OUT "long __stop___kallsyms = 0;\n"; + exit 0; +} + +print OUT "#include \n"; +print OUT "#include \n\n"; +print OUT "#define NUM_SECTIONS\t\t", int(keys %sections), "\n"; +print OUT "#define NUM_SYMBOLS\t\t", $nextsym, "\n"; +print OUT "#define NUM_STRING_CHARS\t", $strtablen, "\n"; +print OUT "#define FIRST_SECTION_ADDR\t0x", $sectlist[0]->[2], "UL /* section ", $sectlist[0]->[0], " */\n"; +print OUT "#define LAST_SECTION_ADDR\t0x", $sectlist[$nextsect-1]->[2], "UL + 0x", $sectlist[$nextsect-1]->[1], "UL /* section ", $sectlist[$nextsect-1]->[0], " */\n"; +print OUT <[2], "UL, 0x", $sectref->[1], ", ", $sectref->[3], "},\t /* [$i] ", $sectref->[0], " */\n"; +} + +print OUT <[1]; + print OUT "\t{SOFF(", $secref->[4], "), 0x", $symref->[2], "UL, ", $symref->[3], "}, /* ", $symref->[0], " */\n"; +} + +print OUT <[0], "\\0\"\t/* ", $sectref->[3], " */\n"; +} +for (my $i = 0; $i < $nextsym; $i++) { + my $symref = $symbols[$i]; + print OUT "\t\"", $symref->[0], "\\0\"\t/* ", $symref->[3], " */\n"; +} + + +print OUT <new(hex(substr($hexstr, 0, 8))); +# my $biglow = Math::BigInt->new(hex(substr($hexstr, 8, 8))); +# return $bighi * $shift32multiplier + $biglow; +#} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kdb/kdba_bp.c linuxppc64_2_4/arch/ppc64/kdb/kdba_bp.c --- linux-2.4.5-ac18/arch/ppc64/kdb/kdba_bp.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kdb/kdba_bp.c Mon Jun 4 10:24:01 2001 @@ -0,0 +1,725 @@ +/* + * Kernel Debugger Architecture Dependent Breakpoint Handling + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Scott Lurndal 1999/12/12 + * v1.0 restructuring. + * Keith Owens 2000/05/23 + * KDB v1.2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include "privinst.h" + +static char *kdba_rwtypes[] = { "Instruction(Register)", "Data Write", + "I/O", "Data Access"}; + +/* + * Table describing processor architecture hardware + * breakpoint registers. + */ + +kdbhard_bp_t kdb_hardbreaks[KDB_MAXHARDBPT]; + +/* + * kdba_db_trap + * + * Perform breakpoint processing upon entry to the + * processor debugger fault. Determine and print + * the active breakpoint. + * + * Parameters: + * ef Exception frame containing machine register state + * error Error number passed to kdb. + * Outputs: + * None. + * Returns: + * KDB_DB_BPT Standard instruction or data breakpoint encountered + * KDB_DB_SS Single Step fault ('ss' command or end of 'ssb' command) + * KDB_DB_SSB Single Step fault, caller should continue ('ssb' command) + * KDB_DB_SSBPT Single step over breakpoint + * KDB_DB_NOBPT No existing kdb breakpoint matches this debug exception + * Locking: + * None. + * Remarks: + * Yup, there be goto's here. + * + * If multiple processors receive debug exceptions simultaneously, + * one may be waiting at the kdb fence in kdb() while the user + * issues a 'bc' command to clear the breakpoint the processor + * which is waiting has already encountered. If this is the case, + * the debug registers will no longer match any entry in the + * breakpoint table, and we'll return the value KDB_DB_NOBPT. + * This can cause a panic in die_if_kernel(). It is safer to + * disable the breakpoint (bd), go until all processors are past + * the breakpoint then clear the breakpoint (bc). This code + * recognises a breakpoint even when disabled but not when it has + * been cleared. + * + * WARNING: This routine clears the debug state. It should be called + * once per debug and the result cached. + */ + +kdb_dbtrap_t +kdba_db_trap(kdb_eframe_t ef, int error_unused) +{ + kdb_machreg_t msr,trap; + int rw, reg; + int i; + kdb_dbtrap_t rv = KDB_DB_BPT; + kdb_bp_t *bp; + + msr = get_msr(); + trap = ef->trap; + if (KDB_DEBUG(BP)) + kdb_printf("kdb: msr 0x%lx trap 0x%lx\n", msr,trap); + if (msr & MSR_SE || ((trap & 0x700) || (trap & 0xd00))) + { + if (KDB_STATE(SSBPT)) { + if (KDB_DEBUG(BP)) + kdb_printf("ssbpt\n"); + KDB_STATE_CLEAR(SSBPT); + for(i=0,bp=kdb_breakpoints; + i < KDB_MAXBPT; + i++, bp++) { + if (KDB_DEBUG(BP)) + kdb_printf("bp 0x%p enabled %d delayed %d global %d cpu %d\n", + bp, bp->bp_enabled, bp->bp_delayed, bp->bp_global, bp->bp_cpu); + if (!bp->bp_enabled) + continue; + if (!bp->bp_global && bp->bp_cpu != smp_processor_id()) + continue; + if (KDB_DEBUG(BP)) + kdb_printf("bp for this cpu\n"); + if (bp->bp_delayed) { + bp->bp_delayed = 0; + if (KDB_DEBUG(BP)) + kdb_printf("kdba_installbp\n"); + kdba_installbp(ef, bp); + if (!KDB_STATE(DOING_SS)) { + set_msr(get_msr() & ~MSR_SE); + return(KDB_DB_SSBPT); + } + break; + } + } + if (i == KDB_MAXBPT) { + kdb_printf("kdb: Unable to find delayed breakpoint\n"); + } + if (!KDB_STATE(DOING_SS)) { + set_msr(get_msr() & ~MSR_SE); + return(KDB_DB_NOBPT); + } + /* FALLTHROUGH */ + } + + /* + * KDB_STATE_DOING_SS is set when the kernel debugger is using + * the processor trap flag to single-step a processor. If a + * single step trap occurs and this flag is clear, the SS trap + * will be ignored by KDB and the kernel will be allowed to deal + * with it as necessary (e.g. for ptrace). + */ + if (!KDB_STATE(DOING_SS)) + goto unknown; + + /* single step */ + rv = KDB_DB_SS; /* Indicate single step */ + if (KDB_STATE(DOING_SSB)) { + + kdb_id1(ef->nip); + rv = KDB_DB_SSB; /* Indicate ssb - dismiss immediately */ + } else { + /* + * Print current insn + */ + kdb_printf("SS trap at "); + kdb_symbol_print(ef->nip, NULL, KDB_SP_DEFAULT|KDB_SP_NEWLINE); + kdb_id1(ef->nip); + KDB_STATE_CLEAR(DOING_SS); + } + + if (rv != KDB_DB_SSB) + set_msr(get_msr() & ~MSR_SE); + } + if (rv > 0) + goto handled; + + goto handle; + + +handle: + + /* + * Determine which breakpoint was encountered. + */ + for(i=0, bp=kdb_breakpoints; ibp_free) + && (bp->bp_global || bp->bp_cpu == smp_processor_id()) + && (bp->bp_hard) + && (bp->bp_hard->bph_reg == reg)) { + /* + * Hit this breakpoint. + */ +// kdb_printf("%s breakpoint #%d at " kdb_bfd_vma_fmt "\n", + kdb_printf("%s breakpoint #%d at 0x%ld\n", + kdba_rwtypes[rw], + i, bp->bp_addr); + + /* + * For an instruction breakpoint, disassemble + * the current instruction. + */ + if (rw == 0) { + kdb_id1(ef->nip); + } + + goto handled; + } + } + +unknown: + rv = KDB_DB_NOBPT; /* Cause kdb() to return */ + +handled: + + + return rv; +} + +/* + * kdba_bp_trap + * + * Perform breakpoint processing upon entry to the + * processor breakpoint instruction fault. Determine and print + * the active breakpoint. + * + * Parameters: + * ef Exception frame containing machine register state + * error Error number passed to kdb. + * Outputs: + * None. + * Returns: + * 0 Standard instruction or data breakpoint encountered + * 1 Single Step fault ('ss' command) + * 2 Single Step fault, caller should continue ('ssb' command) + * 3 No existing kdb breakpoint matches this debug exception + * Locking: + * None. + * Remarks: + * + * If multiple processors receive debug exceptions simultaneously, + * one may be waiting at the kdb fence in kdb() while the user + * issues a 'bc' command to clear the breakpoint the processor which + * is waiting has already encountered. If this is the case, the + * debug registers will no longer match any entry in the breakpoint + * table, and we'll return the value '3'. This can cause a panic + * in die_if_kernel(). It is safer to disable the breakpoint (bd), + * 'go' until all processors are past the breakpoint then clear the + * breakpoint (bc). This code recognises a breakpoint even when + * disabled but not when it has been cleared. + * + * WARNING: This routine resets the eip. It should be called + * once per breakpoint and the result cached. + */ + +kdb_dbtrap_t +kdba_bp_trap(kdb_eframe_t ef, int error_unused) +{ + int i; + kdb_dbtrap_t rv; + kdb_bp_t *bp; + + /* + * Determine which breakpoint was encountered. + */ + if (KDB_DEBUG(BP)) + kdb_printf("kdba_bp_trap: eip=0x%lx (not adjusted) " + "msr=0x%lx trap=0x%lx ef=0x%p esp=0x%lx\n", + ef->nip, ef->msr, ef->trap, ef, ef->gpr[1]); + + rv = KDB_DB_NOBPT; /* Cause kdb() to return */ + + for(i=0, bp=kdb_breakpoints; ibp_free) + continue; + if (!bp->bp_global && bp->bp_cpu != smp_processor_id()) + continue; + if (bp->bp_addr == (ef->nip - bp->bp_adjust)) { + /* Hit this breakpoint. */ + ef->nip -= bp->bp_adjust; + kdb_printf("Instruction(i) breakpoint #%d at 0x%lx (adjusted)\n", + i, ef->nip); + kdb_id1(ef->nip); + rv = KDB_DB_BPT; + bp->bp_delay = 1; + break; + } + } + + return rv; +} + +/* + * kdba_handle_bp + * + * Handle an instruction-breakpoint trap. Called when re-installing + * an enabled breakpoint which has has the bp_delay bit set. + * + * Parameters: + * Returns: + * Locking: + * Remarks: + * + * Ok, we really need to: + * 1) Restore the original instruction byte + * 2) Single Step + * 3) Restore breakpoint instruction + * 4) Continue. + * + * + */ + +static void +kdba_handle_bp(kdb_eframe_t ef, kdb_bp_t *bp) +{ + if (!ef) { + kdb_printf("kdba_handle_bp: ef == NULL\n"); + return; + } + + if (KDB_DEBUG(BP)) + kdb_printf("ef->eip = 0x%lx\n", ef->nip); + + /* + * Setup single step + */ + kdba_setsinglestep(ef); + + /* KDB_STATE_SSBPT is set when the kernel debugger must single step + * a task in order to re-establish an instruction breakpoint which + * uses the instruction replacement mechanism. + */ + KDB_STATE_SET(SSBPT); + + /* + * Reset delay attribute + */ + bp->bp_delay = 0; + bp->bp_delayed = 1; +} + + +/* + * kdba_bptype + * + * Return a string describing type of breakpoint. + * + * Parameters: + * bph Pointer to hardware breakpoint description + * Outputs: + * None. + * Returns: + * Character string. + * Locking: + * None. + * Remarks: + */ + +char * +kdba_bptype(kdbhard_bp_t *bph) +{ + char *mode; + + mode = kdba_rwtypes[bph->bph_mode]; + + return mode; +} + +/* + * kdba_printbpreg + * + * Print register name assigned to breakpoint + * + * Parameters: + * bph Pointer hardware breakpoint structure + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + */ + +void +kdba_printbpreg(kdbhard_bp_t *bph) +{ + kdb_printf(" in dr%ld", bph->bph_reg); +} + +/* + * kdba_printbp + * + * Print string describing hardware breakpoint. + * + * Parameters: + * bph Pointer to hardware breakpoint description + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + */ + +void +kdba_printbp(kdb_bp_t *bp) +{ + kdb_printf("\n is enabled"); + if (bp->bp_hardtype) { + kdba_printbpreg(bp->bp_hard); + if (bp->bp_hard->bph_mode != 0) { + kdb_printf(" for %d bytes", + bp->bp_hard->bph_length+1); + } + } +} + +/* + * kdba_parsebp + * + * Parse architecture dependent portion of the + * breakpoint command. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic for failure + * Locking: + * None. + * Remarks: + * for Ia32 architure, data access, data write and + * I/O breakpoints are supported in addition to instruction + * breakpoints. + * + * {datar|dataw|io|inst} [length] + */ + +int +kdba_parsebp(int argc, const char **argv, int *nextargp, kdb_bp_t *bp) +{ + int nextarg = *nextargp; + int diag; + kdbhard_bp_t *bph = &bp->bp_template; + + bph->bph_mode = 0; /* Default to instruction breakpoint */ + bph->bph_length = 0; /* Length must be zero for insn bp */ + if ((argc + 1) != nextarg) { + if (strnicmp(argv[nextarg], "datar", sizeof("datar")) == 0) { + bph->bph_mode = 3; + } else if (strnicmp(argv[nextarg], "dataw", sizeof("dataw")) == 0) { + bph->bph_mode = 1; + } else if (strnicmp(argv[nextarg], "io", sizeof("io")) == 0) { + bph->bph_mode = 2; + } else if (strnicmp(argv[nextarg], "inst", sizeof("inst")) == 0) { + bph->bph_mode = 0; + } else { + return KDB_ARGCOUNT; + } + + bph->bph_length = 3; /* Default to 4 byte */ + + nextarg++; + + if ((argc + 1) != nextarg) { + unsigned long len; + + diag = kdbgetularg((char *)argv[nextarg], + &len); + if (diag) + return diag; + + + if ((len > 4) || (len == 3)) + return KDB_BADLENGTH; + + bph->bph_length = len; + bph->bph_length--; /* Normalize for debug register */ + nextarg++; + } + + if ((argc + 1) != nextarg) + return KDB_ARGCOUNT; + + /* + * Indicate to architecture independent level that + * a hardware register assignment is required to enable + * this breakpoint. + */ + + bph->bph_free = 0; + } else { + if (KDB_DEBUG(BP)) + kdb_printf("kdba_bp: no args, forcehw is %d\n", bp->bp_forcehw); + if (bp->bp_forcehw) { + /* + * We are forced to use a hardware register for this + * breakpoint because either the bph or bpha + * commands were used to establish this breakpoint. + */ + bph->bph_free = 0; + } else { + /* + * Indicate to architecture dependent level that + * the instruction replacement breakpoint technique + * should be used for this breakpoint. + */ + bph->bph_free = 1; + bp->bp_adjust = 1; /* software, int 3 is one byte */ + } + } + + *nextargp = nextarg; + return 0; +} + +/* + * kdba_allocbp + * + * Associate a hardware register with a breakpoint. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * A pointer to the allocated register kdbhard_bp_t structure for + * success, Null and a non-zero diagnostic for failure. + * Locking: + * None. + * Remarks: + */ + +kdbhard_bp_t * +kdba_allocbp(kdbhard_bp_t *bph, int *diagp) +{ + int i; + kdbhard_bp_t *newbph; + + for(i=0,newbph=kdb_hardbreaks; i < KDB_MAXHARDBPT; i++, newbph++) { + if (newbph->bph_free) { + break; + } + } + + if (i == KDB_MAXHARDBPT) { + *diagp = KDB_TOOMANYDBREGS; + return NULL; + } + + *diagp = 0; + + /* + * Copy data from template. Can't just copy the entire template + * here because the register number in kdb_hardbreaks must be + * preserved. + */ + newbph->bph_data = bph->bph_data; + newbph->bph_write = bph->bph_write; + newbph->bph_mode = bph->bph_mode; + newbph->bph_length = bph->bph_length; + + /* + * Mark entry allocated. + */ + newbph->bph_free = 0; + + return newbph; +} + +/* + * kdba_freebp + * + * Deallocate a hardware breakpoint + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic for failure + * Locking: + * None. + * Remarks: + */ + +void +kdba_freebp(kdbhard_bp_t *bph) +{ + bph->bph_free = 1; +} + +/* + * kdba_initbp + * + * Initialize the breakpoint table for the hardware breakpoint + * register. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * Zero for success, a kdb diagnostic for failure + * Locking: + * None. + * Remarks: + * + * There is one entry per register. On the ia32 architecture + * all the registers are interchangeable, so no special allocation + * criteria are required. + */ + +void +kdba_initbp(void) +{ + int i; + kdbhard_bp_t *bph; + + /* + * Clear the hardware breakpoint table + */ + + memset(kdb_hardbreaks, '\0', sizeof(kdb_hardbreaks)); + + for(i=0,bph=kdb_hardbreaks; ibph_reg = i; + bph->bph_free = 1; + } +} + +/* + * kdba_installbp + * + * Install a breakpoint + * + * Parameters: + * ef Exception frame + * bp Breakpoint structure for the breakpoint to be installed + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * For hardware breakpoints, a debug register is allocated + * and assigned to the breakpoint. If no debug register is + * available, a warning message is printed and the breakpoint + * is disabled. + * + * For instruction replacement breakpoints, we must single-step + * over the replaced instruction at this point so we can re-install + * the breakpoint instruction after the single-step. + */ + +void +kdba_installbp(kdb_eframe_t ef, kdb_bp_t *bp) +{ + /* + * Install the breakpoint, if it is not already installed. + */ + + if (KDB_DEBUG(BP)) { + kdb_printf("kdba_installbp bp_installed %d\n", bp->bp_installed); + } + if (!bp->bp_installed) { + if (bp->bp_hardtype) { + //kdba_installdbreg(bp); + bp->bp_installed = 1; + if (KDB_DEBUG(BP)) { + kdb_printf("kdba_installbp hardware reg %ld at " kdb_bfd_vma_fmt "\n", + bp->bp_hard->bph_reg, bp->bp_addr); + } + } else if (bp->bp_delay) { + if (KDB_DEBUG(BP)) + kdb_printf("kdba_installbp delayed bp\n"); + kdba_handle_bp(ef, bp); + } else { + bp->bp_inst = kdba_getword(bp->bp_addr, 1); + kdba_putword(bp->bp_addr, 1, 0x7fe00008); + bp->bp_instvalid = 1; + if (KDB_DEBUG(BP)) + kdb_printf("kdba_installbp instruction 0x%x at " kdb_bfd_vma_fmt "\n", + 0x7fe00008, bp->bp_addr); + bp->bp_installed = 1; + } + } +} + +/* + * kdba_removebp + * + * Make a breakpoint ineffective. + * + * Parameters: + * None. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + */ + +void +kdba_removebp(kdb_bp_t *bp) +{ + /* + * For hardware breakpoints, remove it from the active register, + * for software breakpoints, restore the instruction stream. + */ + if (KDB_DEBUG(BP)) { + kdb_printf("kdba_removebp bp_installed %d\n", bp->bp_installed); + } + if (bp->bp_installed) { + if (bp->bp_hardtype) { + if (KDB_DEBUG(BP)) { + kdb_printf("kdb: removing hardware reg %ld at " kdb_bfd_vma_fmt "\n", + bp->bp_hard->bph_reg, bp->bp_addr); + } +// kdba_removedbreg(bp); + } else if (bp->bp_instvalid) { + if (KDB_DEBUG(BP)) + kdb_printf("kdb: restoring instruction 0x%x at " kdb_bfd_vma_fmt "\n", + bp->bp_inst, bp->bp_addr); + kdba_putword(bp->bp_addr, 1, bp->bp_inst); + bp->bp_instvalid = 0; + } + bp->bp_installed = 0; + } +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kdb/kdba_bt.c linuxppc64_2_4/arch/ppc64/kdb/kdba_bt.c --- linux-2.4.5-ac18/arch/ppc64/kdb/kdba_bt.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kdb/kdba_bt.c Mon Jun 4 10:24:01 2001 @@ -0,0 +1,263 @@ +/* + * Minimalist Kernel Debugger - Architecture Dependent Stack Traceback + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Srinivasa Thirumalachar + * RSE support for ia64 + * Masahiro Adegawa 1999/12/01 + * 'sr' command, active flag in 'ps' + * Scott Lurndal 1999/12/12 + * Significantly restructure for linux2.3 + * Keith Owens 2000/05/23 + * KDB v1.2 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include /* for STACK_FRAME_OVERHEAD */ +#include +#include "privinst.h" + +void systemreset(struct pt_regs *regs) +{ + udbg_printf("Oh no!\n"); + kdb(KDB_REASON_OOPS, 0, (kdb_eframe_t) regs); + for (;;); +} + +/* Copy a block of memory using kdba_getword(). + * This is not efficient. + */ +static void kdba_getmem(unsigned long addr, void *p, int size) +{ + unsigned char *dst = (unsigned char *)p; + while (size > 0) { + *dst++ = kdba_getword(addr++, 1); + size--; + } +} + + +/* + * kdba_bt_stack_ppc + * + * kdba_bt_stack with ppc specific parameters. + * Specification as kdba_bt_stack plus :- + * + * Inputs: + * As kba_bt_stack plus + * regs_esp If 1 get esp from the registers (exception frame), if 0 + * get esp from kdba_getregcontents. + */ + +static int +kdba_bt_stack_ppc(struct pt_regs *regs, kdb_machreg_t *addr, int argcount, + struct task_struct *p, int regs_esp) +{ + kdb_machreg_t esp,eip,ebp; + kdb_symtab_t symtab, *sym; + kdbtbtable_t tbtab; + /* declare these as raw ptrs so we don't get func descriptors */ + extern void *ret_from_except, *ret_from_syscall_1, *do_bottom_half_ret; + + /* + * The caller may have supplied an address at which the + * stack traceback operation should begin. This address + * is assumed by this code to point to a return-address + * on the stack to be traced back. + * + * The end result of this will make it appear as if a function + * entitled '' was called from the function which + * contains return-address. + */ + if (addr) { + eip = 0; + esp = *addr; + ebp=0; + } else { + ebp=regs->link; + eip = regs->nip; + if (regs_esp) + esp = regs->gpr[1]; + else + kdba_getregcontents("esp", regs, &esp); + } + + kdb_printf(" SP PC Function(args)\n"); + + /* (Ref: 64-bit PowerPC ELF ABI Spplement; Ian Lance Taylor, Zembu Labs). + A PPC stack frame looks like this: + + High Address + Back Chain + FP reg save area + GP reg save area + Local var space + Parameter save area (SP+48) + TOC save area (SP+40) + link editor doubleword (SP+32) + compiler doubleword (SP+24) + LR save (SP+16) + CR save (SP+8) + Back Chain (SP+0) + + Note that the LR (ret addr) may not be saved in the *current* frame if + no functions have been called from the current function. + */ + + /* + * Run through the activation records and print them. + */ + while (1) { + kdbnearsym(eip, &symtab); + kdb_printf("0x%016lx 0x%016lx ", esp, eip); + kdba_find_tb_table(eip, &tbtab); + sym = symtab.sym_name ? &symtab : &tbtab.symtab; /* use fake symtab if necessary */ + if (sym->sym_name) { + kdb_printf("%s", sym->sym_name); + if (eip - sym->sym_start > 0) + kdb_printf("+0x%lx", eip - sym->sym_start); + } + kdb_printf("\n"); + if (eip == (kdb_machreg_t)ret_from_except || + eip == (kdb_machreg_t)ret_from_syscall_1 || + eip == (kdb_machreg_t)do_bottom_half_ret) { + /* pull exception regs from the stack */ + struct pt_regs eregs; + kdba_getmem(esp+STACK_FRAME_OVERHEAD, &eregs, sizeof(eregs)); + kdb_printf(" [exception: %lx regs 0x%lx]\n", eregs.trap, esp+STACK_FRAME_OVERHEAD); + esp = eregs.gpr[1]; + eip = eregs.nip; + if (!esp) + break; + } else { + esp = kdba_getword(esp, 8); + if (!esp) + break; + eip = kdba_getword(esp+16, 8); /* saved lr */ + } + } + + return 0; +} + +/* + * kdba_bt_stack + * + * This function implements the 'bt' command. Print a stack + * traceback. + * + * bt [] (addr-exp is for alternate stacks) + * btp (Kernel stack for ) + * + * address expression refers to a return address on the stack. It + * may be preceeded by a frame pointer. + * + * Inputs: + * regs registers at time kdb was entered. + * addr Pointer to Address provided to 'bt' command, if any. + * argcount + * p Pointer to task for 'btp' command. + * Outputs: + * None. + * Returns: + * zero for success, a kdb diagnostic if error + * Locking: + * none. + * Remarks: + * mds comes in handy when examining the stack to do a manual + * traceback. + */ + +int +kdba_bt_stack(struct pt_regs *regs, kdb_machreg_t *addr, int argcount, + struct task_struct *p) +{ + return(kdba_bt_stack_ppc(regs, addr, argcount, p, 0)); +} + +int +kdba_bt_process(struct task_struct *p, int argcount) +{ + struct pt_regs taskregs; + + memset(&taskregs, 0, sizeof(taskregs)); + if (p->thread.regs == NULL) + { + struct pt_regs regs; + asm volatile ("std 0,0(%0)\n\ + std 1,8(%0)\n\ + std 2,16(%0)\n\ + std 3,24(%0)\n\ + std 4,32(%0)\n\ + std 5,40(%0)\n\ + std 6,48(%0)\n\ + std 7,56(%0)\n\ + std 8,64(%0)\n\ + std 9,72(%0)\n\ + std 10,80(%0)\n\ + std 11,88(%0)\n\ + std 12,96(%0)\n\ + std 13,104(%0)\n\ + std 14,112(%0)\n\ + std 15,120(%0)\n\ + std 16,128(%0)\n\ + std 17,136(%0)\n\ + std 18,144(%0)\n\ + std 19,152(%0)\n\ + std 20,160(%0)\n\ + std 21,168(%0)\n\ + std 22,176(%0)\n\ + std 23,184(%0)\n\ + std 24,192(%0)\n\ + std 25,200(%0)\n\ + std 26,208(%0)\n\ + std 27,216(%0)\n\ + std 28,224(%0)\n\ + std 29,232(%0)\n\ + std 30,240(%0)\n\ + std 31,248(%0)" : : "b" (®s)); + regs.nip = regs.link = ((unsigned long *)regs.gpr[1])[1]; + regs.msr = get_msr(); + regs.ctr = get_ctr(); + regs.xer = get_xer(); + regs.ccr = get_cr(); + regs.trap = 0; + taskregs = regs; + } + else + { + taskregs.nip = p->thread.regs->nip; + taskregs.gpr[1] = p->thread.regs->gpr[1]; + taskregs.link = p->thread.regs->link; + + } + kdb_printf("taskregs.gpr[1]=%lx\n", taskregs.gpr[1]); + if (taskregs.gpr[1] < (unsigned long)p || + taskregs.gpr[1] >= (unsigned long)p + 8192) { + kdb_printf("Stack is not in task_struct, backtrace not available\n"); + return(0); + } + kdb_printf("About to backtrace process!\n"); + return kdba_bt_stack_ppc(&taskregs, NULL, argcount, p, 1); + +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kdb/kdba_id.c linuxppc64_2_4/arch/ppc64/kdb/kdba_id.c --- linux-2.4.5-ac18/arch/ppc64/kdb/kdba_id.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kdb/kdba_id.c Mon Jun 4 10:24:01 2001 @@ -0,0 +1,276 @@ +/* + * Minimalist Kernel Debugger - Architecture Dependent Instruction Disassembly + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Srinivasa Thirumalachar + * RSE support for ia64 + * Masahiro Adegawa 1999/12/01 + * 'sr' command, active flag in 'ps' + * Scott Lurndal 1999/12/12 + * Significantly restructure for linux2.3 + * Keith Owens 2000/05/23 + * KDB v1.2 + * + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * kdba_dis_getsym + * + * Get a symbol for the disassembler. + * + * Parameters: + * addr Address for which to get symbol + * dip Pointer to disassemble_info + * Returns: + * 0 + * Locking: + * Remarks: + * Not used for kdb. + */ + +/* ARGSUSED */ +static int +kdba_dis_getsym(bfd_vma addr, disassemble_info *dip) +{ + + return 0; +} + +/* + * kdba_printaddress + * + * Print (symbolically) an address. + * + * Parameters: + * addr Address for which to get symbol + * dip Pointer to disassemble_info + * flag True if a ":" sequence should follow the address + * Returns: + * number of chars printed + * Locking: + * Remarks: + * + */ + +/* ARGSUSED */ +void +kdba_printaddress(kdb_machreg_t addr, disassemble_info *dip, int flag) +{ + kdb_symtab_t symtab; + + /* + * Print a symbol name or address as necessary. + */ + kdbnearsym(addr, &symtab); + if (symtab.sym_name) { + /* Do not use kdb_symbol_print here, it always does + * kdb_printf but we want dip->fprintf_func. + */ + dip->fprintf_func(dip->stream, + "0x%0*lx %s", + 2*sizeof(addr), addr, symtab.sym_name); + /* Add offset if needed. Pad output with blanks to get + * consistent size symbols for disassembly listings. + */ + if (addr == symtab.sym_start) { + if (!flag) + dip->fprintf_func(dip->stream, " "); + } else { + int len, i; + char buf[20]; + sprintf(buf, "%lx", addr - symtab.sym_start); + dip->fprintf_func(dip->stream, "+0x%s", buf); + if (!flag) { + len = strlen(buf); + for (i = len; i < 6; i++) + dip->fprintf_func(dip->stream, " "); + } + } + + } else { + dip->fprintf_func(dip->stream, "0x%0*lx", 2*sizeof(addr), addr); + } + + if (flag) + dip->fprintf_func(dip->stream, ": "); +} + +/* + * kdba_dis_printaddr + * + * Print (symbolically) an address. Called by GNU disassembly + * code via disassemble_info structure. + * + * Parameters: + * addr Address for which to get symbol + * dip Pointer to disassemble_info + * Returns: + * number of chars printed. + * Locking: + * Remarks: + * This function will never append ":" to the printed + * symbolic address. + */ + +static void +kdba_dis_printaddr(bfd_vma addr, disassemble_info *dip) +{ + return kdba_printaddress(addr, dip, 0); +} + +/* + * kdba_dis_getmem + * + * Fetch 'length' bytes from 'addr' into 'buf'. + * + * Parameters: + * addr Address for which to get symbol + * buf Address of buffer to fill with bytes from 'addr' + * length Number of bytes to fetch + * dip Pointer to disassemble_info + * Returns: + * 0 + * Locking: + * Remarks: + * + */ + +/* ARGSUSED */ +static int +kdba_dis_getmem(bfd_vma addr, bfd_byte *buf, unsigned int length, disassemble_info *dip) +{ + bfd_byte *bp = buf; + int i; + + /* + * Fill the provided buffer with bytes from + * memory, starting at address 'addr' for 'length bytes. + * + */ + + for(i=0; iread_memory_func = kdba_dis_getmem; + dip->print_address_func = kdba_dis_printaddr; + dip->symbol_at_address_func = kdba_dis_getsym; + + dip->flavour = bfd_target_elf_flavour; + dip->arch = bfd_arch_powerpc; + dip->mach = bfd_mach_ppc_750; + dip->endian = BFD_ENDIAN_BIG; + + dip->display_endian = BFD_ENDIAN_BIG; +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kdb/kdba_io.c linuxppc64_2_4/arch/ppc64/kdb/kdba_io.c --- linux-2.4.5-ac18/arch/ppc64/kdb/kdba_io.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kdb/kdba_io.c Mon Jun 4 10:24:01 2001 @@ -0,0 +1,162 @@ +/* + * Kernel Debugger Console I/O handler + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Chuck Fleckenstein 1999/07/20 + * Move kdb_info struct declaration to this file + * for cases where serial support is not compiled into + * the kernel. + * + * Masahiro Adegawa 1999/07/20 + * Handle some peculiarities of japanese 86/106 + * keyboards. + * + * marc@mucom.co.il 1999/07/20 + * Catch buffer overflow for serial input. + * + * Scott Foehner + * Port to ia64 + * + * Scott Lurndal 2000/01/03 + * Restructure for v1.0 + * + * Keith Owens 2000/05/23 + * KDB v1.2 + * + * Andi Kleen 2000/03/19 + * Support simultaneous input from serial line and keyboard. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#undef FILE +#include "nonstdio.h" + +int kdb_port; + +/* + * This module contains code to read characters from the keyboard or a serial + * port. + * + * It is used by the kernel debugger, and is polled, not interrupt driven. + * + */ + + +void +kdb_resetkeyboard(void) +{ +#if 0 + kdb_kbdsend(KBD_CMD_ENABLE); +#endif +} + +int inchar(void); + +#if defined(CONFIG_VT) +/* + * Check if the keyboard controller has a keypress for us. + * Some parts (Enter Release, LED change) are still blocking polled here, + * but hopefully they are all short. + */ +static int get_kbd_char(void) +{ + int keychar; + keychar = inchar(); + if (keychar == '\n') + { + kdb_printf("\n"); + } + /* + * echo the character. + */ + kdb_printf("%c", keychar); + + return keychar ; +} +#endif /* CONFIG_VT */ + + +typedef int (*get_char_func)(void); + +static get_char_func poll_funcs[] = { +#if defined(CONFIG_VT) + get_kbd_char, +#endif + NULL +}; +void flush_input(void); +char * +kdba_read(char *buffer, size_t bufsize) +{ + char *cp = buffer; + char *bufend = buffer+bufsize-2; /* Reserve space for newline and null byte */ + flush_input(); + for (;;) { + int key; + get_char_func *f; + for (f = &poll_funcs[0]; ; ++f) { + if (*f == NULL) + f = &poll_funcs[0]; + key = (*f)(); + if (key != -1) + break; + } + + /* Echo is done in the low level functions */ + switch (key) { + case '\b': /* backspace */ + if (cp > buffer) { + xmon_printf("\b \b"); + --cp; + } + break; + case '\n': /* enter */ + *cp++ = '\n'; + *cp++ = '\0'; + return buffer; + default: + if (cp < bufend) + *cp++ = key; + break; + } + } +} +static char line[256]; +static char *lineptr; +void flush_input(void) +{ + lineptr = NULL; +} +int inchar(void) +{ + if (lineptr == NULL || *lineptr == 0) + { + if (xmon_fgets(line, sizeof(line), xmon_stdin) == NULL) + { + lineptr = NULL; + return EOF; + } + lineptr = line; + } + return *lineptr++; +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kdb/kdbasupport.c linuxppc64_2_4/arch/ppc64/kdb/kdbasupport.c --- linux-2.4.5-ac18/arch/ppc64/kdb/kdbasupport.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kdb/kdbasupport.c Mon Jun 4 14:38:32 2001 @@ -0,0 +1,1140 @@ +/* + * Kernel Debugger Architecture Independent Support Functions + * + * Copyright (C) 1999 Silicon Graphics, Inc. + * Copyright (C) Scott Lurndal (slurn@engr.sgi.com) + * Copyright (C) Scott Foehner (sfoehner@engr.sgi.com) + * Copyright (C) Srinivasa Thirumalachar (sprasad@engr.sgi.com) + * + * See the file LIA-COPYRIGHT for additional information. + * + * Written March 1999 by Scott Lurndal at Silicon Graphics, Inc. + * + * Modifications from: + * Richard Bass 1999/07/20 + * Many bug fixes and enhancements. + * Scott Foehner + * Port to ia64 + * Scott Lurndal 1999/12/12 + * v1.0 restructuring. + * Keith Owens 2000/05/23 + * KDB v1.2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "privinst.h" +#include + +char *kdb_diemsg; + +/* + * kdba_prologue + * + * This function analyzes a gcc-generated function prototype + * with or without frame pointers to determine the amount of + * automatic storage and register save storage is used on the + * stack of the target function. It only counts instructions + * that have been executed up to but excluding the current eip. + * Inputs: + * code Start address of function code to analyze + * pc Current program counter within function + * sp Current stack pointer for function + * fp Current frame pointer for function, may not be valid + * ss Start of stack for current process. + * caller 1 if looking for data on the caller frame, 0 for callee. + * Outputs: + * ar Activation record, all fields may be set. fp and oldfp + * are 0 if they cannot be extracted. return is 0 if the + * code cannot find a valid return address. args and arg0 + * are 0 if the number of arguments cannot be safely + * calculated. + * Returns: + * 1 if prologue is valid, 0 otherwise. If pc is 0 treat it as a + * valid prologue to allow bt on wild branches. + * Locking: + * None. + * Remarks: + * + */ +int +kdba_prologue(const kdb_symtab_t *symtab, kdb_machreg_t pc, kdb_machreg_t sp, + kdb_machreg_t fp, kdb_machreg_t ss, int caller, kdb_ar_t *ar) +{ + /* We don't currently use kdb's generic activation record scanning + * code to handle backtrace. + */ + return 0; +} + + + +/* + * kdba_getregcontents + * + * Return the contents of the register specified by the + * input string argument. Return an error if the string + * does not match a machine register. + * + * The following pseudo register names are supported: + * ®s - Prints address of exception frame + * kesp - Prints kernel stack pointer at time of fault + * cesp - Prints current kernel stack pointer, inside kdb + * ceflags - Prints current flags, inside kdb + * % - Uses the value of the registers at the + * last time the user process entered kernel + * mode, instead of the registers at the time + * kdb was entered. + * + * Parameters: + * regname Pointer to string naming register + * regs Pointer to structure containing registers. + * Outputs: + * *contents Pointer to unsigned long to recieve register contents + * Returns: + * 0 Success + * KDB_BADREG Invalid register name + * Locking: + * None. + * Remarks: + * If kdb was entered via an interrupt from the kernel itself then + * ss and esp are *not* on the stack. + */ + +static struct kdbregs { + char *reg_name; + size_t reg_offset; +} kdbreglist[] = { + { "gpr0", offsetof(struct pt_regs, gpr[0]) }, + { "gpr1", offsetof(struct pt_regs, gpr[1]) }, + { "gpr2", offsetof(struct pt_regs, gpr[2]) }, + { "gpr3", offsetof(struct pt_regs, gpr[3]) }, + { "gpr4", offsetof(struct pt_regs, gpr[4]) }, + { "gpr5", offsetof(struct pt_regs, gpr[5]) }, + { "gpr6", offsetof(struct pt_regs, gpr[6]) }, + { "gpr7", offsetof(struct pt_regs, gpr[7]) }, + { "gpr8", offsetof(struct pt_regs, gpr[8]) }, + { "gpr9", offsetof(struct pt_regs, gpr[9]) }, + { "gpr10", offsetof(struct pt_regs, gpr[10]) }, + { "gpr11", offsetof(struct pt_regs, gpr[11]) }, + { "gpr12", offsetof(struct pt_regs, gpr[12]) }, + { "gpr13", offsetof(struct pt_regs, gpr[13]) }, + { "gpr14", offsetof(struct pt_regs, gpr[14]) }, + { "gpr15", offsetof(struct pt_regs, gpr[15]) }, + { "gpr16", offsetof(struct pt_regs, gpr[16]) }, + { "gpr17", offsetof(struct pt_regs, gpr[17]) }, + { "gpr18", offsetof(struct pt_regs, gpr[18]) }, + { "gpr19", offsetof(struct pt_regs, gpr[19]) }, + { "gpr20", offsetof(struct pt_regs, gpr[20]) }, + { "gpr21", offsetof(struct pt_regs, gpr[21]) }, + { "gpr22", offsetof(struct pt_regs, gpr[22]) }, + { "gpr23", offsetof(struct pt_regs, gpr[23]) }, + { "gpr24", offsetof(struct pt_regs, gpr[24]) }, + { "gpr25", offsetof(struct pt_regs, gpr[25]) }, + { "gpr26", offsetof(struct pt_regs, gpr[26]) }, + { "gpr27", offsetof(struct pt_regs, gpr[27]) }, + { "gpr28", offsetof(struct pt_regs, gpr[28]) }, + { "gpr29", offsetof(struct pt_regs, gpr[29]) }, + { "gpr30", offsetof(struct pt_regs, gpr[30]) }, + { "gpr31", offsetof(struct pt_regs, gpr[31]) }, + { "eip", offsetof(struct pt_regs, nip) }, + { "msr", offsetof(struct pt_regs, msr) }, + { "esp", offsetof(struct pt_regs, gpr[1]) }, + { "orig_gpr3", offsetof(struct pt_regs, orig_gpr3) }, + { "ctr", offsetof(struct pt_regs, ctr) }, + { "link", offsetof(struct pt_regs, link) }, + { "xer", offsetof(struct pt_regs, xer) }, + { "ccr", offsetof(struct pt_regs, ccr) }, + { "mq", offsetof(struct pt_regs, mq) }, + { "trap", offsetof(struct pt_regs, trap) }, + { "dar", offsetof(struct pt_regs, dar) }, + { "dsisr", offsetof(struct pt_regs, dsisr) }, + { "result", offsetof(struct pt_regs, result) }, + +}; + +static const int nkdbreglist = sizeof(kdbreglist) / sizeof(struct kdbregs); + +unsigned long +getsp(void) +{ + unsigned long x; + asm("mr %0,1" : "=r" (x):); + return x; +} +int +kdba_getregcontents(const char *regname, + struct pt_regs *regs, + kdb_machreg_t *contents) +{ + int i; + + if (strcmp(regname, "®s") == 0) { + *contents = (unsigned long)regs; + return 0; + } + + if (strcmp(regname, "kesp") == 0) { + *contents = (unsigned long) current->thread.ksp; + return 0; + } + + if (strcmp(regname, "cesp") == 0) { + *contents = getsp(); + return 0; + } + + if (strcmp(regname, "ceflags") == 0) { + int flags; + save_flags(flags); + *contents = flags; + return 0; + } + + if (regname[0] == '%') { + /* User registers: %%e[a-c]x, etc */ + regname++; + regs = (struct pt_regs *) + (current->thread.ksp - sizeof(struct pt_regs)); + } + + for (i=0; i + * + * Parameters: + * regname Pointer to string naming register + * regs Pointer to structure containing registers. + * contents Unsigned long containing new register contents + * Outputs: + * Returns: + * 0 Success + * KDB_BADREG Invalid register name + * Locking: + * None. + * Remarks: + */ + +int +kdba_setregcontents(const char *regname, + struct pt_regs *regs, + unsigned long contents) +{ + int i; + + if (regname[0] == '%') { + regname++; + regs = (struct pt_regs *) + (current->thread.ksp - sizeof(struct pt_regs)); + } + + for (i=0; ithread.ksp - sizeof(struct pt_regs)); + } + + if (type == NULL) { + struct kdbregs *rlp; + kdb_machreg_t contents; + + for (i=0, rlp=kdbreglist; ireg_name, regs, &contents); + kdb_printf("%-5s = 0x%p%c", rlp->reg_name, (void *)contents, (++count % 2) ? ' ' : '\n'); + } + + kdb_printf("®s = 0x%p\n", regs); + + return 0; + } + + switch (type[0]) { + case 'm': + break; + case 'r': + break; + default: + return KDB_BADREG; + } + + /* NOTREACHED */ + return 0; +} + +kdb_machreg_t +kdba_getpc(kdb_eframe_t ef) +{ + return ef->nip; +} + +int +kdba_setpc(kdb_eframe_t ef, kdb_machreg_t newpc) +{ + ef->nip = newpc; + KDB_STATE_SET(IP_ADJUSTED); + return 0; +} + +/* + * kdba_main_loop + * + * Do any architecture specific set up before entering the main kdb loop. + * The primary function of this routine is to make all processes look the + * same to kdb, kdb must be able to list a process without worrying if the + * process is running or blocked, so make all process look as though they + * are blocked. + * + * Inputs: + * reason The reason KDB was invoked + * error The hardware-defined error code + * error2 kdb's current reason code. Initially error but can change + * acording to kdb state. + * db_result Result from break or debug point. + * ef The exception frame at time of fault/breakpoint. If reason + * is KDB_REASON_SILENT then ef is NULL, otherwise it should + * always be valid. + * Returns: + * 0 KDB was invoked for an event which it wasn't responsible + * 1 KDB handled the event for which it was invoked. + * Outputs: + * Sets eip and esp in current->thread. + * Locking: + * None. + * Remarks: + * none. + */ + +int +kdba_main_loop(kdb_reason_t reason, kdb_reason_t reason2, int error, + kdb_dbtrap_t db_result, kdb_eframe_t ef) +{ + int rv; + unsigned int msr; + if (current->thread.regs == NULL) + { + struct pt_regs regs; + asm volatile ("std 0,0(%0)\n\ + std 1,8(%0)\n\ + std 2,16(%0)\n\ + std 3,24(%0)\n\ + std 4,32(%0)\n\ + std 5,40(%0)\n\ + std 6,48(%0)\n\ + std 7,56(%0)\n\ + std 8,64(%0)\n\ + std 9,72(%0)\n\ + std 10,80(%0)\n\ + std 11,88(%0)\n\ + std 12,96(%0)\n\ + std 13,104(%0)\n\ + std 14,112(%0)\n\ + std 15,120(%0)\n\ + std 16,128(%0)\n\ + std 17,136(%0)\n\ + std 18,144(%0)\n\ + std 19,152(%0)\n\ + std 20,160(%0)\n\ + std 21,168(%0)\n\ + std 22,176(%0)\n\ + std 23,184(%0)\n\ + std 24,192(%0)\n\ + std 25,200(%0)\n\ + std 26,208(%0)\n\ + std 27,216(%0)\n\ + std 28,224(%0)\n\ + std 29,232(%0)\n\ + std 30,240(%0)\n\ + std 31,248(%0)" : : "b" (®s)); + /* Fetch the link reg for this stack frame. + NOTE: the prev kdb_printf fills in the lr. */ + regs.nip = regs.link = ((unsigned long *)regs.gpr[1])[2]; + regs.msr = get_msr(); + regs.ctr = get_ctr(); + regs.xer = get_xer(); + regs.ccr = get_cr(); + regs.trap = 0; + current->thread.regs = ®s; + } + if (ef) { + kdba_getregcontents("eip", ef, &(current->thread.regs->nip)); + kdba_getregcontents("esp", ef, &(current->thread.regs->gpr[1])); + + } + msr = get_msr(); + set_msr( msr & ~0x8000); + rv = kdb_main_loop(reason, reason2, error, db_result, ef); + set_msr(msr); + return rv; +} + +void +kdba_disableint(kdb_intstate_t *state) +{ + int *fp = (int *)state; + int flags; + + save_flags(flags); + cli(); + + *fp = flags; +} + +void +kdba_restoreint(kdb_intstate_t *state) +{ + int flags = *(int *)state; + restore_flags(flags); +} + +void +kdba_setsinglestep(struct pt_regs *regs) +{ + regs->msr |= MSR_SE; +} + +void +kdba_clearsinglestep(struct pt_regs *regs) +{ + + regs->msr &= ~MSR_SE; +} +int +kdb_getcurrentframe(struct pt_regs *regs) +{ + regs->gpr[1] = getsp(); + return 0; +} + + +#ifdef KDB_HAVE_LONGJMP +int kdb_setjmp(kdb_jmp_buf *buf) +{ + asm volatile ( + "mflr 0; std 0,0(%0)\n\ + std 1,8(%0)\n\ + std 2,16(%0)\n\ + mfcr 0; std 0,24(%0)\n\ + std 13,32(%0)\n\ + std 14,40(%0)\n\ + std 15,48(%0)\n\ + std 16,56(%0)\n\ + std 17,64(%0)\n\ + std 18,72(%0)\n\ + std 19,80(%0)\n\ + std 20,88(%0)\n\ + std 21,96(%0)\n\ + std 22,104(%0)\n\ + std 23,112(%0)\n\ + std 24,120(%0)\n\ + std 25,128(%0)\n\ + std 26,136(%0)\n\ + std 27,144(%0)\n\ + std 28,152(%0)\n\ + std 29,160(%0)\n\ + std 30,168(%0)\n\ + std 31,176(%0)\n\ + " : : "r" (buf)); + KDB_STATE_SET(LONGJMP); + return 0; +} +void kdb_longjmp(kdb_jmp_buf *buf, int val) +{ + if (val == 0) + val = 1; + asm volatile ( + "ld 13,32(%0)\n\ + ld 14,40(%0)\n\ + ld 15,48(%0)\n\ + ld 16,56(%0)\n\ + ld 17,64(%0)\n\ + ld 18,72(%0)\n\ + ld 19,80(%0)\n\ + ld 20,88(%0)\n\ + ld 21,96(%0)\n\ + ld 22,104(%0)\n\ + ld 23,112(%0)\n\ + ld 24,120(%0)\n\ + ld 25,128(%0)\n\ + ld 26,136(%0)\n\ + ld 27,144(%0)\n\ + ld 28,152(%0)\n\ + ld 29,160(%0)\n\ + ld 30,168(%0)\n\ + ld 31,176(%0)\n\ + ld 0,24(%0)\n\ + mtcrf 0x38,0\n\ + ld 0,0(%0)\n\ + ld 1,8(%0)\n\ + ld 2,16(%0)\n\ + mtlr 0\n\ + mr 3,%1\n\ + " : : "r" (buf), "r" (val)); +} +#endif + +/* + * kdba_enable_mce + * + * This function is called once on each CPU to enable machine + * check exception handling. + * + * Inputs: + * None. + * Outputs: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * + */ + +void +kdba_enable_mce(void) +{ +} + +/* + * kdba_enable_lbr + * + * Enable last branch recording. + * + * Parameters: + * None. + * Returns: + * None + * Locking: + * None + * Remarks: + * None. + */ + +void +kdba_enable_lbr(void) +{ +} + +/* + * kdba_disable_lbr + * + * disable last branch recording. + * + * Parameters: + * None. + * Returns: + * None + * Locking: + * None + * Remarks: + * None. + */ + +void +kdba_disable_lbr(void) +{ +} + +/* + * kdba_print_lbr + * + * Print last branch and last exception addresses + * + * Parameters: + * None. + * Returns: + * None + * Locking: + * None + * Remarks: + * None. + */ + +void +kdba_print_lbr(void) +{ +} + +/* + * kdba_getword + * + * Architecture specific function to access kernel virtual + * address space. + * + * Parameters: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * None. + */ + +unsigned long +kdba_getword(unsigned long addr, size_t width) +{ + /* + * This function checks the address for validity. Any address + * in the range PAGE_OFFSET to high_memory is legal, any address + * which maps to a vmalloc region is legal, and any address which + * is a user address, we use get_user() to verify validity. + */ + + if (addr < PAGE_OFFSET) { + /* + * Usermode address. + */ + unsigned long diag; + unsigned long ulval; + + switch (width) { + case 8: + { unsigned long *lp; + + lp = (unsigned long *) addr; + diag = get_user(ulval, lp); + break; + } + case 4: + { unsigned int *ip; + + ip = (unsigned int *) addr; + diag = get_user(ulval, ip); + break; + } + case 2: + { unsigned short *sp; + + sp = (unsigned short *) addr; + diag = get_user(ulval, sp); + break; + } + case 1: + { unsigned char *cp; + + cp = (unsigned char *) addr; + diag = get_user(ulval, cp); + break; + } + default: + kdb_printf("kdbgetword: Bad width\n"); + return 0L; + } + + if (diag) { + if (!KDB_STATE(SUPPRESS)) { + kdb_printf("kdb: Bad user address 0x%lx\n", addr); + KDB_STATE_SET(SUPPRESS); + } + return 0L; + } + KDB_STATE_CLEAR(SUPPRESS); + return ulval; + } + +#if 0 + if (addr > (unsigned long)high_memory) { + if (!kdb_vmlist_check(addr, addr+width)) { + /* + * Would appear to be an illegal kernel address; + * Print a message once, and don't print again until + * a legal address is used. + */ + if (!KDB_STATE(SUPPRESS)) { + kdb_printf("kdb: Bad kernel address 0x%lx (above highmem 0x%lx)\n", addr, high_memory); + KDB_STATE_SET(SUPPRESS); + } + return 0L; + } + } +#endif + + /* + * A good address. Reset error flag. + */ + KDB_STATE_CLEAR(SUPPRESS); + + switch (width) { + case 8: + { unsigned long *lp; + + lp = (unsigned long *)(addr); + return *lp; + } + case 4: + { unsigned int *ip; + + ip = (unsigned int *)(addr); + return *ip; + } + case 2: + { unsigned short *sp; + + sp = (unsigned short *)(addr); + return *sp; + } + case 1: + { unsigned char *cp; + + cp = (unsigned char *)(addr); + return *cp; + } + } + + kdb_printf("kdbgetword: Bad width\n"); + return 0L; +} + + +/* + * kdba_putword + * + * Architecture specific function to access kernel virtual + * address space. + * + * Parameters: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * None. + */ + +unsigned long +kdba_putword(unsigned long addr, size_t size, unsigned long contents) +{ + /* + * This function checks the address for validity. Any address + * in the range PAGE_OFFSET to high_memory is legal, any address + * which maps to a vmalloc region is legal, and any address which + * is a user address, we use get_user() to verify validity. + */ + + if (addr < PAGE_OFFSET) { + /* + * Usermode address. + */ + unsigned long diag; + + switch (size) { + case 4: + { unsigned long *lp; + + lp = (unsigned long *) addr; + diag = put_user(contents, lp); + break; + } + case 2: + { unsigned short *sp; + + sp = (unsigned short *) addr; + diag = put_user(contents, sp); + break; + } + case 1: + { unsigned char *cp; + + cp = (unsigned char *) addr; + diag = put_user(contents, cp); + break; + } + default: + kdb_printf("kdba_putword: Bad width\n"); + return 0; + } + + if (diag) { + if (!KDB_STATE(SUPPRESS)) { + kdb_printf("kdb: Bad user address 0x%lx\n", addr); + KDB_STATE_SET(SUPPRESS); + } + return 0; + } + KDB_STATE_CLEAR(SUPPRESS); + return 0; + } + +#if 0 + if (addr > (unsigned long)high_memory) { + if (!kdb_vmlist_check(addr, addr+size)) { + /* + * Would appear to be an illegal kernel address; + * Print a message once, and don't print again until + * a legal address is used. + */ + if (!KDB_STATE(SUPPRESS)) { + kdb_printf("kdb: Bad kernel address 0x%lx\n", addr); + KDB_STATE_SET(SUPPRESS); + } + return 0L; + } + } +#endif + + /* + * A good address. Reset error flag. + */ + KDB_STATE_CLEAR(SUPPRESS); + + switch (size) { + case 4: + { unsigned long *lp; + + lp = (unsigned long *)(addr); + *lp = contents; + return 0; + } + case 2: + { unsigned short *sp; + + sp = (unsigned short *)(addr); + *sp = (unsigned short) contents; + return 0; + } + case 1: + { unsigned char *cp; + + cp = (unsigned char *)(addr); + *cp = (unsigned char) contents; + return 0; + } + } + + kdb_printf("kdba_putword: Bad width\n"); + return 0; +} + +/* + * kdba_callback_die + * + * Callback function for kernel 'die' function. + * + * Parameters: + * regs Register contents at time of trap + * error_code Trap-specific error code value + * trapno Trap number + * vp Pointer to die message + * Returns: + * Returns 1 if fault handled by kdb. + * Locking: + * None. + * Remarks: + * + */ +int +kdba_callback_die(struct pt_regs *regs, int error_code, long trapno, void *vp) +{ + /* + * Save a pointer to the message provided to 'die()'. + */ + kdb_diemsg = (char *)vp; + + return kdb(KDB_REASON_OOPS, error_code, (kdb_eframe_t) regs); +} + +/* + * kdba_callback_bp + * + * Callback function for kernel breakpoint trap. + * + * Parameters: + * regs Register contents at time of trap + * error_code Trap-specific error code value + * trapno Trap number + * vp Not Used. + * Returns: + * Returns 1 if fault handled by kdb. + * Locking: + * None. + * Remarks: + * + */ + +int +kdba_callback_bp(struct pt_regs *regs, int error_code, long trapno, void *vp) +{ + int diag; + + if (KDB_DEBUG(BP)) + kdb_printf("cb_bp: e_c = %d tn = %ld regs = 0x%p\n", error_code, + trapno, regs); + + diag = kdb(KDB_REASON_BREAK, error_code, (kdb_eframe_t) regs); + + if (KDB_DEBUG(BP)) + kdb_printf("cb_bp: e_c = %d tn = %ld regs = 0x%p diag = %d\n", error_code, + trapno, regs, diag); + return diag; +} + +/* + * kdba_callback_debug + * + * Callback function for kernel debug register trap. + * + * Parameters: + * regs Register contents at time of trap + * error_code Trap-specific error code value + * trapno Trap number + * vp Not used. + * Returns: + * Returns 1 if fault handled by kdb. + * Locking: + * None. + * Remarks: + * + */ + +int +kdba_callback_debug(struct pt_regs *regs, int error_code, long trapno, void *vp) +{ + return kdb(KDB_REASON_DEBUG, error_code, (kdb_eframe_t) regs); +} + +/* + * kdba_init + * + * Architecture specific initialization. + * + * Parameters: + * None. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * None. + */ + +void __init +kdba_init(void) +{ + kdba_enable_lbr(); + + return; +} + +/* + * kdba_adjust_ip + * + * Architecture specific adjustment of instruction pointer before leaving + * kdb. + * + * Parameters: + * reason The reason KDB was invoked + * error The hardware-defined error code + * ef The exception frame at time of fault/breakpoint. If reason + * is KDB_REASON_SILENT then ef is NULL, otherwise it should + * always be valid. + * Returns: + * None. + * Locking: + * None. + * Remarks: + * noop on ix86. + */ + +void +kdba_adjust_ip(kdb_reason_t reason, int error, kdb_eframe_t ef) +{ + return; +} + + + +/* + * kdba_find_tb_table + * + * Find the traceback table (defined by the ELF64 ABI) located at + * the end of the function containing pc. + * + * Parameters: + * eip starting instruction addr. does not need to be at the start of the func. + * tab table to populate if successful + * Returns: + * non-zero if successful. unsuccessful means that a valid tb table was not found + * Locking: + * None. + * Remarks: + * None. + */ +int kdba_find_tb_table(kdb_machreg_t eip, kdbtbtable_t *tab) +{ + kdb_machreg_t codeaddr = eip; + kdb_machreg_t codeaddr_max; + kdb_machreg_t tbtab_start; + int instr; + int num_parms; + + if (tab == NULL) + return 0; + memset(tab, 0, sizeof(tab)); + + /* Scan instructions starting at codeaddr for 128k max */ + for (codeaddr_max = codeaddr + 128*1024*4; + codeaddr < codeaddr_max; + codeaddr += 4) { + instr = kdba_getword(codeaddr, 4); + if (instr == 0) { + /* table should follow. */ + int version; + unsigned long flags; + tbtab_start = codeaddr; /* save it to compute func start addr */ + codeaddr += 4; + flags = kdba_getword(codeaddr, 8); + tab->flags = flags; + version = (flags >> 56) & 0xff; + if (version != 0) + continue; /* No tb table here. */ + /* Now, like the version, some of the flags are values + that are more conveniently extracted... */ + tab->fp_saved = (flags >> 24) & 0x3f; + tab->gpr_saved = (flags >> 16) & 0x3f; + tab->fixedparms = (flags >> 8) & 0xff; + tab->floatparms = (flags >> 1) & 0x7f; + codeaddr += 8; + num_parms = tab->fixedparms + tab->floatparms; + if (num_parms) { + unsigned int parminfo; + int parm; + if (num_parms > 32) + return 1; /* incomplete */ + parminfo = kdba_getword(codeaddr, 4); + /* decode parminfo...32 bits. + A zero means fixed. A one means float and the + following bit determines single (0) or double (1). + */ + for (parm = 0; parm < num_parms; parm++) { + if (parminfo & 0x80000000) { + parminfo <<= 1; + if (parminfo & 0x80000000) + tab->parminfo[parm] = KDBTBTAB_PARMDFLOAT; + else + tab->parminfo[parm] = KDBTBTAB_PARMSFLOAT; + } else { + tab->parminfo[parm] = KDBTBTAB_PARMFIXED; + } + parminfo <<= 1; + } + codeaddr += 4; + } + if (flags & KDBTBTAB_FLAGSHASTBOFF) { + tab->tb_offset = kdba_getword(codeaddr, 4); + if (tab->tb_offset > 0) { + tab->funcstart = tbtab_start - tab->tb_offset; + } + codeaddr += 4; + } + /* hand_mask appears to be always be omitted. */ + if (flags & KDBTBTAB_FLAGSHASCTL) { + /* Assume this will never happen for C or asm */ + return 1; /* incomplete */ + } + if (flags & KDBTBTAB_FLAGSNAMEPRESENT) { + int i; + short namlen = kdba_getword(codeaddr, 2); + if (namlen >= sizeof(tab->name)) + namlen = sizeof(tab->name)-1; + codeaddr += 2; + for (i = 0; i < namlen; i++) { + tab->name[i] = kdba_getword(codeaddr++, 1); + } + tab->name[namlen] = '\0'; + } + /* Fake up a symtab entry in case the caller finds it useful */ + tab->symtab.value = tab->symtab.sym_start = tab->funcstart; + tab->symtab.sym_name = tab->name; + tab->symtab.sym_end = tbtab_start; + return 1; + } + } + return 0; /* hit max...sorry. */ +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kdb/nonstdio.h linuxppc64_2_4/arch/ppc64/kdb/nonstdio.h --- linux-2.4.5-ac18/arch/ppc64/kdb/nonstdio.h Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kdb/nonstdio.h Mon Jun 4 10:24:01 2001 @@ -0,0 +1,22 @@ +typedef int FILE; +extern FILE *xmon_stdin, *xmon_stdout; +#define EOF (-1) +#define stdin xmon_stdin +#define stdout xmon_stdout +#define printf xmon_printf +#define fprintf xmon_fprintf +#define fputs xmon_fputs +#define fgets xmon_fgets +#define putchar xmon_putchar +#define getchar xmon_getchar +#define putc xmon_putc +#define getc xmon_getc +#define fopen(n, m) NULL +#define fflush(f) do {} while (0) +#define fclose(f) do {} while (0) +extern char *fgets(char *, int, void *); +extern void xmon_printf(const char *, ...); +extern void xmon_fprintf(void *, const char *, ...); +extern void xmon_sprintf(char *, const char *, ...); + +#define perror(s) printf("%s: no files!\n", (s)) diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kdb/opintl.h linuxppc64_2_4/arch/ppc64/kdb/opintl.h --- linux-2.4.5-ac18/arch/ppc64/kdb/opintl.h Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kdb/opintl.h Mon Jun 4 10:24:01 2001 @@ -0,0 +1,42 @@ +/* opintl.h - opcodes specific header for gettext code. + Copyright (C) 1998, 1999 Free Software Foundation, Inc. + + Written by Tom Tromey + + This file is part of the opcodes library used by GAS and the GNU binutils. + + You should have received a copy of the GNU General Public License + along with GAS; see the file COPYING. If not, write to the Free + Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ + +#ifdef ENABLE_NLS +# include +/* Note the use of dgetext() and PACKAGE here, rather than gettext(). + + This is because the code in this directory is used to build a library which + will be linked with code in other directories to form programs. We want to + maintain a seperate translation file for this directory however, rather + than being forced to merge it with that of any program linked to + libopcodes. This is a library, so it cannot depend on the catalog + currently loaded. + + In order to do this, we have to make sure that when we extract messages we + use the OPCODES domain rather than the domain of the program that included + the opcodes library, (eg OBJDUMP). Hence we use dgettext (PACKAGE, String) + and define PACKAGE to be 'opcodes'. (See the code in configure). */ +# define _(String) dgettext (PACKAGE, String) +# ifdef gettext_noop +# define N_(String) gettext_noop (String) +# else +# define N_(String) (String) +# endif +#else +# define gettext(Msgid) (Msgid) +# define dgettext(Domainname, Msgid) (Msgid) +# define dcgettext(Domainname, Msgid, Category) (Msgid) +# define textdomain(Domainname) while (0) /* nothing */ +# define bindtextdomain(Domainname, Dirname) while (0) /* nothing */ +# define _(String) (String) +# define N_(String) (String) +#endif diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kdb/ppc-dis.c linuxppc64_2_4/arch/ppc64/kdb/ppc-dis.c --- linux-2.4.5-ac18/arch/ppc64/kdb/ppc-dis.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kdb/ppc-dis.c Mon Jun 4 10:24:01 2001 @@ -0,0 +1,275 @@ +/* ppc-dis.c -- Disassemble PowerPC instructions + Copyright 1994 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +2, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +will be useful, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this file; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#ifdef __KERNEL__ +#include +#include +#include +#include +#include "ppc.h" +#include +#else +#include +#include "sysdep.h" +#include "dis-asm.h" +#include "opcode/ppc.h" +#endif +bfd_vma +bfd_getb32 (addr) + register const bfd_byte *addr; +{ + unsigned long v; + + v = (unsigned long) addr[0] << 24; + v |= (unsigned long) addr[1] << 16; + v |= (unsigned long) addr[2] << 8; + v |= (unsigned long) addr[3]; + return (bfd_vma) v; +} + +bfd_vma +bfd_getl32 (addr) + register const bfd_byte *addr; +{ + unsigned long v; + + v = (unsigned long) addr[0]; + v |= (unsigned long) addr[1] << 8; + v |= (unsigned long) addr[2] << 16; + v |= (unsigned long) addr[3] << 24; + return (bfd_vma) v; +} +/* This file provides several disassembler functions, all of which use + the disassembler interface defined in dis-asm.h. Several functions + are provided because this file handles disassembly for the PowerPC + in both big and little endian mode and also for the POWER (RS/6000) + chip. */ + +static int print_insn_powerpc PARAMS ((bfd_vma, struct disassemble_info *, + int bigendian, int dialect)); + +/* Print a big endian PowerPC instruction. For convenience, also + disassemble instructions supported by the Motorola PowerPC 601 + and the Altivec vector unit. */ + +int +print_insn_big_powerpc (memaddr, info) + bfd_vma memaddr; + struct disassemble_info *info; +{ + return print_insn_powerpc (memaddr, info, 1, + PPC_OPCODE_PPC | PPC_OPCODE_601 | + PPC_OPCODE_ALTIVEC); +} + +/* Print a little endian PowerPC instruction. For convenience, also + disassemble instructions supported by the Motorola PowerPC 601 + and the Altivec vector unit. */ + +int +print_insn_little_powerpc (memaddr, info) + bfd_vma memaddr; + struct disassemble_info *info; +{ + return print_insn_powerpc (memaddr, info, 0, + PPC_OPCODE_PPC | PPC_OPCODE_601 | + PPC_OPCODE_ALTIVEC); +} + +/* Print a POWER (RS/6000) instruction. */ + +int +print_insn_rs6000 (memaddr, info) + bfd_vma memaddr; + struct disassemble_info *info; +{ + return print_insn_powerpc (memaddr, info, 1, PPC_OPCODE_POWER); +} + +/* Print a PowerPC or POWER instruction. */ + +static int +print_insn_powerpc (memaddr, info, bigendian, dialect) + bfd_vma memaddr; + struct disassemble_info *info; + int bigendian; + int dialect; +{ + bfd_byte buffer[4]; + int status; + unsigned long insn; + const struct powerpc_opcode *opcode; + const struct powerpc_opcode *opcode_end; + unsigned long op; + + status = (*info->read_memory_func) (memaddr, buffer, 4, info); + if (status != 0) + { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } + + if (bigendian) + insn = bfd_getb32 (buffer); + else + insn = bfd_getl32 (buffer); + + /* Get the major opcode of the instruction. */ + op = PPC_OP (insn); + + /* Find the first match in the opcode table. We could speed this up + a bit by doing a binary search on the major opcode. */ + opcode_end = powerpc_opcodes + powerpc_num_opcodes; + for (opcode = powerpc_opcodes; opcode < opcode_end; opcode++) + { + unsigned long table_op; + const unsigned char *opindex; + const struct powerpc_operand *operand; + int invalid; + int need_comma; + int need_paren; + + table_op = PPC_OP (opcode->opcode); + if (op < table_op) + break; + if (op > table_op) + continue; + + if ((insn & opcode->mask) != opcode->opcode + || (opcode->flags & dialect) == 0) + continue; + + /* Make two passes over the operands. First see if any of them + have extraction functions, and, if they do, make sure the + instruction is valid. */ + invalid = 0; + for (opindex = opcode->operands; *opindex != 0; opindex++) + { + operand = powerpc_operands + *opindex; + if (operand->extract) + (*operand->extract) (insn, &invalid); + } + if (invalid) + continue; + + /* The instruction is valid. */ + (*info->fprintf_func) (info->stream, "%s", opcode->name); + if (opcode->operands[0] != 0) + (*info->fprintf_func) (info->stream, "\t"); + + /* Now extract and print the operands. */ + need_comma = 0; + need_paren = 0; + for (opindex = opcode->operands; *opindex != 0; opindex++) + { + long value; + + operand = powerpc_operands + *opindex; + + /* Operands that are marked FAKE are simply ignored. We + already made sure that the extract function considered + the instruction to be valid. */ + if ((operand->flags & PPC_OPERAND_FAKE) != 0) + continue; + + /* Extract the value from the instruction. */ + if (operand->extract) + value = (*operand->extract) (insn, (int *) NULL); + else + { + value = (insn >> operand->shift) & ((1 << operand->bits) - 1); + if ((operand->flags & PPC_OPERAND_SIGNED) != 0 + && (value & (1 << (operand->bits - 1))) != 0) + value -= 1 << operand->bits; + } + + /* If the operand is optional, and the value is zero, don't + print anything. */ + if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0 + && (operand->flags & PPC_OPERAND_NEXT) == 0 + && value == 0) + continue; + + if (need_comma) + { + (*info->fprintf_func) (info->stream, ","); + need_comma = 0; + } + + /* Print the operand as directed by the flags. */ + if ((operand->flags & PPC_OPERAND_GPR) != 0) + (*info->fprintf_func) (info->stream, "r%ld", value); + else if ((operand->flags & PPC_OPERAND_FPR) != 0) + (*info->fprintf_func) (info->stream, "f%ld", value); + else if ((operand->flags & PPC_OPERAND_VR) != 0) + (*info->fprintf_func) (info->stream, "v%ld", value); + else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0) + (*info->print_address_func) (memaddr + value, info); + else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0) + (*info->print_address_func) ((bfd_vma) value & 0xffffffff, info); + else if ((operand->flags & PPC_OPERAND_CR) == 0 + || (dialect & PPC_OPCODE_PPC) == 0) + (*info->fprintf_func) (info->stream, "%ld", value); + else + { + if (operand->bits == 3) + (*info->fprintf_func) (info->stream, "cr%d", value); + else + { + static const char *cbnames[4] = { "lt", "gt", "eq", "so" }; + int cr; + int cc; + + cr = value >> 2; + if (cr != 0) + (*info->fprintf_func) (info->stream, "4*cr%d", cr); + cc = value & 3; + if (cc != 0) + { + if (cr != 0) + (*info->fprintf_func) (info->stream, "+"); + (*info->fprintf_func) (info->stream, "%s", cbnames[cc]); + } + } + } + + if (need_paren) + { + (*info->fprintf_func) (info->stream, ")"); + need_paren = 0; + } + + if ((operand->flags & PPC_OPERAND_PARENS) == 0) + need_comma = 1; + else + { + (*info->fprintf_func) (info->stream, "("); + need_paren = 1; + } + } + + /* We have found and printed an instruction; return. */ + return 4; + } + + /* We could not find a match. */ + (*info->fprintf_func) (info->stream, ".long 0x%lx", insn); + + return 4; +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kdb/ppc-opc.c linuxppc64_2_4/arch/ppc64/kdb/ppc-opc.c --- linux-2.4.5-ac18/arch/ppc64/kdb/ppc-opc.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kdb/ppc-opc.c Mon Jun 4 10:24:01 2001 @@ -0,0 +1,3491 @@ +/* ppc-opc.c -- PowerPC opcode list + Copyright (c) 1994, 95, 96, 97, 98, 99, 2000 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +2, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +will be useful, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this file; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA +02111-1307, USA. */ +#ifndef __KERNEL__ +#include +#include "sysdep.h" +#include "opcode/ppc.h" +#include "opintl.h" +#else +#include +#include +#include +#include +#include "ppc.h" +#include "opintl.h" +#endif +/* This file holds the PowerPC opcode table. The opcode table + includes almost all of the extended instruction mnemonics. This + permits the disassembler to use them, and simplifies the assembler + logic, at the cost of increasing the table size. The table is + strictly constant data, so the compiler should be able to put it in + the .text section. + + This file also holds the operand table. All knowledge about + inserting operands into instructions and vice-versa is kept in this + file. */ + +/* Local insertion and extraction functions. */ + +static unsigned long insert_bat PARAMS ((unsigned long, long, const char **)); +static long extract_bat PARAMS ((unsigned long, int *)); +static unsigned long insert_bba PARAMS ((unsigned long, long, const char **)); +static long extract_bba PARAMS ((unsigned long, int *)); +static unsigned long insert_bd PARAMS ((unsigned long, long, const char **)); +static long extract_bd PARAMS ((unsigned long, int *)); +static unsigned long insert_bdm PARAMS ((unsigned long, long, const char **)); +static long extract_bdm PARAMS ((unsigned long, int *)); +static unsigned long insert_bdp PARAMS ((unsigned long, long, const char **)); +static long extract_bdp PARAMS ((unsigned long, int *)); +static int valid_bo PARAMS ((long)); +static unsigned long insert_bo PARAMS ((unsigned long, long, const char **)); +static long extract_bo PARAMS ((unsigned long, int *)); +static unsigned long insert_boe PARAMS ((unsigned long, long, const char **)); +static long extract_boe PARAMS ((unsigned long, int *)); +static unsigned long insert_ds PARAMS ((unsigned long, long, const char **)); +static long extract_ds PARAMS ((unsigned long, int *)); +static unsigned long insert_li PARAMS ((unsigned long, long, const char **)); +static long extract_li PARAMS ((unsigned long, int *)); +static unsigned long insert_mbe PARAMS ((unsigned long, long, const char **)); +static long extract_mbe PARAMS ((unsigned long, int *)); +static unsigned long insert_mb6 PARAMS ((unsigned long, long, const char **)); +static long extract_mb6 PARAMS ((unsigned long, int *)); +static unsigned long insert_nb PARAMS ((unsigned long, long, const char **)); +static long extract_nb PARAMS ((unsigned long, int *)); +static unsigned long insert_nsi PARAMS ((unsigned long, long, const char **)); +static long extract_nsi PARAMS ((unsigned long, int *)); +static unsigned long insert_ral PARAMS ((unsigned long, long, const char **)); +static unsigned long insert_ram PARAMS ((unsigned long, long, const char **)); +static unsigned long insert_ras PARAMS ((unsigned long, long, const char **)); +static unsigned long insert_rbs PARAMS ((unsigned long, long, const char **)); +static long extract_rbs PARAMS ((unsigned long, int *)); +static unsigned long insert_sh6 PARAMS ((unsigned long, long, const char **)); +static long extract_sh6 PARAMS ((unsigned long, int *)); +static unsigned long insert_spr PARAMS ((unsigned long, long, const char **)); +static long extract_spr PARAMS ((unsigned long, int *)); +static unsigned long insert_tbr PARAMS ((unsigned long, long, const char **)); +static long extract_tbr PARAMS ((unsigned long, int *)); + +/* The operands table. + + The fields are bits, shift, insert, extract, flags. + + We used to put parens around the various additions, like the one + for BA just below. However, that caused trouble with feeble + compilers with a limit on depth of a parenthesized expression, like + (reportedly) the compiler in Microsoft Developer Studio 5. So we + omit the parens, since the macros are never used in a context where + the addition will be ambiguous. */ + +const struct powerpc_operand powerpc_operands[] = +{ + /* The zero index is used to indicate the end of the list of + operands. */ +#define UNUSED 0 + { 0, 0, 0, 0, 0 }, + + /* The BA field in an XL form instruction. */ +#define BA UNUSED + 1 +#define BA_MASK (0x1f << 16) + { 5, 16, 0, 0, PPC_OPERAND_CR }, + + /* The BA field in an XL form instruction when it must be the same + as the BT field in the same instruction. */ +#define BAT BA + 1 + { 5, 16, insert_bat, extract_bat, PPC_OPERAND_FAKE }, + + /* The BB field in an XL form instruction. */ +#define BB BAT + 1 +#define BB_MASK (0x1f << 11) + { 5, 11, 0, 0, PPC_OPERAND_CR }, + + /* The BB field in an XL form instruction when it must be the same + as the BA field in the same instruction. */ +#define BBA BB + 1 + { 5, 11, insert_bba, extract_bba, PPC_OPERAND_FAKE }, + + /* The BD field in a B form instruction. The lower two bits are + forced to zero. */ +#define BD BBA + 1 + { 16, 0, insert_bd, extract_bd, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when absolute addressing is + used. */ +#define BDA BD + 1 + { 16, 0, insert_bd, extract_bd, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the - modifier is used. + This sets the y bit of the BO field appropriately. */ +#define BDM BDA + 1 + { 16, 0, insert_bdm, extract_bdm, + PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the - modifier is used + and absolute address is used. */ +#define BDMA BDM + 1 + { 16, 0, insert_bdm, extract_bdm, + PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the + modifier is used. + This sets the y bit of the BO field appropriately. */ +#define BDP BDMA + 1 + { 16, 0, insert_bdp, extract_bdp, + PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the + modifier is used + and absolute addressing is used. */ +#define BDPA BDP + 1 + { 16, 0, insert_bdp, extract_bdp, + PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The BF field in an X or XL form instruction. */ +#define BF BDPA + 1 + { 3, 23, 0, 0, PPC_OPERAND_CR }, + + /* An optional BF field. This is used for comparison instructions, + in which an omitted BF field is taken as zero. */ +#define OBF BF + 1 + { 3, 23, 0, 0, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL }, + + /* The BFA field in an X or XL form instruction. */ +#define BFA OBF + 1 + { 3, 18, 0, 0, PPC_OPERAND_CR }, + + /* The BI field in a B form or XL form instruction. */ +#define BI BFA + 1 +#define BI_MASK (0x1f << 16) + { 5, 16, 0, 0, PPC_OPERAND_CR }, + + /* The BO field in a B form instruction. Certain values are + illegal. */ +#define BO BI + 1 +#define BO_MASK (0x1f << 21) + { 5, 21, insert_bo, extract_bo, 0 }, + + /* The BO field in a B form instruction when the + or - modifier is + used. This is like the BO field, but it must be even. */ +#define BOE BO + 1 + { 5, 21, insert_boe, extract_boe, 0 }, + + /* The BT field in an X or XL form instruction. */ +#define BT BOE + 1 + { 5, 21, 0, 0, PPC_OPERAND_CR }, + + /* The condition register number portion of the BI field in a B form + or XL form instruction. This is used for the extended + conditional branch mnemonics, which set the lower two bits of the + BI field. This field is optional. */ +#define CR BT + 1 + { 3, 18, 0, 0, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL }, + + /* The D field in a D form instruction. This is a displacement off + a register, and implies that the next operand is a register in + parentheses. */ +#define D CR + 1 + { 16, 0, 0, 0, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED }, + + /* The DS field in a DS form instruction. This is like D, but the + lower two bits are forced to zero. */ +#define DS D + 1 + { 16, 0, insert_ds, extract_ds, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED }, + + /* The E field in a wrteei instruction. */ +#define E DS + 1 + { 1, 15, 0, 0, 0 }, + + /* The FL1 field in a POWER SC form instruction. */ +#define FL1 E + 1 + { 4, 12, 0, 0, 0 }, + + /* The FL2 field in a POWER SC form instruction. */ +#define FL2 FL1 + 1 + { 3, 2, 0, 0, 0 }, + + /* The FLM field in an XFL form instruction. */ +#define FLM FL2 + 1 + { 8, 17, 0, 0, 0 }, + + /* The FRA field in an X or A form instruction. */ +#define FRA FLM + 1 +#define FRA_MASK (0x1f << 16) + { 5, 16, 0, 0, PPC_OPERAND_FPR }, + + /* The FRB field in an X or A form instruction. */ +#define FRB FRA + 1 +#define FRB_MASK (0x1f << 11) + { 5, 11, 0, 0, PPC_OPERAND_FPR }, + + /* The FRC field in an A form instruction. */ +#define FRC FRB + 1 +#define FRC_MASK (0x1f << 6) + { 5, 6, 0, 0, PPC_OPERAND_FPR }, + + /* The FRS field in an X form instruction or the FRT field in a D, X + or A form instruction. */ +#define FRS FRC + 1 +#define FRT FRS + { 5, 21, 0, 0, PPC_OPERAND_FPR }, + + /* The FXM field in an XFX instruction. */ +#define FXM FRS + 1 +#define FXM_MASK (0xff << 12) + { 8, 12, 0, 0, 0 }, + + /* The L field in a D or X form instruction. */ +#define L FXM + 1 + { 1, 21, 0, 0, PPC_OPERAND_OPTIONAL }, + + /* The LEV field in a POWER SC form instruction. */ +#define LEV L + 1 + { 7, 5, 0, 0, 0 }, + + /* The LI field in an I form instruction. The lower two bits are + forced to zero. */ +#define LI LEV + 1 + { 26, 0, insert_li, extract_li, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The LI field in an I form instruction when used as an absolute + address. */ +#define LIA LI + 1 + { 26, 0, insert_li, extract_li, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The MB field in an M form instruction. */ +#define MB LIA + 1 +#define MB_MASK (0x1f << 6) + { 5, 6, 0, 0, 0 }, + + /* The ME field in an M form instruction. */ +#define ME MB + 1 +#define ME_MASK (0x1f << 1) + { 5, 1, 0, 0, 0 }, + + /* The MB and ME fields in an M form instruction expressed a single + operand which is a bitmask indicating which bits to select. This + is a two operand form using PPC_OPERAND_NEXT. See the + description in opcode/ppc.h for what this means. */ +#define MBE ME + 1 + { 5, 6, 0, 0, PPC_OPERAND_OPTIONAL | PPC_OPERAND_NEXT }, + { 32, 0, insert_mbe, extract_mbe, 0 }, + + /* The MB or ME field in an MD or MDS form instruction. The high + bit is wrapped to the low end. */ +#define MB6 MBE + 2 +#define ME6 MB6 +#define MB6_MASK (0x3f << 5) + { 6, 5, insert_mb6, extract_mb6, 0 }, + + /* The NB field in an X form instruction. The value 32 is stored as + 0. */ +#define NB MB6 + 1 + { 6, 11, insert_nb, extract_nb, 0 }, + + /* The NSI field in a D form instruction. This is the same as the + SI field, only negated. */ +#define NSI NB + 1 + { 16, 0, insert_nsi, extract_nsi, + PPC_OPERAND_NEGATIVE | PPC_OPERAND_SIGNED }, + + /* The RA field in an D, DS, X, XO, M, or MDS form instruction. */ +#define RA NSI + 1 +#define RA_MASK (0x1f << 16) + { 5, 16, 0, 0, PPC_OPERAND_GPR }, + + /* The RA field in a D or X form instruction which is an updating + load, which means that the RA field may not be zero and may not + equal the RT field. */ +#define RAL RA + 1 + { 5, 16, insert_ral, 0, PPC_OPERAND_GPR }, + + /* The RA field in an lmw instruction, which has special value + restrictions. */ +#define RAM RAL + 1 + { 5, 16, insert_ram, 0, PPC_OPERAND_GPR }, + + /* The RA field in a D or X form instruction which is an updating + store or an updating floating point load, which means that the RA + field may not be zero. */ +#define RAS RAM + 1 + { 5, 16, insert_ras, 0, PPC_OPERAND_GPR }, + + /* The RB field in an X, XO, M, or MDS form instruction. */ +#define RB RAS + 1 +#define RB_MASK (0x1f << 11) + { 5, 11, 0, 0, PPC_OPERAND_GPR }, + + /* The RB field in an X form instruction when it must be the same as + the RS field in the instruction. This is used for extended + mnemonics like mr. */ +#define RBS RB + 1 + { 5, 1, insert_rbs, extract_rbs, PPC_OPERAND_FAKE }, + + /* The RS field in a D, DS, X, XFX, XS, M, MD or MDS form + instruction or the RT field in a D, DS, X, XFX or XO form + instruction. */ +#define RS RBS + 1 +#define RT RS +#define RT_MASK (0x1f << 21) + { 5, 21, 0, 0, PPC_OPERAND_GPR }, + + /* The SH field in an X or M form instruction. */ +#define SH RS + 1 +#define SH_MASK (0x1f << 11) + { 5, 11, 0, 0, 0 }, + + /* The SH field in an MD form instruction. This is split. */ +#define SH6 SH + 1 +#define SH6_MASK ((0x1f << 11) | (1 << 1)) + { 6, 1, insert_sh6, extract_sh6, 0 }, + + /* The SI field in a D form instruction. */ +#define SI SH6 + 1 + { 16, 0, 0, 0, PPC_OPERAND_SIGNED }, + + /* The SI field in a D form instruction when we accept a wide range + of positive values. */ +#define SISIGNOPT SI + 1 + { 16, 0, 0, 0, PPC_OPERAND_SIGNED | PPC_OPERAND_SIGNOPT }, + + /* The SPR field in an XFX form instruction. This is flipped--the + lower 5 bits are stored in the upper 5 and vice- versa. */ +#define SPR SISIGNOPT + 1 +#define SPR_MASK (0x3ff << 11) + { 10, 11, insert_spr, extract_spr, 0 }, + + /* The BAT index number in an XFX form m[ft]ibat[lu] instruction. */ +#define SPRBAT SPR + 1 +#define SPRBAT_MASK (0x3 << 17) + { 2, 17, 0, 0, 0 }, + + /* The SPRG register number in an XFX form m[ft]sprg instruction. */ +#define SPRG SPRBAT + 1 +#define SPRG_MASK (0x3 << 16) + { 2, 16, 0, 0, 0 }, + + /* The SR field in an X form instruction. */ +#define SR SPRG + 1 + { 4, 16, 0, 0, 0 }, + + /* The SV field in a POWER SC form instruction. */ +#define SV SR + 1 + { 14, 2, 0, 0, 0 }, + + /* The TBR field in an XFX form instruction. This is like the SPR + field, but it is optional. */ +#define TBR SV + 1 + { 10, 11, insert_tbr, extract_tbr, PPC_OPERAND_OPTIONAL }, + + /* The TO field in a D or X form instruction. */ +#define TO TBR + 1 +#define TO_MASK (0x1f << 21) + { 5, 21, 0, 0, 0 }, + + /* The U field in an X form instruction. */ +#define U TO + 1 + { 4, 12, 0, 0, 0 }, + + /* The UI field in a D form instruction. */ +#define UI U + 1 + { 16, 0, 0, 0, 0 }, + + /* The VA field in a VA, VX or VXR form instruction. */ +#define VA UI + 1 +#define VA_MASK (0x1f << 16) + {5, 16, 0, 0, PPC_OPERAND_VR}, + + /* The VB field in a VA, VX or VXR form instruction. */ +#define VB VA + 1 +#define VB_MASK (0x1f << 11) + {5, 11, 0, 0, PPC_OPERAND_VR}, + + /* The VC field in a VA form instruction. */ +#define VC VB + 1 +#define VC_MASK (0x1f << 6) + {5, 6, 0, 0, PPC_OPERAND_VR}, + + /* The VD or VS field in a VA, VX, VXR or X form instruction. */ +#define VD VC + 1 +#define VS VD +#define VD_MASK (0x1f << 21) + {5, 21, 0, 0, PPC_OPERAND_VR}, + + /* The SIMM field in a VX form instruction. */ +#define SIMM VD + 1 + { 5, 16, 0, 0, PPC_OPERAND_SIGNED}, + + /* The UIMM field in a VX form instruction. */ +#define UIMM SIMM + 1 + { 5, 16, 0, 0, 0 }, + + /* The SHB field in a VA form instruction. */ +#define SHB UIMM + 1 + { 4, 6, 0, 0, 0 }, +}; + +/* The functions used to insert and extract complicated operands. */ + +/* The BA field in an XL form instruction when it must be the same as + the BT field in the same instruction. This operand is marked FAKE. + The insertion function just copies the BT field into the BA field, + and the extraction function just checks that the fields are the + same. */ + +/*ARGSUSED*/ +static unsigned long +insert_bat (insn, value, errmsg) + unsigned long insn; + long value ATTRIBUTE_UNUSED; + const char **errmsg ATTRIBUTE_UNUSED; +{ + return insn | (((insn >> 21) & 0x1f) << 16); +} + +static long +extract_bat (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn >> 21) & 0x1f) != ((insn >> 16) & 0x1f)) + *invalid = 1; + return 0; +} + +/* The BB field in an XL form instruction when it must be the same as + the BA field in the same instruction. This operand is marked FAKE. + The insertion function just copies the BA field into the BB field, + and the extraction function just checks that the fields are the + same. */ + +/*ARGSUSED*/ +static unsigned long +insert_bba (insn, value, errmsg) + unsigned long insn; + long value ATTRIBUTE_UNUSED; + const char **errmsg ATTRIBUTE_UNUSED; +{ + return insn | (((insn >> 16) & 0x1f) << 11); +} + +static long +extract_bba (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn >> 16) & 0x1f) != ((insn >> 11) & 0x1f)) + *invalid = 1; + return 0; +} + +/* The BD field in a B form instruction. The lower two bits are + forced to zero. */ + +/*ARGSUSED*/ +static unsigned long +insert_bd (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg ATTRIBUTE_UNUSED; +{ + return insn | (value & 0xfffc); +} + +/*ARGSUSED*/ +static long +extract_bd (insn, invalid) + unsigned long insn; + int *invalid ATTRIBUTE_UNUSED; +{ + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* The BD field in a B form instruction when the - modifier is used. + This modifier means that the branch is not expected to be taken. + We must set the y bit of the BO field to 1 if the offset is + negative. When extracting, we require that the y bit be 1 and that + the offset be positive, since if the y bit is 0 we just want to + print the normal form of the instruction. */ + +/*ARGSUSED*/ +static unsigned long +insert_bdm (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg ATTRIBUTE_UNUSED; +{ + if ((value & 0x8000) != 0) + insn |= 1 << 21; + return insn | (value & 0xfffc); +} + +static long +extract_bdm (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn & (1 << 21)) == 0 + || (insn & (1 << 15)) == 0)) + *invalid = 1; + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* The BD field in a B form instruction when the + modifier is used. + This is like BDM, above, except that the branch is expected to be + taken. */ + +/*ARGSUSED*/ +static unsigned long +insert_bdp (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg ATTRIBUTE_UNUSED; +{ + if ((value & 0x8000) == 0) + insn |= 1 << 21; + return insn | (value & 0xfffc); +} + +static long +extract_bdp (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn & (1 << 21)) == 0 + || (insn & (1 << 15)) != 0)) + *invalid = 1; + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* Check for legal values of a BO field. */ + +static int +valid_bo (value) + long value; +{ + /* Certain encodings have bits that are required to be zero. These + are (z must be zero, y may be anything): + 001zy + 011zy + 1z00y + 1z01y + 1z1zz + */ + switch (value & 0x14) + { + default: + case 0: + return 1; + case 0x4: + return (value & 0x2) == 0; + case 0x10: + return (value & 0x8) == 0; + case 0x14: + return value == 0x14; + } +} + +/* The BO field in a B form instruction. Warn about attempts to set + the field to an illegal value. */ + +static unsigned long +insert_bo (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (errmsg != (const char **) NULL + && ! valid_bo (value)) + *errmsg = _("invalid conditional option"); + return insn | ((value & 0x1f) << 21); +} + +static long +extract_bo (insn, invalid) + unsigned long insn; + int *invalid; +{ + long value; + + value = (insn >> 21) & 0x1f; + if (invalid != (int *) NULL + && ! valid_bo (value)) + *invalid = 1; + return value; +} + +/* The BO field in a B form instruction when the + or - modifier is + used. This is like the BO field, but it must be even. When + extracting it, we force it to be even. */ + +static unsigned long +insert_boe (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (errmsg != (const char **) NULL) + { + if (! valid_bo (value)) + *errmsg = _("invalid conditional option"); + else if ((value & 1) != 0) + *errmsg = _("attempt to set y bit when using + or - modifier"); + } + return insn | ((value & 0x1f) << 21); +} + +static long +extract_boe (insn, invalid) + unsigned long insn; + int *invalid; +{ + long value; + + value = (insn >> 21) & 0x1f; + if (invalid != (int *) NULL + && ! valid_bo (value)) + *invalid = 1; + return value & 0x1e; +} + +/* The DS field in a DS form instruction. This is like D, but the + lower two bits are forced to zero. */ + +/*ARGSUSED*/ +static unsigned long +insert_ds (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg ATTRIBUTE_UNUSED; +{ + return insn | (value & 0xfffc); +} + +/*ARGSUSED*/ +static long +extract_ds (insn, invalid) + unsigned long insn; + int *invalid ATTRIBUTE_UNUSED; +{ + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* The LI field in an I form instruction. The lower two bits are + forced to zero. */ + +/*ARGSUSED*/ +static unsigned long +insert_li (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if ((value & 3) != 0 && errmsg != (const char **) NULL) + *errmsg = _("ignoring least significant bits in branch offset"); + return insn | (value & 0x3fffffc); +} + +/*ARGSUSED*/ +static long +extract_li (insn, invalid) + unsigned long insn; + int *invalid ATTRIBUTE_UNUSED; +{ + if ((insn & 0x2000000) != 0) + return (insn & 0x3fffffc) - 0x4000000; + else + return insn & 0x3fffffc; +} + +/* The MB and ME fields in an M form instruction expressed as a single + operand which is itself a bitmask. The extraction function always + marks it as invalid, since we never want to recognize an + instruction which uses a field of this type. */ + +static unsigned long +insert_mbe (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + unsigned long uval, mask; + int mb, me, mx, count, last; + + uval = value; + + if (uval == 0) + { + if (errmsg != (const char **) NULL) + *errmsg = _("illegal bitmask"); + return insn; + } + + mb = 0; + me = 32; + if ((uval & 1) != 0) + last = 1; + else + last = 0; + count = 0; + + /* mb: location of last 0->1 transition */ + /* me: location of last 1->0 transition */ + /* count: # transitions */ + + for (mx = 0, mask = 1 << 31; mx < 32; ++mx, mask >>= 1) + { + if ((uval & mask) && !last) + { + ++count; + mb = mx; + last = 1; + } + else if (!(uval & mask) && last) + { + ++count; + me = mx; + last = 0; + } + } + if (me == 0) + me = 32; + + if (count != 2 && (count != 0 || ! last)) + { + if (errmsg != (const char **) NULL) + *errmsg = _("illegal bitmask"); + } + + return insn | (mb << 6) | ((me - 1) << 1); +} + +static long +extract_mbe (insn, invalid) + unsigned long insn; + int *invalid; +{ + long ret; + int mb, me; + int i; + + if (invalid != (int *) NULL) + *invalid = 1; + + mb = (insn >> 6) & 0x1f; + me = (insn >> 1) & 0x1f; + if (mb < me + 1) + { + ret = 0; + for (i = mb; i <= me; i++) + ret |= (long) 1 << (31 - i); + } + else if (mb == me + 1) + ret = ~0; + else /* (mb > me + 1) */ + { + ret = ~ (long) 0; + for (i = me + 1; i < mb; i++) + ret &= ~ ((long) 1 << (31 - i)); + } + return ret; +} + +/* The MB or ME field in an MD or MDS form instruction. The high bit + is wrapped to the low end. */ + +/*ARGSUSED*/ +static unsigned long +insert_mb6 (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg ATTRIBUTE_UNUSED; +{ + return insn | ((value & 0x1f) << 6) | (value & 0x20); +} + +/*ARGSUSED*/ +static long +extract_mb6 (insn, invalid) + unsigned long insn; + int *invalid ATTRIBUTE_UNUSED; +{ + return ((insn >> 6) & 0x1f) | (insn & 0x20); +} + +/* The NB field in an X form instruction. The value 32 is stored as + 0. */ + +static unsigned long +insert_nb (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (value < 0 || value > 32) + *errmsg = _("value out of range"); + if (value == 32) + value = 0; + return insn | ((value & 0x1f) << 11); +} + +/*ARGSUSED*/ +static long +extract_nb (insn, invalid) + unsigned long insn; + int *invalid ATTRIBUTE_UNUSED; +{ + long ret; + + ret = (insn >> 11) & 0x1f; + if (ret == 0) + ret = 32; + return ret; +} + +/* The NSI field in a D form instruction. This is the same as the SI + field, only negated. The extraction function always marks it as + invalid, since we never want to recognize an instruction which uses + a field of this type. */ + +/*ARGSUSED*/ +static unsigned long +insert_nsi (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg ATTRIBUTE_UNUSED; +{ + return insn | ((- value) & 0xffff); +} + +static long +extract_nsi (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL) + *invalid = 1; + if ((insn & 0x8000) != 0) + return - ((long)(insn & 0xffff) - 0x10000); + else + return - (long)(insn & 0xffff); +} + +/* The RA field in a D or X form instruction which is an updating + load, which means that the RA field may not be zero and may not + equal the RT field. */ + +static unsigned long +insert_ral (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (value == 0 + || (unsigned long) value == ((insn >> 21) & 0x1f)) + *errmsg = "invalid register operand when updating"; + return insn | ((value & 0x1f) << 16); +} + +/* The RA field in an lmw instruction, which has special value + restrictions. */ + +static unsigned long +insert_ram (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if ((unsigned long) value >= ((insn >> 21) & 0x1f)) + *errmsg = _("index register in load range"); + return insn | ((value & 0x1f) << 16); +} + +/* The RA field in a D or X form instruction which is an updating + store or an updating floating point load, which means that the RA + field may not be zero. */ + +static unsigned long +insert_ras (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (value == 0) + *errmsg = _("invalid register operand when updating"); + return insn | ((value & 0x1f) << 16); +} + +/* The RB field in an X form instruction when it must be the same as + the RS field in the instruction. This is used for extended + mnemonics like mr. This operand is marked FAKE. The insertion + function just copies the BT field into the BA field, and the + extraction function just checks that the fields are the same. */ + +/*ARGSUSED*/ +static unsigned long +insert_rbs (insn, value, errmsg) + unsigned long insn; + long value ATTRIBUTE_UNUSED; + const char **errmsg ATTRIBUTE_UNUSED; +{ + return insn | (((insn >> 21) & 0x1f) << 11); +} + +static long +extract_rbs (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn >> 21) & 0x1f) != ((insn >> 11) & 0x1f)) + *invalid = 1; + return 0; +} + +/* The SH field in an MD form instruction. This is split. */ + +/*ARGSUSED*/ +static unsigned long +insert_sh6 (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg ATTRIBUTE_UNUSED; +{ + return insn | ((value & 0x1f) << 11) | ((value & 0x20) >> 4); +} + +/*ARGSUSED*/ +static long +extract_sh6 (insn, invalid) + unsigned long insn; + int *invalid ATTRIBUTE_UNUSED; +{ + return ((insn >> 11) & 0x1f) | ((insn << 4) & 0x20); +} + +/* The SPR field in an XFX form instruction. This is flipped--the + lower 5 bits are stored in the upper 5 and vice- versa. */ + +static unsigned long +insert_spr (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg ATTRIBUTE_UNUSED; +{ + return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6); +} + +static long +extract_spr (insn, invalid) + unsigned long insn; + int *invalid ATTRIBUTE_UNUSED; +{ + return ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0); +} + +/* The TBR field in an XFX instruction. This is just like SPR, but it + is optional. When TBR is omitted, it must be inserted as 268 (the + magic number of the TB register). These functions treat 0 + (indicating an omitted optional operand) as 268. This means that + ``mftb 4,0'' is not handled correctly. This does not matter very + much, since the architecture manual does not define mftb as + accepting any values other than 268 or 269. */ + +#define TB (268) + +static unsigned long +insert_tbr (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg ATTRIBUTE_UNUSED; +{ + if (value == 0) + value = TB; + return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6); +} + +static long +extract_tbr (insn, invalid) + unsigned long insn; + int *invalid ATTRIBUTE_UNUSED; +{ + long ret; + + ret = ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0); + if (ret == TB) + ret = 0; + return ret; +} + +/* Macros used to form opcodes. */ + +/* The main opcode. */ +#define OP(x) ((((unsigned long)(x)) & 0x3f) << 26) +#define OP_MASK OP (0x3f) + +/* The main opcode combined with a trap code in the TO field of a D + form instruction. Used for extended mnemonics for the trap + instructions. */ +#define OPTO(x,to) (OP (x) | ((((unsigned long)(to)) & 0x1f) << 21)) +#define OPTO_MASK (OP_MASK | TO_MASK) + +/* The main opcode combined with a comparison size bit in the L field + of a D form or X form instruction. Used for extended mnemonics for + the comparison instructions. */ +#define OPL(x,l) (OP (x) | ((((unsigned long)(l)) & 1) << 21)) +#define OPL_MASK OPL (0x3f,1) + +/* An A form instruction. */ +#define A(op, xop, rc) (OP (op) | ((((unsigned long)(xop)) & 0x1f) << 1) | (((unsigned long)(rc)) & 1)) +#define A_MASK A (0x3f, 0x1f, 1) + +/* An A_MASK with the FRB field fixed. */ +#define AFRB_MASK (A_MASK | FRB_MASK) + +/* An A_MASK with the FRC field fixed. */ +#define AFRC_MASK (A_MASK | FRC_MASK) + +/* An A_MASK with the FRA and FRC fields fixed. */ +#define AFRAFRC_MASK (A_MASK | FRA_MASK | FRC_MASK) + +/* A B form instruction. */ +#define B(op, aa, lk) (OP (op) | ((((unsigned long)(aa)) & 1) << 1) | ((lk) & 1)) +#define B_MASK B (0x3f, 1, 1) + +/* A B form instruction setting the BO field. */ +#define BBO(op, bo, aa, lk) (B ((op), (aa), (lk)) | ((((unsigned long)(bo)) & 0x1f) << 21)) +#define BBO_MASK BBO (0x3f, 0x1f, 1, 1) + +/* A BBO_MASK with the y bit of the BO field removed. This permits + matching a conditional branch regardless of the setting of the y + bit. */ +#define Y_MASK (((unsigned long)1) << 21) +#define BBOY_MASK (BBO_MASK &~ Y_MASK) + +/* A B form instruction setting the BO field and the condition bits of + the BI field. */ +#define BBOCB(op, bo, cb, aa, lk) \ + (BBO ((op), (bo), (aa), (lk)) | ((((unsigned long)(cb)) & 0x3) << 16)) +#define BBOCB_MASK BBOCB (0x3f, 0x1f, 0x3, 1, 1) + +/* A BBOCB_MASK with the y bit of the BO field removed. */ +#define BBOYCB_MASK (BBOCB_MASK &~ Y_MASK) + +/* A BBOYCB_MASK in which the BI field is fixed. */ +#define BBOYBI_MASK (BBOYCB_MASK | BI_MASK) + +/* The main opcode mask with the RA field clear. */ +#define DRA_MASK (OP_MASK | RA_MASK) + +/* A DS form instruction. */ +#define DSO(op, xop) (OP (op) | ((xop) & 0x3)) +#define DS_MASK DSO (0x3f, 3) + +/* An M form instruction. */ +#define M(op, rc) (OP (op) | ((rc) & 1)) +#define M_MASK M (0x3f, 1) + +/* An M form instruction with the ME field specified. */ +#define MME(op, me, rc) (M ((op), (rc)) | ((((unsigned long)(me)) & 0x1f) << 1)) + +/* An M_MASK with the MB and ME fields fixed. */ +#define MMBME_MASK (M_MASK | MB_MASK | ME_MASK) + +/* An M_MASK with the SH and ME fields fixed. */ +#define MSHME_MASK (M_MASK | SH_MASK | ME_MASK) + +/* An MD form instruction. */ +#define MD(op, xop, rc) (OP (op) | ((((unsigned long)(xop)) & 0x7) << 2) | ((rc) & 1)) +#define MD_MASK MD (0x3f, 0x7, 1) + +/* An MD_MASK with the MB field fixed. */ +#define MDMB_MASK (MD_MASK | MB6_MASK) + +/* An MD_MASK with the SH field fixed. */ +#define MDSH_MASK (MD_MASK | SH6_MASK) + +/* An MDS form instruction. */ +#define MDS(op, xop, rc) (OP (op) | ((((unsigned long)(xop)) & 0xf) << 1) | ((rc) & 1)) +#define MDS_MASK MDS (0x3f, 0xf, 1) + +/* An MDS_MASK with the MB field fixed. */ +#define MDSMB_MASK (MDS_MASK | MB6_MASK) + +/* An SC form instruction. */ +#define SC(op, sa, lk) (OP (op) | ((((unsigned long)(sa)) & 1) << 1) | ((lk) & 1)) +#define SC_MASK (OP_MASK | (((unsigned long)0x3ff) << 16) | (((unsigned long)1) << 1) | 1) + +/* An VX form instruction. */ +#define VX(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x7ff)) + +/* The mask for an VX form instruction. */ +#define VX_MASK VX(0x3f, 0x7ff) + +/* An VA form instruction. */ +#define VXA(op, xop) (OP (op) | (((unsigned long)(xop)) & 0x07f)) + +/* The mask for an VA form instruction. */ +#define VXA_MASK VXA(0x3f, 0x7f) + +/* An VXR form instruction. */ +#define VXR(op, xop, rc) (OP (op) | (((rc) & 1) << 10) | (((unsigned long)(xop)) & 0x3ff)) + +/* The mask for a VXR form instruction. */ +#define VXR_MASK VXR(0x3f, 0x3ff, 1) + +/* An X form instruction. */ +#define X(op, xop) (OP (op) | ((((unsigned long)(xop)) & 0x3ff) << 1)) + +/* An X form instruction with the RC bit specified. */ +#define XRC(op, xop, rc) (X ((op), (xop)) | ((rc) & 1)) + +/* The mask for an X form instruction. */ +#define X_MASK XRC (0x3f, 0x3ff, 1) + +/* An X_MASK with the RA field fixed. */ +#define XRA_MASK (X_MASK | RA_MASK) + +/* An X_MASK with the RB field fixed. */ +#define XRB_MASK (X_MASK | RB_MASK) + +/* An X_MASK with the RT field fixed. */ +#define XRT_MASK (X_MASK | RT_MASK) + +/* An X_MASK with the RA and RB fields fixed. */ +#define XRARB_MASK (X_MASK | RA_MASK | RB_MASK) + +/* An X_MASK with the RT and RA fields fixed. */ +#define XRTRA_MASK (X_MASK | RT_MASK | RA_MASK) + +/* An X form comparison instruction. */ +#define XCMPL(op, xop, l) (X ((op), (xop)) | ((((unsigned long)(l)) & 1) << 21)) + +/* The mask for an X form comparison instruction. */ +#define XCMP_MASK (X_MASK | (((unsigned long)1) << 22)) + +/* The mask for an X form comparison instruction with the L field + fixed. */ +#define XCMPL_MASK (XCMP_MASK | (((unsigned long)1) << 21)) + +/* An X form trap instruction with the TO field specified. */ +#define XTO(op, xop, to) (X ((op), (xop)) | ((((unsigned long)(to)) & 0x1f) << 21)) +#define XTO_MASK (X_MASK | TO_MASK) + +/* An X form tlb instruction with the SH field specified. */ +#define XTLB(op, xop, sh) (X ((op), (xop)) | ((((unsigned long)(sh)) & 0x1f) << 11)) +#define XTLB_MASK (X_MASK | SH_MASK) + +/* An XFL form instruction. */ +#define XFL(op, xop, rc) (OP (op) | ((((unsigned long)(xop)) & 0x3ff) << 1) | (((unsigned long)(rc)) & 1)) +#define XFL_MASK (XFL (0x3f, 0x3ff, 1) | (((unsigned long)1) << 25) | (((unsigned long)1) << 16)) + +/* An XL form instruction with the LK field set to 0. */ +#define XL(op, xop) (OP (op) | ((((unsigned long)(xop)) & 0x3ff) << 1)) + +/* An XL form instruction which uses the LK field. */ +#define XLLK(op, xop, lk) (XL ((op), (xop)) | ((lk) & 1)) + +/* The mask for an XL form instruction. */ +#define XL_MASK XLLK (0x3f, 0x3ff, 1) + +/* An XL form instruction which explicitly sets the BO field. */ +#define XLO(op, bo, xop, lk) \ + (XLLK ((op), (xop), (lk)) | ((((unsigned long)(bo)) & 0x1f) << 21)) +#define XLO_MASK (XL_MASK | BO_MASK) + +/* An XL form instruction which explicitly sets the y bit of the BO + field. */ +#define XLYLK(op, xop, y, lk) (XLLK ((op), (xop), (lk)) | ((((unsigned long)(y)) & 1) << 21)) +#define XLYLK_MASK (XL_MASK | Y_MASK) + +/* An XL form instruction which sets the BO field and the condition + bits of the BI field. */ +#define XLOCB(op, bo, cb, xop, lk) \ + (XLO ((op), (bo), (xop), (lk)) | ((((unsigned long)(cb)) & 3) << 16)) +#define XLOCB_MASK XLOCB (0x3f, 0x1f, 0x3, 0x3ff, 1) + +/* An XL_MASK or XLYLK_MASK or XLOCB_MASK with the BB field fixed. */ +#define XLBB_MASK (XL_MASK | BB_MASK) +#define XLYBB_MASK (XLYLK_MASK | BB_MASK) +#define XLBOCBBB_MASK (XLOCB_MASK | BB_MASK) + +/* An XL_MASK with the BO and BB fields fixed. */ +#define XLBOBB_MASK (XL_MASK | BO_MASK | BB_MASK) + +/* An XL_MASK with the BO, BI and BB fields fixed. */ +#define XLBOBIBB_MASK (XL_MASK | BO_MASK | BI_MASK | BB_MASK) + +/* An XO form instruction. */ +#define XO(op, xop, oe, rc) \ + (OP (op) | ((((unsigned long)(xop)) & 0x1ff) << 1) | ((((unsigned long)(oe)) & 1) << 10) | (((unsigned long)(rc)) & 1)) +#define XO_MASK XO (0x3f, 0x1ff, 1, 1) + +/* An XO_MASK with the RB field fixed. */ +#define XORB_MASK (XO_MASK | RB_MASK) + +/* An XS form instruction. */ +#define XS(op, xop, rc) (OP (op) | ((((unsigned long)(xop)) & 0x1ff) << 2) | (((unsigned long)(rc)) & 1)) +#define XS_MASK XS (0x3f, 0x1ff, 1) + +/* A mask for the FXM version of an XFX form instruction. */ +#define XFXFXM_MASK (X_MASK | (((unsigned long)1) << 20) | (((unsigned long)1) << 11)) + +/* An XFX form instruction with the FXM field filled in. */ +#define XFXM(op, xop, fxm) \ + (X ((op), (xop)) | ((((unsigned long)(fxm)) & 0xff) << 12)) + +/* An XFX form instruction with the SPR field filled in. */ +#define XSPR(op, xop, spr) \ + (X ((op), (xop)) | ((((unsigned long)(spr)) & 0x1f) << 16) | ((((unsigned long)(spr)) & 0x3e0) << 6)) +#define XSPR_MASK (X_MASK | SPR_MASK) + +/* An XFX form instruction with the SPR field filled in except for the + SPRBAT field. */ +#define XSPRBAT_MASK (XSPR_MASK &~ SPRBAT_MASK) + +/* An XFX form instruction with the SPR field filled in except for the + SPRG field. */ +#define XSPRG_MASK (XSPR_MASK &~ SPRG_MASK) + +/* An X form instruction with everything filled in except the E field. */ +#define XE_MASK (0xffff7fff) + +/* The BO encodings used in extended conditional branch mnemonics. */ +#define BODNZF (0x0) +#define BODNZFP (0x1) +#define BODZF (0x2) +#define BODZFP (0x3) +#define BOF (0x4) +#define BOFP (0x5) +#define BODNZT (0x8) +#define BODNZTP (0x9) +#define BODZT (0xa) +#define BODZTP (0xb) +#define BOT (0xc) +#define BOTP (0xd) +#define BODNZ (0x10) +#define BODNZP (0x11) +#define BODZ (0x12) +#define BODZP (0x13) +#define BOU (0x14) + +/* The BI condition bit encodings used in extended conditional branch + mnemonics. */ +#define CBLT (0) +#define CBGT (1) +#define CBEQ (2) +#define CBSO (3) + +/* The TO encodings used in extended trap mnemonics. */ +#define TOLGT (0x1) +#define TOLLT (0x2) +#define TOEQ (0x4) +#define TOLGE (0x5) +#define TOLNL (0x5) +#define TOLLE (0x6) +#define TOLNG (0x6) +#define TOGT (0x8) +#define TOGE (0xc) +#define TONL (0xc) +#define TOLT (0x10) +#define TOLE (0x14) +#define TONG (0x14) +#define TONE (0x18) +#define TOU (0x1f) + +/* Smaller names for the flags so each entry in the opcodes table will + fit on a single line. */ +#undef PPC +#define PPC PPC_OPCODE_PPC | PPC_OPCODE_ANY +#define PPCCOM PPC_OPCODE_PPC | PPC_OPCODE_COMMON | PPC_OPCODE_ANY +#define PPC32 PPC_OPCODE_PPC | PPC_OPCODE_32 | PPC_OPCODE_ANY +#undef PPC64 +#define PPC64 PPC_OPCODE_PPC | PPC_OPCODE_64 | PPC_OPCODE_ANY +#define PPCONLY PPC_OPCODE_PPC +#define PPC403 PPC +#define PPC405 PPC403 +#define PPC750 PPC +#define PPC860 PPC +#define PPCVEC PPC_OPCODE_ALTIVEC | PPC_OPCODE_ANY +#define POWER PPC_OPCODE_POWER | PPC_OPCODE_ANY +#define POWER2 PPC_OPCODE_POWER | PPC_OPCODE_POWER2 | PPC_OPCODE_ANY +#define PPCPWR2 PPC_OPCODE_PPC | PPC_OPCODE_POWER | PPC_OPCODE_POWER2 | PPC_OPCODE_ANY +#define POWER32 PPC_OPCODE_POWER | PPC_OPCODE_ANY | PPC_OPCODE_32 +#define COM PPC_OPCODE_POWER | PPC_OPCODE_PPC | PPC_OPCODE_COMMON | PPC_OPCODE_ANY +#define COM32 PPC_OPCODE_POWER | PPC_OPCODE_PPC | PPC_OPCODE_COMMON | PPC_OPCODE_ANY | PPC_OPCODE_32 +#define M601 PPC_OPCODE_POWER | PPC_OPCODE_601 | PPC_OPCODE_ANY +#define PWRCOM PPC_OPCODE_POWER | PPC_OPCODE_601 | PPC_OPCODE_COMMON | PPC_OPCODE_ANY +#define MFDEC1 PPC_OPCODE_POWER +#define MFDEC2 PPC_OPCODE_PPC | PPC_OPCODE_601 + +/* The opcode table. + + The format of the opcode table is: + + NAME OPCODE MASK FLAGS { OPERANDS } + + NAME is the name of the instruction. + OPCODE is the instruction opcode. + MASK is the opcode mask; this is used to tell the disassembler + which bits in the actual opcode must match OPCODE. + FLAGS are flags indicated what processors support the instruction. + OPERANDS is the list of operands. + + The disassembler reads the table in order and prints the first + instruction which matches, so this table is sorted to put more + specific instructions before more general instructions. It is also + sorted by major opcode. */ + +const struct powerpc_opcode powerpc_opcodes[] = { +{ "tdlgti", OPTO(2,TOLGT), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdllti", OPTO(2,TOLLT), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdeqi", OPTO(2,TOEQ), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdlgei", OPTO(2,TOLGE), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdlnli", OPTO(2,TOLNL), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdllei", OPTO(2,TOLLE), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdlngi", OPTO(2,TOLNG), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdgti", OPTO(2,TOGT), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdgei", OPTO(2,TOGE), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdnli", OPTO(2,TONL), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdlti", OPTO(2,TOLT), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdlei", OPTO(2,TOLE), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdngi", OPTO(2,TONG), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdnei", OPTO(2,TONE), OPTO_MASK, PPC64, { RA, SI } }, +{ "tdi", OP(2), OP_MASK, PPC64, { TO, RA, SI } }, + +{ "twlgti", OPTO(3,TOLGT), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tlgti", OPTO(3,TOLGT), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twllti", OPTO(3,TOLLT), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tllti", OPTO(3,TOLLT), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "tweqi", OPTO(3,TOEQ), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "teqi", OPTO(3,TOEQ), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twlgei", OPTO(3,TOLGE), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tlgei", OPTO(3,TOLGE), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twlnli", OPTO(3,TOLNL), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tlnli", OPTO(3,TOLNL), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twllei", OPTO(3,TOLLE), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tllei", OPTO(3,TOLLE), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twlngi", OPTO(3,TOLNG), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tlngi", OPTO(3,TOLNG), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twgti", OPTO(3,TOGT), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tgti", OPTO(3,TOGT), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twgei", OPTO(3,TOGE), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tgei", OPTO(3,TOGE), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twnli", OPTO(3,TONL), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tnli", OPTO(3,TONL), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twlti", OPTO(3,TOLT), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tlti", OPTO(3,TOLT), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twlei", OPTO(3,TOLE), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tlei", OPTO(3,TOLE), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twngi", OPTO(3,TONG), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tngi", OPTO(3,TONG), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twnei", OPTO(3,TONE), OPTO_MASK, PPCCOM, { RA, SI } }, +{ "tnei", OPTO(3,TONE), OPTO_MASK, PWRCOM, { RA, SI } }, +{ "twi", OP(3), OP_MASK, PPCCOM, { TO, RA, SI } }, +{ "ti", OP(3), OP_MASK, PWRCOM, { TO, RA, SI } }, + +{ "macchw", XO(4,172,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchw.", XO(4,172,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchwo", XO(4,172,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchwo.", XO(4,172,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchws", XO(4,236,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchws.", XO(4,236,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchwso", XO(4,236,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchwso.", XO(4,236,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchwsu", XO(4,204,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchwsu.", XO(4,204,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchwsuo", XO(4,204,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchwsuo.", XO(4,204,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchwu", XO(4,140,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchwu.", XO(4,140,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchwuo", XO(4,140,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "macchwuo.", XO(4,140,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhw", XO(4,44,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhw.", XO(4,44,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhwo", XO(4,44,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhwo.", XO(4,44,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhws", XO(4,108,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhws.", XO(4,108,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhwso", XO(4,108,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhwso.", XO(4,108,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhwsu", XO(4,76,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhwsu.", XO(4,76,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhwsuo", XO(4,76,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhwsuo.", XO(4,76,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhwu", XO(4,12,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhwu.", XO(4,12,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhwuo", XO(4,12,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "machhwuo.", XO(4,12,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhw", XO(4,428,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhw.", XO(4,428,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhwo", XO(4,428,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhwo.", XO(4,428,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhws", XO(4,492,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhws.", XO(4,492,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhwso", XO(4,492,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhwso.", XO(4,492,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhwsu", XO(4,460,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhwsu.", XO(4,460,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhwsuo", XO(4,460,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhwsuo.", XO(4,460,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhwu", XO(4,396,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhwu.", XO(4,396,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhwuo", XO(4,396,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "maclhwuo.", XO(4,396,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "mulchw", XRC(4,168,0), X_MASK, PPC405, { RT, RA, RB } }, +{ "mulchw.", XRC(4,168,1), X_MASK, PPC405, { RT, RA, RB } }, +{ "mulchwu", XRC(4,136,0), X_MASK, PPC405, { RT, RA, RB } }, +{ "mulchwu.", XRC(4,136,1), X_MASK, PPC405, { RT, RA, RB } }, +{ "mulhhw", XRC(4,40,0), X_MASK, PPC405, { RT, RA, RB } }, +{ "mulhhw.", XRC(4,40,1), X_MASK, PPC405, { RT, RA, RB } }, +{ "mulhhwu", XRC(4,8,0), X_MASK, PPC405, { RT, RA, RB } }, +{ "mulhhwu.", XRC(4,8,1), X_MASK, PPC405, { RT, RA, RB } }, +{ "mullhw", XRC(4,424,0), X_MASK, PPC405, { RT, RA, RB } }, +{ "mullhw.", XRC(4,424,1), X_MASK, PPC405, { RT, RA, RB } }, +{ "mullhwu", XRC(4,392,0), X_MASK, PPC405, { RT, RA, RB } }, +{ "mullhwu.", XRC(4,392,1), X_MASK, PPC405, { RT, RA, RB } }, +{ "nmacchw", XO(4,174,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmacchw.", XO(4,174,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmacchwo", XO(4,174,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmacchwo.", XO(4,174,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmacchws", XO(4,238,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmacchws.", XO(4,238,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmacchwso", XO(4,238,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmacchwso.", XO(4,238,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmachhw", XO(4,46,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmachhw.", XO(4,46,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmachhwo", XO(4,46,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmachhwo.", XO(4,46,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmachhws", XO(4,110,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmachhws.", XO(4,110,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmachhwso", XO(4,110,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmachhwso.", XO(4,110,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmaclhw", XO(4,430,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmaclhw.", XO(4,430,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmaclhwo", XO(4,430,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmaclhwo.", XO(4,430,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmaclhws", XO(4,494,0,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmaclhws.", XO(4,494,0,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmaclhwso", XO(4,494,1,0), XO_MASK, PPC405, { RT, RA, RB } }, +{ "nmaclhwso.", XO(4,494,1,1), XO_MASK, PPC405, { RT, RA, RB } }, +{ "mfvscr", VX(4, 1540), VX_MASK, PPCVEC, { VD } }, +{ "mtvscr", VX(4, 1604), VX_MASK, PPCVEC, { VD } }, +{ "vaddcuw", VX(4, 384), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vaddfp", VX(4, 10), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vaddsbs", VX(4, 768), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vaddshs", VX(4, 832), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vaddsws", VX(4, 896), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vaddubm", VX(4, 0), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vaddubs", VX(4, 512), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vadduhm", VX(4, 64), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vadduhs", VX(4, 576), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vadduwm", VX(4, 128), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vadduws", VX(4, 640), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vand", VX(4, 1028), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vandc", VX(4, 1092), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vavgsb", VX(4, 1282), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vavgsh", VX(4, 1346), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vavgsw", VX(4, 1410), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vavgub", VX(4, 1026), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vavguh", VX(4, 1090), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vavguw", VX(4, 1154), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcfsx", VX(4, 842), VX_MASK, PPCVEC, { VD, VB, UIMM } }, +{ "vcfux", VX(4, 778), VX_MASK, PPCVEC, { VD, VB, UIMM } }, +{ "vcmpbfp", VXR(4, 966, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpbfp.", VXR(4, 966, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpeqfp", VXR(4, 198, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpeqfp.", VXR(4, 198, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpequb", VXR(4, 6, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpequb.", VXR(4, 6, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpequh", VXR(4, 70, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpequh.", VXR(4, 70, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpequw", VXR(4, 134, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpequw.", VXR(4, 134, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgefp", VXR(4, 454, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgefp.", VXR(4, 454, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtfp", VXR(4, 710, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtfp.", VXR(4, 710, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtsb", VXR(4, 774, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtsb.", VXR(4, 774, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtsh", VXR(4, 838, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtsh.", VXR(4, 838, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtsw", VXR(4, 902, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtsw.", VXR(4, 902, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtub", VXR(4, 518, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtub.", VXR(4, 518, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtuh", VXR(4, 582, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtuh.", VXR(4, 582, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtuw", VXR(4, 646, 0), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vcmpgtuw.", VXR(4, 646, 1), VXR_MASK, PPCVEC, { VD, VA, VB } }, +{ "vctsxs", VX(4, 970), VX_MASK, PPCVEC, { VD, VB, UIMM } }, +{ "vctuxs", VX(4, 906), VX_MASK, PPCVEC, { VD, VB, UIMM } }, +{ "vexptefp", VX(4, 394), VX_MASK, PPCVEC, { VD, VB } }, +{ "vlogefp", VX(4, 458), VX_MASK, PPCVEC, { VD, VB } }, +{ "vmaddfp", VXA(4, 46), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vmaxfp", VX(4, 1034), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmaxsb", VX(4, 258), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmaxsh", VX(4, 322), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmaxsw", VX(4, 386), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmaxub", VX(4, 2), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmaxuh", VX(4, 66), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmaxuw", VX(4, 130), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmhaddshs", VXA(4, 32), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vmhraddshs", VXA(4, 33), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vminfp", VX(4, 1098), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vminsb", VX(4, 770), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vminsh", VX(4, 834), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vminsw", VX(4, 898), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vminub", VX(4, 514), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vminuh", VX(4, 578), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vminuw", VX(4, 642), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmladduhm", VXA(4, 34), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vmrghb", VX(4, 12), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmrghh", VX(4, 76), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmrghw", VX(4, 140), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmrglb", VX(4, 268), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmrglh", VX(4, 332), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmrglw", VX(4, 396), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmsummbm", VXA(4, 37), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vmsumshm", VXA(4, 40), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vmsumshs", VXA(4, 41), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vmsumubm", VXA(4, 36), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vmsumuhm", VXA(4, 38), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vmsumuhs", VXA(4, 39), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vmulesb", VX(4, 776), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmulesh", VX(4, 840), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmuleub", VX(4, 520), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmuleuh", VX(4, 584), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmulosb", VX(4, 264), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmulosh", VX(4, 328), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmuloub", VX(4, 8), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vmulouh", VX(4, 72), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vnmsubfp", VXA(4, 47), VXA_MASK, PPCVEC, { VD, VA, VC, VB } }, +{ "vnor", VX(4, 1284), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vor", VX(4, 1156), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vperm", VXA(4, 43), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vpkpx", VX(4, 782), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vpkshss", VX(4, 398), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vpkshus", VX(4, 270), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vpkswss", VX(4, 462), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vpkswus", VX(4, 334), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vpkuhum", VX(4, 14), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vpkuhus", VX(4, 142), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vpkuwum", VX(4, 78), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vpkuwus", VX(4, 206), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vrefp", VX(4, 266), VX_MASK, PPCVEC, { VD, VB } }, +{ "vrfim", VX(4, 714), VX_MASK, PPCVEC, { VD, VB } }, +{ "vrfin", VX(4, 522), VX_MASK, PPCVEC, { VD, VB } }, +{ "vrfip", VX(4, 650), VX_MASK, PPCVEC, { VD, VB } }, +{ "vrfiz", VX(4, 586), VX_MASK, PPCVEC, { VD, VB } }, +{ "vrlb", VX(4, 4), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vrlh", VX(4, 68), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vrlw", VX(4, 132), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vrsqrtefp", VX(4, 330), VX_MASK, PPCVEC, { VD, VB } }, +{ "vsel", VXA(4, 42), VXA_MASK, PPCVEC, { VD, VA, VB, VC } }, +{ "vsl", VX(4, 452), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vslb", VX(4, 260), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsldoi", VXA(4, 44), VXA_MASK, PPCVEC, { VD, VA, VB, SHB } }, +{ "vslh", VX(4, 324), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vslo", VX(4, 1036), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vslw", VX(4, 388), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vspltb", VX(4, 524), VX_MASK, PPCVEC, { VD, VB, UIMM } }, +{ "vsplth", VX(4, 588), VX_MASK, PPCVEC, { VD, VB, UIMM } }, +{ "vspltisb", VX(4, 780), VX_MASK, PPCVEC, { VD, SIMM } }, +{ "vspltish", VX(4, 844), VX_MASK, PPCVEC, { VD, SIMM } }, +{ "vspltisw", VX(4, 908), VX_MASK, PPCVEC, { VD, SIMM } }, +{ "vspltw", VX(4, 652), VX_MASK, PPCVEC, { VD, VB, UIMM } }, +{ "vsr", VX(4, 708), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsrab", VX(4, 772), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsrah", VX(4, 836), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsraw", VX(4, 900), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsrb", VX(4, 516), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsrh", VX(4, 580), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsro", VX(4, 1100), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsrw", VX(4, 644), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubcuw", VX(4, 1408), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubfp", VX(4, 74), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubsbs", VX(4, 1792), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubshs", VX(4, 1856), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubsws", VX(4, 1920), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsububm", VX(4, 1024), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsububs", VX(4, 1536), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubuhm", VX(4, 1088), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubuhs", VX(4, 1600), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubuwm", VX(4, 1152), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsubuws", VX(4, 1664), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsumsws", VX(4, 1928), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsum2sws", VX(4, 1672), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsum4sbs", VX(4, 1800), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsum4shs", VX(4, 1608), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vsum4ubs", VX(4, 1544), VX_MASK, PPCVEC, { VD, VA, VB } }, +{ "vupkhpx", VX(4, 846), VX_MASK, PPCVEC, { VD, VB } }, +{ "vupkhsb", VX(4, 526), VX_MASK, PPCVEC, { VD, VB } }, +{ "vupkhsh", VX(4, 590), VX_MASK, PPCVEC, { VD, VB } }, +{ "vupklpx", VX(4, 974), VX_MASK, PPCVEC, { VD, VB } }, +{ "vupklsb", VX(4, 654), VX_MASK, PPCVEC, { VD, VB } }, +{ "vupklsh", VX(4, 718), VX_MASK, PPCVEC, { VD, VB } }, +{ "vxor", VX(4, 1220), VX_MASK, PPCVEC, { VD, VA, VB } }, + +{ "mulli", OP(7), OP_MASK, PPCCOM, { RT, RA, SI } }, +{ "muli", OP(7), OP_MASK, PWRCOM, { RT, RA, SI } }, + +{ "subfic", OP(8), OP_MASK, PPCCOM, { RT, RA, SI } }, +{ "sfi", OP(8), OP_MASK, PWRCOM, { RT, RA, SI } }, + +{ "dozi", OP(9), OP_MASK, M601, { RT, RA, SI } }, + +{ "cmplwi", OPL(10,0), OPL_MASK, PPCCOM, { OBF, RA, UI } }, +{ "cmpldi", OPL(10,1), OPL_MASK, PPC64, { OBF, RA, UI } }, +{ "cmpli", OP(10), OP_MASK, PPCONLY, { BF, L, RA, UI } }, +{ "cmpli", OP(10), OP_MASK, PWRCOM, { BF, RA, UI } }, + +{ "cmpwi", OPL(11,0), OPL_MASK, PPCCOM, { OBF, RA, SI } }, +{ "cmpdi", OPL(11,1), OPL_MASK, PPC64, { OBF, RA, SI } }, +{ "cmpi", OP(11), OP_MASK, PPCONLY, { BF, L, RA, SI } }, +{ "cmpi", OP(11), OP_MASK, PWRCOM, { BF, RA, SI } }, + +{ "addic", OP(12), OP_MASK, PPCCOM, { RT, RA, SI } }, +{ "ai", OP(12), OP_MASK, PWRCOM, { RT, RA, SI } }, +{ "subic", OP(12), OP_MASK, PPCCOM, { RT, RA, NSI } }, + +{ "addic.", OP(13), OP_MASK, PPCCOM, { RT, RA, SI } }, +{ "ai.", OP(13), OP_MASK, PWRCOM, { RT, RA, SI } }, +{ "subic.", OP(13), OP_MASK, PPCCOM, { RT, RA, NSI } }, + +{ "li", OP(14), DRA_MASK, PPCCOM, { RT, SI } }, +{ "lil", OP(14), DRA_MASK, PWRCOM, { RT, SI } }, +{ "addi", OP(14), OP_MASK, PPCCOM, { RT, RA, SI } }, +{ "cal", OP(14), OP_MASK, PWRCOM, { RT, D, RA } }, +{ "subi", OP(14), OP_MASK, PPCCOM, { RT, RA, NSI } }, +{ "la", OP(14), OP_MASK, PPCCOM, { RT, D, RA } }, + +{ "lis", OP(15), DRA_MASK, PPCCOM, { RT, SISIGNOPT } }, +{ "liu", OP(15), DRA_MASK, PWRCOM, { RT, SISIGNOPT } }, +{ "addis", OP(15), OP_MASK, PPCCOM, { RT,RA,SISIGNOPT } }, +{ "cau", OP(15), OP_MASK, PWRCOM, { RT,RA,SISIGNOPT } }, +{ "subis", OP(15), OP_MASK, PPCCOM, { RT, RA, NSI } }, + +{ "bdnz-", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPCCOM, { BDM } }, +{ "bdnz+", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPCCOM, { BDP } }, +{ "bdnz", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPCCOM, { BD } }, +{ "bdn", BBO(16,BODNZ,0,0), BBOYBI_MASK, PWRCOM, { BD } }, +{ "bdnzl-", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPCCOM, { BDM } }, +{ "bdnzl+", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPCCOM, { BDP } }, +{ "bdnzl", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPCCOM, { BD } }, +{ "bdnl", BBO(16,BODNZ,0,1), BBOYBI_MASK, PWRCOM, { BD } }, +{ "bdnza-", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPCCOM, { BDMA } }, +{ "bdnza+", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPCCOM, { BDPA } }, +{ "bdnza", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPCCOM, { BDA } }, +{ "bdna", BBO(16,BODNZ,1,0), BBOYBI_MASK, PWRCOM, { BDA } }, +{ "bdnzla-", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPCCOM, { BDMA } }, +{ "bdnzla+", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPCCOM, { BDPA } }, +{ "bdnzla", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPCCOM, { BDA } }, +{ "bdnla", BBO(16,BODNZ,1,1), BBOYBI_MASK, PWRCOM, { BDA } }, +{ "bdz-", BBO(16,BODZ,0,0), BBOYBI_MASK, PPCCOM, { BDM } }, +{ "bdz+", BBO(16,BODZ,0,0), BBOYBI_MASK, PPCCOM, { BDP } }, +{ "bdz", BBO(16,BODZ,0,0), BBOYBI_MASK, COM, { BD } }, +{ "bdzl-", BBO(16,BODZ,0,1), BBOYBI_MASK, PPCCOM, { BDM } }, +{ "bdzl+", BBO(16,BODZ,0,1), BBOYBI_MASK, PPCCOM, { BDP } }, +{ "bdzl", BBO(16,BODZ,0,1), BBOYBI_MASK, COM, { BD } }, +{ "bdza-", BBO(16,BODZ,1,0), BBOYBI_MASK, PPCCOM, { BDMA } }, +{ "bdza+", BBO(16,BODZ,1,0), BBOYBI_MASK, PPCCOM, { BDPA } }, +{ "bdza", BBO(16,BODZ,1,0), BBOYBI_MASK, COM, { BDA } }, +{ "bdzla-", BBO(16,BODZ,1,1), BBOYBI_MASK, PPCCOM, { BDMA } }, +{ "bdzla+", BBO(16,BODZ,1,1), BBOYBI_MASK, PPCCOM, { BDPA } }, +{ "bdzla", BBO(16,BODZ,1,1), BBOYBI_MASK, COM, { BDA } }, +{ "blt-", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "blt+", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "blt", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, COM, { CR, BD } }, +{ "bltl-", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bltl+", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bltl", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, COM, { CR, BD } }, +{ "blta-", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "blta+", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "blta", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bltla-", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bltla+", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bltla", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bgt-", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bgt+", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bgt", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, COM, { CR, BD } }, +{ "bgtl-", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bgtl+", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bgtl", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, COM, { CR, BD } }, +{ "bgta-", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bgta+", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bgta", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bgtla-", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bgtla+", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bgtla", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, COM, { CR, BDA } }, +{ "beq-", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "beq+", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "beq", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, COM, { CR, BD } }, +{ "beql-", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "beql+", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "beql", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, COM, { CR, BD } }, +{ "beqa-", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "beqa+", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "beqa", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, COM, { CR, BDA } }, +{ "beqla-", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "beqla+", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "beqla", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bso-", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bso+", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bso", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, COM, { CR, BD } }, +{ "bsol-", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bsol+", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bsol", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, COM, { CR, BD } }, +{ "bsoa-", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bsoa+", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bsoa", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bsola-", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bsola+", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bsola", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bun-", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bun+", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bun", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPCCOM, { CR, BD } }, +{ "bunl-", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bunl+", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bunl", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPCCOM, { CR, BD } }, +{ "buna-", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "buna+", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "buna", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPCCOM, { CR, BDA } }, +{ "bunla-", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bunla+", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bunla", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPCCOM, { CR, BDA } }, +{ "bge-", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bge+", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bge", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, COM, { CR, BD } }, +{ "bgel-", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bgel+", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bgel", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, COM, { CR, BD } }, +{ "bgea-", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bgea+", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bgea", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bgela-", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bgela+", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bgela", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bnl-", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bnl+", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bnl", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, COM, { CR, BD } }, +{ "bnll-", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bnll+", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bnll", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, COM, { CR, BD } }, +{ "bnla-", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnla+", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnla", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bnlla-", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnlla+", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnlla", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, COM, { CR, BDA } }, +{ "ble-", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "ble+", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "ble", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, COM, { CR, BD } }, +{ "blel-", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "blel+", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "blel", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, COM, { CR, BD } }, +{ "blea-", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "blea+", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "blea", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, COM, { CR, BDA } }, +{ "blela-", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "blela+", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "blela", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bng-", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bng+", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bng", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, COM, { CR, BD } }, +{ "bngl-", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bngl+", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bngl", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, COM, { CR, BD } }, +{ "bnga-", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnga+", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnga", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bngla-", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bngla+", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bngla", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bne-", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bne+", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bne", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, COM, { CR, BD } }, +{ "bnel-", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bnel+", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bnel", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, COM, { CR, BD } }, +{ "bnea-", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnea+", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnea", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bnela-", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnela+", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnela", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bns-", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bns+", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bns", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, COM, { CR, BD } }, +{ "bnsl-", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bnsl+", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bnsl", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, COM, { CR, BD } }, +{ "bnsa-", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnsa+", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnsa", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bnsla-", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnsla+", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnsla", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, COM, { CR, BDA } }, +{ "bnu-", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bnu+", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bnu", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPCCOM, { CR, BD } }, +{ "bnul-", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPCCOM, { CR, BDM } }, +{ "bnul+", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPCCOM, { CR, BDP } }, +{ "bnul", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPCCOM, { CR, BD } }, +{ "bnua-", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnua+", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnua", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPCCOM, { CR, BDA } }, +{ "bnula-", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPCCOM, { CR, BDMA } }, +{ "bnula+", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPCCOM, { CR, BDPA } }, +{ "bnula", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPCCOM, { CR, BDA } }, +{ "bdnzt-", BBO(16,BODNZT,0,0), BBOY_MASK, PPCCOM, { BI, BDM } }, +{ "bdnzt+", BBO(16,BODNZT,0,0), BBOY_MASK, PPCCOM, { BI, BDP } }, +{ "bdnzt", BBO(16,BODNZT,0,0), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bdnztl-", BBO(16,BODNZT,0,1), BBOY_MASK, PPCCOM, { BI, BDM } }, +{ "bdnztl+", BBO(16,BODNZT,0,1), BBOY_MASK, PPCCOM, { BI, BDP } }, +{ "bdnztl", BBO(16,BODNZT,0,1), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bdnzta-", BBO(16,BODNZT,1,0), BBOY_MASK, PPCCOM, { BI, BDMA } }, +{ "bdnzta+", BBO(16,BODNZT,1,0), BBOY_MASK, PPCCOM, { BI, BDPA } }, +{ "bdnzta", BBO(16,BODNZT,1,0), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bdnztla-",BBO(16,BODNZT,1,1), BBOY_MASK, PPCCOM, { BI, BDMA } }, +{ "bdnztla+",BBO(16,BODNZT,1,1), BBOY_MASK, PPCCOM, { BI, BDPA } }, +{ "bdnztla", BBO(16,BODNZT,1,1), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bdnzf-", BBO(16,BODNZF,0,0), BBOY_MASK, PPCCOM, { BI, BDM } }, +{ "bdnzf+", BBO(16,BODNZF,0,0), BBOY_MASK, PPCCOM, { BI, BDP } }, +{ "bdnzf", BBO(16,BODNZF,0,0), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bdnzfl-", BBO(16,BODNZF,0,1), BBOY_MASK, PPCCOM, { BI, BDM } }, +{ "bdnzfl+", BBO(16,BODNZF,0,1), BBOY_MASK, PPCCOM, { BI, BDP } }, +{ "bdnzfl", BBO(16,BODNZF,0,1), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bdnzfa-", BBO(16,BODNZF,1,0), BBOY_MASK, PPCCOM, { BI, BDMA } }, +{ "bdnzfa+", BBO(16,BODNZF,1,0), BBOY_MASK, PPCCOM, { BI, BDPA } }, +{ "bdnzfa", BBO(16,BODNZF,1,0), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bdnzfla-",BBO(16,BODNZF,1,1), BBOY_MASK, PPCCOM, { BI, BDMA } }, +{ "bdnzfla+",BBO(16,BODNZF,1,1), BBOY_MASK, PPCCOM, { BI, BDPA } }, +{ "bdnzfla", BBO(16,BODNZF,1,1), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bt-", BBO(16,BOT,0,0), BBOY_MASK, PPCCOM, { BI, BDM } }, +{ "bt+", BBO(16,BOT,0,0), BBOY_MASK, PPCCOM, { BI, BDP } }, +{ "bt", BBO(16,BOT,0,0), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bbt", BBO(16,BOT,0,0), BBOY_MASK, PWRCOM, { BI, BD } }, +{ "btl-", BBO(16,BOT,0,1), BBOY_MASK, PPCCOM, { BI, BDM } }, +{ "btl+", BBO(16,BOT,0,1), BBOY_MASK, PPCCOM, { BI, BDP } }, +{ "btl", BBO(16,BOT,0,1), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bbtl", BBO(16,BOT,0,1), BBOY_MASK, PWRCOM, { BI, BD } }, +{ "bta-", BBO(16,BOT,1,0), BBOY_MASK, PPCCOM, { BI, BDMA } }, +{ "bta+", BBO(16,BOT,1,0), BBOY_MASK, PPCCOM, { BI, BDPA } }, +{ "bta", BBO(16,BOT,1,0), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bbta", BBO(16,BOT,1,0), BBOY_MASK, PWRCOM, { BI, BDA } }, +{ "btla-", BBO(16,BOT,1,1), BBOY_MASK, PPCCOM, { BI, BDMA } }, +{ "btla+", BBO(16,BOT,1,1), BBOY_MASK, PPCCOM, { BI, BDPA } }, +{ "btla", BBO(16,BOT,1,1), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bbtla", BBO(16,BOT,1,1), BBOY_MASK, PWRCOM, { BI, BDA } }, +{ "bf-", BBO(16,BOF,0,0), BBOY_MASK, PPCCOM, { BI, BDM } }, +{ "bf+", BBO(16,BOF,0,0), BBOY_MASK, PPCCOM, { BI, BDP } }, +{ "bf", BBO(16,BOF,0,0), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bbf", BBO(16,BOF,0,0), BBOY_MASK, PWRCOM, { BI, BD } }, +{ "bfl-", BBO(16,BOF,0,1), BBOY_MASK, PPCCOM, { BI, BDM } }, +{ "bfl+", BBO(16,BOF,0,1), BBOY_MASK, PPCCOM, { BI, BDP } }, +{ "bfl", BBO(16,BOF,0,1), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bbfl", BBO(16,BOF,0,1), BBOY_MASK, PWRCOM, { BI, BD } }, +{ "bfa-", BBO(16,BOF,1,0), BBOY_MASK, PPCCOM, { BI, BDMA } }, +{ "bfa+", BBO(16,BOF,1,0), BBOY_MASK, PPCCOM, { BI, BDPA } }, +{ "bfa", BBO(16,BOF,1,0), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bbfa", BBO(16,BOF,1,0), BBOY_MASK, PWRCOM, { BI, BDA } }, +{ "bfla-", BBO(16,BOF,1,1), BBOY_MASK, PPCCOM, { BI, BDMA } }, +{ "bfla+", BBO(16,BOF,1,1), BBOY_MASK, PPCCOM, { BI, BDPA } }, +{ "bfla", BBO(16,BOF,1,1), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bbfla", BBO(16,BOF,1,1), BBOY_MASK, PWRCOM, { BI, BDA } }, +{ "bdzt-", BBO(16,BODZT,0,0), BBOY_MASK, PPCCOM, { BI, BDM } }, +{ "bdzt+", BBO(16,BODZT,0,0), BBOY_MASK, PPCCOM, { BI, BDP } }, +{ "bdzt", BBO(16,BODZT,0,0), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bdztl-", BBO(16,BODZT,0,1), BBOY_MASK, PPCCOM, { BI, BDM } }, +{ "bdztl+", BBO(16,BODZT,0,1), BBOY_MASK, PPCCOM, { BI, BDP } }, +{ "bdztl", BBO(16,BODZT,0,1), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bdzta-", BBO(16,BODZT,1,0), BBOY_MASK, PPCCOM, { BI, BDMA } }, +{ "bdzta+", BBO(16,BODZT,1,0), BBOY_MASK, PPCCOM, { BI, BDPA } }, +{ "bdzta", BBO(16,BODZT,1,0), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bdztla-", BBO(16,BODZT,1,1), BBOY_MASK, PPCCOM, { BI, BDMA } }, +{ "bdztla+", BBO(16,BODZT,1,1), BBOY_MASK, PPCCOM, { BI, BDPA } }, +{ "bdztla", BBO(16,BODZT,1,1), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bdzf-", BBO(16,BODZF,0,0), BBOY_MASK, PPCCOM, { BI, BDM } }, +{ "bdzf+", BBO(16,BODZF,0,0), BBOY_MASK, PPCCOM, { BI, BDP } }, +{ "bdzf", BBO(16,BODZF,0,0), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bdzfl-", BBO(16,BODZF,0,1), BBOY_MASK, PPCCOM, { BI, BDM } }, +{ "bdzfl+", BBO(16,BODZF,0,1), BBOY_MASK, PPCCOM, { BI, BDP } }, +{ "bdzfl", BBO(16,BODZF,0,1), BBOY_MASK, PPCCOM, { BI, BD } }, +{ "bdzfa-", BBO(16,BODZF,1,0), BBOY_MASK, PPCCOM, { BI, BDMA } }, +{ "bdzfa+", BBO(16,BODZF,1,0), BBOY_MASK, PPCCOM, { BI, BDPA } }, +{ "bdzfa", BBO(16,BODZF,1,0), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bdzfla-", BBO(16,BODZF,1,1), BBOY_MASK, PPCCOM, { BI, BDMA } }, +{ "bdzfla+", BBO(16,BODZF,1,1), BBOY_MASK, PPCCOM, { BI, BDPA } }, +{ "bdzfla", BBO(16,BODZF,1,1), BBOY_MASK, PPCCOM, { BI, BDA } }, +{ "bc-", B(16,0,0), B_MASK, PPCCOM, { BOE, BI, BDM } }, +{ "bc+", B(16,0,0), B_MASK, PPCCOM, { BOE, BI, BDP } }, +{ "bc", B(16,0,0), B_MASK, COM, { BO, BI, BD } }, +{ "bcl-", B(16,0,1), B_MASK, PPCCOM, { BOE, BI, BDM } }, +{ "bcl+", B(16,0,1), B_MASK, PPCCOM, { BOE, BI, BDP } }, +{ "bcl", B(16,0,1), B_MASK, COM, { BO, BI, BD } }, +{ "bca-", B(16,1,0), B_MASK, PPCCOM, { BOE, BI, BDMA } }, +{ "bca+", B(16,1,0), B_MASK, PPCCOM, { BOE, BI, BDPA } }, +{ "bca", B(16,1,0), B_MASK, COM, { BO, BI, BDA } }, +{ "bcla-", B(16,1,1), B_MASK, PPCCOM, { BOE, BI, BDMA } }, +{ "bcla+", B(16,1,1), B_MASK, PPCCOM, { BOE, BI, BDPA } }, +{ "bcla", B(16,1,1), B_MASK, COM, { BO, BI, BDA } }, + +{ "sc", SC(17,1,0), 0xffffffff, PPC, { 0 } }, +{ "svc", SC(17,0,0), SC_MASK, POWER, { LEV, FL1, FL2 } }, +{ "svcl", SC(17,0,1), SC_MASK, POWER, { LEV, FL1, FL2 } }, +{ "svca", SC(17,1,0), SC_MASK, PWRCOM, { SV } }, +{ "svcla", SC(17,1,1), SC_MASK, POWER, { SV } }, + +{ "b", B(18,0,0), B_MASK, COM, { LI } }, +{ "bl", B(18,0,1), B_MASK, COM, { LI } }, +{ "ba", B(18,1,0), B_MASK, COM, { LIA } }, +{ "bla", B(18,1,1), B_MASK, COM, { LIA } }, + +{ "mcrf", XL(19,0), XLBB_MASK|(3<<21)|(3<<16), COM, { BF, BFA } }, + +{ "blr", XLO(19,BOU,16,0), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "br", XLO(19,BOU,16,0), XLBOBIBB_MASK, PWRCOM, { 0 } }, +{ "blrl", XLO(19,BOU,16,1), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "brl", XLO(19,BOU,16,1), XLBOBIBB_MASK, PWRCOM, { 0 } }, +{ "bdnzlr", XLO(19,BODNZ,16,0), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bdnzlr-", XLO(19,BODNZ,16,0), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bdnzlr+", XLO(19,BODNZP,16,0), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bdnzlrl", XLO(19,BODNZ,16,1), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bdnzlrl-",XLO(19,BODNZ,16,1), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bdnzlrl+",XLO(19,BODNZP,16,1), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bdzlr", XLO(19,BODZ,16,0), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bdzlr-", XLO(19,BODZ,16,0), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bdzlr+", XLO(19,BODZP,16,0), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bdzlrl", XLO(19,BODZ,16,1), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bdzlrl-", XLO(19,BODZ,16,1), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bdzlrl+", XLO(19,BODZP,16,1), XLBOBIBB_MASK, PPCCOM, { 0 } }, +{ "bltlr", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bltlr-", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bltlr+", XLOCB(19,BOTP,CBLT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bltr", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bltlrl", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bltlrl-", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bltlrl+", XLOCB(19,BOTP,CBLT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bltrl", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bgtlr", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtlr-", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtlr+", XLOCB(19,BOTP,CBGT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtr", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bgtlrl", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtlrl-", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtlrl+", XLOCB(19,BOTP,CBGT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtrl", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "beqlr", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqlr-", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqlr+", XLOCB(19,BOTP,CBEQ,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqr", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "beqlrl", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqlrl-", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqlrl+", XLOCB(19,BOTP,CBEQ,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqrl", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bsolr", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsolr-", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsolr+", XLOCB(19,BOTP,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsor", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bsolrl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsolrl-", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsolrl+", XLOCB(19,BOTP,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsorl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bunlr", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bunlr-", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bunlr+", XLOCB(19,BOTP,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bunlrl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bunlrl-", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bunlrl+", XLOCB(19,BOTP,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgelr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgelr-", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgelr+", XLOCB(19,BOFP,CBLT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bger", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bgelrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgelrl-", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgelrl+", XLOCB(19,BOFP,CBLT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgerl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnllr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnllr-", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnllr+", XLOCB(19,BOFP,CBLT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnlr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnllrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnllrl-", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnllrl+", XLOCB(19,BOFP,CBLT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnlrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "blelr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "blelr-", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "blelr+", XLOCB(19,BOFP,CBGT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bler", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "blelrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "blelrl-", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "blelrl+", XLOCB(19,BOFP,CBGT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "blerl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnglr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnglr-", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnglr+", XLOCB(19,BOFP,CBGT,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bngr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnglrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnglrl-", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnglrl+", XLOCB(19,BOFP,CBGT,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bngrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnelr", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnelr-", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnelr+", XLOCB(19,BOFP,CBEQ,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bner", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnelrl", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnelrl-", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnelrl+", XLOCB(19,BOFP,CBEQ,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnerl", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnslr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnslr-", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnslr+", XLOCB(19,BOFP,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnsr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnslrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnslrl-", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnslrl+", XLOCB(19,BOFP,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnsrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PWRCOM, { CR } }, +{ "bnulr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnulr-", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnulr+", XLOCB(19,BOFP,CBSO,16,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnulrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnulrl-", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnulrl+", XLOCB(19,BOFP,CBSO,16,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "btlr", XLO(19,BOT,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "btlr-", XLO(19,BOT,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "btlr+", XLO(19,BOTP,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bbtr", XLO(19,BOT,16,0), XLBOBB_MASK, PWRCOM, { BI } }, +{ "btlrl", XLO(19,BOT,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "btlrl-", XLO(19,BOT,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "btlrl+", XLO(19,BOTP,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bbtrl", XLO(19,BOT,16,1), XLBOBB_MASK, PWRCOM, { BI } }, +{ "bflr", XLO(19,BOF,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bflr-", XLO(19,BOF,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bflr+", XLO(19,BOFP,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bbfr", XLO(19,BOF,16,0), XLBOBB_MASK, PWRCOM, { BI } }, +{ "bflrl", XLO(19,BOF,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bflrl-", XLO(19,BOF,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bflrl+", XLO(19,BOFP,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bbfrl", XLO(19,BOF,16,1), XLBOBB_MASK, PWRCOM, { BI } }, +{ "bdnztlr", XLO(19,BODNZT,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdnztlr-",XLO(19,BODNZT,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdnztlr+",XLO(19,BODNZTP,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdnztlrl",XLO(19,BODNZT,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdnztlrl-",XLO(19,BODNZT,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdnztlrl+",XLO(19,BODNZTP,16,1), XLBOBB_MASK, PPCCOM,{ BI } }, +{ "bdnzflr", XLO(19,BODNZF,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdnzflr-",XLO(19,BODNZF,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdnzflr+",XLO(19,BODNZFP,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdnzflrl",XLO(19,BODNZF,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdnzflrl-",XLO(19,BODNZF,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdnzflrl+",XLO(19,BODNZFP,16,1), XLBOBB_MASK, PPCCOM,{ BI } }, +{ "bdztlr", XLO(19,BODZT,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdztlr-", XLO(19,BODZT,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdztlr+", XLO(19,BODZTP,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdztlrl", XLO(19,BODZT,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdztlrl-",XLO(19,BODZT,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdztlrl+",XLO(19,BODZTP,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdzflr", XLO(19,BODZF,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdzflr-", XLO(19,BODZF,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdzflr+", XLO(19,BODZFP,16,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdzflrl", XLO(19,BODZF,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdzflrl-",XLO(19,BODZF,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bdzflrl+",XLO(19,BODZFP,16,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bclr", XLLK(19,16,0), XLYBB_MASK, PPCCOM, { BO, BI } }, +{ "bclrl", XLLK(19,16,1), XLYBB_MASK, PPCCOM, { BO, BI } }, +{ "bclr+", XLYLK(19,16,1,0), XLYBB_MASK, PPCCOM, { BOE, BI } }, +{ "bclrl+", XLYLK(19,16,1,1), XLYBB_MASK, PPCCOM, { BOE, BI } }, +{ "bclr-", XLYLK(19,16,0,0), XLYBB_MASK, PPCCOM, { BOE, BI } }, +{ "bclrl-", XLYLK(19,16,0,1), XLYBB_MASK, PPCCOM, { BOE, BI } }, +{ "bcr", XLLK(19,16,0), XLBB_MASK, PWRCOM, { BO, BI } }, +{ "bcrl", XLLK(19,16,1), XLBB_MASK, PWRCOM, { BO, BI } }, + +{ "rfid", XL(19,18), 0xffffffff, PPC64, { 0 } }, + +{ "crnot", XL(19,33), XL_MASK, PPCCOM, { BT, BA, BBA } }, +{ "crnor", XL(19,33), XL_MASK, COM, { BT, BA, BB } }, + +{ "rfi", XL(19,50), 0xffffffff, COM, { 0 } }, +{ "rfci", XL(19,51), 0xffffffff, PPC403, { 0 } }, + +{ "rfsvc", XL(19,82), 0xffffffff, POWER, { 0 } }, + +{ "crandc", XL(19,129), XL_MASK, COM, { BT, BA, BB } }, + +{ "isync", XL(19,150), 0xffffffff, PPCCOM, { 0 } }, +{ "ics", XL(19,150), 0xffffffff, PWRCOM, { 0 } }, + +{ "crclr", XL(19,193), XL_MASK, PPCCOM, { BT, BAT, BBA } }, +{ "crxor", XL(19,193), XL_MASK, COM, { BT, BA, BB } }, + +{ "crnand", XL(19,225), XL_MASK, COM, { BT, BA, BB } }, + +{ "crand", XL(19,257), XL_MASK, COM, { BT, BA, BB } }, + +{ "crset", XL(19,289), XL_MASK, PPCCOM, { BT, BAT, BBA } }, +{ "creqv", XL(19,289), XL_MASK, COM, { BT, BA, BB } }, + +{ "crorc", XL(19,417), XL_MASK, COM, { BT, BA, BB } }, + +{ "crmove", XL(19,449), XL_MASK, PPCCOM, { BT, BA, BBA } }, +{ "cror", XL(19,449), XL_MASK, COM, { BT, BA, BB } }, + +{ "bctr", XLO(19,BOU,528,0), XLBOBIBB_MASK, COM, { 0 } }, +{ "bctrl", XLO(19,BOU,528,1), XLBOBIBB_MASK, COM, { 0 } }, +{ "bltctr", XLOCB(19,BOT,CBLT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bltctr-", XLOCB(19,BOT,CBLT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bltctr+", XLOCB(19,BOTP,CBLT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bltctrl", XLOCB(19,BOT,CBLT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bltctrl-",XLOCB(19,BOT,CBLT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bltctrl+",XLOCB(19,BOTP,CBLT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtctr", XLOCB(19,BOT,CBGT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtctr-", XLOCB(19,BOT,CBGT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtctr+", XLOCB(19,BOTP,CBGT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtctrl", XLOCB(19,BOT,CBGT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtctrl-",XLOCB(19,BOT,CBGT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgtctrl+",XLOCB(19,BOTP,CBGT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqctr", XLOCB(19,BOT,CBEQ,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqctr-", XLOCB(19,BOT,CBEQ,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqctr+", XLOCB(19,BOTP,CBEQ,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqctrl", XLOCB(19,BOT,CBEQ,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqctrl-",XLOCB(19,BOT,CBEQ,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "beqctrl+",XLOCB(19,BOTP,CBEQ,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsoctr", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsoctr-", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsoctr+", XLOCB(19,BOTP,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsoctrl", XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsoctrl-",XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bsoctrl+",XLOCB(19,BOTP,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bunctr", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bunctr-", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bunctr+", XLOCB(19,BOTP,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bunctrl", XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bunctrl-",XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bunctrl+",XLOCB(19,BOTP,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgectr", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgectr-", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgectr+", XLOCB(19,BOFP,CBLT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgectrl", XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgectrl-",XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bgectrl+",XLOCB(19,BOFP,CBLT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnlctr", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnlctr-", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnlctr+", XLOCB(19,BOFP,CBLT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnlctrl", XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnlctrl-",XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnlctrl+",XLOCB(19,BOFP,CBLT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "blectr", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "blectr-", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "blectr+", XLOCB(19,BOFP,CBGT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "blectrl", XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "blectrl-",XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "blectrl+",XLOCB(19,BOFP,CBGT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bngctr", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bngctr-", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bngctr+", XLOCB(19,BOFP,CBGT,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bngctrl", XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bngctrl-",XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bngctrl+",XLOCB(19,BOFP,CBGT,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnectr", XLOCB(19,BOF,CBEQ,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnectr-", XLOCB(19,BOF,CBEQ,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnectr+", XLOCB(19,BOFP,CBEQ,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnectrl", XLOCB(19,BOF,CBEQ,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnectrl-",XLOCB(19,BOF,CBEQ,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnectrl+",XLOCB(19,BOFP,CBEQ,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnsctr", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnsctr-", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnsctr+", XLOCB(19,BOFP,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnsctrl", XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnsctrl-",XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnsctrl+",XLOCB(19,BOFP,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnuctr", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnuctr-", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnuctr+", XLOCB(19,BOFP,CBSO,528,0), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnuctrl", XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnuctrl-",XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "bnuctrl+",XLOCB(19,BOFP,CBSO,528,1), XLBOCBBB_MASK, PPCCOM, { CR } }, +{ "btctr", XLO(19,BOT,528,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "btctr-", XLO(19,BOT,528,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "btctr+", XLO(19,BOTP,528,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "btctrl", XLO(19,BOT,528,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "btctrl-", XLO(19,BOT,528,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "btctrl+", XLO(19,BOTP,528,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bfctr", XLO(19,BOF,528,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bfctr-", XLO(19,BOF,528,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bfctr+", XLO(19,BOFP,528,0), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bfctrl", XLO(19,BOF,528,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bfctrl-", XLO(19,BOF,528,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bfctrl+", XLO(19,BOFP,528,1), XLBOBB_MASK, PPCCOM, { BI } }, +{ "bcctr", XLLK(19,528,0), XLYBB_MASK, PPCCOM, { BO, BI } }, +{ "bcctr-", XLYLK(19,528,0,0), XLYBB_MASK, PPCCOM, { BOE, BI } }, +{ "bcctr+", XLYLK(19,528,1,0), XLYBB_MASK, PPCCOM, { BOE, BI } }, +{ "bcctrl", XLLK(19,528,1), XLYBB_MASK, PPCCOM, { BO, BI } }, +{ "bcctrl-", XLYLK(19,528,0,1), XLYBB_MASK, PPCCOM, { BOE, BI } }, +{ "bcctrl+", XLYLK(19,528,1,1), XLYBB_MASK, PPCCOM, { BOE, BI } }, +{ "bcc", XLLK(19,528,0), XLBB_MASK, PWRCOM, { BO, BI } }, +{ "bccl", XLLK(19,528,1), XLBB_MASK, PWRCOM, { BO, BI } }, + +{ "rlwimi", M(20,0), M_MASK, PPCCOM, { RA,RS,SH,MBE,ME } }, +{ "rlimi", M(20,0), M_MASK, PWRCOM, { RA,RS,SH,MBE,ME } }, + +{ "rlwimi.", M(20,1), M_MASK, PPCCOM, { RA,RS,SH,MBE,ME } }, +{ "rlimi.", M(20,1), M_MASK, PWRCOM, { RA,RS,SH,MBE,ME } }, + +{ "rotlwi", MME(21,31,0), MMBME_MASK, PPCCOM, { RA, RS, SH } }, +{ "clrlwi", MME(21,31,0), MSHME_MASK, PPCCOM, { RA, RS, MB } }, +{ "rlwinm", M(21,0), M_MASK, PPCCOM, { RA,RS,SH,MBE,ME } }, +{ "rlinm", M(21,0), M_MASK, PWRCOM, { RA,RS,SH,MBE,ME } }, +{ "rotlwi.", MME(21,31,1), MMBME_MASK, PPCCOM, { RA,RS,SH } }, +{ "clrlwi.", MME(21,31,1), MSHME_MASK, PPCCOM, { RA, RS, MB } }, +{ "rlwinm.", M(21,1), M_MASK, PPCCOM, { RA,RS,SH,MBE,ME } }, +{ "rlinm.", M(21,1), M_MASK, PWRCOM, { RA,RS,SH,MBE,ME } }, + +{ "rlmi", M(22,0), M_MASK, M601, { RA,RS,RB,MBE,ME } }, +{ "rlmi.", M(22,1), M_MASK, M601, { RA,RS,RB,MBE,ME } }, + +{ "rotlw", MME(23,31,0), MMBME_MASK, PPCCOM, { RA, RS, RB } }, +{ "rlwnm", M(23,0), M_MASK, PPCCOM, { RA,RS,RB,MBE,ME } }, +{ "rlnm", M(23,0), M_MASK, PWRCOM, { RA,RS,RB,MBE,ME } }, +{ "rotlw.", MME(23,31,1), MMBME_MASK, PPCCOM, { RA, RS, RB } }, +{ "rlwnm.", M(23,1), M_MASK, PPCCOM, { RA,RS,RB,MBE,ME } }, +{ "rlnm.", M(23,1), M_MASK, PWRCOM, { RA,RS,RB,MBE,ME } }, + +{ "nop", OP(24), 0xffffffff, PPCCOM, { 0 } }, +{ "ori", OP(24), OP_MASK, PPCCOM, { RA, RS, UI } }, +{ "oril", OP(24), OP_MASK, PWRCOM, { RA, RS, UI } }, + +{ "oris", OP(25), OP_MASK, PPCCOM, { RA, RS, UI } }, +{ "oriu", OP(25), OP_MASK, PWRCOM, { RA, RS, UI } }, + +{ "xori", OP(26), OP_MASK, PPCCOM, { RA, RS, UI } }, +{ "xoril", OP(26), OP_MASK, PWRCOM, { RA, RS, UI } }, + +{ "xoris", OP(27), OP_MASK, PPCCOM, { RA, RS, UI } }, +{ "xoriu", OP(27), OP_MASK, PWRCOM, { RA, RS, UI } }, + +{ "andi.", OP(28), OP_MASK, PPCCOM, { RA, RS, UI } }, +{ "andil.", OP(28), OP_MASK, PWRCOM, { RA, RS, UI } }, + +{ "andis.", OP(29), OP_MASK, PPCCOM, { RA, RS, UI } }, +{ "andiu.", OP(29), OP_MASK, PWRCOM, { RA, RS, UI } }, + +{ "rotldi", MD(30,0,0), MDMB_MASK, PPC64, { RA, RS, SH6 } }, +{ "clrldi", MD(30,0,0), MDSH_MASK, PPC64, { RA, RS, MB6 } }, +{ "rldicl", MD(30,0,0), MD_MASK, PPC64, { RA, RS, SH6, MB6 } }, +{ "rotldi.", MD(30,0,1), MDMB_MASK, PPC64, { RA, RS, SH6 } }, +{ "clrldi.", MD(30,0,1), MDSH_MASK, PPC64, { RA, RS, MB6 } }, +{ "rldicl.", MD(30,0,1), MD_MASK, PPC64, { RA, RS, SH6, MB6 } }, + +{ "rldicr", MD(30,1,0), MD_MASK, PPC64, { RA, RS, SH6, ME6 } }, +{ "rldicr.", MD(30,1,1), MD_MASK, PPC64, { RA, RS, SH6, ME6 } }, + +{ "rldic", MD(30,2,0), MD_MASK, PPC64, { RA, RS, SH6, MB6 } }, +{ "rldic.", MD(30,2,1), MD_MASK, PPC64, { RA, RS, SH6, MB6 } }, + +{ "rldimi", MD(30,3,0), MD_MASK, PPC64, { RA, RS, SH6, MB6 } }, +{ "rldimi.", MD(30,3,1), MD_MASK, PPC64, { RA, RS, SH6, MB6 } }, + +{ "rotld", MDS(30,8,0), MDSMB_MASK, PPC64, { RA, RS, RB } }, +{ "rldcl", MDS(30,8,0), MDS_MASK, PPC64, { RA, RS, RB, MB6 } }, +{ "rotld.", MDS(30,8,1), MDSMB_MASK, PPC64, { RA, RS, RB } }, +{ "rldcl.", MDS(30,8,1), MDS_MASK, PPC64, { RA, RS, RB, MB6 } }, + +{ "rldcr", MDS(30,9,0), MDS_MASK, PPC64, { RA, RS, RB, ME6 } }, +{ "rldcr.", MDS(30,9,1), MDS_MASK, PPC64, { RA, RS, RB, ME6 } }, + +{ "cmpw", XCMPL(31,0,0), XCMPL_MASK, PPCCOM, { OBF, RA, RB } }, +{ "cmpd", XCMPL(31,0,1), XCMPL_MASK, PPC64, { OBF, RA, RB } }, +{ "cmp", X(31,0), XCMP_MASK, PPCONLY, { BF, L, RA, RB } }, +{ "cmp", X(31,0), XCMPL_MASK, PWRCOM, { BF, RA, RB } }, + +{ "twlgt", XTO(31,4,TOLGT), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tlgt", XTO(31,4,TOLGT), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twllt", XTO(31,4,TOLLT), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tllt", XTO(31,4,TOLLT), XTO_MASK, PWRCOM, { RA, RB } }, +{ "tweq", XTO(31,4,TOEQ), XTO_MASK, PPCCOM, { RA, RB } }, +{ "teq", XTO(31,4,TOEQ), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twlge", XTO(31,4,TOLGE), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tlge", XTO(31,4,TOLGE), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twlnl", XTO(31,4,TOLNL), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tlnl", XTO(31,4,TOLNL), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twlle", XTO(31,4,TOLLE), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tlle", XTO(31,4,TOLLE), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twlng", XTO(31,4,TOLNG), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tlng", XTO(31,4,TOLNG), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twgt", XTO(31,4,TOGT), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tgt", XTO(31,4,TOGT), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twge", XTO(31,4,TOGE), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tge", XTO(31,4,TOGE), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twnl", XTO(31,4,TONL), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tnl", XTO(31,4,TONL), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twlt", XTO(31,4,TOLT), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tlt", XTO(31,4,TOLT), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twle", XTO(31,4,TOLE), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tle", XTO(31,4,TOLE), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twng", XTO(31,4,TONG), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tng", XTO(31,4,TONG), XTO_MASK, PWRCOM, { RA, RB } }, +{ "twne", XTO(31,4,TONE), XTO_MASK, PPCCOM, { RA, RB } }, +{ "tne", XTO(31,4,TONE), XTO_MASK, PWRCOM, { RA, RB } }, +{ "trap", XTO(31,4,TOU), 0xffffffff, PPCCOM, { 0 } }, +{ "tw", X(31,4), X_MASK, PPCCOM, { TO, RA, RB } }, +{ "t", X(31,4), X_MASK, PWRCOM, { TO, RA, RB } }, + +{ "subfc", XO(31,8,0,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "sf", XO(31,8,0,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "subc", XO(31,8,0,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfc.", XO(31,8,0,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "sf.", XO(31,8,0,1), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "subc.", XO(31,8,0,1), XO_MASK, PPCCOM, { RT, RB, RA } }, +{ "subfco", XO(31,8,1,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "sfo", XO(31,8,1,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "subco", XO(31,8,1,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfco.", XO(31,8,1,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "sfo.", XO(31,8,1,1), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "subco.", XO(31,8,1,1), XO_MASK, PPC, { RT, RB, RA } }, + +{ "mulhdu", XO(31,9,0,0), XO_MASK, PPC64, { RT, RA, RB } }, +{ "mulhdu.", XO(31,9,0,1), XO_MASK, PPC64, { RT, RA, RB } }, + +{ "addc", XO(31,10,0,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "a", XO(31,10,0,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "addc.", XO(31,10,0,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "a.", XO(31,10,0,1), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "addco", XO(31,10,1,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "ao", XO(31,10,1,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "addco.", XO(31,10,1,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "ao.", XO(31,10,1,1), XO_MASK, PWRCOM, { RT, RA, RB } }, + +{ "mulhwu", XO(31,11,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "mulhwu.", XO(31,11,0,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "mfcr", X(31,19), XRARB_MASK, COM, { RT } }, + +{ "lwarx", X(31,20), X_MASK, PPC, { RT, RA, RB } }, + +{ "ldx", X(31,21), X_MASK, PPC64, { RT, RA, RB } }, + +{ "lwzx", X(31,23), X_MASK, PPCCOM, { RT, RA, RB } }, +{ "lx", X(31,23), X_MASK, PWRCOM, { RT, RA, RB } }, + +{ "slw", XRC(31,24,0), X_MASK, PPCCOM, { RA, RS, RB } }, +{ "sl", XRC(31,24,0), X_MASK, PWRCOM, { RA, RS, RB } }, +{ "slw.", XRC(31,24,1), X_MASK, PPCCOM, { RA, RS, RB } }, +{ "sl.", XRC(31,24,1), X_MASK, PWRCOM, { RA, RS, RB } }, + +{ "cntlzw", XRC(31,26,0), XRB_MASK, PPCCOM, { RA, RS } }, +{ "cntlz", XRC(31,26,0), XRB_MASK, PWRCOM, { RA, RS } }, +{ "cntlzw.", XRC(31,26,1), XRB_MASK, PPCCOM, { RA, RS } }, +{ "cntlz.", XRC(31,26,1), XRB_MASK, PWRCOM, { RA, RS } }, + +{ "sld", XRC(31,27,0), X_MASK, PPC64, { RA, RS, RB } }, +{ "sld.", XRC(31,27,1), X_MASK, PPC64, { RA, RS, RB } }, + +{ "and", XRC(31,28,0), X_MASK, COM, { RA, RS, RB } }, +{ "and.", XRC(31,28,1), X_MASK, COM, { RA, RS, RB } }, + +{ "maskg", XRC(31,29,0), X_MASK, M601, { RA, RS, RB } }, +{ "maskg.", XRC(31,29,1), X_MASK, M601, { RA, RS, RB } }, + +{ "cmplw", XCMPL(31,32,0), XCMPL_MASK, PPCCOM, { OBF, RA, RB } }, +{ "cmpld", XCMPL(31,32,1), XCMPL_MASK, PPC64, { OBF, RA, RB } }, +{ "cmpl", X(31,32), XCMP_MASK, PPCONLY, { BF, L, RA, RB } }, +{ "cmpl", X(31,32), XCMPL_MASK, PWRCOM, { BF, RA, RB } }, + +{ "subf", XO(31,40,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sub", XO(31,40,0,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subf.", XO(31,40,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sub.", XO(31,40,0,1), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfo", XO(31,40,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "subo", XO(31,40,1,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfo.", XO(31,40,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "subo.", XO(31,40,1,1), XO_MASK, PPC, { RT, RB, RA } }, + +{ "ldux", X(31,53), X_MASK, PPC64, { RT, RAL, RB } }, + +{ "dcbst", X(31,54), XRT_MASK, PPC, { RA, RB } }, + +{ "lwzux", X(31,55), X_MASK, PPCCOM, { RT, RAL, RB } }, +{ "lux", X(31,55), X_MASK, PWRCOM, { RT, RA, RB } }, + +{ "cntlzd", XRC(31,58,0), XRB_MASK, PPC64, { RA, RS } }, +{ "cntlzd.", XRC(31,58,1), XRB_MASK, PPC64, { RA, RS } }, + +{ "andc", XRC(31,60,0), X_MASK, COM, { RA, RS, RB } }, +{ "andc.", XRC(31,60,1), X_MASK, COM, { RA, RS, RB } }, + +{ "tdlgt", XTO(31,68,TOLGT), XTO_MASK, PPC64, { RA, RB } }, +{ "tdllt", XTO(31,68,TOLLT), XTO_MASK, PPC64, { RA, RB } }, +{ "tdeq", XTO(31,68,TOEQ), XTO_MASK, PPC64, { RA, RB } }, +{ "tdlge", XTO(31,68,TOLGE), XTO_MASK, PPC64, { RA, RB } }, +{ "tdlnl", XTO(31,68,TOLNL), XTO_MASK, PPC64, { RA, RB } }, +{ "tdlle", XTO(31,68,TOLLE), XTO_MASK, PPC64, { RA, RB } }, +{ "tdlng", XTO(31,68,TOLNG), XTO_MASK, PPC64, { RA, RB } }, +{ "tdgt", XTO(31,68,TOGT), XTO_MASK, PPC64, { RA, RB } }, +{ "tdge", XTO(31,68,TOGE), XTO_MASK, PPC64, { RA, RB } }, +{ "tdnl", XTO(31,68,TONL), XTO_MASK, PPC64, { RA, RB } }, +{ "tdlt", XTO(31,68,TOLT), XTO_MASK, PPC64, { RA, RB } }, +{ "tdle", XTO(31,68,TOLE), XTO_MASK, PPC64, { RA, RB } }, +{ "tdng", XTO(31,68,TONG), XTO_MASK, PPC64, { RA, RB } }, +{ "tdne", XTO(31,68,TONE), XTO_MASK, PPC64, { RA, RB } }, +{ "td", X(31,68), X_MASK, PPC64, { TO, RA, RB } }, + +{ "mulhd", XO(31,73,0,0), XO_MASK, PPC64, { RT, RA, RB } }, +{ "mulhd.", XO(31,73,0,1), XO_MASK, PPC64, { RT, RA, RB } }, + +{ "mulhw", XO(31,75,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "mulhw.", XO(31,75,0,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "mtsrd", X(31,82), XRB_MASK|(1<<20), PPC64, { SR, RS } }, + +{ "mfmsr", X(31,83), XRARB_MASK, COM, { RT } }, + +{ "ldarx", X(31,84), X_MASK, PPC64, { RT, RA, RB } }, + +{ "dcbf", X(31,86), XRT_MASK, PPC, { RA, RB } }, + +{ "lbzx", X(31,87), X_MASK, COM, { RT, RA, RB } }, + +{ "neg", XO(31,104,0,0), XORB_MASK, COM, { RT, RA } }, +{ "neg.", XO(31,104,0,1), XORB_MASK, COM, { RT, RA } }, +{ "nego", XO(31,104,1,0), XORB_MASK, COM, { RT, RA } }, +{ "nego.", XO(31,104,1,1), XORB_MASK, COM, { RT, RA } }, + +{ "mul", XO(31,107,0,0), XO_MASK, M601, { RT, RA, RB } }, +{ "mul.", XO(31,107,0,1), XO_MASK, M601, { RT, RA, RB } }, +{ "mulo", XO(31,107,1,0), XO_MASK, M601, { RT, RA, RB } }, +{ "mulo.", XO(31,107,1,1), XO_MASK, M601, { RT, RA, RB } }, + +{ "mtsrdin", X(31,114), XRA_MASK, PPC64, { RS, RB } }, + +{ "clf", X(31,118), XRB_MASK, POWER, { RT, RA } }, + +{ "lbzux", X(31,119), X_MASK, COM, { RT, RAL, RB } }, + +{ "not", XRC(31,124,0), X_MASK, COM, { RA, RS, RBS } }, +{ "nor", XRC(31,124,0), X_MASK, COM, { RA, RS, RB } }, +{ "not.", XRC(31,124,1), X_MASK, COM, { RA, RS, RBS } }, +{ "nor.", XRC(31,124,1), X_MASK, COM, { RA, RS, RB } }, + +{ "wrtee", X(31,131), XRARB_MASK, PPC403, { RS } }, + +{ "subfe", XO(31,136,0,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "sfe", XO(31,136,0,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "subfe.", XO(31,136,0,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "sfe.", XO(31,136,0,1), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "subfeo", XO(31,136,1,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "sfeo", XO(31,136,1,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "subfeo.", XO(31,136,1,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "sfeo.", XO(31,136,1,1), XO_MASK, PWRCOM, { RT, RA, RB } }, + +{ "adde", XO(31,138,0,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "ae", XO(31,138,0,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "adde.", XO(31,138,0,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "ae.", XO(31,138,0,1), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "addeo", XO(31,138,1,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "aeo", XO(31,138,1,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "addeo.", XO(31,138,1,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "aeo.", XO(31,138,1,1), XO_MASK, PWRCOM, { RT, RA, RB } }, + +{ "mtcr", XFXM(31,144,0xff), XFXFXM_MASK|FXM_MASK, COM, { RS }}, +{ "mtcrf", X(31,144), XFXFXM_MASK, COM, { FXM, RS } }, + +{ "mtmsr", X(31,146), XRARB_MASK, COM, { RS } }, + +{ "stdx", X(31,149), X_MASK, PPC64, { RS, RA, RB } }, + +{ "stwcx.", XRC(31,150,1), X_MASK, PPC, { RS, RA, RB } }, + +{ "stwx", X(31,151), X_MASK, PPCCOM, { RS, RA, RB } }, +{ "stx", X(31,151), X_MASK, PWRCOM, { RS, RA, RB } }, + +{ "slq", XRC(31,152,0), X_MASK, M601, { RA, RS, RB } }, +{ "slq.", XRC(31,152,1), X_MASK, M601, { RA, RS, RB } }, + +{ "sle", XRC(31,153,0), X_MASK, M601, { RA, RS, RB } }, +{ "sle.", XRC(31,153,1), X_MASK, M601, { RA, RS, RB } }, + +{ "wrteei", X(31,163), XE_MASK, PPC403, { E } }, + +{ "mtmsrd", X(31,178), XRARB_MASK, PPC64, { RS } }, + +{ "stdux", X(31,181), X_MASK, PPC64, { RS, RAS, RB } }, + +{ "stwux", X(31,183), X_MASK, PPCCOM, { RS, RAS, RB } }, +{ "stux", X(31,183), X_MASK, PWRCOM, { RS, RA, RB } }, + +{ "sliq", XRC(31,184,0), X_MASK, M601, { RA, RS, SH } }, +{ "sliq.", XRC(31,184,1), X_MASK, M601, { RA, RS, SH } }, + +{ "subfze", XO(31,200,0,0), XORB_MASK, PPCCOM, { RT, RA } }, +{ "sfze", XO(31,200,0,0), XORB_MASK, PWRCOM, { RT, RA } }, +{ "subfze.", XO(31,200,0,1), XORB_MASK, PPCCOM, { RT, RA } }, +{ "sfze.", XO(31,200,0,1), XORB_MASK, PWRCOM, { RT, RA } }, +{ "subfzeo", XO(31,200,1,0), XORB_MASK, PPCCOM, { RT, RA } }, +{ "sfzeo", XO(31,200,1,0), XORB_MASK, PWRCOM, { RT, RA } }, +{ "subfzeo.",XO(31,200,1,1), XORB_MASK, PPCCOM, { RT, RA } }, +{ "sfzeo.", XO(31,200,1,1), XORB_MASK, PWRCOM, { RT, RA } }, + +{ "addze", XO(31,202,0,0), XORB_MASK, PPCCOM, { RT, RA } }, +{ "aze", XO(31,202,0,0), XORB_MASK, PWRCOM, { RT, RA } }, +{ "addze.", XO(31,202,0,1), XORB_MASK, PPCCOM, { RT, RA } }, +{ "aze.", XO(31,202,0,1), XORB_MASK, PWRCOM, { RT, RA } }, +{ "addzeo", XO(31,202,1,0), XORB_MASK, PPCCOM, { RT, RA } }, +{ "azeo", XO(31,202,1,0), XORB_MASK, PWRCOM, { RT, RA } }, +{ "addzeo.", XO(31,202,1,1), XORB_MASK, PPCCOM, { RT, RA } }, +{ "azeo.", XO(31,202,1,1), XORB_MASK, PWRCOM, { RT, RA } }, + +{ "mtsr", X(31,210), XRB_MASK|(1<<20), COM32, { SR, RS } }, + +{ "stdcx.", XRC(31,214,1), X_MASK, PPC64, { RS, RA, RB } }, + +{ "stbx", X(31,215), X_MASK, COM, { RS, RA, RB } }, + +{ "sllq", XRC(31,216,0), X_MASK, M601, { RA, RS, RB } }, +{ "sllq.", XRC(31,216,1), X_MASK, M601, { RA, RS, RB } }, + +{ "sleq", XRC(31,217,0), X_MASK, M601, { RA, RS, RB } }, +{ "sleq.", XRC(31,217,1), X_MASK, M601, { RA, RS, RB } }, + +{ "subfme", XO(31,232,0,0), XORB_MASK, PPCCOM, { RT, RA } }, +{ "sfme", XO(31,232,0,0), XORB_MASK, PWRCOM, { RT, RA } }, +{ "subfme.", XO(31,232,0,1), XORB_MASK, PPCCOM, { RT, RA } }, +{ "sfme.", XO(31,232,0,1), XORB_MASK, PWRCOM, { RT, RA } }, +{ "subfmeo", XO(31,232,1,0), XORB_MASK, PPCCOM, { RT, RA } }, +{ "sfmeo", XO(31,232,1,0), XORB_MASK, PWRCOM, { RT, RA } }, +{ "subfmeo.",XO(31,232,1,1), XORB_MASK, PPCCOM, { RT, RA } }, +{ "sfmeo.", XO(31,232,1,1), XORB_MASK, PWRCOM, { RT, RA } }, + +{ "mulld", XO(31,233,0,0), XO_MASK, PPC64, { RT, RA, RB } }, +{ "mulld.", XO(31,233,0,1), XO_MASK, PPC64, { RT, RA, RB } }, +{ "mulldo", XO(31,233,1,0), XO_MASK, PPC64, { RT, RA, RB } }, +{ "mulldo.", XO(31,233,1,1), XO_MASK, PPC64, { RT, RA, RB } }, + +{ "addme", XO(31,234,0,0), XORB_MASK, PPCCOM, { RT, RA } }, +{ "ame", XO(31,234,0,0), XORB_MASK, PWRCOM, { RT, RA } }, +{ "addme.", XO(31,234,0,1), XORB_MASK, PPCCOM, { RT, RA } }, +{ "ame.", XO(31,234,0,1), XORB_MASK, PWRCOM, { RT, RA } }, +{ "addmeo", XO(31,234,1,0), XORB_MASK, PPCCOM, { RT, RA } }, +{ "ameo", XO(31,234,1,0), XORB_MASK, PWRCOM, { RT, RA } }, +{ "addmeo.", XO(31,234,1,1), XORB_MASK, PPCCOM, { RT, RA } }, +{ "ameo.", XO(31,234,1,1), XORB_MASK, PWRCOM, { RT, RA } }, + +{ "mullw", XO(31,235,0,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "muls", XO(31,235,0,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "mullw.", XO(31,235,0,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "muls.", XO(31,235,0,1), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "mullwo", XO(31,235,1,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "mulso", XO(31,235,1,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "mullwo.", XO(31,235,1,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "mulso.", XO(31,235,1,1), XO_MASK, PWRCOM, { RT, RA, RB } }, + +{ "mtsrin", X(31,242), XRA_MASK, PPC32, { RS, RB } }, +{ "mtsri", X(31,242), XRA_MASK, POWER32, { RS, RB } }, + +{ "dcbtst", X(31,246), XRT_MASK, PPC, { RA, RB } }, + +{ "stbux", X(31,247), X_MASK, COM, { RS, RAS, RB } }, + +{ "slliq", XRC(31,248,0), X_MASK, M601, { RA, RS, SH } }, +{ "slliq.", XRC(31,248,1), X_MASK, M601, { RA, RS, SH } }, + +{ "doz", XO(31,264,0,0), XO_MASK, M601, { RT, RA, RB } }, +{ "doz.", XO(31,264,0,1), XO_MASK, M601, { RT, RA, RB } }, +{ "dozo", XO(31,264,1,0), XO_MASK, M601, { RT, RA, RB } }, +{ "dozo.", XO(31,264,1,1), XO_MASK, M601, { RT, RA, RB } }, + +{ "add", XO(31,266,0,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "cax", XO(31,266,0,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "add.", XO(31,266,0,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "cax.", XO(31,266,0,1), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "addo", XO(31,266,1,0), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "caxo", XO(31,266,1,0), XO_MASK, PWRCOM, { RT, RA, RB } }, +{ "addo.", XO(31,266,1,1), XO_MASK, PPCCOM, { RT, RA, RB } }, +{ "caxo.", XO(31,266,1,1), XO_MASK, PWRCOM, { RT, RA, RB } }, + +{ "lscbx", XRC(31,277,0), X_MASK, M601, { RT, RA, RB } }, +{ "lscbx.", XRC(31,277,1), X_MASK, M601, { RT, RA, RB } }, + +{ "dcbt", X(31,278), XRT_MASK, PPC, { RA, RB } }, + +{ "lhzx", X(31,279), X_MASK, COM, { RT, RA, RB } }, + +{ "icbt", X(31,262), XRT_MASK, PPC403, { RA, RB } }, + +{ "eqv", XRC(31,284,0), X_MASK, COM, { RA, RS, RB } }, +{ "eqv.", XRC(31,284,1), X_MASK, COM, { RA, RS, RB } }, + +{ "tlbie", X(31,306), XRTRA_MASK, PPC, { RB } }, +{ "tlbi", X(31,306), XRT_MASK, POWER, { RA, RB } }, + +{ "eciwx", X(31,310), X_MASK, PPC, { RT, RA, RB } }, + +{ "lhzux", X(31,311), X_MASK, COM, { RT, RAL, RB } }, + +{ "xor", XRC(31,316,0), X_MASK, COM, { RA, RS, RB } }, +{ "xor.", XRC(31,316,1), X_MASK, COM, { RA, RS, RB } }, + +{ "mfexisr", XSPR(31,323,64), XSPR_MASK, PPC403, { RT } }, +{ "mfexier", XSPR(31,323,66), XSPR_MASK, PPC403, { RT } }, +{ "mfbr0", XSPR(31,323,128), XSPR_MASK, PPC403, { RT } }, +{ "mfbr1", XSPR(31,323,129), XSPR_MASK, PPC403, { RT } }, +{ "mfbr2", XSPR(31,323,130), XSPR_MASK, PPC403, { RT } }, +{ "mfbr3", XSPR(31,323,131), XSPR_MASK, PPC403, { RT } }, +{ "mfbr4", XSPR(31,323,132), XSPR_MASK, PPC403, { RT } }, +{ "mfbr5", XSPR(31,323,133), XSPR_MASK, PPC403, { RT } }, +{ "mfbr6", XSPR(31,323,134), XSPR_MASK, PPC403, { RT } }, +{ "mfbr7", XSPR(31,323,135), XSPR_MASK, PPC403, { RT } }, +{ "mfbear", XSPR(31,323,144), XSPR_MASK, PPC403, { RT } }, +{ "mfbesr", XSPR(31,323,145), XSPR_MASK, PPC403, { RT } }, +{ "mfiocr", XSPR(31,323,160), XSPR_MASK, PPC403, { RT } }, +{ "mfdmacr0", XSPR(31,323,192), XSPR_MASK, PPC403, { RT } }, +{ "mfdmact0", XSPR(31,323,193), XSPR_MASK, PPC403, { RT } }, +{ "mfdmada0", XSPR(31,323,194), XSPR_MASK, PPC403, { RT } }, +{ "mfdmasa0", XSPR(31,323,195), XSPR_MASK, PPC403, { RT } }, +{ "mfdmacc0", XSPR(31,323,196), XSPR_MASK, PPC403, { RT } }, +{ "mfdmacr1", XSPR(31,323,200), XSPR_MASK, PPC403, { RT } }, +{ "mfdmact1", XSPR(31,323,201), XSPR_MASK, PPC403, { RT } }, +{ "mfdmada1", XSPR(31,323,202), XSPR_MASK, PPC403, { RT } }, +{ "mfdmasa1", XSPR(31,323,203), XSPR_MASK, PPC403, { RT } }, +{ "mfdmacc1", XSPR(31,323,204), XSPR_MASK, PPC403, { RT } }, +{ "mfdmacr2", XSPR(31,323,208), XSPR_MASK, PPC403, { RT } }, +{ "mfdmact2", XSPR(31,323,209), XSPR_MASK, PPC403, { RT } }, +{ "mfdmada2", XSPR(31,323,210), XSPR_MASK, PPC403, { RT } }, +{ "mfdmasa2", XSPR(31,323,211), XSPR_MASK, PPC403, { RT } }, +{ "mfdmacc2", XSPR(31,323,212), XSPR_MASK, PPC403, { RT } }, +{ "mfdmacr3", XSPR(31,323,216), XSPR_MASK, PPC403, { RT } }, +{ "mfdmact3", XSPR(31,323,217), XSPR_MASK, PPC403, { RT } }, +{ "mfdmada3", XSPR(31,323,218), XSPR_MASK, PPC403, { RT } }, +{ "mfdmasa3", XSPR(31,323,219), XSPR_MASK, PPC403, { RT } }, +{ "mfdmacc3", XSPR(31,323,220), XSPR_MASK, PPC403, { RT } }, +{ "mfdmasr", XSPR(31,323,224), XSPR_MASK, PPC403, { RT } }, +{ "mfdcr", X(31,323), X_MASK, PPC403, { RT, SPR } }, + +{ "div", XO(31,331,0,0), XO_MASK, M601, { RT, RA, RB } }, +{ "div.", XO(31,331,0,1), XO_MASK, M601, { RT, RA, RB } }, +{ "divo", XO(31,331,1,0), XO_MASK, M601, { RT, RA, RB } }, +{ "divo.", XO(31,331,1,1), XO_MASK, M601, { RT, RA, RB } }, + +{ "mfmq", XSPR(31,339,0), XSPR_MASK, M601, { RT } }, +{ "mfxer", XSPR(31,339,1), XSPR_MASK, COM, { RT } }, +{ "mfrtcu", XSPR(31,339,4), XSPR_MASK, COM, { RT } }, +{ "mfrtcl", XSPR(31,339,5), XSPR_MASK, COM, { RT } }, +{ "mfdec", XSPR(31,339,6), XSPR_MASK, MFDEC1, { RT } }, +{ "mflr", XSPR(31,339,8), XSPR_MASK, COM, { RT } }, +{ "mfctr", XSPR(31,339,9), XSPR_MASK, COM, { RT } }, +{ "mftid", XSPR(31,339,17), XSPR_MASK, POWER, { RT } }, +{ "mfdsisr", XSPR(31,339,18), XSPR_MASK, COM, { RT } }, +{ "mfdar", XSPR(31,339,19), XSPR_MASK, COM, { RT } }, +{ "mfdec", XSPR(31,339,22), XSPR_MASK, MFDEC2, { RT } }, +{ "mfsdr0", XSPR(31,339,24), XSPR_MASK, POWER, { RT } }, +{ "mfsdr1", XSPR(31,339,25), XSPR_MASK, COM, { RT } }, +{ "mfsrr0", XSPR(31,339,26), XSPR_MASK, COM, { RT } }, +{ "mfsrr1", XSPR(31,339,27), XSPR_MASK, COM, { RT } }, +{ "mfcmpa", XSPR(31,339,144), XSPR_MASK, PPC860, { RT } }, +{ "mfcmpb", XSPR(31,339,145), XSPR_MASK, PPC860, { RT } }, +{ "mfcmpc", XSPR(31,339,146), XSPR_MASK, PPC860, { RT } }, +{ "mfcmpd", XSPR(31,339,147), XSPR_MASK, PPC860, { RT } }, +{ "mficr", XSPR(31,339,148), XSPR_MASK, PPC860, { RT } }, +{ "mfder", XSPR(31,339,149), XSPR_MASK, PPC860, { RT } }, +{ "mfcounta", XSPR(31,339,150), XSPR_MASK, PPC860, { RT } }, +{ "mfcountb", XSPR(31,339,151), XSPR_MASK, PPC860, { RT } }, +{ "mfcmpe", XSPR(31,339,152), XSPR_MASK, PPC860, { RT } }, +{ "mfcmpf", XSPR(31,339,153), XSPR_MASK, PPC860, { RT } }, +{ "mfcmpg", XSPR(31,339,154), XSPR_MASK, PPC860, { RT } }, +{ "mfcmph", XSPR(31,339,155), XSPR_MASK, PPC860, { RT } }, +{ "mflctrl1", XSPR(31,339,156), XSPR_MASK, PPC860, { RT } }, +{ "mflctrl2", XSPR(31,339,157), XSPR_MASK, PPC860, { RT } }, +{ "mfictrl", XSPR(31,339,158), XSPR_MASK, PPC860, { RT } }, +{ "mfbar", XSPR(31,339,159), XSPR_MASK, PPC860, { RT } }, +{ "mfsprg4", XSPR(31,339,260), XSPR_MASK, PPC405, { RT } }, +{ "mfsprg5", XSPR(31,339,261), XSPR_MASK, PPC405, { RT } }, +{ "mfsprg6", XSPR(31,339,262), XSPR_MASK, PPC405, { RT } }, +{ "mfsprg7", XSPR(31,339,263), XSPR_MASK, PPC405, { RT } }, +{ "mfsprg", XSPR(31,339,272), XSPRG_MASK, PPC, { RT, SPRG } }, +{ "mfsprg0", XSPR(31,339,272), XSPR_MASK, PPC, { RT } }, +{ "mfsprg1", XSPR(31,339,273), XSPR_MASK, PPC, { RT } }, +{ "mfsprg2", XSPR(31,339,274), XSPR_MASK, PPC, { RT } }, +{ "mfsprg3", XSPR(31,339,275), XSPR_MASK, PPC, { RT } }, +{ "mfasr", XSPR(31,339,280), XSPR_MASK, PPC64, { RT } }, +{ "mfear", XSPR(31,339,282), XSPR_MASK, PPC, { RT } }, +{ "mfpvr", XSPR(31,339,287), XSPR_MASK, PPC, { RT } }, +{ "mfibatu", XSPR(31,339,528), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfibatl", XSPR(31,339,529), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfdbatu", XSPR(31,339,536), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfdbatl", XSPR(31,339,537), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfic_cst", XSPR(31,339,560), XSPR_MASK, PPC860, { RT } }, +{ "mfic_adr", XSPR(31,339,561), XSPR_MASK, PPC860, { RT } }, +{ "mfic_dat", XSPR(31,339,562), XSPR_MASK, PPC860, { RT } }, +{ "mfdc_cst", XSPR(31,339,568), XSPR_MASK, PPC860, { RT } }, +{ "mfdc_adr", XSPR(31,339,569), XSPR_MASK, PPC860, { RT } }, +{ "mfdc_dat", XSPR(31,339,570), XSPR_MASK, PPC860, { RT } }, +{ "mfdpdr", XSPR(31,339,630), XSPR_MASK, PPC860, { RT } }, +{ "mfdpir", XSPR(31,339,631), XSPR_MASK, PPC860, { RT } }, +{ "mfimmr", XSPR(31,339,638), XSPR_MASK, PPC860, { RT } }, +{ "mfmi_ctr", XSPR(31,339,784), XSPR_MASK, PPC860, { RT } }, +{ "mfmi_ap", XSPR(31,339,786), XSPR_MASK, PPC860, { RT } }, +{ "mfmi_epn", XSPR(31,339,787), XSPR_MASK, PPC860, { RT } }, +{ "mfmi_twc", XSPR(31,339,789), XSPR_MASK, PPC860, { RT } }, +{ "mfmi_rpn", XSPR(31,339,790), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_ctr", XSPR(31,339,792), XSPR_MASK, PPC860, { RT } }, +{ "mfm_casid",XSPR(31,339,793), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_ap", XSPR(31,339,794), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_epn", XSPR(31,339,795), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_twb", XSPR(31,339,796), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_twc", XSPR(31,339,797), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_rpn", XSPR(31,339,798), XSPR_MASK, PPC860, { RT } }, +{ "mfm_tw", XSPR(31,339,799), XSPR_MASK, PPC860, { RT } }, +{ "mfmi_dbcam",XSPR(31,339,816), XSPR_MASK, PPC860, { RT } }, +{ "mfmi_dbram0",XSPR(31,339,817), XSPR_MASK, PPC860, { RT } }, +{ "mfmi_dbram1",XSPR(31,339,818), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_dbcam", XSPR(31,339,824), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_dbram0",XSPR(31,339,825), XSPR_MASK, PPC860, { RT } }, +{ "mfmd_dbram1",XSPR(31,339,826), XSPR_MASK, PPC860, { RT } }, +{ "mfzpr", XSPR(31,339,944), XSPR_MASK, PPC403, { RT } }, +{ "mfpid", XSPR(31,339,945), XSPR_MASK, PPC403, { RT } }, +{ "mfccr0", XSPR(31,339,947), XSPR_MASK, PPC405, { RT } }, +{ "mficdbdr", XSPR(31,339,979), XSPR_MASK, PPC403, { RT } }, +{ "mfummcr0", XSPR(31,339,936), XSPR_MASK, PPC750, { RT } }, +{ "mfupmc1", XSPR(31,339,937), XSPR_MASK, PPC750, { RT } }, +{ "mfupmc2", XSPR(31,339,938), XSPR_MASK, PPC750, { RT } }, +{ "mfusia", XSPR(31,339,939), XSPR_MASK, PPC750, { RT } }, +{ "mfummcr1", XSPR(31,339,940), XSPR_MASK, PPC750, { RT } }, +{ "mfupmc3", XSPR(31,339,941), XSPR_MASK, PPC750, { RT } }, +{ "mfupmc4", XSPR(31,339,942), XSPR_MASK, PPC750, { RT } }, +{ "mfiac3", XSPR(31,339,948), XSPR_MASK, PPC405, { RT } }, +{ "mfiac4", XSPR(31,339,949), XSPR_MASK, PPC405, { RT } }, +{ "mfdvc1", XSPR(31,339,950), XSPR_MASK, PPC405, { RT } }, +{ "mfdvc2", XSPR(31,339,951), XSPR_MASK, PPC405, { RT } }, +{ "mfmmcr0", XSPR(31,339,952), XSPR_MASK, PPC750, { RT } }, +{ "mfpmc1", XSPR(31,339,953), XSPR_MASK, PPC750, { RT } }, +{ "mfsgr", XSPR(31,339,953), XSPR_MASK, PPC403, { RT } }, +{ "mfpmc2", XSPR(31,339,954), XSPR_MASK, PPC750, { RT } }, +{ "mfdcwr", XSPR(31,339,954), XSPR_MASK, PPC403, { RT } }, +{ "mfsia", XSPR(31,339,955), XSPR_MASK, PPC750, { RT } }, +{ "mfsler", XSPR(31,339,955), XSPR_MASK, PPC405, { RT } }, +{ "mfmmcr1", XSPR(31,339,956), XSPR_MASK, PPC750, { RT } }, +{ "mfsu0r", XSPR(31,339,956), XSPR_MASK, PPC405, { RT } }, +{ "mfpmc3", XSPR(31,339,957), XSPR_MASK, PPC750, { RT } }, +{ "mfdbcr1", XSPR(31,339,957), XSPR_MASK, PPC405, { RT } }, +{ "mfpmc4", XSPR(31,339,958), XSPR_MASK, PPC750, { RT } }, +{ "mfesr", XSPR(31,339,980), XSPR_MASK, PPC403, { RT } }, +{ "mfdear", XSPR(31,339,981), XSPR_MASK, PPC403, { RT } }, +{ "mfevpr", XSPR(31,339,982), XSPR_MASK, PPC403, { RT } }, +{ "mfcdbcr", XSPR(31,339,983), XSPR_MASK, PPC403, { RT } }, +{ "mftsr", XSPR(31,339,984), XSPR_MASK, PPC403, { RT } }, +{ "mftcr", XSPR(31,339,986), XSPR_MASK, PPC403, { RT } }, +{ "mfpit", XSPR(31,339,987), XSPR_MASK, PPC403, { RT } }, +{ "mftbhi", XSPR(31,339,988), XSPR_MASK, PPC403, { RT } }, +{ "mftblo", XSPR(31,339,989), XSPR_MASK, PPC403, { RT } }, +{ "mfsrr2", XSPR(31,339,990), XSPR_MASK, PPC403, { RT } }, +{ "mfsrr3", XSPR(31,339,991), XSPR_MASK, PPC403, { RT } }, +{ "mfdbsr", XSPR(31,339,1008), XSPR_MASK, PPC403, { RT } }, +{ "mfdbcr0", XSPR(31,339,1010), XSPR_MASK, PPC405, { RT } }, +{ "mfiac1", XSPR(31,339,1012), XSPR_MASK, PPC403, { RT } }, +{ "mfiac2", XSPR(31,339,1013), XSPR_MASK, PPC403, { RT } }, +{ "mfdac1", XSPR(31,339,1014), XSPR_MASK, PPC403, { RT } }, +{ "mfdac2", XSPR(31,339,1015), XSPR_MASK, PPC403, { RT } }, +{ "mfdccr", XSPR(31,339,1018), XSPR_MASK, PPC403, { RT } }, +{ "mficcr", XSPR(31,339,1019), XSPR_MASK, PPC403, { RT } }, +{ "mfpbl1", XSPR(31,339,1020), XSPR_MASK, PPC403, { RT } }, +{ "mfpbu1", XSPR(31,339,1021), XSPR_MASK, PPC403, { RT } }, +{ "mfpbl2", XSPR(31,339,1022), XSPR_MASK, PPC403, { RT } }, +{ "mfpbu2", XSPR(31,339,1023), XSPR_MASK, PPC403, { RT } }, +{ "mfl2cr", XSPR(31,339,1017), XSPR_MASK, PPC750, { RT } }, +{ "mfictc", XSPR(31,339,1019), XSPR_MASK, PPC750, { RT } }, +{ "mfthrm1", XSPR(31,339,1020), XSPR_MASK, PPC750, { RT } }, +{ "mfthrm2", XSPR(31,339,1021), XSPR_MASK, PPC750, { RT } }, +{ "mfthrm3", XSPR(31,339,1022), XSPR_MASK, PPC750, { RT } }, +{ "mfspr", X(31,339), X_MASK, COM, { RT, SPR } }, + +{ "lwax", X(31,341), X_MASK, PPC64, { RT, RA, RB } }, + +{ "lhax", X(31,343), X_MASK, COM, { RT, RA, RB } }, + +{ "dccci", X(31,454), XRT_MASK, PPC403, { RA, RB } }, + +{ "abs", XO(31,360,0,0), XORB_MASK, M601, { RT, RA } }, +{ "abs.", XO(31,360,0,1), XORB_MASK, M601, { RT, RA } }, +{ "abso", XO(31,360,1,0), XORB_MASK, M601, { RT, RA } }, +{ "abso.", XO(31,360,1,1), XORB_MASK, M601, { RT, RA } }, + +{ "divs", XO(31,363,0,0), XO_MASK, M601, { RT, RA, RB } }, +{ "divs.", XO(31,363,0,1), XO_MASK, M601, { RT, RA, RB } }, +{ "divso", XO(31,363,1,0), XO_MASK, M601, { RT, RA, RB } }, +{ "divso.", XO(31,363,1,1), XO_MASK, M601, { RT, RA, RB } }, + +{ "tlbia", X(31,370), 0xffffffff, PPC, { 0 } }, + +{ "mftbl", XSPR(31,371,268), XSPR_MASK, PPC, { RT } }, +{ "mftbu", XSPR(31,371,269), XSPR_MASK, PPC, { RT } }, +{ "mftb", X(31,371), X_MASK, PPC, { RT, TBR } }, + +{ "lwaux", X(31,373), X_MASK, PPC64, { RT, RAL, RB } }, + +{ "lhaux", X(31,375), X_MASK, COM, { RT, RAL, RB } }, + +{ "sthx", X(31,407), X_MASK, COM, { RS, RA, RB } }, + +{ "lfqx", X(31,791), X_MASK, POWER2, { FRT, RA, RB } }, + +{ "lfqux", X(31,823), X_MASK, POWER2, { FRT, RA, RB } }, + +{ "stfqx", X(31,919), X_MASK, POWER2, { FRS, RA, RB } }, + +{ "stfqux", X(31,951), X_MASK, POWER2, { FRS, RA, RB } }, + +{ "orc", XRC(31,412,0), X_MASK, COM, { RA, RS, RB } }, +{ "orc.", XRC(31,412,1), X_MASK, COM, { RA, RS, RB } }, + +{ "sradi", XS(31,413,0), XS_MASK, PPC64, { RA, RS, SH6 } }, +{ "sradi.", XS(31,413,1), XS_MASK, PPC64, { RA, RS, SH6 } }, + +{ "slbie", X(31,434), XRTRA_MASK, PPC64, { RB } }, + +{ "ecowx", X(31,438), X_MASK, PPC, { RT, RA, RB } }, + +{ "sthux", X(31,439), X_MASK, COM, { RS, RAS, RB } }, + +{ "mr", XRC(31,444,0), X_MASK, COM, { RA, RS, RBS } }, +{ "or", XRC(31,444,0), X_MASK, COM, { RA, RS, RB } }, +{ "mr.", XRC(31,444,1), X_MASK, COM, { RA, RS, RBS } }, +{ "or.", XRC(31,444,1), X_MASK, COM, { RA, RS, RB } }, + +{ "mtexisr", XSPR(31,451,64), XSPR_MASK, PPC403, { RT } }, +{ "mtexier", XSPR(31,451,66), XSPR_MASK, PPC403, { RT } }, +{ "mtbr0", XSPR(31,451,128), XSPR_MASK, PPC403, { RT } }, +{ "mtbr1", XSPR(31,451,129), XSPR_MASK, PPC403, { RT } }, +{ "mtbr2", XSPR(31,451,130), XSPR_MASK, PPC403, { RT } }, +{ "mtbr3", XSPR(31,451,131), XSPR_MASK, PPC403, { RT } }, +{ "mtbr4", XSPR(31,451,132), XSPR_MASK, PPC403, { RT } }, +{ "mtbr5", XSPR(31,451,133), XSPR_MASK, PPC403, { RT } }, +{ "mtbr6", XSPR(31,451,134), XSPR_MASK, PPC403, { RT } }, +{ "mtbr7", XSPR(31,451,135), XSPR_MASK, PPC403, { RT } }, +{ "mtbear", XSPR(31,451,144), XSPR_MASK, PPC403, { RT } }, +{ "mtbesr", XSPR(31,451,145), XSPR_MASK, PPC403, { RT } }, +{ "mtiocr", XSPR(31,451,160), XSPR_MASK, PPC403, { RT } }, +{ "mtdmacr0", XSPR(31,451,192), XSPR_MASK, PPC403, { RT } }, +{ "mtdmact0", XSPR(31,451,193), XSPR_MASK, PPC403, { RT } }, +{ "mtdmada0", XSPR(31,451,194), XSPR_MASK, PPC403, { RT } }, +{ "mtdmasa0", XSPR(31,451,195), XSPR_MASK, PPC403, { RT } }, +{ "mtdmacc0", XSPR(31,451,196), XSPR_MASK, PPC403, { RT } }, +{ "mtdmacr1", XSPR(31,451,200), XSPR_MASK, PPC403, { RT } }, +{ "mtdmact1", XSPR(31,451,201), XSPR_MASK, PPC403, { RT } }, +{ "mtdmada1", XSPR(31,451,202), XSPR_MASK, PPC403, { RT } }, +{ "mtdmasa1", XSPR(31,451,203), XSPR_MASK, PPC403, { RT } }, +{ "mtdmacc1", XSPR(31,451,204), XSPR_MASK, PPC403, { RT } }, +{ "mtdmacr2", XSPR(31,451,208), XSPR_MASK, PPC403, { RT } }, +{ "mtdmact2", XSPR(31,451,209), XSPR_MASK, PPC403, { RT } }, +{ "mtdmada2", XSPR(31,451,210), XSPR_MASK, PPC403, { RT } }, +{ "mtdmasa2", XSPR(31,451,211), XSPR_MASK, PPC403, { RT } }, +{ "mtdmacc2", XSPR(31,451,212), XSPR_MASK, PPC403, { RT } }, +{ "mtdmacr3", XSPR(31,451,216), XSPR_MASK, PPC403, { RT } }, +{ "mtdmact3", XSPR(31,451,217), XSPR_MASK, PPC403, { RT } }, +{ "mtdmada3", XSPR(31,451,218), XSPR_MASK, PPC403, { RT } }, +{ "mtdmasa3", XSPR(31,451,219), XSPR_MASK, PPC403, { RT } }, +{ "mtdmacc3", XSPR(31,451,220), XSPR_MASK, PPC403, { RT } }, +{ "mtdmasr", XSPR(31,451,224), XSPR_MASK, PPC403, { RT } }, +{ "mtummcr0", XSPR(31,451,936), XSPR_MASK, PPC750, { RT } }, +{ "mtupmc1", XSPR(31,451,937), XSPR_MASK, PPC750, { RT } }, +{ "mtupmc2", XSPR(31,451,938), XSPR_MASK, PPC750, { RT } }, +{ "mtusia", XSPR(31,451,939), XSPR_MASK, PPC750, { RT } }, +{ "mtummcr1", XSPR(31,451,940), XSPR_MASK, PPC750, { RT } }, +{ "mtupmc3", XSPR(31,451,941), XSPR_MASK, PPC750, { RT } }, +{ "mtupmc4", XSPR(31,451,942), XSPR_MASK, PPC750, { RT } }, +{ "mtmmcr0", XSPR(31,451,952), XSPR_MASK, PPC750, { RT } }, +{ "mtpmc1", XSPR(31,451,953), XSPR_MASK, PPC750, { RT } }, +{ "mtpmc2", XSPR(31,451,954), XSPR_MASK, PPC750, { RT } }, +{ "mtsia", XSPR(31,451,955), XSPR_MASK, PPC750, { RT } }, +{ "mtmmcr1", XSPR(31,451,956), XSPR_MASK, PPC750, { RT } }, +{ "mtpmc3", XSPR(31,451,957), XSPR_MASK, PPC750, { RT } }, +{ "mtpmc4", XSPR(31,451,958), XSPR_MASK, PPC750, { RT } }, +{ "mtl2cr", XSPR(31,451,1017), XSPR_MASK, PPC750, { RT } }, +{ "mtictc", XSPR(31,451,1019), XSPR_MASK, PPC750, { RT } }, +{ "mtthrm1", XSPR(31,451,1020), XSPR_MASK, PPC750, { RT } }, +{ "mtthrm2", XSPR(31,451,1021), XSPR_MASK, PPC750, { RT } }, +{ "mtthrm3", XSPR(31,451,1022), XSPR_MASK, PPC750, { RT } }, +{ "mtdcr", X(31,451), X_MASK, PPC403, { SPR, RS } }, + +{ "divdu", XO(31,457,0,0), XO_MASK, PPC64, { RT, RA, RB } }, +{ "divdu.", XO(31,457,0,1), XO_MASK, PPC64, { RT, RA, RB } }, +{ "divduo", XO(31,457,1,0), XO_MASK, PPC64, { RT, RA, RB } }, +{ "divduo.", XO(31,457,1,1), XO_MASK, PPC64, { RT, RA, RB } }, + +{ "divwu", XO(31,459,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwu.", XO(31,459,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwuo", XO(31,459,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwuo.", XO(31,459,1,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "mtmq", XSPR(31,467,0), XSPR_MASK, M601, { RS } }, +{ "mtxer", XSPR(31,467,1), XSPR_MASK, COM, { RS } }, +{ "mtlr", XSPR(31,467,8), XSPR_MASK, COM, { RS } }, +{ "mtctr", XSPR(31,467,9), XSPR_MASK, COM, { RS } }, +{ "mttid", XSPR(31,467,17), XSPR_MASK, POWER, { RS } }, +{ "mtdsisr", XSPR(31,467,18), XSPR_MASK, COM, { RS } }, +{ "mtdar", XSPR(31,467,19), XSPR_MASK, COM, { RS } }, +{ "mtrtcu", XSPR(31,467,20), XSPR_MASK, COM, { RS } }, +{ "mtrtcl", XSPR(31,467,21), XSPR_MASK, COM, { RS } }, +{ "mtdec", XSPR(31,467,22), XSPR_MASK, COM, { RS } }, +{ "mtsdr0", XSPR(31,467,24), XSPR_MASK, POWER, { RS } }, +{ "mtsdr1", XSPR(31,467,25), XSPR_MASK, COM, { RS } }, +{ "mtsrr0", XSPR(31,467,26), XSPR_MASK, COM, { RS } }, +{ "mtsrr1", XSPR(31,467,27), XSPR_MASK, COM, { RS } }, +{ "mtcmpa", XSPR(31,467,144), XSPR_MASK, PPC860, { RT } }, +{ "mtcmpb", XSPR(31,467,145), XSPR_MASK, PPC860, { RT } }, +{ "mtcmpc", XSPR(31,467,146), XSPR_MASK, PPC860, { RT } }, +{ "mtcmpd", XSPR(31,467,147), XSPR_MASK, PPC860, { RT } }, +{ "mticr", XSPR(31,467,148), XSPR_MASK, PPC860, { RT } }, +{ "mtder", XSPR(31,467,149), XSPR_MASK, PPC860, { RT } }, +{ "mtcounta", XSPR(31,467,150), XSPR_MASK, PPC860, { RT } }, +{ "mtcountb", XSPR(31,467,151), XSPR_MASK, PPC860, { RT } }, +{ "mtcmpe", XSPR(31,467,152), XSPR_MASK, PPC860, { RT } }, +{ "mtcmpf", XSPR(31,467,153), XSPR_MASK, PPC860, { RT } }, +{ "mtcmpg", XSPR(31,467,154), XSPR_MASK, PPC860, { RT } }, +{ "mtcmph", XSPR(31,467,155), XSPR_MASK, PPC860, { RT } }, +{ "mtlctrl1", XSPR(31,467,156), XSPR_MASK, PPC860, { RT } }, +{ "mtlctrl2", XSPR(31,467,157), XSPR_MASK, PPC860, { RT } }, +{ "mtictrl", XSPR(31,467,158), XSPR_MASK, PPC860, { RT } }, +{ "mtbar", XSPR(31,467,159), XSPR_MASK, PPC860, { RT } }, +{ "mtsprg", XSPR(31,467,272), XSPRG_MASK, PPC, { SPRG, RS } }, +{ "mtsprg0", XSPR(31,467,272), XSPR_MASK, PPC, { RT } }, +{ "mtsprg1", XSPR(31,467,273), XSPR_MASK, PPC, { RT } }, +{ "mtsprg2", XSPR(31,467,274), XSPR_MASK, PPC, { RT } }, +{ "mtsprg3", XSPR(31,467,275), XSPR_MASK, PPC, { RT } }, +{ "mtsprg4", XSPR(31,467,276), XSPR_MASK, PPC405, { RT } }, +{ "mtsprg5", XSPR(31,467,277), XSPR_MASK, PPC405, { RT } }, +{ "mtsprg6", XSPR(31,467,278), XSPR_MASK, PPC405, { RT } }, +{ "mtsprg7", XSPR(31,467,279), XSPR_MASK, PPC405, { RT } }, +{ "mtasr", XSPR(31,467,280), XSPR_MASK, PPC64, { RS } }, +{ "mtear", XSPR(31,467,282), XSPR_MASK, PPC, { RS } }, +{ "mttbl", XSPR(31,467,284), XSPR_MASK, PPC, { RS } }, +{ "mttbu", XSPR(31,467,285), XSPR_MASK, PPC, { RS } }, +{ "mtibatu", XSPR(31,467,528), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtibatl", XSPR(31,467,529), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtdbatu", XSPR(31,467,536), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtdbatl", XSPR(31,467,537), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtzpr", XSPR(31,467,944), XSPR_MASK, PPC403, { RT } }, +{ "mtpid", XSPR(31,467,945), XSPR_MASK, PPC403, { RT } }, +{ "mtccr0", XSPR(31,467,947), XSPR_MASK, PPC405, { RT } }, +{ "mtiac3", XSPR(31,467,948), XSPR_MASK, PPC405, { RT } }, +{ "mtiac4", XSPR(31,467,949), XSPR_MASK, PPC405, { RT } }, +{ "mtdvc1", XSPR(31,467,950), XSPR_MASK, PPC405, { RT } }, +{ "mtdvc2", XSPR(31,467,951), XSPR_MASK, PPC405, { RT } }, +{ "mtsgr", XSPR(31,467,953), XSPR_MASK, PPC403, { RT } }, +{ "mtdcwr", XSPR(31,467,954), XSPR_MASK, PPC403, { RT } }, +{ "mtsler", XSPR(31,467,955), XSPR_MASK, PPC405, { RT } }, +{ "mtsu0r", XSPR(31,467,956), XSPR_MASK, PPC405, { RT } }, +{ "mtdbcr1", XSPR(31,467,957), XSPR_MASK, PPC405, { RT } }, +{ "mticdbdr",XSPR(31,467,979), XSPR_MASK, PPC403, { RT } }, +{ "mtesr", XSPR(31,467,980), XSPR_MASK, PPC403, { RT } }, +{ "mtdear", XSPR(31,467,981), XSPR_MASK, PPC403, { RT } }, +{ "mtevpr", XSPR(31,467,982), XSPR_MASK, PPC403, { RT } }, +{ "mtcdbcr", XSPR(31,467,983), XSPR_MASK, PPC403, { RT } }, +{ "mttsr", XSPR(31,467,984), XSPR_MASK, PPC403, { RT } }, +{ "mttcr", XSPR(31,467,986), XSPR_MASK, PPC403, { RT } }, +{ "mtpit", XSPR(31,467,987), XSPR_MASK, PPC403, { RT } }, +{ "mttbhi", XSPR(31,467,988), XSPR_MASK, PPC403, { RT } }, +{ "mttblo", XSPR(31,467,989), XSPR_MASK, PPC403, { RT } }, +{ "mtsrr2", XSPR(31,467,990), XSPR_MASK, PPC403, { RT } }, +{ "mtsrr3", XSPR(31,467,991), XSPR_MASK, PPC403, { RT } }, +{ "mtdbsr", XSPR(31,467,1008), XSPR_MASK, PPC403, { RT } }, +{ "mtdbcr0", XSPR(31,467,1010), XSPR_MASK, PPC405, { RT } }, +{ "mtiac1", XSPR(31,467,1012), XSPR_MASK, PPC403, { RT } }, +{ "mtiac2", XSPR(31,467,1013), XSPR_MASK, PPC403, { RT } }, +{ "mtdac1", XSPR(31,467,1014), XSPR_MASK, PPC403, { RT } }, +{ "mtdac2", XSPR(31,467,1015), XSPR_MASK, PPC403, { RT } }, +{ "mtdccr", XSPR(31,467,1018), XSPR_MASK, PPC403, { RT } }, +{ "mticcr", XSPR(31,467,1019), XSPR_MASK, PPC403, { RT } }, +{ "mtpbl1", XSPR(31,467,1020), XSPR_MASK, PPC403, { RT } }, +{ "mtpbu1", XSPR(31,467,1021), XSPR_MASK, PPC403, { RT } }, +{ "mtpbl2", XSPR(31,467,1022), XSPR_MASK, PPC403, { RT } }, +{ "mtpbu2", XSPR(31,467,1023), XSPR_MASK, PPC403, { RT } }, +{ "mtspr", X(31,467), X_MASK, COM, { SPR, RS } }, + +{ "dcbi", X(31,470), XRT_MASK, PPC, { RA, RB } }, + +{ "nand", XRC(31,476,0), X_MASK, COM, { RA, RS, RB } }, +{ "nand.", XRC(31,476,1), X_MASK, COM, { RA, RS, RB } }, + +{ "dcread", X(31,486), X_MASK, PPC403, { RT, RA, RB }}, + +{ "nabs", XO(31,488,0,0), XORB_MASK, M601, { RT, RA } }, +{ "nabs.", XO(31,488,0,1), XORB_MASK, M601, { RT, RA } }, +{ "nabso", XO(31,488,1,0), XORB_MASK, M601, { RT, RA } }, +{ "nabso.", XO(31,488,1,1), XORB_MASK, M601, { RT, RA } }, + +{ "divd", XO(31,489,0,0), XO_MASK, PPC64, { RT, RA, RB } }, +{ "divd.", XO(31,489,0,1), XO_MASK, PPC64, { RT, RA, RB } }, +{ "divdo", XO(31,489,1,0), XO_MASK, PPC64, { RT, RA, RB } }, +{ "divdo.", XO(31,489,1,1), XO_MASK, PPC64, { RT, RA, RB } }, + +{ "divw", XO(31,491,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divw.", XO(31,491,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwo", XO(31,491,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwo.", XO(31,491,1,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "slbia", X(31,498), 0xffffffff, PPC64, { 0 } }, + +{ "cli", X(31,502), XRB_MASK, POWER, { RT, RA } }, + +{ "mcrxr", X(31,512), XRARB_MASK|(3<<21), COM, { BF } }, + +{ "clcs", X(31,531), XRB_MASK, M601, { RT, RA } }, + +{ "lswx", X(31,533), X_MASK, PPCCOM, { RT, RA, RB } }, +{ "lsx", X(31,533), X_MASK, PWRCOM, { RT, RA, RB } }, + +{ "lwbrx", X(31,534), X_MASK, PPCCOM, { RT, RA, RB } }, +{ "lbrx", X(31,534), X_MASK, PWRCOM, { RT, RA, RB } }, + +{ "lfsx", X(31,535), X_MASK, COM, { FRT, RA, RB } }, + +{ "srw", XRC(31,536,0), X_MASK, PPCCOM, { RA, RS, RB } }, +{ "sr", XRC(31,536,0), X_MASK, PWRCOM, { RA, RS, RB } }, +{ "srw.", XRC(31,536,1), X_MASK, PPCCOM, { RA, RS, RB } }, +{ "sr.", XRC(31,536,1), X_MASK, PWRCOM, { RA, RS, RB } }, + +{ "rrib", XRC(31,537,0), X_MASK, M601, { RA, RS, RB } }, +{ "rrib.", XRC(31,537,1), X_MASK, M601, { RA, RS, RB } }, + +{ "srd", XRC(31,539,0), X_MASK, PPC64, { RA, RS, RB } }, +{ "srd.", XRC(31,539,1), X_MASK, PPC64, { RA, RS, RB } }, + +{ "maskir", XRC(31,541,0), X_MASK, M601, { RA, RS, RB } }, +{ "maskir.", XRC(31,541,1), X_MASK, M601, { RA, RS, RB } }, + +{ "tlbsync", X(31,566), 0xffffffff, PPC, { 0 } }, + +{ "lfsux", X(31,567), X_MASK, COM, { FRT, RAS, RB } }, + +{ "mfsr", X(31,595), XRB_MASK|(1<<20), COM32, { RT, SR } }, + +{ "lswi", X(31,597), X_MASK, PPCCOM, { RT, RA, NB } }, +{ "lsi", X(31,597), X_MASK, PWRCOM, { RT, RA, NB } }, + +{ "sync", X(31,598), 0xffffffff, PPCCOM, { 0 } }, +{ "dcs", X(31,598), 0xffffffff, PWRCOM, { 0 } }, + +{ "lfdx", X(31,599), X_MASK, COM, { FRT, RA, RB } }, + +{ "mfsri", X(31,627), X_MASK, PWRCOM, { RT, RA, RB } }, + +{ "dclst", X(31,630), XRB_MASK, PWRCOM, { RS, RA } }, + +{ "lfdux", X(31,631), X_MASK, COM, { FRT, RAS, RB } }, + +{ "mfsrin", X(31,659), XRA_MASK, PPC32, { RT, RB } }, + +{ "stswx", X(31,661), X_MASK, PPCCOM, { RS, RA, RB } }, +{ "stsx", X(31,661), X_MASK, PWRCOM, { RS, RA, RB } }, + +{ "stwbrx", X(31,662), X_MASK, PPCCOM, { RS, RA, RB } }, +{ "stbrx", X(31,662), X_MASK, PWRCOM, { RS, RA, RB } }, + +{ "stfsx", X(31,663), X_MASK, COM, { FRS, RA, RB } }, + +{ "srq", XRC(31,664,0), X_MASK, M601, { RA, RS, RB } }, +{ "srq.", XRC(31,664,1), X_MASK, M601, { RA, RS, RB } }, + +{ "sre", XRC(31,665,0), X_MASK, M601, { RA, RS, RB } }, +{ "sre.", XRC(31,665,1), X_MASK, M601, { RA, RS, RB } }, + +{ "stfsux", X(31,695), X_MASK, COM, { FRS, RAS, RB } }, + +{ "sriq", XRC(31,696,0), X_MASK, M601, { RA, RS, SH } }, +{ "sriq.", XRC(31,696,1), X_MASK, M601, { RA, RS, SH } }, + +{ "stswi", X(31,725), X_MASK, PPCCOM, { RS, RA, NB } }, +{ "stsi", X(31,725), X_MASK, PWRCOM, { RS, RA, NB } }, + +{ "stfdx", X(31,727), X_MASK, COM, { FRS, RA, RB } }, + +{ "srlq", XRC(31,728,0), X_MASK, M601, { RA, RS, RB } }, +{ "srlq.", XRC(31,728,1), X_MASK, M601, { RA, RS, RB } }, + +{ "sreq", XRC(31,729,0), X_MASK, M601, { RA, RS, RB } }, +{ "sreq.", XRC(31,729,1), X_MASK, M601, { RA, RS, RB } }, + +{ "dcba", X(31,758), XRT_MASK, PPC405, { RA, RB } }, + +{ "stfdux", X(31,759), X_MASK, COM, { FRS, RAS, RB } }, + +{ "srliq", XRC(31,760,0), X_MASK, M601, { RA, RS, SH } }, +{ "srliq.", XRC(31,760,1), X_MASK, M601, { RA, RS, SH } }, + +{ "lhbrx", X(31,790), X_MASK, COM, { RT, RA, RB } }, + +{ "sraw", XRC(31,792,0), X_MASK, PPCCOM, { RA, RS, RB } }, +{ "sra", XRC(31,792,0), X_MASK, PWRCOM, { RA, RS, RB } }, +{ "sraw.", XRC(31,792,1), X_MASK, PPCCOM, { RA, RS, RB } }, +{ "sra.", XRC(31,792,1), X_MASK, PWRCOM, { RA, RS, RB } }, + +{ "srad", XRC(31,794,0), X_MASK, PPC64, { RA, RS, RB } }, +{ "srad.", XRC(31,794,1), X_MASK, PPC64, { RA, RS, RB } }, + +{ "rac", X(31,818), X_MASK, PWRCOM, { RT, RA, RB } }, + +{ "srawi", XRC(31,824,0), X_MASK, PPCCOM, { RA, RS, SH } }, +{ "srai", XRC(31,824,0), X_MASK, PWRCOM, { RA, RS, SH } }, +{ "srawi.", XRC(31,824,1), X_MASK, PPCCOM, { RA, RS, SH } }, +{ "srai.", XRC(31,824,1), X_MASK, PWRCOM, { RA, RS, SH } }, + +{ "eieio", X(31,854), 0xffffffff, PPC, { 0 } }, + +{ "tlbsx", XRC(31,914,0), X_MASK, PPC403, { RT, RA, RB } }, +{ "tlbsx.", XRC(31,914,1), X_MASK, PPC403, { RT, RA, RB } }, + +{ "sthbrx", X(31,918), X_MASK, COM, { RS, RA, RB } }, + +{ "sraq", XRC(31,920,0), X_MASK, M601, { RA, RS, RB } }, +{ "sraq.", XRC(31,920,1), X_MASK, M601, { RA, RS, RB } }, + +{ "srea", XRC(31,921,0), X_MASK, M601, { RA, RS, RB } }, +{ "srea.", XRC(31,921,1), X_MASK, M601, { RA, RS, RB } }, + +{ "extsh", XRC(31,922,0), XRB_MASK, PPCCOM, { RA, RS } }, +{ "exts", XRC(31,922,0), XRB_MASK, PWRCOM, { RA, RS } }, +{ "extsh.", XRC(31,922,1), XRB_MASK, PPCCOM, { RA, RS } }, +{ "exts.", XRC(31,922,1), XRB_MASK, PWRCOM, { RA, RS } }, + +{ "tlbrehi", XTLB(31,946,0), XTLB_MASK, PPC403, { RT, RA } }, +{ "tlbrelo", XTLB(31,946,1), XTLB_MASK, PPC403, { RT, RA } }, +{ "tlbre", X(31,946), X_MASK, PPC403, { RT, RA, SH } }, + +{ "sraiq", XRC(31,952,0), X_MASK, M601, { RA, RS, SH } }, +{ "sraiq.", XRC(31,952,1), X_MASK, M601, { RA, RS, SH } }, + +{ "extsb", XRC(31,954,0), XRB_MASK, PPC, { RA, RS} }, +{ "extsb.", XRC(31,954,1), XRB_MASK, PPC, { RA, RS} }, + +{ "iccci", X(31,966), XRT_MASK, PPC403, { RA, RB } }, + +{ "tlbld", X(31,978), XRTRA_MASK, PPC, { RB } }, + +{ "tlbwehi", XTLB(31,978,0), XTLB_MASK, PPC403, { RT, RA } }, +{ "tlbwelo", XTLB(31,978,1), XTLB_MASK, PPC403, { RT, RA } }, +{ "tlbwe", X(31,978), X_MASK, PPC403, { RS, RA, SH } }, + +{ "icbi", X(31,982), XRT_MASK, PPC, { RA, RB } }, + +{ "stfiwx", X(31,983), X_MASK, PPC, { FRS, RA, RB } }, + +{ "extsw", XRC(31,986,0), XRB_MASK, PPC, { RA, RS } }, +{ "extsw.", XRC(31,986,1), XRB_MASK, PPC, { RA, RS } }, + +{ "icread", X(31,998), XRT_MASK, PPC403, { RA, RB } }, + +{ "tlbli", X(31,1010), XRTRA_MASK, PPC, { RB } }, + +{ "dcbz", X(31,1014), XRT_MASK, PPC, { RA, RB } }, +{ "dclz", X(31,1014), XRT_MASK, PPC, { RA, RB } }, + +{ "lvebx", X(31, 7), X_MASK, PPCVEC, { VD, RA, RB } }, +{ "lvehx", X(31, 39), X_MASK, PPCVEC, { VD, RA, RB } }, +{ "lvewx", X(31, 71), X_MASK, PPCVEC, { VD, RA, RB } }, +{ "lvsl", X(31, 6), X_MASK, PPCVEC, { VD, RA, RB } }, +{ "lvsr", X(31, 38), X_MASK, PPCVEC, { VD, RA, RB } }, +{ "lvx", X(31, 103), X_MASK, PPCVEC, { VD, RA, RB } }, +{ "lvxl", X(31, 359), X_MASK, PPCVEC, { VD, RA, RB } }, +{ "stvebx", X(31, 135), X_MASK, PPCVEC, { VS, RA, RB } }, +{ "stvehx", X(31, 167), X_MASK, PPCVEC, { VS, RA, RB } }, +{ "stvewx", X(31, 199), X_MASK, PPCVEC, { VS, RA, RB } }, +{ "stvx", X(31, 231), X_MASK, PPCVEC, { VS, RA, RB } }, +{ "stvxl", X(31, 487), X_MASK, PPCVEC, { VS, RA, RB } }, + +{ "lwz", OP(32), OP_MASK, PPCCOM, { RT, D, RA } }, +{ "l", OP(32), OP_MASK, PWRCOM, { RT, D, RA } }, + +{ "lwzu", OP(33), OP_MASK, PPCCOM, { RT, D, RAL } }, +{ "lu", OP(33), OP_MASK, PWRCOM, { RT, D, RA } }, + +{ "lbz", OP(34), OP_MASK, COM, { RT, D, RA } }, + +{ "lbzu", OP(35), OP_MASK, COM, { RT, D, RAL } }, + +{ "stw", OP(36), OP_MASK, PPCCOM, { RS, D, RA } }, +{ "st", OP(36), OP_MASK, PWRCOM, { RS, D, RA } }, + +{ "stwu", OP(37), OP_MASK, PPCCOM, { RS, D, RAS } }, +{ "stu", OP(37), OP_MASK, PWRCOM, { RS, D, RA } }, + +{ "stb", OP(38), OP_MASK, COM, { RS, D, RA } }, + +{ "stbu", OP(39), OP_MASK, COM, { RS, D, RAS } }, + +{ "lhz", OP(40), OP_MASK, COM, { RT, D, RA } }, + +{ "lhzu", OP(41), OP_MASK, COM, { RT, D, RAL } }, + +{ "lha", OP(42), OP_MASK, COM, { RT, D, RA } }, + +{ "lhau", OP(43), OP_MASK, COM, { RT, D, RAL } }, + +{ "sth", OP(44), OP_MASK, COM, { RS, D, RA } }, + +{ "sthu", OP(45), OP_MASK, COM, { RS, D, RAS } }, + +{ "lmw", OP(46), OP_MASK, PPCCOM, { RT, D, RAM } }, +{ "lm", OP(46), OP_MASK, PWRCOM, { RT, D, RA } }, + +{ "stmw", OP(47), OP_MASK, PPCCOM, { RS, D, RA } }, +{ "stm", OP(47), OP_MASK, PWRCOM, { RS, D, RA } }, + +{ "lfs", OP(48), OP_MASK, COM, { FRT, D, RA } }, + +{ "lfsu", OP(49), OP_MASK, COM, { FRT, D, RAS } }, + +{ "lfd", OP(50), OP_MASK, COM, { FRT, D, RA } }, + +{ "lfdu", OP(51), OP_MASK, COM, { FRT, D, RAS } }, + +{ "stfs", OP(52), OP_MASK, COM, { FRS, D, RA } }, + +{ "stfsu", OP(53), OP_MASK, COM, { FRS, D, RAS } }, + +{ "stfd", OP(54), OP_MASK, COM, { FRS, D, RA } }, + +{ "stfdu", OP(55), OP_MASK, COM, { FRS, D, RAS } }, + +{ "lfq", OP(56), OP_MASK, POWER2, { FRT, D, RA } }, + +{ "lfqu", OP(57), OP_MASK, POWER2, { FRT, D, RA } }, + +{ "ld", DSO(58,0), DS_MASK, PPC64, { RT, DS, RA } }, + +{ "ldu", DSO(58,1), DS_MASK, PPC64, { RT, DS, RAL } }, + +{ "lwa", DSO(58,2), DS_MASK, PPC64, { RT, DS, RA } }, + +{ "fdivs", A(59,18,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fdivs.", A(59,18,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, + +{ "fsubs", A(59,20,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fsubs.", A(59,20,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, + +{ "fadds", A(59,21,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fadds.", A(59,21,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, + +{ "fsqrts", A(59,22,0), AFRAFRC_MASK, PPC, { FRT, FRB } }, +{ "fsqrts.", A(59,22,1), AFRAFRC_MASK, PPC, { FRT, FRB } }, + +{ "fres", A(59,24,0), AFRAFRC_MASK, PPC, { FRT, FRB } }, +{ "fres.", A(59,24,1), AFRAFRC_MASK, PPC, { FRT, FRB } }, + +{ "fmuls", A(59,25,0), AFRB_MASK, PPC, { FRT, FRA, FRC } }, +{ "fmuls.", A(59,25,1), AFRB_MASK, PPC, { FRT, FRA, FRC } }, + +{ "fmsubs", A(59,28,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fmsubs.", A(59,28,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fmadds", A(59,29,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fmadds.", A(59,29,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fnmsubs", A(59,30,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnmsubs.",A(59,30,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fnmadds", A(59,31,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnmadds.",A(59,31,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "stfq", OP(60), OP_MASK, POWER2, { FRS, D, RA } }, + +{ "stfqu", OP(61), OP_MASK, POWER2, { FRS, D, RA } }, + +{ "std", DSO(62,0), DS_MASK, PPC64, { RS, DS, RA } }, + +{ "stdu", DSO(62,1), DS_MASK, PPC64, { RS, DS, RAS } }, + +{ "fcmpu", X(63,0), X_MASK|(3<<21), COM, { BF, FRA, FRB } }, + +{ "frsp", XRC(63,12,0), XRA_MASK, COM, { FRT, FRB } }, +{ "frsp.", XRC(63,12,1), XRA_MASK, COM, { FRT, FRB } }, + +{ "fctiw", XRC(63,14,0), XRA_MASK, PPCCOM, { FRT, FRB } }, +{ "fcir", XRC(63,14,0), XRA_MASK, POWER2, { FRT, FRB } }, +{ "fctiw.", XRC(63,14,1), XRA_MASK, PPCCOM, { FRT, FRB } }, +{ "fcir.", XRC(63,14,1), XRA_MASK, POWER2, { FRT, FRB } }, + +{ "fctiwz", XRC(63,15,0), XRA_MASK, PPCCOM, { FRT, FRB } }, +{ "fcirz", XRC(63,15,0), XRA_MASK, POWER2, { FRT, FRB } }, +{ "fctiwz.", XRC(63,15,1), XRA_MASK, PPCCOM, { FRT, FRB } }, +{ "fcirz.", XRC(63,15,1), XRA_MASK, POWER2, { FRT, FRB } }, + +{ "fdiv", A(63,18,0), AFRC_MASK, PPCCOM, { FRT, FRA, FRB } }, +{ "fd", A(63,18,0), AFRC_MASK, PWRCOM, { FRT, FRA, FRB } }, +{ "fdiv.", A(63,18,1), AFRC_MASK, PPCCOM, { FRT, FRA, FRB } }, +{ "fd.", A(63,18,1), AFRC_MASK, PWRCOM, { FRT, FRA, FRB } }, + +{ "fsub", A(63,20,0), AFRC_MASK, PPCCOM, { FRT, FRA, FRB } }, +{ "fs", A(63,20,0), AFRC_MASK, PWRCOM, { FRT, FRA, FRB } }, +{ "fsub.", A(63,20,1), AFRC_MASK, PPCCOM, { FRT, FRA, FRB } }, +{ "fs.", A(63,20,1), AFRC_MASK, PWRCOM, { FRT, FRA, FRB } }, + +{ "fadd", A(63,21,0), AFRC_MASK, PPCCOM, { FRT, FRA, FRB } }, +{ "fa", A(63,21,0), AFRC_MASK, PWRCOM, { FRT, FRA, FRB } }, +{ "fadd.", A(63,21,1), AFRC_MASK, PPCCOM, { FRT, FRA, FRB } }, +{ "fa.", A(63,21,1), AFRC_MASK, PWRCOM, { FRT, FRA, FRB } }, + +{ "fsqrt", A(63,22,0), AFRAFRC_MASK, PPCPWR2, { FRT, FRB } }, +{ "fsqrt.", A(63,22,1), AFRAFRC_MASK, PPCPWR2, { FRT, FRB } }, + +{ "fsel", A(63,23,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fsel.", A(63,23,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fmul", A(63,25,0), AFRB_MASK, PPCCOM, { FRT, FRA, FRC } }, +{ "fm", A(63,25,0), AFRB_MASK, PWRCOM, { FRT, FRA, FRC } }, +{ "fmul.", A(63,25,1), AFRB_MASK, PPCCOM, { FRT, FRA, FRC } }, +{ "fm.", A(63,25,1), AFRB_MASK, PWRCOM, { FRT, FRA, FRC } }, + +{ "frsqrte", A(63,26,0), AFRAFRC_MASK, PPC, { FRT, FRB } }, +{ "frsqrte.",A(63,26,1), AFRAFRC_MASK, PPC, { FRT, FRB } }, + +{ "fmsub", A(63,28,0), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } }, +{ "fms", A(63,28,0), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } }, +{ "fmsub.", A(63,28,1), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } }, +{ "fms.", A(63,28,1), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } }, + +{ "fmadd", A(63,29,0), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } }, +{ "fma", A(63,29,0), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } }, +{ "fmadd.", A(63,29,1), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } }, +{ "fma.", A(63,29,1), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } }, + +{ "fnmsub", A(63,30,0), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } }, +{ "fnms", A(63,30,0), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } }, +{ "fnmsub.", A(63,30,1), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } }, +{ "fnms.", A(63,30,1), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } }, + +{ "fnmadd", A(63,31,0), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } }, +{ "fnma", A(63,31,0), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } }, +{ "fnmadd.", A(63,31,1), A_MASK, PPCCOM, { FRT,FRA,FRC,FRB } }, +{ "fnma.", A(63,31,1), A_MASK, PWRCOM, { FRT,FRA,FRC,FRB } }, + +{ "fcmpo", X(63,32), X_MASK|(3<<21), COM, { BF, FRA, FRB } }, + +{ "mtfsb1", XRC(63,38,0), XRARB_MASK, COM, { BT } }, +{ "mtfsb1.", XRC(63,38,1), XRARB_MASK, COM, { BT } }, + +{ "fneg", XRC(63,40,0), XRA_MASK, COM, { FRT, FRB } }, +{ "fneg.", XRC(63,40,1), XRA_MASK, COM, { FRT, FRB } }, + +{ "mcrfs", X(63,64), XRB_MASK|(3<<21)|(3<<16), COM, { BF, BFA } }, + +{ "mtfsb0", XRC(63,70,0), XRARB_MASK, COM, { BT } }, +{ "mtfsb0.", XRC(63,70,1), XRARB_MASK, COM, { BT } }, + +{ "fmr", XRC(63,72,0), XRA_MASK, COM, { FRT, FRB } }, +{ "fmr.", XRC(63,72,1), XRA_MASK, COM, { FRT, FRB } }, + +{ "mtfsfi", XRC(63,134,0), XRA_MASK|(3<<21)|(1<<11), COM, { BF, U } }, +{ "mtfsfi.", XRC(63,134,1), XRA_MASK|(3<<21)|(1<<11), COM, { BF, U } }, + +{ "fnabs", XRC(63,136,0), XRA_MASK, COM, { FRT, FRB } }, +{ "fnabs.", XRC(63,136,1), XRA_MASK, COM, { FRT, FRB } }, + +{ "fabs", XRC(63,264,0), XRA_MASK, COM, { FRT, FRB } }, +{ "fabs.", XRC(63,264,1), XRA_MASK, COM, { FRT, FRB } }, + +{ "mffs", XRC(63,583,0), XRARB_MASK, COM, { FRT } }, +{ "mffs.", XRC(63,583,1), XRARB_MASK, COM, { FRT } }, + +{ "mtfsf", XFL(63,711,0), XFL_MASK, COM, { FLM, FRB } }, +{ "mtfsf.", XFL(63,711,1), XFL_MASK, COM, { FLM, FRB } }, + +{ "fctid", XRC(63,814,0), XRA_MASK, PPC64, { FRT, FRB } }, +{ "fctid.", XRC(63,814,1), XRA_MASK, PPC64, { FRT, FRB } }, + +{ "fctidz", XRC(63,815,0), XRA_MASK, PPC64, { FRT, FRB } }, +{ "fctidz.", XRC(63,815,1), XRA_MASK, PPC64, { FRT, FRB } }, + +{ "fcfid", XRC(63,846,0), XRA_MASK, PPC64, { FRT, FRB } }, +{ "fcfid.", XRC(63,846,1), XRA_MASK, PPC64, { FRT, FRB } }, + +}; + +const int powerpc_num_opcodes = + sizeof (powerpc_opcodes) / sizeof (powerpc_opcodes[0]); + +/* The macro table. This is only used by the assembler. */ + +/* The expressions of the form (-x ! 31) & (x | 31) have the value 0 + when x=0; 32-x when x is between 1 and 31; are negative if x is + negative; and are 32 or more otherwise. This is what you want + when, for instance, you are emulating a right shift by a + rotate-left-and-mask, because the underlying instructions support + shifts of size 0 but not shifts of size 32. By comparison, when + extracting x bits from some word you want to use just 32-x, because + the underlying instructions don't support extracting 0 bits but do + support extracting the whole word (32 bits in this case). */ + +const struct powerpc_macro powerpc_macros[] = { +{ "extldi", 4, PPC64, "rldicr %0,%1,%3,(%2)-1" }, +{ "extldi.", 4, PPC64, "rldicr. %0,%1,%3,(%2)-1" }, +{ "extrdi", 4, PPC64, "rldicl %0,%1,(%2)+(%3),64-(%2)" }, +{ "extrdi.", 4, PPC64, "rldicl. %0,%1,(%2)+(%3),64-(%2)" }, +{ "insrdi", 4, PPC64, "rldimi %0,%1,64-((%2)+(%3)),%3" }, +{ "insrdi.", 4, PPC64, "rldimi. %0,%1,64-((%2)+(%3)),%3" }, +{ "rotrdi", 3, PPC64, "rldicl %0,%1,(-(%2)!63)&((%2)|63),0" }, +{ "rotrdi.", 3, PPC64, "rldicl. %0,%1,(-(%2)!63)&((%2)|63),0" }, +{ "sldi", 3, PPC64, "rldicr %0,%1,%2,63-(%2)" }, +{ "sldi.", 3, PPC64, "rldicr. %0,%1,%2,63-(%2)" }, +{ "srdi", 3, PPC64, "rldicl %0,%1,(-(%2)!63)&((%2)|63),%2" }, +{ "srdi.", 3, PPC64, "rldicl. %0,%1,(-(%2)!63)&((%2)|63),%2" }, +{ "clrrdi", 3, PPC64, "rldicr %0,%1,0,63-(%2)" }, +{ "clrrdi.", 3, PPC64, "rldicr. %0,%1,0,63-(%2)" }, +{ "clrlsldi",4, PPC64, "rldic %0,%1,%3,(%2)-(%3)" }, +{ "clrlsldi.",4, PPC64, "rldic. %0,%1,%3,(%2)-(%3)" }, + +{ "extlwi", 4, PPCCOM, "rlwinm %0,%1,%3,0,(%2)-1" }, +{ "extlwi.", 4, PPCCOM, "rlwinm. %0,%1,%3,0,(%2)-1" }, +{ "extrwi", 4, PPCCOM, "rlwinm %0,%1,(%2)+(%3),32-(%2),31" }, +{ "extrwi.", 4, PPCCOM, "rlwinm. %0,%1,(%2)+(%3),32-(%2),31" }, +{ "inslwi", 4, PPCCOM, "rlwimi %0,%1,(-(%3)!31)&((%3)|31),%3,(%2)+(%3)-1" }, +{ "inslwi.", 4, PPCCOM, "rlwimi. %0,%1,(-(%3)!31)&((%3)|31),%3,(%2)+(%3)-1"}, +{ "insrwi", 4, PPCCOM, "rlwimi %0,%1,32-((%2)+(%3)),%3,(%2)+(%3)-1" }, +{ "insrwi.", 4, PPCCOM, "rlwimi. %0,%1,32-((%2)+(%3)),%3,(%2)+(%3)-1"}, +{ "rotrwi", 3, PPCCOM, "rlwinm %0,%1,(-(%2)!31)&((%2)|31),0,31" }, +{ "rotrwi.", 3, PPCCOM, "rlwinm. %0,%1,(-(%2)!31)&((%2)|31),0,31" }, +{ "slwi", 3, PPCCOM, "rlwinm %0,%1,%2,0,31-(%2)" }, +{ "sli", 3, PWRCOM, "rlinm %0,%1,%2,0,31-(%2)" }, +{ "slwi.", 3, PPCCOM, "rlwinm. %0,%1,%2,0,31-(%2)" }, +{ "sli.", 3, PWRCOM, "rlinm. %0,%1,%2,0,31-(%2)" }, +{ "srwi", 3, PPCCOM, "rlwinm %0,%1,(-(%2)!31)&((%2)|31),%2,31" }, +{ "sri", 3, PWRCOM, "rlinm %0,%1,(-(%2)!31)&((%2)|31),%2,31" }, +{ "srwi.", 3, PPCCOM, "rlwinm. %0,%1,(-(%2)!31)&((%2)|31),%2,31" }, +{ "sri.", 3, PWRCOM, "rlinm. %0,%1,(-(%2)!31)&((%2)|31),%2,31" }, +{ "clrrwi", 3, PPCCOM, "rlwinm %0,%1,0,0,31-(%2)" }, +{ "clrrwi.", 3, PPCCOM, "rlwinm. %0,%1,0,0,31-(%2)" }, +{ "clrlslwi",4, PPCCOM, "rlwinm %0,%1,%3,(%2)-(%3),31-(%3)" }, +{ "clrlslwi.",4, PPCCOM, "rlwinm. %0,%1,%3,(%2)-(%3),31-(%3)" }, + +}; + +const int powerpc_num_macros = + sizeof (powerpc_macros) / sizeof (powerpc_macros[0]); diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kdb/ppc.h linuxppc64_2_4/arch/ppc64/kdb/ppc.h --- linux-2.4.5-ac18/arch/ppc64/kdb/ppc.h Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kdb/ppc.h Mon Jun 4 10:24:01 2001 @@ -0,0 +1,259 @@ +/* ppc.h -- Header file for PowerPC opcode table + Copyright 1994, 1995 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +1, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +will be useful, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this file; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef PPC_H +#define PPC_H + +/* The opcode table is an array of struct powerpc_opcode. */ + +struct powerpc_opcode +{ + /* The opcode name. */ + const char *name; + + /* The opcode itself. Those bits which will be filled in with + operands are zeroes. */ + unsigned long opcode; + + /* The opcode mask. This is used by the disassembler. This is a + mask containing ones indicating those bits which must match the + opcode field, and zeroes indicating those bits which need not + match (and are presumably filled in by operands). */ + unsigned long mask; + + /* One bit flags for the opcode. These are used to indicate which + specific processors support the instructions. The defined values + are listed below. */ + unsigned long flags; + + /* An array of operand codes. Each code is an index into the + operand table. They appear in the order which the operands must + appear in assembly code, and are terminated by a zero. */ + unsigned char operands[8]; +}; + +/* The table itself is sorted by major opcode number, and is otherwise + in the order in which the disassembler should consider + instructions. */ +extern const struct powerpc_opcode powerpc_opcodes[]; +extern const int powerpc_num_opcodes; + +/* Values defined for the flags field of a struct powerpc_opcode. */ + +/* Opcode is defined for the PowerPC architecture. */ +#define PPC_OPCODE_PPC (01) + +/* Opcode is defined for the POWER (RS/6000) architecture. */ +#define PPC_OPCODE_POWER (02) + +/* Opcode is defined for the POWER2 (Rios 2) architecture. */ +#define PPC_OPCODE_POWER2 (04) + +/* Opcode is only defined on 32 bit architectures. */ +#define PPC_OPCODE_32 (010) + +/* Opcode is only defined on 64 bit architectures. */ +#define PPC_OPCODE_64 (020) + +/* Opcode is supported by the Motorola PowerPC 601 processor. The 601 + is assumed to support all PowerPC (PPC_OPCODE_PPC) instructions, + but it also supports many additional POWER instructions. */ +#define PPC_OPCODE_601 (040) + +/* Opcode is supported in both the Power and PowerPC architectures + (ie, compiler's -mcpu=common or assembler's -mcom). */ +#define PPC_OPCODE_COMMON (0100) + +/* Opcode is supported for any Power or PowerPC platform (this is + for the assembler's -many option, and it eliminates duplicates). */ +#define PPC_OPCODE_ANY (0200) + +/* Opcode is supported as part of the 64-bit bridge. */ +#define PPC_OPCODE_64_BRIDGE (0400) + +/* Opcode is supported by Altivec Vector Unit */ +#define PPC_OPCODE_ALTIVEC (01000) + +/* A macro to extract the major opcode from an instruction. */ +#define PPC_OP(i) (((i) >> 26) & 0x3f) + +/* The operands table is an array of struct powerpc_operand. */ + +struct powerpc_operand +{ + /* The number of bits in the operand. */ + int bits; + + /* How far the operand is left shifted in the instruction. */ + int shift; + + /* Insertion function. This is used by the assembler. To insert an + operand value into an instruction, check this field. + + If it is NULL, execute + i |= (op & ((1 << o->bits) - 1)) << o->shift; + (i is the instruction which we are filling in, o is a pointer to + this structure, and op is the opcode value; this assumes twos + complement arithmetic). + + If this field is not NULL, then simply call it with the + instruction and the operand value. It will return the new value + of the instruction. If the ERRMSG argument is not NULL, then if + the operand value is illegal, *ERRMSG will be set to a warning + string (the operand will be inserted in any case). If the + operand value is legal, *ERRMSG will be unchanged (most operands + can accept any value). */ + unsigned long (*insert) PARAMS ((unsigned long instruction, long op, + const char **errmsg)); + + /* Extraction function. This is used by the disassembler. To + extract this operand type from an instruction, check this field. + + If it is NULL, compute + op = ((i) >> o->shift) & ((1 << o->bits) - 1); + if ((o->flags & PPC_OPERAND_SIGNED) != 0 + && (op & (1 << (o->bits - 1))) != 0) + op -= 1 << o->bits; + (i is the instruction, o is a pointer to this structure, and op + is the result; this assumes twos complement arithmetic). + + If this field is not NULL, then simply call it with the + instruction value. It will return the value of the operand. If + the INVALID argument is not NULL, *INVALID will be set to + non-zero if this operand type can not actually be extracted from + this operand (i.e., the instruction does not match). If the + operand is valid, *INVALID will not be changed. */ + long (*extract) PARAMS ((unsigned long instruction, int *invalid)); + + /* One bit syntax flags. */ + unsigned long flags; +}; + +/* Elements in the table are retrieved by indexing with values from + the operands field of the powerpc_opcodes table. */ + +extern const struct powerpc_operand powerpc_operands[]; + +/* Values defined for the flags field of a struct powerpc_operand. */ + +/* This operand takes signed values. */ +#define PPC_OPERAND_SIGNED (01) + +/* This operand takes signed values, but also accepts a full positive + range of values when running in 32 bit mode. That is, if bits is + 16, it takes any value from -0x8000 to 0xffff. In 64 bit mode, + this flag is ignored. */ +#define PPC_OPERAND_SIGNOPT (02) + +/* This operand does not actually exist in the assembler input. This + is used to support extended mnemonics such as mr, for which two + operands fields are identical. The assembler should call the + insert function with any op value. The disassembler should call + the extract function, ignore the return value, and check the value + placed in the valid argument. */ +#define PPC_OPERAND_FAKE (04) + +/* The next operand should be wrapped in parentheses rather than + separated from this one by a comma. This is used for the load and + store instructions which want their operands to look like + reg,displacement(reg) + */ +#define PPC_OPERAND_PARENS (010) + +/* This operand may use the symbolic names for the CR fields, which + are + lt 0 gt 1 eq 2 so 3 un 3 + cr0 0 cr1 1 cr2 2 cr3 3 + cr4 4 cr5 5 cr6 6 cr7 7 + These may be combined arithmetically, as in cr2*4+gt. These are + only supported on the PowerPC, not the POWER. */ +#define PPC_OPERAND_CR (020) + +/* This operand names a register. The disassembler uses this to print + register names with a leading 'r'. */ +#define PPC_OPERAND_GPR (040) + +/* This operand names a floating point register. The disassembler + prints these with a leading 'f'. */ +#define PPC_OPERAND_FPR (0100) + +/* This operand is a relative branch displacement. The disassembler + prints these symbolically if possible. */ +#define PPC_OPERAND_RELATIVE (0200) + +/* This operand is an absolute branch address. The disassembler + prints these symbolically if possible. */ +#define PPC_OPERAND_ABSOLUTE (0400) + +/* This operand is optional, and is zero if omitted. This is used for + the optional BF and L fields in the comparison instructions. The + assembler must count the number of operands remaining on the line, + and the number of operands remaining for the opcode, and decide + whether this operand is present or not. The disassembler should + print this operand out only if it is not zero. */ +#define PPC_OPERAND_OPTIONAL (01000) + +/* This flag is only used with PPC_OPERAND_OPTIONAL. If this operand + is omitted, then for the next operand use this operand value plus + 1, ignoring the next operand field for the opcode. This wretched + hack is needed because the Power rotate instructions can take + either 4 or 5 operands. The disassembler should print this operand + out regardless of the PPC_OPERAND_OPTIONAL field. */ +#define PPC_OPERAND_NEXT (02000) + +/* This operand should be regarded as a negative number for the + purposes of overflow checking (i.e., the normal most negative + number is disallowed and one more than the normal most positive + number is allowed). This flag will only be set for a signed + operand. */ +#define PPC_OPERAND_NEGATIVE (04000) + +/* This operand names a vector unit register. The disassembler + prints these with a leading 'v'. */ +#define PPC_OPERAND_VR (010000) + + +/* The POWER and PowerPC assemblers use a few macros. We keep them + with the operands table for simplicity. The macro table is an + array of struct powerpc_macro. */ + +struct powerpc_macro +{ + /* The macro name. */ + const char *name; + + /* The number of operands the macro takes. */ + unsigned int operands; + + /* One bit flags for the opcode. These are used to indicate which + specific processors support the instructions. The values are the + same as those for the struct powerpc_opcode flags field. */ + unsigned long flags; + + /* A format string to turn the macro into a normal instruction. + Each %N in the string is replaced with operand number N (zero + based). */ + const char *format; +}; + +extern const struct powerpc_macro powerpc_macros[]; +extern const int powerpc_num_macros; + +#endif /* PPC_H */ diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kdb/privinst.h linuxppc64_2_4/arch/ppc64/kdb/privinst.h --- linux-2.4.5-ac18/arch/ppc64/kdb/privinst.h Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kdb/privinst.h Mon Jun 4 10:24:01 2001 @@ -0,0 +1,103 @@ +/* + * 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 + +#define GETREG(reg) \ + static inline unsigned long get_ ## reg (void) \ + { unsigned long ret; asm volatile ("mf" #reg " %0" : "=r" (ret) :); return ret; } + +#define SETREG(reg) \ + static inline void set_ ## reg (unsigned long val) \ + { asm volatile ("mt" #reg " %0" : : "r" (val)); } + +GETREG(msr) +SETREG(msr) +SETREG(msrd) +GETREG(cr) + +#define GSETSPR(n, name) \ + static inline long get_ ## name (void) \ + { long ret; asm volatile ("mfspr %0," #n : "=r" (ret) : ); return ret; } \ + static inline void set_ ## name (long val) \ + { asm volatile ("mtspr " #n ",%0" : : "r" (val)); } + +GSETSPR(0, mq) +GSETSPR(1, xer) +GSETSPR(4, rtcu) +GSETSPR(5, rtcl) +GSETSPR(8, lr) +GSETSPR(9, ctr) +GSETSPR(18, dsisr) +GSETSPR(19, dar) +GSETSPR(22, dec) +GSETSPR(25, sdr1) +GSETSPR(26, srr0) +GSETSPR(27, srr1) +GSETSPR(272, sprg0) +GSETSPR(273, sprg1) +GSETSPR(274, sprg2) +GSETSPR(275, sprg3) +GSETSPR(282, ear) +GSETSPR(287, pvr) +#ifndef CONFIG_8xx +GSETSPR(528, bat0u) +GSETSPR(529, bat0l) +GSETSPR(530, bat1u) +GSETSPR(531, bat1l) +GSETSPR(532, bat2u) +GSETSPR(533, bat2l) +GSETSPR(534, bat3u) +GSETSPR(535, bat3l) +GSETSPR(1008, hid0) +GSETSPR(1009, hid1) +GSETSPR(1010, iabr) +GSETSPR(1013, dabr) +GSETSPR(1023, pir) +#else +GSETSPR(144, cmpa) +GSETSPR(145, cmpb) +GSETSPR(146, cmpc) +GSETSPR(147, cmpd) +GSETSPR(158, ictrl) +#endif + +static inline int get_sr(int n) +{ + int ret; + +#if 0 +// DRENG does not assemble + asm (" mfsrin %0,%1" : "=r" (ret) : "r" (n << 28)); +#endif + return ret; +} + +static inline void set_sr(int n, int val) +{ +#if 0 +// DRENG does not assemble + asm ("mtsrin %0,%1" : : "r" (val), "r" (n << 28)); +#endif +} + +static inline void store_inst(void *p) +{ + asm volatile ("dcbst 0,%0; sync; icbi 0,%0; isync" : : "r" (p)); +} + +static inline void cflush(void *p) +{ + asm volatile ("dcbf 0,%0; icbi 0,%0" : : "r" (p)); +} + +static inline void cinval(void *p) +{ + asm volatile ("dcbi 0,%0; icbi 0,%0" : : "r" (p)); +} + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kdb/start.c linuxppc64_2_4/arch/ppc64/kdb/start.c --- linux-2.4.5-ac18/arch/ppc64/kdb/start.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kdb/start.c Mon Jun 4 14:32:59 2001 @@ -0,0 +1,268 @@ +/* + * Copyright (C) 1996 Paul Mackerras. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_SMP +#include +#endif + +#define TXRDY 0x20 +#define RXRDY 0x01 + +static volatile unsigned char *sccc, * volatile sccd; +extern void xmon_printf(const char *fmt, ...); + + +extern void *comport1; +void +xmon_map_scc(void) +{ + /* should already be mapped by the kernel boot */ + sccd = (volatile unsigned char *) (((unsigned long)comport1)); + sccc = (volatile unsigned char *) (((unsigned long)comport1)+5); +} + +static int scc_initialized = 0; + +void xmon_init_scc(void); + +int +xmon_write(void *handle, void *ptr, int nb) +{ + char *p = ptr; + int i, c, ct; + +#ifdef CONFIG_SMP + static unsigned long xmon_write_lock; + int lock_wait = 1000000; + int locked; + + while ((locked = test_and_set_bit(0, &xmon_write_lock)) != 0) + if (--lock_wait == 0) + break; +#endif + + if (!scc_initialized) + xmon_init_scc(); + ct = 0; + for (i = 0; i < nb; ++i) { + while ((*sccc & TXRDY) == 0) + /* spin for ready */; + c = p[i]; + if (c == '\n' && !ct) { + c = '\r'; + ct = 1; + --i; + } else { + ct = 0; + } + sccd[3] &= ~0x80; /* reset DLAB */ + eieio(); + *sccd = c; + eieio(); + } + +#ifdef CONFIG_SMP + if (!locked) + clear_bit(0, &xmon_write_lock); +#endif + return nb; +} + + +int +xmon_read(void *handle, void *ptr, int nb) +{ + char *p = ptr; + int i; + + if (!scc_initialized) + xmon_init_scc(); + for (i = 0; i < nb; ++i) { + while ((*sccc & RXRDY) == 0) + /* spin for ready */; + sccd[3] &= ~0x80; /* reset DLAB */ + eieio(); + *p++ = *sccd; + } + return i; +} + +int +xmon_read_poll(void) +{ + if ((*sccc & RXRDY) == 0) + return -1; + sccd[3] &= ~0x80; /* reset DLAB */ + eieio(); + return *sccd; +} + +void +xmon_init_scc() +{ + sccd[3] = 0x83; eieio(); /* LCR = 8N1 + DLAB */ + sccd[0] = 12; eieio(); /* DLL = 9600 baud */ + sccd[1] = 0; eieio(); + sccd[2] = 0; eieio(); /* FCR = 0 */ + sccd[3] = 3; eieio(); /* LCR = 8N1 */ + sccd[1] = 0; eieio(); /* IER = 0 */ + scc_initialized = 1; +} + + +void *xmon_stdin; +void *xmon_stdout; +void *xmon_stderr; + +void +xmon_init(void) +{ +} + +int +xmon_putc(int c, void *f) +{ + char ch = c; + + if (c == '\n') + xmon_putc('\r', f); + return xmon_write(f, &ch, 1) == 1? c: -1; +} + +int +xmon_putchar(int c) +{ + return xmon_putc(c, xmon_stdout); +} + +int +xmon_fputs(char *str, void *f) +{ + int n = strlen(str); + + return xmon_write(f, str, n) == n? 0: -1; +} + +int +xmon_readchar(void) +{ + char ch; + + for (;;) { + switch (xmon_read(xmon_stdin, &ch, 1)) { + case 1: + return ch; + case -1: + xmon_printf("read(stdin) returned -1\r\n", 0, 0); + return -1; + } + } +} + +static char line[256]; +static char *lineptr; +static int lineleft; + + +int +xmon_getchar(void) +{ + int c; + + if (lineleft == 0) { + lineptr = line; + for (;;) { + c = xmon_readchar(); + if (c == -1 || c == 4) + break; + if (c == '\r' || c == '\n') { + *lineptr++ = '\n'; + xmon_putchar('\n'); + break; + } + switch (c) { + case 0177: + case '\b': + if (lineptr > line) { + xmon_putchar('\b'); + xmon_putchar(' '); + xmon_putchar('\b'); + --lineptr; + } + break; + case 'U' & 0x1F: + while (lineptr > line) { + xmon_putchar('\b'); + xmon_putchar(' '); + xmon_putchar('\b'); + --lineptr; + } + break; + default: + if (lineptr >= &line[sizeof(line) - 1]) + xmon_putchar('\a'); + else { + xmon_putchar(c); + *lineptr++ = c; + } + } + } + lineleft = lineptr - line; + lineptr = line; + } + if (lineleft == 0) + return -1; + --lineleft; + return *lineptr++; +} + +char * +xmon_fgets(char *str, int nb, void *f) +{ + char *p; + int c; + + for (p = str; p < str + nb - 1; ) { + c = xmon_getchar(); + if (c == -1) { + if (p == str) + return 0; + break; + } + *p++ = c; + if (c == '\n') + break; + } + *p = 0; + return str; +} + +void +xmon_enter(void) +{ +#ifdef CONFIG_ADB_PMU + pmu_suspend(); +#endif +} + +void +xmon_leave(void) +{ +#ifdef CONFIG_ADB_PMU + pmu_resume(); +#endif +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kdb/subr_prf.c linuxppc64_2_4/arch/ppc64/kdb/subr_prf.c --- linux-2.4.5-ac18/arch/ppc64/kdb/subr_prf.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kdb/subr_prf.c Mon Jun 4 14:13:55 2001 @@ -0,0 +1,50 @@ +/* + * Written by Cort Dougan to replace the version originally used + * by Paul Mackerras, which came from NetBSD and thus had copyright + * conflicts with Linux. + * + * This file makes liberal use of the standard linux utility + * routines to reduce the size of the binary. We assume we can + * trust some parts of Linux inside the debugger. + * -- Cort (cort@cs.nmt.edu) + * + * Copyright (C) 1999 Cort Dougan. + */ + +#include +#include +#include +#include "nonstdio.h" + +extern int xmon_write(void *, void *, int); + +void +xmon_vfprintf(void *f, const char *fmt, va_list ap) +{ + static char xmon_buf[2048]; + int n; + + n = vsprintf(xmon_buf, fmt, ap); + xmon_write(f, xmon_buf, n); +} + +void +xmon_printf(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + xmon_vfprintf(stdout, fmt, ap); + va_end(ap); +} + +void +xmon_fprintf(void *f, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + xmon_vfprintf(f, fmt, ap); + va_end(ap); +} + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/HvCall.c linuxppc64_2_4/arch/ppc64/kernel/HvCall.c --- linux-2.4.5-ac18/arch/ppc64/kernel/HvCall.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/HvCall.c Fri May 4 17:13:59 2001 @@ -0,0 +1,121 @@ +/* + * HvCall.c + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * 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 +#include +#include +#include +#include +#include +#include +#ifndef _HVCALLSC_H +#include +#endif +#include + +#ifndef _HVTYPES_H +#include +#endif + + +/*===================================================================== + * Note that this call takes at MOST one page worth of data + */ +int HvCall_readLogBuffer(HvLpIndex lpIndex, void *buffer, u64 bufLen) +{ + struct HvLpBufferList *bufList; + u64 bytesLeft = bufLen; + u64 leftThisPage; + u64 curPtr = virt_to_absolute( (unsigned long) buffer ); + u64 retVal; + int npages; + int i; + + npages = 0; + while (bytesLeft) + { + npages++; + leftThisPage = ((curPtr & PAGE_MASK) + PAGE_SIZE) - curPtr; + + if (leftThisPage > bytesLeft) + bytesLeft = 0; + else + bytesLeft -= leftThisPage; + + curPtr = (curPtr & PAGE_MASK) + PAGE_SIZE; + } + + if (npages == 0) + return 0; + + bufList = (struct HvLpBufferList *)kmalloc(npages * sizeof(struct HvLpBufferList), GFP_ATOMIC); + bytesLeft = bufLen; + curPtr = virt_to_absolute( (unsigned long) buffer ); + for(i=0; i bytesLeft) + { + bufList[i].len = bytesLeft; + bytesLeft = 0; + } + else + { + bufList[i].len = leftThisPage; + bytesLeft -= leftThisPage; + } + + curPtr = (curPtr & PAGE_MASK) + PAGE_SIZE; + } + + + retVal = HvCall3(HvCallBaseReadLogBuffer,lpIndex, virt_to_absolute((unsigned long)bufList), bufLen); + + kfree(bufList); + + return (int)retVal; +} + +/*===================================================================== + */ +void HvCall_writeLogBuffer(const void *buffer, u64 bufLen) +{ + struct HvLpBufferList bufList; + u64 bytesLeft = bufLen; + u64 leftThisPage; + u64 curPtr = virt_to_absolute( (unsigned long) buffer ); + + while (bytesLeft) + { + bufList.addr = curPtr; + + leftThisPage = ((curPtr & PAGE_MASK) + PAGE_SIZE) - curPtr; + + if (leftThisPage > bytesLeft) + { + bufList.len = bytesLeft; + bytesLeft = 0; + } + else + { + bufList.len = leftThisPage; + bytesLeft -= leftThisPage; + } + + curPtr = (curPtr & PAGE_MASK) + PAGE_SIZE; + } + + + HvCall2(HvCallBaseWriteLogBuffer, virt_to_absolute((unsigned long)&bufList), bufLen); + +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/HvLpEvent.c linuxppc64_2_4/arch/ppc64/kernel/HvLpEvent.c --- linux-2.4.5-ac18/arch/ppc64/kernel/HvLpEvent.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/HvLpEvent.c Fri May 4 17:13:58 2001 @@ -0,0 +1,77 @@ +/* + * Copyright 2001 Mike Corrigan IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include + +/* Array of LpEvent handler functions */ +LpEventHandler lpEventHandler[HvLpEvent_Type_NumTypes]; +unsigned lpEventHandlerPaths[HvLpEvent_Type_NumTypes]; + +/* Register a handler for an LpEvent type */ + +int HvLpEvent_registerHandler( HvLpEvent_Type eventType, LpEventHandler handler ) +{ + int rc = 1; + if ( eventType < HvLpEvent_Type_NumTypes ) { + lpEventHandler[eventType] = handler; + rc = 0; + } + return rc; + +} + +int HvLpEvent_unregisterHandler( HvLpEvent_Type eventType ) +{ + int rc = 1; + if ( eventType < HvLpEvent_Type_NumTypes ) { + if ( !lpEventHandlerPaths[eventType] ) { + lpEventHandler[eventType] = NULL; + rc = 0; + } + } + return rc; +} + +/* (lpIndex is the partition index of the target partition. + * needed only for VirtualIo, VirtualLan and SessionMgr. Zero + * indicates to use our partition index - for the other types) + */ +int HvLpEvent_openPath( HvLpEvent_Type eventType, HvLpIndex lpIndex ) +{ + int rc = 1; + if ( eventType < HvLpEvent_Type_NumTypes && + lpEventHandler[eventType] ) { + if ( lpIndex == 0 ) + lpIndex = itLpNaca.xLpIndex; + HvCallEvent_openLpEventPath( lpIndex, eventType ); + ++lpEventHandlerPaths[eventType]; + rc = 0; + } + return rc; +} + +int HvLpEvent_closePath( HvLpEvent_Type eventType, HvLpIndex lpIndex ) +{ + int rc = 1; + if ( eventType < HvLpEvent_Type_NumTypes && + lpEventHandler[eventType] && + lpEventHandlerPaths[eventType] ) { + if ( lpIndex == 0 ) + lpIndex = itLpNaca.xLpIndex; + HvCallEvent_closeLpEventPath( lpIndex, eventType ); + --lpEventHandlerPaths[eventType]; + rc = 0; + } + return rc; +} + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/ItLpQueue.c linuxppc64_2_4/arch/ppc64/kernel/ItLpQueue.c --- linux-2.4.5-ac18/arch/ppc64/kernel/ItLpQueue.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/ItLpQueue.c Fri May 4 17:13:58 2001 @@ -0,0 +1,119 @@ +/* + * ItLpQueue.c + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * 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 +#include +#include +#include +#include +#include +#include + +/* Array of LpEvent handler functions */ +extern LpEventHandler lpEventHandler[HvLpEvent_Type_NumTypes]; + +struct HvLpEvent * ItLpQueue_getNextLpEvent( struct ItLpQueue * lpQueue ) +{ + struct HvLpEvent * nextLpEvent = + (struct HvLpEvent *)lpQueue->xSlicCurEventPtr; + if ( nextLpEvent->xFlags.xValid ) { + /* Set pointer to next potential event */ + lpQueue->xSlicCurEventPtr += ((nextLpEvent->xSizeMinus1 + + LpEventAlign ) / + LpEventAlign ) * + LpEventAlign; + /* Wrap to beginning if no room at end */ + if (lpQueue->xSlicCurEventPtr > lpQueue->xSlicLastValidEventPtr) + lpQueue->xSlicCurEventPtr = lpQueue->xSlicEventStackPtr; + } + else + nextLpEvent = NULL; + + return nextLpEvent; +} + +int ItLpQueue_isLpIntPending( struct ItLpQueue * lpQueue ) +{ + struct HvLpEvent * nextLpEvent = + (struct HvLpEvent *)lpQueue->xSlicCurEventPtr; + return ( nextLpEvent->xFlags.xValid | + lpQueue->xPlicOverflowIntPending); +} + +void ItLpQueue_clearValid( struct HvLpEvent * event ) +{ + /* Clear the valid bit of the event + * Also clear bits within this event that might + * look like valid bits (on 64-byte boundaries) + */ + unsigned extra = (( event->xSizeMinus1 + LpEventAlign ) / + LpEventAlign ) - 1; + switch ( extra ) { + case 3: + ((struct HvLpEvent*)((char*)event+3*LpEventAlign))->xFlags.xValid=0; + case 2: + ((struct HvLpEvent*)((char*)event+2*LpEventAlign))->xFlags.xValid=0; + case 1: + ((struct HvLpEvent*)((char*)event+1*LpEventAlign))->xFlags.xValid=0; + case 0: + } + mb(); + event->xFlags.xValid = 0; +} + +unsigned ItLpQueue_process( struct ItLpQueue * lpQueue, struct pt_regs *regs ) +{ + unsigned numIntsProcessed = 0; + struct HvLpEvent * nextLpEvent; + + for (;;) { + nextLpEvent = ItLpQueue_getNextLpEvent( lpQueue ); + if ( nextLpEvent ) { + /* Count events to return to caller + * and count processed events in lpQueue + */ + ++numIntsProcessed; + lpQueue->xLpIntCount++; + /* Call appropriate handler here, passing + * a pointer to the LpEvent. The handler + * must make a copy of the LpEvent if it + * needs it in a bottom half. (perhaps for + * an ACK) + * + * Handlers are responsible for ACK processing + * + * The Hypervisor guarantees that LpEvents will + * only be delivered with types that we have + * registered for, so no type check is necessary + * here! + */ + if ( nextLpEvent->xType < HvLpEvent_Type_NumTypes ) + lpQueue->xLpIntCountByType[nextLpEvent->xType]++; + if ( nextLpEvent->xType < HvLpEvent_Type_NumTypes && + lpEventHandler[nextLpEvent->xType] ) + lpEventHandler[nextLpEvent->xType](nextLpEvent, regs); + else + printk(KERN_INFO "Unexpected Lp Event type=%d\n", nextLpEvent->xType ); + + ItLpQueue_clearValid( nextLpEvent ); + } + else /* No more valid events + * If overflow events are pending + * process them + */ + if ( lpQueue->xPlicOverflowIntPending ) { + HvCallEvent_getOverflowLpEvents( + lpQueue->xIndex); + } + else /* If nothing left then we are done */ + break; + } + return numIntsProcessed; +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/LparData.c linuxppc64_2_4/arch/ppc64/kernel/LparData.c --- linux-2.4.5-ac18/arch/ppc64/kernel/LparData.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/LparData.c Wed Aug 1 14:06:12 2001 @@ -0,0 +1,221 @@ +/* + * Copyright 2001 Mike Corrigan, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#define __KERNEL__ 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern char _start_boltedStacks[]; + +/* The LparMap data is now located at offset 0x6000 in head.S + * It was put there so that the HvReleaseData could address it + * with a 32-bit offset as required by the iSeries hypervisor + * + * The Naca has a pointer to the ItVpdAreas. The hypervisor finds + * the Naca via the HvReleaseData area. The HvReleaseData has the + * offset into the Naca of the pointer to the ItVpdAreas. + */ + +extern struct ItVpdAreas itVpdAreas; + +/* The LpQueue is used to pass event data from the hypervisor to + * the partition. This is where I/O interrupt events are communicated. + * The ItLpQueue must be initialized (even though only to all zeros) + * If it were uninitialized (in .bss) it would get zeroed after the + * kernel gets control. The hypervisor will have filled in some fields + * before the kernel gets control. By initializing it we keep it out + * of the .bss + */ + +struct ItLpQueue xItLpQueue = {}; + + +/* The HvReleaseData is the root of the information shared between + * the hypervisor and Linux. + */ + +struct HvReleaseData + hvReleaseData = { 0xc8a5d9c4, /* desc = "HvRD" ebcdic */ + sizeof(struct HvReleaseData), + offsetof(struct Naca, xItVpdAreas), + (struct Naca *)(KERNELBASE+0x4000), /* 64-bit Naca address */ + 0x6000, /* offset of LparMap within loadarea (see head.S) */ + 0, + 1, /* tags inactive */ + 0, /* 64 bit */ + 0, /* shared processors */ + 0, /* HMT allowed */ + 6, /* TEMP: This allows non-GA driver */ + 3, /* We are v5r1m0 */ + 3, /* Min supported PLIC = v5r1m0 */ + 3, /* Min usuable PLIC = v5r1m0 */ + { 0xd3, 0x89, 0x95, 0xa4, /* "Linux 2.4 "*/ + 0xa7, 0x40, 0xf2, 0x4b, + 0xf4, 0x4b, 0xf6, 0xf4 }, + {0} + }; + +extern void SystemReset_Iseries(void); +extern void MachineCheck_Iseries(void); +extern void DataAccess_Iseries(void); +extern void InstructionAccess_Iseries(void); +extern void HardwareInterrupt_Iseries(void); +extern void Alignment_Iseries(void); +extern void ProgramCheck_Iseries(void); +extern void FPUnavailable_Iseries(void); +extern void Decrementer_Iseries(void); +extern void Trap_0a_Iseries(void); +extern void Trap_0b_Iseries(void); +extern void SystemCall_Iseries(void); +extern void SingleStep_Iseries(void); +extern void Trap_0e_Iseries(void); +extern void PerformanceMonitor_Iseries(void); + +struct ItLpNaca itLpNaca = { 0xd397d581, /* desc = "LpNa" ebcdic */ + 0x0400, /* size of ItLpNaca */ + 0x0300, 19, /* offset to int array, # ents */ + 0, 0, 0, /* Part # of primary, serv, me */ + 0, 0x100, /* # of LP queues, offset */ + 0, 0, 0, /* Piranha stuff */ + { 0,0,0,0,0 }, /* reserved */ + 0,0,0,0,0,0,0, /* stuff */ + { 0,0,0,0,0 }, /* reserved */ + 0, /* reserved */ + 0, /* VRM index of PLIC */ + 0, 0, /* min supported, compat SLIC */ + 0, /* 64-bit addr of load area */ + 0, /* chunks for load area */ + 0, 0, /* PASE mask, seg table */ + { 0 }, /* 64 reserved bytes */ + { 0 }, /* 128 reserved bytes */ + { 0 }, /* Old LP Queue */ + { 0 }, /* 384 reserved bytes */ + { + (u64)SystemReset_Iseries, /* 0x100 System Reset */ + (u64)MachineCheck_Iseries, /* 0x200 Machine Check */ + (u64)DataAccess_Iseries, /* 0x300 Data Access */ + (u64)InstructionAccess_Iseries, /* 0x400 Instruction Access */ + (u64)HardwareInterrupt_Iseries, /* 0x500 External */ + (u64)Alignment_Iseries, /* 0x600 Alignment */ + (u64)ProgramCheck_Iseries, /* 0x700 Program Check */ + (u64)FPUnavailable_Iseries, /* 0x800 FP Unavailable */ + (u64)Decrementer_Iseries, /* 0x900 Decrementer */ + (u64)Trap_0a_Iseries, /* 0xa00 Trap 0A */ + (u64)Trap_0b_Iseries, /* 0xb00 Trap 0B */ + (u64)SystemCall_Iseries, /* 0xc00 System Call */ + (u64)SingleStep_Iseries, /* 0xd00 Single Step */ + (u64)Trap_0e_Iseries, /* 0xe00 Trap 0E */ + (u64)PerformanceMonitor_Iseries,/* 0xf00 Performance Monitor */ + 0xc0001000, /* int 0x1000 */ + 0xc0001010, /* int 0x1010 */ + 0xc0001020, /* int 0x1020 CPU ctls */ + (u64)HardwareInterrupt_Iseries /* SC Ret Hdlr */ + /* 0x380 D-SLB */ + /* 0x480 I-SLB */ + } + }; + +struct ItIplParmsReal xItIplParmsReal = {}; + +struct IoHriProcessorVpd xIoHriProcessorVpd[maxProcessors] = { + { + xInstCacheOperandSize: 32, + xDataCacheOperandSize: 32, + xProcFreq: 50000000, + xTimeBaseFreq: 50000000, + xPVR: 0x3600 + } +}; + + +u64 xMsVpd[3400] = {}; /* Space for Main Store Vpd 27,200 bytes */ + +u64 xRecoveryLogBuffer[32] = {}; /* Space for Recovery Log Buffer */ + +struct SpCommArea xSpCommArea = { + 0xE2D7C3C2, + 1, + {0}, + 0, 0, 0, 0, {0} +}; + +struct ItVpdAreas itVpdAreas = { + 0xc9a3e5c1, /* "ItVA" */ + sizeof( struct ItVpdAreas ), + 0, 0, + 26, /* # VPD array entries */ + 10, /* # DMA array entries */ + maxProcessors*2, maxProcessors, /* Max logical, physical procs */ + offsetof(struct ItVpdAreas,xPlicDmaToks),/* offset to DMA toks */ + offsetof(struct ItVpdAreas,xSlicVpdAdrs),/* offset to VPD addrs */ + offsetof(struct ItVpdAreas,xPlicDmaLens),/* offset to DMA lens */ + offsetof(struct ItVpdAreas,xSlicVpdLens),/* offset to VPD lens */ + 0, /* max slot labels */ + 1, /* max LP queues */ + {0}, {0}, /* reserved */ + {0}, /* DMA lengths */ + {0}, /* DMA tokens */ + { /* VPD lengths */ + 0,0,0,0, /* 0 - 3 */ + sizeof(struct Paca), /* 4 length of Paca */ + 0, /* 5 */ + sizeof(struct ItIplParmsReal),/* 6 length of IPL parms */ + 26992, /* 7 length of MS VPD */ + 0, /* 8 */ + sizeof(struct ItLpNaca),/* 9 length of LP Naca */ + sizeof(struct SpCommArea), /* 10 */ + 256, /* 11 length of Recovery Log Buf */ + 0,0,0,0, /* 12 - 15 */ + sizeof(struct IoHriProcessorVpd),/* 16 length of Proc Vpd */ + 0,0,0,0,0,0, /* 17 - 22 */ + sizeof(struct ItLpQueue),/* 23 length of Lp Queue */ + 0,0 /* 24 - 25 */ + }, + { /* VPD addresses */ + 0,0,0,0, /* 0 - 3 */ + &xPaca[0], /* 4 first Paca */ + 0, /* 5 */ + &xItIplParmsReal, /* 6 IPL parms */ + &xMsVpd, /* 7 MS Vpd */ + 0, /* 8 */ + &itLpNaca, /* 9 LpNaca */ + &xSpCommArea, /* 10 */ + &xRecoveryLogBuffer, /* 11 Recovery Log Buffer */ + 0,0,0,0, /* 12 - 15 */ + &xIoHriProcessorVpd, /* 16 Proc Vpd */ + 0,0,0,0,0,0, /* 17 - 22 */ + &xItLpQueue, /* 23 Lp Queue */ + 0,0 + } + +}; + +u32 msChunks[4096 * 4] = {0}; +u32 totalLpChunks = 0; + +/* Data area used in flush_hash_page */ +long long flush_hash_page_hpte[2]; + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/Makefile linuxppc64_2_4/arch/ppc64/kernel/Makefile --- linux-2.4.5-ac18/arch/ppc64/kernel/Makefile Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/Makefile Fri Jun 15 13:21:52 2001 @@ -0,0 +1,75 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +USE_STANDARD_AS_RULE := true + +EXTRA_CFLAGS = -mno-minimal-toc + +KHEAD := head.o + +all: $(KHEAD) kernel.o + +O_TARGET := kernel.o + +export-objs := ppc_ksyms.o setup.o + +obj-y := ppc_ksyms.o setup.o entry.o traps.o irq.o idle.o time.o process.o signal.o syscalls.o \ + misc.o ptrace.o align.o semaphore.o bitops.o stab.o \ + htab.o pacaData.o LparData.o udbg.o \ + iSeries_setup.o ItLpQueue.o hvCall.o mf.o viopath.o HvLpEvent.o \ + iSeries_proc.o mf_proc.o proc_pmc.o HvCall.o open_pic.o xics.o \ + binfmt_elf32.o sys_ppc32.o sys32.o ioctl32.o ptrace32.o signal32.o \ + pmc.o + +obj-$(CONFIG_PCI) += pci.o + +ifeq ($(CONFIG_PPC_ISERIES),y) +obj-$(CONFIG_PCI) += iSeries_dma.o iSeries_rtc.o +else +obj-$(CONFIG_PCI) += pci_dma.o +endif + +obj-$(CONFIG_KGDB) += ppc-stub.o + +obj-$(CONFIG_SMP) += smp.o + +# tibit: for matrox_init2() +ifeq ($(CONFIG_NVRAM),y) + obj-$(CONFIG_NVRAM) += pmac_nvram.o +endif + +ifeq ($(CONFIG_ALL_PPC),y) + obj-y += prom.o rtas.o rtas-eventscan.o rtas-proc.o chrp_setup.o \ + chrp_time.o pSeries_pci.o i8259.o +endif + +include $(TOPDIR)/Rules.make + +# +# This is just to get the dependencies... +# + +head.o: head.S ppc_defs.h + +ppc_defs.h: mk_defs.c mk_defs_tpl.c \ + $(TOPDIR)/include/asm/mmu.h \ + $(TOPDIR)/include/asm/processor.h \ + $(TOPDIR)/include/asm/pgtable.h \ + $(TOPDIR)/include/asm/ptrace.h + $(CC) $(CFLAGS) -S mk_defs.c +# for bk, this way we can write to the file even if it's not checked out + rm -f ppc_defs.h mk_defs_out.c + grep printf mk_defs.s > mk_defs_out.c + $(HOSTCC) $(HOSTCFLAGS) -o mk_defs_tpl mk_defs_tpl.c + ./mk_defs_tpl > ppc_defs.h + rm mk_defs.s mk_defs_out.c mk_defs_tpl + +checks: checks.c + $(HOSTCC) -I$(HPATH) $(HOSTCFLAGS) -D__KERNEL__ -fno-builtin -o checks checks.c + ./checks diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/align.c linuxppc64_2_4/arch/ppc64/kernel/align.c --- linux-2.4.5-ac18/arch/ppc64/kernel/align.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/align.c Mon Jul 2 22:55:28 2001 @@ -0,0 +1,368 @@ +/* + * align.c - handle alignment exceptions for the Power PC. + * + * Copyright (c) 1996 Paul Mackerras + * Copyright (c) 1998-1999 TiVo, Inc. + * PowerPC 403GCX modifications. + * Copyright (c) 1999 Grant Erickson + * PowerPC 403GCX/405GP modifications. + * Copyright (c) 2001 PPC64 team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct aligninfo { + unsigned char len; + unsigned char flags; +}; + +#define OPCD(inst) (((inst) & 0xFC000000) >> 26) +#define RS(inst) (((inst) & 0x03E00000) >> 21) +#define RA(inst) (((inst) & 0x001F0000) >> 16) +#define IS_DFORM(code) ((code) >= 32 && (code) <= 47) + +#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. */ +#define D 0x100 /* double-word load/store */ + +#define DCBZ 0x5f /* 8xx/82xx dcbz faults when cache not enabled */ + +/* + * 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 */ + { 8, LD }, /* 00 0 1101: ld */ + INVALID, /* 00 0 1110 */ + { 8, ST }, /* 00 0 1111: std */ + { 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 */ + { 8, ST }, /* 00 1 1101: std */ + INVALID, /* 00 1 1110 */ + INVALID, /* 00 1 1111 */ + { 8, LD }, /* 01 0 0000: ldx */ + INVALID, /* 01 0 0001 */ + { 8, ST }, /* 01 0 0010: stdx */ + 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 */ + { 8, LD+U }, /* 01 0 1101: ldu */ + INVALID, /* 01 0 1110 */ + { 8, ST+U }, /* 01 0 1111: stdu */ + { 8, LD+U }, /* 01 1 0000: ldux */ + INVALID, /* 01 1 0001 */ + { 8, ST+U }, /* 01 1 0010: stdux */ + 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 opcode, f1, f2, f3; + int i, t; + int reg, areg; + unsigned char *addr; + union { + int l; + long ll; + float f; + double d; + unsigned char v[8]; + } data; + + if (__is_processor(PV_POWER4)) { + /* + * The POWER4 has a DSISR register but doesn't set it on + * an alignment fault. -- paulus + */ + + instr = *((unsigned int *)regs->nip); + opcode = OPCD(instr); + reg = RS(instr); + areg = RA(instr); + + if (IS_DFORM(opcode)) { + f1 = 0; + f2 = (instr & 0x04000000) >> 26; + f3 = (instr & 0x78000000) >> 27; + } else { + f1 = (instr & 0x00000006) >> 1; + f2 = (instr & 0x00000040) >> 6; + f3 = (instr & 0x00000780) >> 7; + } + + instr = ((f1 << 5) | (f2 << 4) | f3); + } else { + reg = (regs->dsisr >> 5) & 0x1f; /* source/dest register */ + areg = regs->dsisr & 0x1f; /* register to update */ + instr = (regs->dsisr >> 10) & 0x7f; + instr |= (regs->dsisr >> 13) & 0x60; + } + + nb = aligninfo[instr].len; + if (nb == 0) { + long *p; + int i; + + if (instr != DCBZ) + return 0; /* too hard or invalid instruction */ + /* + * The dcbz (data cache block zero) instruction + * gives an alignment fault if used on non-cacheable + * memory. We handle the fault mainly for the + * case when we are running with the cache disabled + * for debugging. + */ + p = (long *) (regs->dar & -L1_CACHE_BYTES); + for (i = 0; i < L1_CACHE_BYTES / sizeof(long); ++i) + p[i] = 0; + return 1; + } + + flags = aligninfo[instr].flags; + + /* For the 4xx-family processors, the 'dar' field of the + * pt_regs structure is overloaded and is really from the DEAR. + */ + + addr = (unsigned char *)regs->dar; + + /* 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) && (regs->msr & MSR_FP)) + giveup_fpu(current); + 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; + } + } + /* Unfortunately D (== 0x100) doesn't fit in the aligninfo[n].flags + field. So synthesize it here. */ + if ((flags & F) == 0 && nb == 8) + flags |= D; + + 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+D: + regs->gpr[reg] = data.ll; + 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+D: + data.ll = 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->thread.fpr[reg] = data.d; + break; + case ST+F: + data.d = current->thread.fpr[reg]; + break; + /* these require some floating point conversions... */ + /* we'd like to use the assignment, but we have to compile + * the kernel with -msoft-float so it doesn't use the + * fp regs for copying 8-byte objects. */ + case LD+F+S: + enable_kernel_fp(); + cvt_fd(&data.f, ¤t->thread.fpr[reg], ¤t->thread.fpscr); + /* current->thread.fpr[reg] = data.f; */ + break; + case ST+F+S: + enable_kernel_fp(); + cvt_df(¤t->thread.fpr[reg], &data.f, ¤t->thread.fpscr); + /* data.f = current->thread.fpr[reg]; */ + break; + default: + printk("align: can't handle flags=%x\n", flags); + 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) { + regs->gpr[areg] = regs->dar; + } + + return 1; +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/binfmt_elf32.c linuxppc64_2_4/arch/ppc64/kernel/binfmt_elf32.c --- linux-2.4.5-ac18/arch/ppc64/kernel/binfmt_elf32.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/binfmt_elf32.c Thu May 17 13:41:22 2001 @@ -0,0 +1,110 @@ +/* + * binfmt_elf32.c: Support 32-bit PPC ELF binaries on Power3 and followons. + * based on the SPARC64 version. + * Copyright (C) 1995, 1996, 1997, 1998 David S. Miller (davem@redhat.com) + * Copyright (C) 1995, 1996, 1997, 1998 Jakub Jelinek (jj@ultra.linux.cz) + * + * Copyright (C) 2000,2001 Ken Aaker (kdaaker@rchland.vnet.ibm.com), IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#define ELF_ARCH EM_PPC +#define ELF_CLASS ELFCLASS32 +#define ELF_DATA ELFDATA2MSB; + +#include +#include +#include +#include + +struct timeval32 +{ + int tv_sec, tv_usec; +}; + +#define elf_prstatus elf_prstatus32 +struct elf_prstatus32 +{ + struct elf_siginfo pr_info; /* Info associated with signal */ + short pr_cursig; /* Current signal */ + unsigned int pr_sigpend; /* Set of pending signals */ + unsigned int pr_sighold; /* Set of held signals */ + pid_t pr_pid; + pid_t pr_ppid; + pid_t pr_pgrp; + pid_t pr_sid; + struct timeval32 pr_utime; /* User time */ + struct timeval32 pr_stime; /* System time */ + struct timeval32 pr_cutime; /* Cumulative user time */ + struct timeval32 pr_cstime; /* Cumulative system time */ + elf_gregset_t pr_reg; /* General purpose registers. */ + int pr_fpvalid; /* True if math co-processor being used. */ +}; + + +#define elf_prpsinfo elf_prpsinfo32 +struct elf_prpsinfo32 +{ + char pr_state; /* numeric process state */ + char pr_sname; /* char for pr_state */ + char pr_zomb; /* zombie */ + char pr_nice; /* nice val */ + char pr_pad[4]; /* pad to put pr_flag on 8 byte boundary */ + unsigned int pr_flag; /* flags */ + u16 pr_uid; + u16 pr_gid; + pid_t pr_pid, pr_ppid, pr_pgrp, pr_sid; + /* Lots missing */ + char pr_fname[16]; /* filename of executable */ + char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */ +}; + +#include + +#undef NEW_TO_OLD_UID +#undef NEW_TO_OLD_GID +#define NEW_TO_OLD_UID(uid) ((uid) > 65535) ? (u16)overflowuid : (u16)(uid) +#define NEW_TO_OLD_GID(gid) ((gid) > 65535) ? (u16)overflowgid : (u16)(gid) + +#undef start_thread +#define start_thread start_thread32 + +#define set_brk set_brk_elf32 +#define padzero padzero_elf32 +#define create_elf_tables create_elf_tables_elf32 +// #define elf_map elf_map_elf32 +#define load_elf_interp load_elf_interp_elf32 +#define load_aout_interp load_aout_interp_elf32 +#define load_elf_binary load_elf_binary_elf32 +#define load_elf_library load_elf_library_elf32 +#define dump_write dump_write_elf32 +#define dump_seek dump_seek_elf32 +#define maydump maydump_elf32 +#define notesize notesize_elf32 +#define dump_regs dump_regs_elf32 +#define writenote writenote_elf32 +#define elf_core_dump elf_core_dump_elf32 +#define init_elf_binfmt init_elf_binfmt_elf32 +#define exit_elf_binfmt exit_elf_binfmt_elf32 + + +#undef CONFIG_BINFMT_ELF +#ifdef CONFIG_BINFMT_ELF32 +#define CONFIG_BINFMT_ELF CONFIG_BINFMT_ELF32 +#endif +#undef CONFIG_BINFMT_ELF_MODULE +#ifdef CONFIG_BINFMT_ELF32_MODULE +#define CONFIG_BINFMT_ELF_MODULE CONFIG_BINFMT_ELF32_MODULE +#endif + +MODULE_DESCRIPTION("Binary format loader for compatibility with 32bit Linux PPC binaries on the Power3+"); +MODULE_AUTHOR("Eric Youngdale, David S. Miller, Jakub Jelinek, Ken Aaker"); + +#undef MODULE_DESCRIPTION +#undef MODULE_AUTHOR + +#include "../../../fs/binfmt_elf.c" diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/bitops.c linuxppc64_2_4/arch/ppc64/kernel/bitops.c --- linux-2.4.5-ac18/arch/ppc64/kernel/bitops.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/bitops.c Tue Jun 5 01:30:37 2001 @@ -0,0 +1,90 @@ +/* + * Copyright (C) 1996 Paul Mackerras. + * Adapted for ppc64 - Todd Inglett, Anton Blanchard + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG_BITOPS + +/* + * Bitops are weird when viewed on big-endian systems. They were designed + * on little endian so the size of the bitset doesn't matter (low order bytes + * come first) as long as the bit in question is valid. + * + * Bits are "tested" often using the C expression (val & (1<> 6); + unsigned long result = offset & ~63UL; + unsigned long tmp; + + if (offset >= size) + return size; + size -= result; + offset &= 63UL; + + if (offset) { + tmp = *p++; + tmp |= ~0UL >> (64-offset); + if (size < 64) + goto found_first; + if (~tmp) + goto found_middle; + size -= 64; + result += 64; + } + while (size & ~63UL) { + if (~(tmp = *(p++))) + goto found_middle; + result += 64; + size -= 64; + } + if (!size) + return result; + tmp = *p; + +found_first: + tmp |= ~0UL << size; + if (tmp == ~0UL) + return result+size; +found_middle: + return result + ffz(tmp); +} + +void BUG_OUTLINE(char* file, unsigned line) +{ + udbg_printf("BUG - kernel BUG at %s:%d! \n", __FILE__, __LINE__); + PPCDBG_ENTER_DEBUGGER(); + printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); + __asm__ __volatile__(".long " BUG_ILLEGAL_INSTR); +} + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/checks.c linuxppc64_2_4/arch/ppc64/kernel/checks.c --- linux-2.4.5-ac18/arch/ppc64/kernel/checks.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/checks.c Mon Jun 4 20:06:03 2001 @@ -0,0 +1,67 @@ +/* + * Copyright 2001 PPC 64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * Do various before compile checks of data structures + * + * This is invoked when you do a make checks + * Is this enough or are there more things that we would like to do here? + * -- tgall + */ +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 -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/chrp_setup.c linuxppc64_2_4/arch/ppc64/kernel/chrp_setup.c --- linux-2.4.5-ac18/arch/ppc64/kernel/chrp_setup.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/chrp_setup.c Wed Jun 27 13:42:12 2001 @@ -0,0 +1,380 @@ +/* + * 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) + * Modified by PPC64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +/* + * bootup setup stuff.. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*#include "time.h" */ +#include "local_irq.h" +#include "i8259.h" +#include "open_pic.h" +#include "xics.h" +#include + +extern volatile unsigned char *chrp_int_ack_special; +extern struct Naca *naca; + +unsigned long chrp_get_rtc_time(void); +int chrp_set_rtc_time(unsigned long nowtime); +void chrp_calibrate_decr(void); +long chrp_time_init(void); + +void chrp_setup_pci_ptrs(void); +void chrp_progress(char *, unsigned short); +void chrp_request_regions(void); + +extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int pckbd_getkeycode(unsigned int scancode); +extern int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char pckbd_unexpected_up(unsigned char keycode); +extern void pckbd_leds(unsigned char leds); +extern int pckbd_rate(struct kbd_repeat *rep); +extern void pckbd_init_hw(void); +extern unsigned char pckbd_sysrq_xlate[128]; +extern void openpic_init_IRQ(void); +extern void pSeries_pcibios_fixup(void); + +kdev_t boot_dev; +unsigned long virtPython0Facilities = 0; // python0 facility area (memory mapped io) (64-bit format) VIRTUAL address. + +extern HPTE *Hash, *Hash_end; +extern unsigned long Hash_size, Hash_mask; +extern int probingmem; +extern unsigned long loops_per_jiffy; + +extern void *comport1; + +#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 + +int __chrp +chrp_get_cpuinfo(char *buffer) +{ + long len; /* i --Unused */ + /* unsigned int t; --Unused */ + struct device_node *root; + const char *model = ""; + + root = find_path_device("/"); + if (root) + model = get_property(root, "model", NULL); + len = sprintf(buffer,"machine\t\t: CHRP %s\n", model); + + return len; +} + +/* + * Fixes for the National Semiconductor PC78308VUL SuperI/O + * + * Some versions of Open Firmware incorrectly initialize the IRQ settings + * for keyboard and mouse + */ +static inline void __init sio_write(u8 val, u8 index) +{ + outb(index, 0x15c); + outb(val, 0x15d); +} + +static inline u8 __init sio_read(u8 index) +{ + outb(index, 0x15c); + return inb(0x15d); +} + +static void __init sio_fixup_irq(const char *name, u8 device, u8 level, + u8 type) +{ + u8 level0, type0, active; + struct device_node *root; + + root = find_path_device("/"); + if (root && + !strncmp(get_property(root, "model", NULL), "IBM,LongTrail", 13 ) ) + { + /* select logical device */ + sio_write(device, 0x07); + active = sio_read(0x30); + level0 = sio_read(0x70); + type0 = sio_read(0x71); + printk("sio: %s irq level %d, type %d, %sactive: ", name, level0, type0, + !active ? "in" : ""); + if (level0 == level && type0 == type && active) + printk("OK\n"); + else { + printk("remapping to level %d, type %d, active\n", level, type); + sio_write(0x01, 0x30); + sio_write(level, 0x70); + sio_write(type, 0x71); + } + } + +} + +void __init chrp_request_regions(void) { + 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"); +} + +void __init +chrp_setup_arch(void) +{ + extern char cmd_line[]; + struct device_node *root; + unsigned int *opprop; + + /* openpic global configuration register (64-bit format). */ + /* openpic Interrupt Source Unit pointer (64-bit format). */ + /* python0 facility area (mmio) (64-bit format) REAL address. */ + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000; + +#ifdef CONFIG_BLK_DEV_INITRD + /* this is fine for chrp */ + initrd_below_start_ok = 1; + + if (initrd_start) + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); + else +#endif + ROOT_DEV = to_kdev_t(0x0802); /* sda2 (sda1 is for the kernel) */ + + printk("Boot arguments: %s\n", cmd_line); + + /* Find and initialize PCI host bridges */ + find_and_init_phbs(); + + /* Find the Open PIC if present */ + root = find_path_device("/"); + opprop = (unsigned int *) get_property(root, + "platform-open-pic", NULL); + if (opprop != 0) { + int n = prom_n_addr_cells(root); + unsigned long openpic; + + for (openpic = 0; n > 0; --n) + openpic = (openpic << 32) + *opprop++; + printk(KERN_DEBUG "OpenPIC addr: %lx\n", openpic); + udbg_printf("OpenPIC addr: %lx\n", openpic); + OpenPIC_Addr = ioremap(openpic, 0x40000); + } + +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif + + /* Get the event scan rate for the rtas so we know how + * often it expects a heartbeat. -- Cort + */ + if (rtas_data.event_scan.rate) { + ppc_md.heartbeat = rtas_event_scan; + ppc_md.heartbeat_reset = (HZ/rtas_data.event_scan.rate*60)-1; + ppc_md.heartbeat_count = 1; + printk("RTAS Event Scan Rate: %lu (%lu jiffies)\n", + rtas_data.event_scan.rate, ppc_md.heartbeat_reset); + } +} + +void __init +chrp_init2(void) +{ + /* + * It is sensitive, when this is called (not too earlu) + * -- tibit + */ + chrp_request_regions(); + ppc_md.progress(UTS_RELEASE, 0x7777); +} + +void __init +chrp_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ +#ifdef CONFIG_BLK_DEV_INITRD + /* take care of initrd if we have one */ + if ( r6 ) + { + initrd_start = r6 + KERNELBASE; + initrd_end = r6 + r7 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_INITRD */ + + /* pci_dram_offset/isa_io_base/isa_mem_base set by setup_pci_ptrs() */ + + ppc_md.ppc_machine = _machine; + + ppc_md.setup_arch = chrp_setup_arch; + ppc_md.setup_residual = NULL; + ppc_md.get_cpuinfo = chrp_get_cpuinfo; + if(naca->interrupt_controller == IC_OPEN_PIC) { + ppc_md.init_IRQ = openpic_init_IRQ; + ppc_md.get_irq = openpic_get_irq; + ppc_md.post_irq = NULL; + } else { + ppc_md.init_IRQ = xics_init_IRQ; + ppc_md.get_irq = xics_get_irq; + ppc_md.post_irq = NULL; + } + ppc_md.pcibios_fixup = pSeries_pcibios_fixup; + + ppc_md.init = chrp_init2; + + ppc_md.restart = rtas_restart; + ppc_md.power_off = rtas_power_off; + ppc_md.halt = rtas_halt; + + ppc_md.time_init = chrp_time_init; + ppc_md.set_rtc_time = chrp_set_rtc_time; + ppc_md.get_rtc_time = chrp_get_rtc_time; + ppc_md.calibrate_decr = chrp_calibrate_decr; + +#ifdef CONFIG_VT + ppc_md.kbd_setkeycode = pckbd_setkeycode; + ppc_md.kbd_getkeycode = pckbd_getkeycode; + ppc_md.kbd_translate = pckbd_translate; + ppc_md.kbd_unexpected_up = pckbd_unexpected_up; + ppc_md.kbd_leds = pckbd_leds; + ppc_md.kbd_init_hw = pckbd_init_hw; + ppc_md.kbd_rate = pckbd_rate; +#ifdef CONFIG_MAGIC_SYSRQ + ppc_md.ppc_kbd_sysrq_xlate = pckbd_sysrq_xlate; + SYSRQ_KEY = 0x63; /* Print Screen */ +#endif /* CONFIG_MAGIC_SYSRQ */ +#endif /* CONFIG_VT */ + + ppc_md.progress = chrp_progress; + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + ppc_ide_md.insw = chrp_ide_insw; + ppc_ide_md.outsw = chrp_ide_outsw; + ppc_ide_md.default_irq = chrp_ide_default_irq; + ppc_ide_md.default_io_base = chrp_ide_default_io_base; + ppc_ide_md.ide_check_region = chrp_ide_check_region; + ppc_ide_md.ide_request_region = chrp_ide_request_region; + ppc_ide_md.ide_release_region = chrp_ide_release_region; + ppc_ide_md.fix_driveid = chrp_ide_fix_driveid; + ppc_ide_md.ide_init_hwif = chrp_ide_init_hwif_ports; + + ppc_ide_md.io_base = _IO_BASE; +#endif + ppc_md.progress("Linux ppc64\n", 0x0); +} + +void __chrp +chrp_progress(char *s, unsigned short hex) +{ + struct device_node *root; + int width, *p; + char *os; + static int max_width; + + if ( (_machine != _MACH_chrp) || !rtas_data.base ) + return; + + if (hex) + udbg_printf(" %s\n", s); + + if (max_width == 0) { + if ( (root = find_path_device("/rtas")) && + (p = (unsigned int *)get_property(root, + "ibm,display-line-length", + NULL)) ) + max_width = *p; + else + max_width = 0x10; + } + + if ( call_rtas( "display-character", 1, 1, NULL, '\r' ) ) + { + /* assume no display-character RTAS method - use hex display */ + call_rtas("set-indicator", 3, 1, NULL, 6, 0, hex); + return; + } + + width = max_width; + os = s; + while ( *os ) + { + if ( (*os == '\n') || (*os == '\r') ) + width = max_width; + else + width--; + call_rtas( "display-character", 1, 1, NULL, *os++ ); + /* if we overwrite the screen length */ + if ( width == 0 ) + while ( (*os != 0) && (*os != '\n') && (*os != '\r') ) + os++; + } + + /* Blank to end of line. */ + while ( width-- > 0 ) + call_rtas( "display-character", 1, 1, NULL, ' ' ); +} + +void chrp_init_map_io_space(void); +void chrp_init_map_io_space() { + /* naca->serialPortAddr is initialized earlier in prom.c */ + comport1 = (void *)ioremap(naca->serialPortAddr, PAGE_SIZE); +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/chrp_time.c linuxppc64_2_4/arch/ppc64/kernel/chrp_time.c --- linux-2.4.5-ac18/arch/ppc64/kernel/chrp_time.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/chrp_time.c Thu May 10 14:36:13 2001 @@ -0,0 +1,201 @@ +/* + * 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 + * Modified for PPC64 by PPC64 Team, Copyright IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +/*#include "time.h" */ + +static int nvram_as1 = NVRAM_AS1; +static int nvram_as0 = NVRAM_AS0; +static int nvram_data = NVRAM_DATA; + +long __init chrp_time_init(void) +{ + struct device_node *rtcs; + int base; + + rtcs = find_compatible_devices("rtc", "pnpPNP,b00"); + if (rtcs == NULL || rtcs->addrs == NULL) { + return 0; + } + base = rtcs->addrs[0].address; + nvram_as1 = 0; + nvram_as0 = base; + nvram_data = base + 1; + + return 0; +} + +int __chrp chrp_cmos_clock_read(int addr) +{ + int retval; + +#if 1 /* DRENG -- see chrp_time_init, chrp_cmos_clock_write + * I think this is broken in general on 64b -- + * nvram_as0 = -1 in 64b format + */ + if (nvram_as1 != 0) { + outb(addr>>8, nvram_as1); + } + outb(addr, nvram_as0); + retval = inb(nvram_data); + return retval; +#else + return(-1); +#endif +} + +void __chrp chrp_cmos_clock_write(unsigned long val, int addr) +{ +#if 1 /* DRENG -- see chrp_cmos_clock_read */ + if (nvram_as1 != 0) + outb(addr>>8, nvram_as1); + outb(addr, nvram_as0); + outb(val, nvram_data); + return; +#else + return; +#endif +} + +/* + * Set the hardware clock. -- Cort + */ +int __chrp 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 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); +} + + +void __init chrp_calibrate_decr(void) +{ + struct device_node *cpu; + int *fp; + unsigned long freq; + + /* + * The cpu node should have a timebase-frequency property + * to tell us the rate at which the decrementer counts. + */ + freq = 16666000; /* hardcoded default */ + cpu = find_type_devices("cpu"); + if (cpu != 0) { + fp = (int *) get_property(cpu, "timebase-frequency", NULL); + if (fp != 0) + freq = *fp; + } + printk("time_init: decrementer frequency = %lu.%.6u MHz\n", + freq/1000000, freq%1000000 ); + + tb_ticks_per_jiffy = freq / HZ; + tb_ticks_per_usec = freq / 1000000; +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/entry.S linuxppc64_2_4/arch/ppc64/kernel/entry.S --- linux-2.4.5-ac18/arch/ppc64/kernel/entry.S Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/entry.S Wed Jun 27 10:43:34 2001 @@ -0,0 +1,614 @@ +/* + * arch/ppc/kernel/entry.S + * + * + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * Rewritten by Cort Dougan (cort@cs.nmt.edu) for PReP + * Copyright (C) 1996 Cort Dougan + * Adapted for Power Macintosh by Paul Mackerras. + * Low-level exception handlers and MMU support + * rewritten by Paul Mackerras. + * Copyright (C) 1996 Paul Mackerras. + * MPC8xx modifications Copyright (C) 1997 Dan Malek (dmalek@jlc.net). + * + * This file contains the system call entry code, context switch + * code, and exception/interrupt return code for PowerPC. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + + +#include "ppc_asm.h" +#include +#include +#include +#include +#include +#include + +#undef SHOW_SYSCALLS +#undef SHOW_SYSCALLS_TASK + +#ifdef SHOW_SYSCALLS_TASK + .data +show_syscalls_task: + .long -1 +#endif + +/* + * Handle a system call. + */ + .text +_GLOBAL(DoSyscall) + mfspr r30,SPRG3 /* Get PACA */ + ld r29,PACACURRENT(r30) + std r0,THREAD+LAST_SYSCALL(r29) + ld r11,_CCR(r1) /* Clear SO bit in CR */ + lis r10,0x1000 + andc r11,r11,r10 + std r11,_CCR(r1) +#ifdef SHOW_SYSCALLS +#ifdef SHOW_SYSCALLS_TASK + LOADBASE(r31,show_syscalls_task) + ld r31,show_syscalls_task@l(r31) + cmp 0,r29,r31 + bne 1f +#endif + LOADADDR(r3,7f) + ld r4,GPR0(r1) + ld r5,GPR3(r1) + ld r6,GPR4(r1) + ld r7,GPR5(r1) + ld r8,GPR6(r1) + ld r9,GPR7(r1) + LOADADDR(r3,77f) + ld r4,GPR8(r1) + ld r5,GPR9(r1) + mr r6,r29 + ld r0,GPR0(r1) + ld r3,GPR3(r1) + ld r4,GPR4(r1) + ld r5,GPR5(r1) + ld r6,GPR6(r1) + ld r7,GPR7(r1) + ld r8,GPR8(r1) +1: +#endif /* SHOW_SYSCALLS */ + cmpi 0,r0,0x7777 /* Special case for 'sys_sigreturn' */ + beq- 10f + cmpi 0,r0,0x6666 /* Special case for 'sys_rt_sigreturn' */ + beq- 16f + + /* MIKEC: I changed TASK_PTRACE to 64-bit here. Should I have? + * TASK_PTRACE is aka task_struct.ptrace (in linux/sched.h) + * which is an unsigned long + */ + ld r10,TASK_PTRACE(r29) + andi. r10,r10,PT_TRACESYS + bne- 50f + cmpli 0,r0,NR_syscalls + bge- 66f +/* Ken Aaker: Need to vector to 32 Bit or default sys_call_table here, + * based on caller's run-mode / personality. + * + */ +#ifdef CONFIG_BINFMT_ELF32 + ld r10,THREAD+THREAD_FLAGS(r29) + andi. r10,r10,PPC_FLAG_32BIT + beq+ 15f + LOADADDR(r10,.sys_call_table32) +/* Now mung the first 4 parameters into shape, by making certain that + * the high bits (most significant 32 bits in 64 bit reg) are 0 + * for the first 4 parameter regs(3-6). + */ + clrldi r3,r3,32 + clrldi r4,r4,32 + clrldi r5,r5,32 + clrldi r6,r6,32 + b 17f +15: +#endif + LOADADDR(r10,.sys_call_table) +17: + slwi r0,r0,3 + ldx r10,r10,r0 /* Fetch system call handler [ptr] */ + cmpi 0,r10,0 + beq- 66f + mtlr r10 + addi r9,r1,STACK_FRAME_OVERHEAD + blrl /* Call handler */ +_GLOBAL(ret_from_syscall_1) +20: std r3,RESULT(r1) /* Save result */ +#ifdef SHOW_SYSCALLS +#ifdef SHOW_SYSCALLS_TASK + /* MIKEC: Will r29 (current) be preserved through any method of getting + to ret_from_syscall_1 ?? */ + cmp 0,r29,r31 + bne 91f +#endif + mr r4,r3 + LOADADDR(r3,79f) + ld r3,RESULT(r1) +91: +#endif + li r10,-_LAST_ERRNO + cmpl 0,r3,r10 + blt 30f + neg r3,r3 + cmpi 0,r3,ERESTARTNOHAND + bne 22f + li r3,EINTR +22: ld r10,_CCR(r1) /* Set SO bit in CR */ + oris r10,r10,0x1000 + std r10,_CCR(r1) +30: std r3,GPR3(r1) /* Update return value */ + b .ret_from_except +66: li r3,ENOSYS + b 22b +/* sys_sigreturn */ +10: addi r3,r1,STACK_FRAME_OVERHEAD + bl .sys_sigreturn + cmpi 0,r3,0 /* Check for restarted system call */ + bge .ret_from_except + b 20b +/* sys_rt_sigreturn */ +16: addi r3,r1,STACK_FRAME_OVERHEAD + bl .sys_rt_sigreturn + cmpi 0,r3,0 /* Check for restarted system call */ + bge .ret_from_except + b 20b + +/* Traced system call support */ +50: bl .syscall_trace + ld r0,GPR0(r1) /* Restore original registers */ + ld r3,GPR3(r1) + ld r4,GPR4(r1) + ld r5,GPR5(r1) + ld r6,GPR6(r1) + ld r7,GPR7(r1) + ld r8,GPR8(r1) + ld r9,GPR9(r1) + cmpli 0,r0,NR_syscalls + bge- 66f +#ifdef CONFIG_BINFMT_ELF32 + ld r10,THREAD+THREAD_FLAGS(r29) + andi. r10,r10,PPC_FLAG_32BIT + beq+ 55f + LOADADDR(r10,.sys_call_table32) +/* Now mung the first 4 parameters into shape, by making certain that + * the high bits (most significant 32 bits in 64 bit reg) are 0 + * for the first 4 parameter regs(3-6). + */ + clrldi r3,r3,32 + clrldi r4,r4,32 + clrldi r5,r5,32 + clrldi r6,r6,32 + b 57f +55: +#endif + LOADADDR(r10,.sys_call_table) +57: + slwi r0,r0,3 + ldx r10,r10,r0 /* Fetch system call handler [ptr] */ + cmpi 0,r10,0 + beq- 66f + mtlr r10 + addi r9,r1,STACK_FRAME_OVERHEAD + blrl /* Call handler */ +_GLOBAL(ret_from_syscall_2) + std r3,RESULT(r1) /* Save result */ + std r3,GPR0(r1) /* temporary gross hack to make strace work */ + li r10,-_LAST_ERRNO + cmpl 0,r3,r10 + blt 60f + neg r3,r3 + cmpi 0,r3,ERESTARTNOHAND + bne 57f + li r3,EINTR +57: ld r10,_CCR(r1) /* Set SO bit in CR */ + oris r10,r10,0x1000 + std r10,_CCR(r1) +60: std r3,GPR3(r1) /* Update return value */ + bl .syscall_trace + b .ret_from_except +66: li r3,ENOSYS + b 57b +#ifdef SHOW_SYSCALLS +7: .string "syscall %d(%x, %x, %x, %x, %x, " +77: .string "%x, %x), current=%p\n" +79: .string " -> %x\n" + .align 2,0 +#endif + +/* + * This routine switches between two different tasks. The process + * state of one is saved on its kernel stack. Then the state + * of the other is restored from its kernel stack. The memory + * management hardware is updated to the second process's state. + * Finally, we can return to the second process, via ret_from_except. + * On entry, r3 points to the THREAD for the current task, r4 + * points to the THREAD for the new task. + * + * Note: there are two ways to get to the "going out" portion + * of this code; either by coming in via the entry (_switch) + * or via "fork" which must set up an environment equivalent + * to the "_switch" path. If you change this (or in particular, the + * SAVE_REGS macro), you'll have to change the fork code also. + * + * The code which creates the new task context is in 'copy_thread' + * in arch/ppc/kernel/process.c + */ +_GLOBAL(_switch) + stdu r1,-INT_FRAME_SIZE(r1) + ld r6,0(r1) + std r6,GPR1(r1) + /* r3-r13 are caller saved -- Cort */ + SAVE_GPR(2, r1) + SAVE_8GPRS(14, r1) + SAVE_10GPRS(22, r1) + mflr r20 /* Return to switch caller */ + mfmsr r22 + li r6,MSR_FP /* Disable floating-point */ + andc r22,r22,r6 + std r20,_NIP(r1) + std r22,_MSR(r1) + std r20,_LINK(r1) + mfcr r20 + mfctr r22 + mfspr r23,XER + std r20,_CCR(r1) + std r22,_CTR(r1) + std r23,_XER(r1) + li r6,0x0ff0 + std r6,TRAP(r1) + std r1,KSP(r3) /* Set old stack pointer */ + + mfspr r5,SPRG3 /* Get Paca */ + ld r3,PACACURRENT(r5) /* Get old 'current' for return value */ + + addi r6,r4,-THREAD /* Convert THREAD to 'current' */ + std r6,PACACURRENT(r5) /* Set new 'current' */ + + ld r1,KSP(r4) /* Load new stack pointer */ + ld r9,_MSR(r1) /* Returning to user mode? */ + andi. r9,r9,MSR_PR + beq+ 10f /* if not, don't adjust kernel stack */ +8: addi r7,r1,INT_FRAME_SIZE /* size of frame */ + std r7,KSP(r4) /* save kernel stack pointer */ + std r1,PACAKSAVE(r5) /* save exception stack pointer */ +10: ld r7,_CTR(r1) + ld r6,_LINK(r1) + mtctr r7 + mtlr r6 + ld r7,_XER(r1) + ld r6,_CCR(r1) + mtspr XER,r7 + mtcrf 0xFF,r6 + /* r3-r13 are destroyed -- Cort */ + REST_GPR(14, r1) + REST_8GPRS(15, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + ld r7,_NIP(r1) /* Restore environment */ + + mfmsr r6 /* Get current interrupt state */ + rldicl r6,r6,48,1 + rldicl r6,r6,16,0 /* clear MSR_EE */ + mtmsrd r6 /* Update machine state */ + + ld r6,_MSR(r1) + mtspr SRR0,r7 + mtspr SRR1,r6 + ld r2,GPR2(r1) + ld r1,GPR1(r1) + + /* sync required to force memory operations on this processor */ + /* to complete before the current thread gives up control. */ + SYNC + rfid + +#ifdef CONFIG_SMP +_GLOBAL(ret_from_smpfork) + bl .schedule_tail + b .ret_from_except +#endif + +_GLOBAL(ret_from_intercept) + /* + * We may be returning from RTL and cannot do the normal checks + * -- Cort + */ + cmpi 0,r3,0 + beq restore + +_GLOBAL(ret_from_except) + ld r5,_MSR(r1) + andi. r5,r5,MSR_EE + beq 2f + +_GLOBAL(lost_irq_ret) +3: LOADBASE(r4,ppc_n_lost_interrupts) + lwz r4,ppc_n_lost_interrupts@l(r4) + cmpi 0,r4,0 + bne- 1f /* do_IRQ if lost interrupts */ + /* + * Check for pending I/O events (iSeries) + * If no I/O events pending then CR0 = "eq" and r4 = 0 + * (kills registers r5 and r6) + */ + CHECKLPQUEUE(r4,r5,r6) + beq+ 4f /* skip do_IRQ if no events */ +1: + addi r3,r1,STACK_FRAME_OVERHEAD + bl .do_IRQ + b 3b /* loop back and handle more */ +4: + /* + * Check for lost decrementer interrupts. + * (If decrementer popped while we were in the hypervisor) + */ + CHECKDECR(r4,r5) + beq+ 5f + addi r3,r1,STACK_FRAME_OVERHEAD + bl .timer_interrupt +5: + LOADADDR(r4,irq_stat) +#ifdef CONFIG_SMP + /* get processor # */ + mfspr r5,SPRG3 /* current task's PACA */ + ld r5,PACACURRENT(r5) /* Get 'current' */ + lwz r3,PROCESSOR(r5) + slwi r3,r3,IRQ_CPUSTAT_SHIFT + add r4,r4,r3 +#endif /* CONFIG_SMP */ + /* + * The fields loaded here are 'active' and 'mask' from + * the irq_stat array. They are 32-bit fields + */ + lwz r5,SOFTIRQ_ACTIVE(r4) + lwz r4,SOFTIRQ_MASK(r4) + and. r5,r5,r4 + beq+ 2f + bl .do_softirq + +_GLOBAL(do_bottom_half_ret) +2: ld r3,_MSR(r1) /* Returning to user mode? */ + andi. r3,r3,MSR_PR + beq+ restore /* if so, check need_resched and signals */ +_GLOBAL(ret_to_user_hook) + nop + /* NEED_RESCHED is a volatile long (64-bits) */ + mfspr r4,SPRG3 /* current task's PACA */ + ld r4,PACACURRENT(r4) /* Get 'current' */ + + ld r3,NEED_RESCHED(r4) + cmpi 0,r3,0 /* check need_resched flag */ + beq+ 7f + bl .schedule + /* SIGPENDING is an int (32-bits) */ +7: + mfspr r4,SPRG3 /* current task's PACA */ + ld r4,PACACURRENT(r4) /* Get 'current' */ + lwz r5,SIGPENDING(r4) /* Check for pending unblocked signals */ + cmpwi 0,r5,0 + beq+ restore + li r3,0 + addi r4,r1,STACK_FRAME_OVERHEAD + bl .do_signal +_GLOBAL(do_signal_ret) +restore: + ld r3,_CTR(r1) + ld r0,_LINK(r1) + mtctr r3 + mtlr r0 + ld r3,_XER(r1) + mtspr XER,r3 + REST_8GPRS(5, r1) + REST_10GPRS(13, r1) + REST_8GPRS(23, r1) + REST_GPR(31, r1) + + /* make sure we hard disable here, even if rtl is active, to protect + * SRR[01] and SPRG2 -- Cort + */ + mfmsr r0 /* Get current interrupt state */ + rldicl r0,r0,48,1 + rldicl r0,r0,16,0 /* clear MSR_EE */ + mtmsrd r0 /* Update machine state */ + + /* if returning to user mode, set new sprg3 and save kernel SP */ + ld r0,_MSR(r1) + andi. r0,r0,MSR_PR + beq+ 1f + addi r0,r1,INT_FRAME_SIZE /* size of frame */ + mfspr r4,SPRG3 /* current task's PACA */ + ld r4,PACACURRENT(r4) /* Get 'current' */ + std r0,THREAD+KSP(r4) /* save kernel stack pointer */ + mfspr r2,SPRG3 /* Get Paca */ + std r1,PACAKSAVE(r2) /* save exception stack pointer */ +1: + ld r0,_MSR(r1) + mtspr SRR1,r0 + ld r2,_CCR(r1) + mtcrf 0xFF,r2 + ld r2,_NIP(r1) + mtspr SRR0,r2 + ld r0,GPR0(r1) + ld r2,GPR2(r1) + ld r3,GPR3(r1) + ld r4,GPR4(r1) + ld r1,GPR1(r1) + + /* sync required to force memory operations on this processor */ + /* to complete before the current thread gives up control. */ + SYNC + rfid + +/* + * Fake an interrupt from kernel mode. + * This is used when enable_irq loses an interrupt. + * We only fill in the stack frame minimally. + */ +_GLOBAL(fake_interrupt) + mflr r0 + std r0,16(r1) + stdu r1,-(INT_FRAME_SIZE)(r1) + std r0,_NIP(r1) + std r0,_LINK(r1) + mfmsr r3 + std r3,_MSR(r1) + li r0,0x0fac + std r0,TRAP(r1) + addi r3,r1,STACK_FRAME_OVERHEAD + li r4,1 + bl .do_IRQ + addi r1,r1,INT_FRAME_SIZE + ld r0,16(r1) + mtlr r0 + blr + +/* + * Fake a decrementer from kernel mode. + * This is used when the decrementer pops in + * the hypervisor. We only fill in the stack + * frame minimally + */ +_GLOBAL(fake_decrementer) + mflr r0 + std r0,16(r1) + stdu r1,-(INT_FRAME_SIZE)(r1) + std r0,_NIP(r1) + std r0,_LINK(r1) + mfmsr r3 + std r3,_MSR(r1) + li r0,0x0fac + std r0,TRAP(r1) + addi r3,r1,STACK_FRAME_OVERHEAD + bl .timer_interrupt + addi r1,r1,INT_FRAME_SIZE + ld r0,16(r1) + mtlr r0 + blr + +/* + * On CHRP, the Run-Time Abstraction Services (RTAS) have to be + * called with the MMU off. + * + * In addition, we need to be in 32b mode, at least for now. + * + * Note: r3 is an input parameter to rtas, so don't trash it... + */ +_GLOBAL(enter_rtas) + mflr r0 + std r0,16(r1) + stdu r1,-INT_FRAME_SIZE(r1) // Save SP and create stack space + + /* Because RTAS is running in 32b mode, it clobbers the high order half + * of all registers that it saves. We therefore save those registers + * RTAS might touch to the stack. (r0, r3-r13 are caller saved) + */ + SAVE_GPR(2, r1) // Save the TOC + SAVE_8GPRS(14, r1) // Save the non-volatiles + SAVE_10GPRS(22, r1) // ditto + + mfcr r4 + std r4,_CCR(r1) + mfctr r5 + std r5,_CTR(r1) + mfspr r6,XER + std r6,_XER(r1) + mfdar r7 + std r7,_DAR(r1) + mfdsisr r8 + std r8,_DSISR(r1) + mfsrr0 r9 + std r9,_DSISR(r1) + + /* Unfortunatly, the stack pointer and the MSR are also clobbered, so they + * are saved in the PACA (SPRG3) which allows us to restore our original + * state after RTAS returns. + */ + mfspr r13,SPRG3 /* Get PACA */ + std r1,PACAR1(r13) + mfmsr r6 + std r6,PACASAVEDMSR(r13) + + /* Setup our real return addr */ + SET_REG_TO_LABEL(r4,.rtas_return_loc) + SET_REG_TO_CONST(r9,KERNELBASE) + sub r4,r4,r9 + mtlr r4 + + li r0,0 + ori r0,r0,MSR_EE|MSR_SE|MSR_BE + andc r0,r6,r0 + + li r9,1 + rldicr r9,r9,MSR_SF_LG,(63-MSR_SF_LG) + ori r9,r9,MSR_IR|MSR_DR|MSR_FE0|MSR_FE1|MSR_FP + andc r6,r0,r9 + sync /* disable interrupts so SRR0/1 */ + mtmsrd r0 /* don't get trashed */ + + SET_REG_TO_LABEL(r4,rtas_data) + ld r5,RTASENTRY(r4) /* get the rtas->entry value */ + ld r4,RTASBASE(r4) /* get the rtas->base value */ + + mtspr SRR0,r5 + mtspr SRR1,r6 + rfid + +_STATIC(rtas_return_loc) + /* relocation is off at this point */ + mfspr r4,SPRG3 /* Get PACA */ + SET_REG_TO_CONST(r5, KERNELBASE) + sub r4,r4,r5 /* RELOC the PACA base pointer */ + + ld r1,PACAR1(r4) /* Restore our SP */ + LOADADDR(r3,.rtas_restore_regs) + ld r4,PACASAVEDMSR(r4) /* Restore our MSR */ + + mtspr SRR0,r3 + mtspr SRR1,r4 + rfid + +_STATIC(rtas_restore_regs) + /* relocation is on at this point */ + REST_GPR(2, r1) // Restore the TOC + REST_8GPRS(14, r1) // Restore the non-volatiles + REST_10GPRS(22, r1) // ditto + + ld r4,_CCR(r1) + mtcr r4 + + ld r5,_CTR(r1) + mtctr r5 + + ld r6,_XER(r1) + mtspr XER,r6 + + ld r7,_DAR(r1) + mtdar r7 + + ld r8,_DSISR(r1) + mtdsisr r8 + + ld r9,_DSISR(r1) + mtsrr0 r9 + + addi r1,r1,INT_FRAME_SIZE + ld r0,16(r1) /* get return address */ + + mtlr r0 + blr /* return to caller */ + + + + + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/head.S linuxppc64_2_4/arch/ppc64/kernel/head.S --- linux-2.4.5-ac18/arch/ppc64/kernel/head.S Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/head.S Mon Jun 18 15:33:06 2001 @@ -0,0 +1,1467 @@ +/* + * arch/ppc64/kernel/head.S + * + * + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Rewritten by Cort Dougan (cort@cs.nmt.edu) for PReP + * Copyright (C) 1996 Cort Dougan + * Adapted for Power Macintosh by Paul Mackerras. + * Low-level exception handlers and MMU support + * rewritten by Paul Mackerras. + * Copyright (C) 1996 Paul Mackerras. + * + * Adapted for 64bit PowerPC by Dave Engebretsen, Peter Bergner, and + * Mike Corrigan {engebret|bergner|mikejc}@us.ibm.com + * + * This file contains the low-level support and setup for the + * PowerPC-64 platform, including trap and interrupt dispatch. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include "ppc_asm.h" +#include "ppc_defs.h" +#include +#include +#include +#include + +/* + * We layout physical memory as follows: + * 0x0000 - 0x00ff : Secondary processor spin code + * 0x0100 - 0x2fff : pSeries Interrupt prologs + * 0x3000 - 0x3fff : Interrupt support + * 0x4000 - 0x4fff : NACA + * 0x5000 - 0x5fff : Initial segment table + * 0x6000 : iSeries and common interrupt prologs + * + */ + +/* + * Entering into this code we make the following assumptions: + * For pSeries: + * 1. The MMU is off & open firmware is running in real mode. + * 2. The kernel is entered at __start + * + * For iSeries: + * 1. The MMU is on (as it always is for iSeries) + * 2. The kernel is entered at SystemReset_Iseries + */ + + .text + .globl _stext +_stext: +_STATIC(__start) + b .__start_initialization_pSeries + + /* At offset 0x20, there is a pointer to iSeries LPAR data. + * This is required by the hypervisor */ + . = 0x20 + .llong hvReleaseData-KERNELBASE + + /* At offset 0x28 and 0x30 are offsets to the msChunks + * array (used by the iSeries LPAR debugger to do translation + * between physical addresses and absolute addresses) and + * to the pidhash table (also used by the debugger) */ + .llong msChunks-KERNELBASE + .llong pidhash-KERNELBASE + + /* Secondary processors spin on this value until it goes to 1. */ + .globl __secondary_hold_spinloop +__secondary_hold_spinloop: + .llong 0x0 + + /* Secondary processors write this value with their cpu # */ + /* after they enter the spin loop immediatly below. */ + .globl __secondary_hold_acknowledge +__secondary_hold_acknowledge: + .llong 0x0 + + . = 0x60 +/* + * The following code is used on pSeries to hold secondary processors + * in a spin loop after they have been freed from OpenFirmware, but + * before the bulk of the kernel has been relocated. This code + * is relocated to physical address 0x60 before prom_init is run. + * All of it must fit below the first exception vector at 0x100. + */ +_GLOBAL(__secondary_hold) + /* Grab our cpu# */ + mr r24,r3 + + /* Tell the master cpu we're here */ + /* Relocation is off & we are located at an address less */ + /* than 0x100, so only need to grab low order offset. */ + li r3,__secondary_hold_acknowledge@l + std r24,0(r3) + + /* Get the address on which we are going to spin. */ + li r3,__secondary_hold_spinloop@l + + /* All secondary cpu's wait here until told to start. */ +100: ld r4,0(r3) + cmpdi 0,r4,1 + bne 100b + + mr r3,r24 + b .pseries_secondary_smp_init + +/* + * The following macros define the code that appears as + * the prologue to each of the exception handlers. They + * are split into three parts to allow a single kernel binary + * to be used for pSeries, pSeries-LPAR and iSeries-LPAR. + */ + +/* + * We make as much of the exception code common between native Pseries + * and Iseries LPAR implementations as possible. + */ + +/* + * This is the start of the interrupt handlers for Pseries + * This code runs with relocation off. + */ + +#define EXCEPTION_PROLOG_PSERIES(label) \ + mtspr SPRG2,r20; /* use SPRG2 as scratch reg */ \ + mfspr r20,SPRG3; /* get Paca virt addr */ \ + clrldi r20,r20,12; /* convert virt to real addr */ \ + std r21,PACAR21(r20); /* Save GPR21 in Paca */ \ + mfspr r21,SRR0; /* EA of interrupted instr */ \ + std r21,LPPACA+LPPACASRR0(r20); /* Set SRR0 in ItLpPaca */ \ + mfspr r21,SRR1; /* machine state at interrupt */ \ + std r21,LPPACA+LPPACASRR1(r20); /* Set SRR1 in ItLpPaca */ \ + SET_REG_TO_LABEL(r21,label); /* Get addr to branch to */ \ + mtspr SRR0,r21; \ + li r20,0x5; /* Turn on 64b, 64bints */ \ + sldi r20,r20,61; \ + ori r20,r20,0x30; /* for generic handlers */ \ + mfmsr r21; /* Turn on IR & DR */ \ + or r21,r21,r20; /* for generic handlers */ \ + mtspr SRR1,r21; \ + rfid /* Jump to generic handlers */ +/* + * This is the start of the interrupt handlers for i_series + * This code runs with relocation on. + */ + +#define EXCEPTION_PROLOG_ISERIES \ + mtspr SPRG2,r20; /* use SPRG2 as scratch reg */\ + mfspr r20,SPRG3; /* get Paca */\ + std r21,PACAR21(r20) /* Save GPR21 in Paca */ + +#define EXCEPTION_PROLOG_COMMON \ + mfspr r20,SPRG3; /* get Paca virt addr */ \ + std r22,PACAR22(r20); /* Save GPR22 in Paca */\ + mfcr r22; \ + ld r21,LPPACA+LPPACASRR1(r20); /* Get SRR1 from ItLpPaca */ \ + andi. r21,r21,MSR_PR; /* Set CR for later branch */ \ + beq+ 1f; \ + ld r21,PACAKSAVE(r20); /* exception stack to use */\ + b 2f; \ +1: \ + subi r21,r1,INT_FRAME_SIZE;/* alloc exc. frame */ \ +2: \ + std r22,_CCR(r21); /* save CR in stackframe */\ + ld r22,PACAR21(r20); /* Get GPR21 from Paca */\ + std r22,GPR21(r21); /* Save GPR21 in stackframe */\ + ld r22,PACAR22(r20); /* Get GPR22 from Paca */\ + std r22,GPR22(r21); /* Save GPR22 in stackframe */\ + std r23,GPR23(r21); /* Save GPR23 in stackframe */\ + mfspr r22,SPRG2; /* Get GPR20 from SPRG2 */\ + std r22,GPR20(r21); /* Save GPR20 in stackframe */\ + mflr r22; \ + std r22,_LINK(r21); \ + mfctr r22; \ + std r22,_CTR(r21); \ + mfspr r22,XER; \ + std r22,_XER(r21); \ + ld r22,LPPACA+LPPACASRR0(r20); /* Get SRR0 from ItLpPaca */\ + ld r23,LPPACA+LPPACASRR1(r20); /* Get SRR1 from ItLpPaca */\ + SAVE_8GPRS(0, r21); \ + SAVE_4GPRS(8, r21); \ + SAVE_2GPRS(12, r21); \ + ld r2,PACATOC(r20); \ + std r1,0(r21); \ + mr r1,r21 +/* + * Note: code which follows this uses cr0.eq (set if from kernel), + * r21, r22 (SRR0), and r23 (SRR1). + */ + +/* + * Exception vectors. + */ +#define STD_EXCEPTION_PSERIES(n, label ) \ + . = n; \ + .globl label##_Pseries; \ +label##_Pseries: \ + EXCEPTION_PROLOG_PSERIES( label##_common ) + +#define STD_EXCEPTION_ISERIES( label ) \ + .globl label##_Iseries; \ +label##_Iseries: \ + EXCEPTION_PROLOG_ISERIES; \ + b label##_common + +#define STD_EXCEPTION_COMMON( trap, label, hdlr ) \ + .globl label##_common; \ +label##_common: \ + EXCEPTION_PROLOG_COMMON; \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + SET_REG_TO_CONST(r20, MSR_KERNEL); \ + li r6,trap; \ + bl .transfer_to_handler; \ + .llong hdlr; \ + .llong .ret_from_except + +/* + * Start of pSeries system interrupt routines + */ + + STD_EXCEPTION_PSERIES( 0x100, SystemReset ) + STD_EXCEPTION_PSERIES( 0x200, MachineCheck ) + STD_EXCEPTION_PSERIES( 0x300, DataAccess ) + STD_EXCEPTION_PSERIES( 0x380, DataAccessSLB ) + STD_EXCEPTION_PSERIES( 0x400, InstructionAccess ) + STD_EXCEPTION_PSERIES( 0x480, InstructionAccessSLB ) + STD_EXCEPTION_PSERIES( 0x500, HardwareInterrupt ) + STD_EXCEPTION_PSERIES( 0x600, Alignment ) + STD_EXCEPTION_PSERIES( 0x700, ProgramCheck ) + STD_EXCEPTION_PSERIES( 0x800, FPUnavailable ) + STD_EXCEPTION_PSERIES( 0x900, Decrementer ) + STD_EXCEPTION_PSERIES( 0xa00, Trap_0a ) + STD_EXCEPTION_PSERIES( 0xb00, Trap_0b ) + STD_EXCEPTION_PSERIES( 0xc00, SystemCall ) + STD_EXCEPTION_PSERIES( 0xd00, SingleStep ) + STD_EXCEPTION_PSERIES( 0xe00, Trap_0e ) + STD_EXCEPTION_PSERIES( 0xf00, PerformanceMonitor ) + + . = 0x4000 + .globl xNaca + /* Save space for naca. + * The first dword of the Naca is required by iSeries LPAR to + * point to itVpdAreas. On pSeries native, this value is not used. + */ + .llong itVpdAreas + .llong 0x0 + .llong 0x0 + .llong xPaca + + /* + * Space for the initial segment table + * For LPAR, the hypervisor must fill in at least one entry + * before we get control (with relocate on) + */ + + . = 0x5000 + + + . = 0x6000 + /* + * The iSeries LPAR map is at this fixed address + * so that the HvReleaseData structure can address + * it with a 32-bit offset. + */ + + .llong 1 /* # ESIDs to be mapped by hypervisor */ + .llong 1 /* # memory ranges to be mapped by hypervisor */ + .llong 5 /* Page # of segment table within load area */ + .llong 0 /* Reserved */ + .llong 0 /* Reserved */ + .llong 0 /* Reserved */ + .llong 0 /* Reserved */ + .llong 0 /* Reserved */ + .llong 0x0c00000000 /* ESID to map (Kernel at EA = 0xC000000000000000) */ + .llong 0x0336962800 /* VSID to map (Kernel at VA = 0x3369628000000000) */ + .llong 8192 /* # pages to map (64 MB) */ + .llong 0 /* Offset from start of loadarea to start of map */ + .llong 0x0003369628000000 /* VPN of first page to map */ + + . = 0x6100 + +/*** ISeries-LPAR interrupt handlers ***/ + + STD_EXCEPTION_ISERIES( MachineCheck ) + STD_EXCEPTION_ISERIES( DataAccess ) + STD_EXCEPTION_ISERIES( DataAccessSLB ) + STD_EXCEPTION_ISERIES( InstructionAccess ) + STD_EXCEPTION_ISERIES( InstructionAccessSLB ) + STD_EXCEPTION_ISERIES( HardwareInterrupt ) + STD_EXCEPTION_ISERIES( Alignment ) + STD_EXCEPTION_ISERIES( ProgramCheck ) + STD_EXCEPTION_ISERIES( FPUnavailable ) + STD_EXCEPTION_ISERIES( Decrementer ) + STD_EXCEPTION_ISERIES( Trap_0a ) + STD_EXCEPTION_ISERIES( Trap_0b ) + STD_EXCEPTION_ISERIES( SystemCall ) + STD_EXCEPTION_ISERIES( SingleStep ) + STD_EXCEPTION_ISERIES( Trap_0e ) + STD_EXCEPTION_ISERIES( PerformanceMonitor ) + + .globl SystemReset_Iseries +SystemReset_Iseries: + mfspr r3,SPRG3 /* Get Paca address */ + lhz r24,PACAPACAINDEX(r3) /* Get processor # */ + cmpi 0,r24,0 /* Are we processor 0? */ + beq .__start_initialization_iSeries /* Start up the first processor */ + mfspr r4,CTRLF + li r5,RUNLATCH /* Turn off the run light */ + andc r4,r4,r5 + mtspr CTRLT,r4 +1: + HMT_LOW +#ifdef CONFIG_SMP + lbz r23,PACAPROCSTART(r3) /* Test if this processor + * should start */ + cmpi 0,r23,0 + beq iseries_secondary_smp_loop /* Loop until told to go */ + bne .__secondary_start /* Loop until told to go */ +iseries_secondary_smp_loop: + /* Let the Hypervisor know we are alive */ + /* 8002 is a call to HvCallCfg::getLps, a harmless Hypervisor function */ + lis r3,0x8002 + rldicr r0,r3,32,15 /* r0 = (r3 << 32) & 0xffff000000000000 */ + rldicl r3,r3,0,48 /* r3 = r3 & 0x000000000000ffff */ + or r3,r3,r0 /* r3 = r3 | r0 */ +#else /* CONFIG_SMP */ + /* Yield the processor. This is required for non-SMP kernels + which are running on multi-threaded machines. */ + lis r3,0x8000 + rldicr r3,r3,32,15 /* r3 = (r3 << 32) & 0xffff000000000000 */ + addi r3,r3,18 /* r3 = -x8000000000000012 which is "yield" */ + li r4,0 /* "yield timed" */ + li r5,-1 /* "yield forever" */ +#endif /* CONFIG_SMP */ + li r0,-1 /* r0=-1 indicates a Hypervisor call */ + sc /* Invoke the hypervisor via a system call */ + mfspr r3,SPRG3 /* Put r3 back */ + b 1b /* If SMP not configured, secondaries + * loop forever */ + +/*** Common interrupt handlers ***/ + + STD_EXCEPTION_COMMON( 0x100, SystemReset, .SystemResetException ) + STD_EXCEPTION_COMMON( 0x200, MachineCheck, .MachineCheckException ) + STD_EXCEPTION_COMMON( 0x500, HardwareInterrupt, .do_IRQ ) + STD_EXCEPTION_COMMON( 0x900, Decrementer, .timer_interrupt ) + STD_EXCEPTION_COMMON( 0xa00, Trap_0a, .UnknownException ) + STD_EXCEPTION_COMMON( 0xb00, Trap_0b, .UnknownException ) + STD_EXCEPTION_COMMON( 0xd00, SingleStep, .SingleStepException ) + STD_EXCEPTION_COMMON( 0xe00, Trap_0e, .UnknownException ) + STD_EXCEPTION_COMMON( 0xf00, PerformanceMonitor, .PerformanceMonitorException ) + + .globl DataAccess_common +DataAccess_common: + EXCEPTION_PROLOG_COMMON + mfspr r20,DSISR + std r20,_DSISR(r21) + andis. r0,r20,0xa450 /* weird error? */ + bne 1f /* if not, try to put a PTE */ + mfspr r3,DAR /* into the hash table */ + std r3,_DAR(r21) + rlwinm r4,r20,32-23,29,29 /* DSISR_STORE -> _PAGE_RW */ + + andis. r0,r20,0x0020 /* Is it a page table fault? */ + beq 2f /* If so handle it */ + li r4,0x300 /* Trap number */ + bl .do_stab_SI + b 1f + +2: bl .do_hash_page_DSI /* Try to handle as hpte fault */ +1: + ld r4,_DAR(r21) + ld r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + + SET_REG_TO_CONST(r20, MSR_KERNEL) + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + li r6,0x300 + bl .transfer_to_handler + .llong .do_page_fault + .llong .ret_from_except + + .globl DataAccessSLB_common +DataAccessSLB_common: + EXCEPTION_PROLOG_COMMON + mfspr r3,DAR + li r4,0x380 /* Exception vector */ + bl .ste_allocate + or. r3,r3,r3 /* Check return code */ + bne 1f /* Branch on failure */ + + /* SLB Allocation success - Return. */ + ld r3,_CCR(r21) + ld r4,_LINK(r21) + ld r5,_CTR(r21) + ld r6,_XER(r21) + + mtcrf 0xff,r3 + mtlr r4 + mtctr r5 + mtspr XER,r6 + REST_8GPRS(0,r21) + REST_4GPRS(8,r21) + REST_2GPRS(12,r21) + mtspr SRR1,r23 + mtspr SRR0,r22 + ld r20,GPR20(r21) + ld r22,GPR22(r21) + ld r23,GPR23(r21) + ld r21,GPR21(r21) + RFID + +1: + mfspr r4,DAR + std r4,_DAR(r21) + mfspr r5,DSISR + std r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + + SET_REG_TO_CONST(r20, MSR_KERNEL) + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + li r6,0x380 + bl .transfer_to_handler + .llong .do_page_fault + .llong .ret_from_except + + .globl InstructionAccess_common +InstructionAccess_common: + EXCEPTION_PROLOG_COMMON + + andis. r0,r23,0x0020 /* no ste found? */ + beq 2f + mr r3,r22 /* SRR0 at interrupt */ + li r4,0x400 /* Trap number */ + bl .do_stab_SI + b 1f + +2: andis. r0,r23,0x4000 /* no pte found? */ + beq 1f /* if so, try to put a PTE */ + mr r3,r22 /* into the hash table */ + bl .do_hash_page_ISI /* Try to handle as hpte fault */ +1: + mr r4,r22 + mr r5,r23 + addi r3,r1,STACK_FRAME_OVERHEAD + SET_REG_TO_CONST(r20, MSR_KERNEL) + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + li r6,0x400 + bl .transfer_to_handler + .llong .do_page_fault + .llong .ret_from_except + + .globl InstructionAccessSLB_common +InstructionAccessSLB_common: + EXCEPTION_PROLOG_COMMON + mr r3,r22 /* SRR0 = NIA */ + li r4,0x480 /* Exception vector */ + bl .ste_allocate + or. r3,r3,r3 /* Check return code */ + bne 1f /* Branch on failure */ + + /* SLB Allocation success - Return. */ + ld r3,_CCR(r21) + ld r4,_LINK(r21) + ld r5,_CTR(r21) + ld r6,_XER(r21) + + mtcrf 0xff,r3 + mtlr r4 + mtctr r5 + mtspr XER,r6 + REST_8GPRS(0,r21) + REST_4GPRS(8,r21) + REST_2GPRS(12,r21) + mtspr SRR1,r23 + mtspr SRR0,r22 + ld r20,GPR20(r21) + ld r22,GPR22(r21) + ld r23,GPR23(r21) + ld r21,GPR21(r21) + RFID + +1: + mfspr r4,DAR + std r4,_DAR(r21) + mfspr r5,DSISR + std r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + + SET_REG_TO_CONST(r20, MSR_KERNEL) + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + li r6,0x380 + bl .transfer_to_handler + .llong .do_page_fault + .llong .ret_from_except + + .globl Alignment_common +Alignment_common: + EXCEPTION_PROLOG_COMMON + mfspr r4,DAR + std r4,_DAR(r21) + mfspr r5,DSISR + std r5,_DSISR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + SET_REG_TO_CONST(r20, MSR_KERNEL) + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + li r6,0x600 + bl .transfer_to_handler + .llong .AlignmentException + .llong .ret_from_except + + .globl ProgramCheck_common +ProgramCheck_common: + EXCEPTION_PROLOG_COMMON + addi r3,r1,STACK_FRAME_OVERHEAD + SET_REG_TO_CONST(r20, MSR_KERNEL) + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + li r6,0x700 + bl .transfer_to_handler + .llong .ProgramCheckException + .llong .ret_from_except + + .globl FPUnavailable_common +FPUnavailable_common: + EXCEPTION_PROLOG_COMMON + bne .load_up_fpu /* if from user, just load it up */ + SET_REG_TO_CONST(r20, MSR_KERNEL) + li r6,0x800 + bl .transfer_to_handler /* if from kernel, take a trap */ + .llong .KernelFP + .llong .ret_from_except + + .globl SystemCall_common +SystemCall_common: + EXCEPTION_PROLOG_COMMON + std r3,ORIG_GPR3(r21) + SET_REG_TO_CONST(r20, MSR_KERNEL) + rlwimi r20,r23,0,16,16 /* copy EE bit from saved MSR */ + li r6,0xC00 + bl .transfer_to_handler + .llong .DoSyscall + .llong .ret_from_except + +_GLOBAL(do_hash_page_ISI) + li r4,0 +_GLOBAL(do_hash_page_DSI) + rlwimi r4,r23,32-13,30,30 /* Insert MSR_PR as _PAGE_USER */ + ori r4,r4,1 /* add _PAGE_PRESENT */ + + mflr r21 /* Save LR in r21 */ + /* r21 restored later from r1 */ + /* + * r3 contains the faulting address + * r4 contains the required access permissions + * + * at return r3 = 0 for success + */ + + bl .hash_page /* build HPTE if possible */ + mtlr r21 /* restore LR */ + mr r21,r1 /* restore r21 */ + + or. r3,r3,r3 /* Check return code */ + bnelr /* Return to DSI or ISI on failure */ + + /* + * The HPTE has been created/updated. Restore and retry the faulting inst + */ + + ld r3,_CCR(r21) + ld r4,_LINK(r21) + ld r5,_CTR(r21) + ld r6,_XER(r21) + mtcrf 0xff,r3 + mtlr r4 + mtctr r5 + mtspr XER,r6 + REST_8GPRS(0,r21) + REST_4GPRS(8,r21) + REST_2GPRS(12,r21) + mtspr SRR1,r23 + mtspr SRR0,r22 + ld r20,GPR20(r21) + ld r22,GPR22(r21) + ld r23,GPR23(r21) + ld r21,GPR21(r21) + rfid + +_GLOBAL(do_stab_SI) + mflr r21 /* Save LR in r21 */ + /* r21 restored later from r1 */ + /* + * r3 contains the faulting address + * r4 contains the required access permissions + * + * at return r3 = 0 for success + */ + + bl .ste_allocate /* build STE if possible */ + mtlr r21 /* restore LR */ + mr r21,r1 /* restore r21 */ + + or. r3,r3,r3 /* Check return code */ + bnelr /* Return to DSI or ISI on failure */ + + /* + * The STE has been created/updated. Restore and retry the faulting inst + */ + + ld r3,_CCR(r21) + ld r4,_LINK(r21) + ld r5,_CTR(r21) + ld r6,_XER(r21) + + mtcrf 0xff,r3 + mtlr r4 + mtctr r5 + mtspr XER,r6 + REST_8GPRS(0,r21) + REST_4GPRS(8,r21) + REST_2GPRS(12,r21) + mtspr SRR1,r23 + mtspr SRR0,r22 + ld r20,GPR20(r21) + ld r22,GPR22(r21) + ld r23,GPR23(r21) + ld r21,GPR21(r21) + rfid + +/* + * This code finishes saving the registers to the exception frame + * and jumps to the appropriate handler for the exception. Address + * translation is already on. + */ +_GLOBAL(transfer_to_handler) +/* + * Save the rest of the registers into the pt_regs structure + */ + std r22,_NIP(r21) + std r23,_MSR(r21) + std r6,TRAP(r21) + ld r6,GPR6(r21) + SAVE_2GPRS(14, r21) + SAVE_4GPRS(16, r21) + SAVE_8GPRS(24, r21) +/* + * Clear any reservation, and clear the RESULT field + */ + li r22,RESULT + stdcx. r22,r22,r21 /* to clear the reservation */ + li r22,0 + std r22,RESULT(r21) +/* + * Test if from user state; result will be tested later + */ + andi. r23,r23,MSR_PR /* Set CR for later branch */ + /* SPRG3 -> PACA */ +/* + * Indicate that r1 contains the kernel stack and + * get the Kernel TOC and CURRENT pointers from the Paca + */ + mfspr r23,SPRG3 /* Get PACA */ + std r22,PACAKSAVE(r23) /* r1 is now kernel sp */ + ld r2,PACATOC(r23) /* Get Kernel TOC pointer */ + ld r22,PACACURRENT(r23) /* Get CURRENT */ +/* + * If from user state, update THREAD.regs + */ + beq 2f /* Modify THREAD.regs if from user */ + addi r24,r1,STACK_FRAME_OVERHEAD + std r24,THREAD+PT_REGS(r22) +2: + +/* + * Since we store 'current' in the PACA now, we don't need to + * set it here. When r2 was used as 'current' it had to be + * set here because it could have been changed by the user. + */ + +/* + * Check for kernel stack overflow + */ + addi r24,r22,TASK_STRUCT_SIZE /* check for kernel stack overflow */ + cmpld 0,r1,r22 + cmpld 1,r1,r24 + crand 1,1,4 + bgt- .stack_ovf /* if r22 < r1 < r22+TASK_STRUCT_SIZE */ + +/* + * Get the handler address and its return address and + * rfid to the handler + */ +/* MIKEC: Do we really need to rfid here? Since we are already running + with relocate on, could we just branch to the handler? + We also will be turning on MSR.EE for some interrupts here +*/ + mflr r23 + ld r24,0(r23) /* virtual address of handler */ + ld r23,8(r23) /* where to go when done */ + mtspr SRR0,r24 + mtspr SRR1,r20 + mtlr r23 + rfid /* jump to handler */ + +/* + * On pSeries, secondary processors spin in the following code. + * At entry, r3 = this processor's number (in Linux terms, not hardware). + */ +_GLOBAL(pseries_secondary_smp_init) + /* Set up a Paca value for this processor. */ + LOADADDR(r24, xPaca) /* Get base vaddr of Paca array */ + mulli r25,r3,PACA_SIZE /* Calculate vaddr of right Paca */ + add r25,r25,r24 /* for this processor. */ + + SET_REG_TO_CONST(r26,KERNELBASE) /* Calculate raddr of the Paca */ + sub r26,r25,r26 + + mtspr SPRG3,r25 /* Save vaddr of Paca in SPRG3 */ + mr r24,r3 /* __secondary_start needs cpu# */ + +1: + HMT_LOW + lbz r23,PACAPROCSTART(r26) /* Test if this processor should */ + /* start. */ + cmpi 0,r23,0 +#ifdef CONFIG_SMP + bne .__secondary_start /* Loop until told to go. */ +#endif + b 1b + +_GLOBAL(__start_initialization_iSeries) + + LOADADDR(r1,init_task_union) + addi r1,r1,TASK_UNION_SIZE + li r0,0 + stdu r0,-STACK_FRAME_OVERHEAD(r1) + + LOADADDR(r2,__toc_start) + addi r2,r2,0x4000 + addi r2,r2,0x4000 + + LOADADDR(r9,naca) + SET_REG_TO_CONST(r4, KERNELBASE) + addi r4,r4,0x4000 + std r4,0(r9) /* set the naca pointer */ + + /* Get the pointer to the segment table */ + ld r6,PACA(r4) /* Get the base Paca pointer */ + ld r4,PACASTABVIRT(r6) + + li r3,1 /* Indicate to skip adding esid C to table */ + bl .stab_initialize + + b .start_here_common + +_GLOBAL(__start_initialization_pSeries) + mr r31,r3 /* save parameters */ + mr r30,r4 + mr r29,r5 + mr r28,r6 + mr r27,r7 + mr r26,r8 /* YABOOT: debug_print() routine */ + mr r25,r9 /* YABOOT: debug_delay() routine */ + mr r24,r10 /* YABOOT: debug_prom() routine */ + + bl .enable_64b_mode + + /* put a relocation offset into r3 */ + bl .reloc_offset + + LOADADDR(r2,__toc_start) + addi r2,r2,0x4000 + addi r2,r2,0x4000 + + /* Relocate the TOC from a virt addr to a real addr */ + sub r2,r2,r3 + + /* setup the naca pointer which is needed by prom_init */ + LOADADDR(r9,naca) + sub r9,r9,r3 /* addr of the variable naca */ + + SET_REG_TO_CONST(r4, KERNELBASE) + sub r4,r4,r3 + addi r4,r4,0x4000 + std r4,0(r9) /* set the value of naca */ + + /* DRENG / PPPBBB Fix the following comment!!! -Peter */ + /* The following copies the first 0x100 bytes of code from the */ + /* load addr to physical addr 0x0. This code causes secondary */ + /* processors to spin until a flag in the PACA is set. This */ + /* is done at this time rather than with the entire kernel */ + /* relocation which is done below because we need to cause the */ + /* processors to spin on code that is not going to move while OF */ + /* is still alive. Although the spin code is not actually run on */ + /* a uniprocessor, we always do this copy. */ + SET_REG_TO_CONST(r4, KERNELBASE)/* Src addr */ + sub r4,r4,r3 /* current address of __start */ + /* the source addr */ + li r3,0 /* Dest addr */ + li r5,0x100 /* # bytes of memory to copy */ + li r6,0 /* Destination offset */ + bl .copy_and_flush /* copy the first 0x100 bytes */ + + mr r3,r31 + mr r4,r30 + mr r5,r29 + mr r6,r28 + mr r7,r27 + mr r8,r26 + mr r9,r25 + mr r10,r24 + + bl .prom_init + + li r24,0 /* cpu # */ + +/* + * At this point, r3 contains the physical address we are running at, + * returned by prom_init() + */ +_STATIC(__after_prom_start) + +/* + * We need to run with __start at physical address 0. + * This will leave some code in the first 256B of + * real memory, which are reserved for software use. + * The remainder of the first page is loaded with the fixed + * interrupt vectors. The next two pages are filled with + * unknown exception placeholders. + * + * Note: This process overwrites the OF exception vectors. + * r26 == relocation offset + * r27 == KERNELBASE + */ + bl .reloc_offset + mr r26,r3 + SET_REG_TO_CONST(r27,KERNELBASE) + + li r3,0 /* target addr */ + + sub r4,r27,r26 /* source addr */ + /* current address of _start */ + /* i.e. where we are running */ + /* the source addr */ + + LOADADDR(r5,copy_to_here) /* # bytes of memory to copy */ + sub r5,r5,r27 + + li r6,0x100 /* Start offset, the first 0x100 */ + /* bytes were copied earlier. */ + + bl .copy_and_flush /* copy the first n bytes */ + /* this includes the code being */ + /* executed here. */ + + li r0,4f@l /* Jump to the copy of this code */ + mtctr r0 /* that we just made */ + bctr + +4: LOADADDR(r9,rtas_data) + sub r9,r9,r26 + ld r5,RTASBASE(r9) /* get the value of rtas->base */ + ld r9,RTASSIZE(r9) /* get the value of rtas->size */ + bl .copy_and_flush /* copy upto rtas->base */ + add r6,r6,r9 /* then skip over rtas->size bytes */ + + LOADADDR(r5,klimit) + sub r5,r5,r26 + ld r5,0(r5) /* get the value of klimit */ + sub r5,r5,r27 + bl .copy_and_flush /* copy the rest */ + b .start_here_pSeries + +/* + * Copy routine used to copy the kernel to start at physical address 0 + * and flush and invalidate the caches as needed. + * r3 = dest addr, r4 = source addr, r5 = copy limit, r6 = start offset + * on exit, r3, r4, r5 are unchanged, r6 is updated to be >= r5. + * + * Note: this routine *only* clobbers r0, r6 and lr + */ +_STATIC(copy_and_flush) + addi r5,r5,-8 + addi r6,r6,-8 +4: li r0,16 /* Use the least common */ + /* denominator cache line */ + /* size. This results in */ + /* extra cache line flushes */ + /* but operation is correct. */ + /* Can't get cache line size */ + /* from NACA as it is being */ + /* moved too. */ + + mtctr r0 /* put # words/line in ctr */ +3: addi r6,r6,8 /* copy a cache line */ + ldx r0,r6,r4 + stdx r0,r6,r3 + bdnz 3b + dcbst r6,r3 /* write it to memory */ + sync + icbi r6,r3 /* flush the icache line */ + cmpld 0,r6,r5 + blt 4b + sync + addi r5,r5,8 + addi r6,r6,8 + blr + +.align 8 +copy_to_here: + +/* + * On kernel stack overflow, load up an initial stack pointer + * and call StackOverflow(regs), which should not return. + */ +_STATIC(stack_ovf) + addi r3,r1,STACK_FRAME_OVERHEAD + LOADADDR(r1,init_task_union) + addi r1,r1,TASK_UNION_SIZE-STACK_FRAME_OVERHEAD + LOADADDR(r24,StackOverflow) + SET_REG_TO_CONST(r20, MSR_KERNEL); + mtspr SRR0,r24 + mtspr SRR1,r20 + rfid + +/* + * Disable FP for the task which had the FPU previously, + * and save its floating-point registers in its thread_struct. + * Enables the FPU for use in the kernel on return. + * On SMP we know the fpu is free, since we give it up every + * switch. -- Cort + */ +_STATIC(load_up_fpu) + mfmsr r5 /* grab the current MSR */ + ori r5,r5,MSR_FP + mtmsrd r5 /* enable use of fpu now */ + SYNC +/* + * For SMP, we don't do lazy FPU switching because it just gets too + * horrendously complex, especially when a task switches from one CPU + * to another. Instead we call giveup_fpu in switch_to. + * + */ +#ifndef CONFIG_SMP + LOADBASE(r3,last_task_used_math) + ld r4,last_task_used_math@l(r3) + cmpi 0,r4,0 + beq 1f + addi r4,r4,THREAD /* want THREAD of last_task_used_math */ + SAVE_32FPRS(0, r4) + mffs fr0 + stfd fr0,THREAD_FPSCR-4(r4) + ld r5,PT_REGS(r4) + ld r4,_MSR-STACK_FRAME_OVERHEAD(r5) + li r20,MSR_FP|MSR_FE0|MSR_FE1 + andc r4,r4,r20 /* disable FP for previous task */ + std r4,_MSR-STACK_FRAME_OVERHEAD(r5) +1: +#endif /* CONFIG_SMP */ + /* enable use of FP after return */ + ori r23,r23,MSR_FP|MSR_FE0|MSR_FE1 + mfspr r5,SPRG3 /* current task's PACA */ + ld r5,PACACURRENT(r5) /* Get 'current' */ + addi r5,r5,THREAD /* Get THREAD */ + lfd fr0,THREAD_FPSCR-4(r5) + mtfsf 0xff,fr0 + REST_32FPRS(0, r5) +#ifndef CONFIG_SMP + subi r4,r5,THREAD /* Back to 'current' */ + std r4,last_task_used_math@l(r3) +#endif /* CONFIG_SMP */ + /* restore registers and return */ + ld r3,_CCR(r21) + ld r4,_LINK(r21) + mtcrf 0xff,r3 + mtlr r4 + REST_2GPRS(1, r21) + REST_4GPRS(3, r21) + /* we haven't used ctr or xer */ + mtspr SRR1,r23 + mtspr SRR0,r22 + REST_GPR(20, r21) + REST_2GPRS(22, r21) + ld r21,GPR21(r21) + rfid + +/* + * FP unavailable trap from kernel - print a message, but let + * the task use FP in the kernel until it returns to user mode. + */ +_GLOBAL(KernelFP) + ld r3,_MSR(r1) + ori r3,r3,MSR_FP + std r3,_MSR(r1) /* enable use of FP after return */ + LOADADDR(r3,86f) + mfspr r4,SPRG3 /* Get PACA */ + ld r4,PACACURRENT(r4) /* current */ + ld r5,_NIP(r1) + b .ret_from_except +86: .string "floating point used in kernel (task=%p, pc=%x)\n" + .align 4 + +/* + * giveup_fpu(tsk) + * Disable FP for the task given as the argument, + * and save the floating-point registers in its thread_struct. + * Enables the FPU for use in the kernel on return. + */ +_GLOBAL(giveup_fpu) + mfmsr r5 + ori r5,r5,MSR_FP + mtmsrd r5 /* enable use of fpu now */ + SYNC + cmpi 0,r3,0 + beqlr- /* if no previous owner, done */ + addi r3,r3,THREAD /* want THREAD of task */ + ld r5,PT_REGS(r3) + cmpi 0,r5,0 + SAVE_32FPRS(0, r3) + mffs fr0 + stfd fr0,THREAD_FPSCR-4(r3) + beq 1f + ld r4,_MSR-STACK_FRAME_OVERHEAD(r5) + li r3,MSR_FP|MSR_FE0|MSR_FE1 + andc r4,r4,r3 /* disable FP for previous task */ + std r4,_MSR-STACK_FRAME_OVERHEAD(r5) +1: +#ifndef CONFIG_SMP + li r5,0 + LOADBASE(r4,last_task_used_math) + std r5,last_task_used_math@l(r4) +#endif /* CONFIG_SMP */ + blr + + + +#ifdef CONFIG_SMP +/* + * This function is called after the master CPU has released the + * secondary processors. The execution environment is relocation off. + * The Paca for this processor has the following fields initialized at + * this point: + * 1. Processor number + * 2. Segment table pointer (virtual address) + * On entry the following are set: + * r24 = cpu# (in Linux terms) + * r25 = Paca virtual address + * r26 = Paca physical address + * SPRG3 = Paca virtual address + */ +_GLOBAL(__secondary_start) + /* turn on 64 bit mode */ + bl .enable_64b_mode + isync + + /* get our current offset. */ + SET_REG_TO_CONST(r27, KERNELBASE) + + HMT_MEDIUM /* Set thread priority to MEDIUM */ + + /* get a virtual pointer to current */ + LOADADDR(r3,current_set) + sub r3,r3,r27 + addi r3,r3,8 + sldi r28,r24,4 /* get current_set[cpu#] */ + ldx r1,r3,r28 + + /* set up the TOC (virtual address) */ + LOADADDR(r2,__toc_start) + addi r2,r2,0x4000 + addi r2,r2,0x4000 + + std r2,PACATOC(r26) + li r6,0 + std r6,PACAKSAVE(r26) + +#ifndef CONFIG_PPC_ISERIES + /* Initialize the page table pointer register. */ + LOADADDR(r6,_SDR1) + sub r6,r6,r27 + ld r6,0(r6) /* get the value of _SDR1 */ + mtspr SDR1,r6 /* set the htab location */ +#endif + /* Initialize the segment table subsystem. */ + ld r4,PACASTABREAL(r26) /* get raddr of segment table */ + ori r3,r4,1 /* turn on valid bit */ + mtasr r3 /* set the stab location */ + li r3,0 /* 0 -> include esid 0xC00000000 */ + slbia + bl .stab_initialize + + /* get a virtual pointer to current */ + SET_REG_TO_CONST(r27, KERNELBASE) + LOADADDR(r3,current_set) + sub r3,r3,r27 + sldi r28,r24,4 /* get current_set[cpu#] */ + ldx r6,r3,r28 + addi r1,r6,TASK_UNION_SIZE + subi r1,r1,STACK_FRAME_OVERHEAD + + addi r3,r3,8 + ld r3,0(r3) + li r7,0 + std r7,0(r3) + mtlr r7 + + mfspr r4,SPRG3 + std r6,PACACURRENT(r4) + + /* enable MMU and jump to start_secondary */ + LOADADDR(r3,.start_secondary) + SET_REG_TO_CONST(r4, MSR_KERNEL) + mtspr SRR0,r3 + mtspr SRR1,r4 + rfid +#endif /* CONFIG_SMP */ + +/* + * This subroutine clobbers r11, r12 and the LR + */ +_GLOBAL(enable_64b_mode) + mfmsr r11 /* grab the current MSR */ + li r12,1 + rldicr r12,r12,MSR_SF_LG,(63-MSR_SF_LG) + or r11,r11,r12 + li r12,1 + rldicr r12,r12,MSR_ISF_LG,(63-MSR_ISF_LG) + or r11,r11,r12 + mtmsrd r11 + SYNC + blr + +/* + * This subroutine clobbers r11, r12 and the LR + */ +_GLOBAL(enable_32b_mode) + mfmsr r11 /* grab the current MSR */ + li r12,1 + rldicr r12,r12,MSR_SF_LG,(63-MSR_SF_LG) + andc r11,r11,r12 + li r12,1 + rldicr r12,r12,MSR_ISF_LG,(63-MSR_ISF_LG) + andc r11,r11,r12 + mtmsrd r11 + SYNC + blr + +#if 0 +_GLOBAL(call_function32b_ptr) + mflr r0 + std r0,16(1) /* Save return address */ + stdu r1,-INT_FRAME_SIZE(r1) /* Save SP and create stack space */ + std r2,40(r1) /* Save our TOC */ + + bl .enable_32b_mode /* Switch to 32b execution mode */ + + SET_REG_TO_CONST(r11, KERNELBASE) + LOADADDR(r12,function32b_ptr) + sub r12,r12,r11 + ld r12,0(r12) /* Setup func's params and branch */ + mtlr r12 + blrl + + bl .enable_64b_mode /* Switch back to 64b execution mode */ + + ld r2,40(r1) /* Reload our TOC */ + la r1,INT_FRAME_SIZE(r1) /* Reclaim stack space and return */ + ld r0,16(r1) + mtlr r0 + blr +#else +_GLOBAL(call_function32b_ptr) + mflr r0 + std r0,16(1) /* Save return address */ + stdu r1,-128(r1) /* Save SP and create stack space */ + std r2,40(r1) /* Save our TOC */ + + std r3,48(r1) /* Save function's entry point */ + std r4,56(r1) /* Save function's actual parameter(s) */ + std r5,64(r1) + std r6,72(r1) + std r7,80(r1) + std r8,88(r1) + std r9,96(r1) + std r10,104(r1) + + bl .enable_32b_mode /* Switch to 32b execution mode */ + + bl .reloc_offset + LOADADDR(r12,function32b_ptr) + sub r12,r12,r3 + ld r12,0(r12) + mtlr r12 + + ld r3,48(r1) /* Save function's entry point */ + ld r4,56(r1) /* Save function's actual parameter(s) */ + ld r5,64(r1) + ld r6,72(r1) + ld r7,80(r1) + ld r8,88(r1) + ld r9,96(r1) + ld r10,104(r1) + blrl + + bl .enable_64b_mode /* Switch back to 64b execution mode */ + + ld r2,40(r1) /* Reload our TOC */ + la r1,128(r1) /* Reclaim stack space and return */ + ld r0,16(r1) + mtlr r0 + blr +#endif + +_GLOBAL(call_function64b_ptr) + mflr r0 + std r0,16(1) /* Save return address */ + stdu r1,-128(r1) /* Save SP and create stack space */ + std r2,40(r1) /* Save our TOC */ + + std r3,48(r1) /* Save function's entry point */ + std r4,56(r1) /* Save function's actual parameter(s) */ + std r5,64(r1) + std r6,72(r1) + std r7,80(r1) + std r8,88(r1) + std r9,96(r1) + std r10,104(r1) + + ld r0,48(r1) /* Setup func's params and branch */ + mtlr r0 + ld r3,56(r1) + ld r4,64(r1) + ld r5,72(r1) + ld r6,80(r1) + ld r7,88(r1) + ld r8,96(r1) + ld r9,104(r1) + blrl + + ld r2,40(r1) /* Reload our TOC */ + la r1,128(r1) /* Reclaim stack space and return */ + ld r0,16(r1) + mtlr r0 + blr + + +_GLOBAL(call_function_ptr) + mflr r0 + std r0,16(1) /* Save return address */ + stdu r1,-64(r1) /* Save SP and create stack space */ + std r2,40(r1) /* Save our TOC */ + + mtlr r3 + mr r3,r4 + mr r4,r5 + mr r5,r6 + mr r6,r7 + mr r7,r8 + mr r8,r9 + mr r9,r10 + blrl + + ld r2,40(r1) /* Reload our TOC */ + la r1,64(r1) /* Reclaim stack space and return */ + ld r0,16(r1) + mtlr r0 + blr + +/* + * This is where the main kernel code starts. + */ +_STATIC(start_here_pSeries) + +#ifdef CONFIG_SMP + /* All secondary cpus are now spinning on a common + * spinloop, release them all now so they can start + * to spin on their individual Paca spinloops. + * For non SMP kernels, the secondary cpus never + * get out of the common spinloop. + */ + li r3,1 + LOADADDR(r5,__secondary_hold_spinloop) + tophys(r4,r5) + std r3,0(r4) +#endif + + /* get a new offset, now that the kernel has moved. */ + bl .reloc_offset + mr r26,r3 + + /* The following gets the stack and TOC set up with the regs */ + /* pointing to the real addr of the kernel stack. This is */ + /* all done to support the C function call below which sets */ + /* up the htab. This is done because we have relocated the */ + /* kernel but are still running in real mode. */ + + /* real ptr to current */ + LOADADDR(r3,init_task_union) + sub r3,r3,r26 + + /* set up a stack pointer (physical address) */ + addi r1,r3,TASK_UNION_SIZE + li r0,0 + stdu r0,-STACK_FRAME_OVERHEAD(r1) + + /* set up the TOC (physical address) */ + LOADADDR(r2,__toc_start) + addi r2,r2,0x4000 + addi r2,r2,0x4000 + sub r2,r2,r26 + + /* setup the naca pointer which is needed by *tab_initialize */ + LOADADDR(r6,naca) + sub r6,r6,r26 /* addr of the variable naca */ + li r27,0x4000 + std r27,0(r6) /* set the value of naca */ + + /* Init naca->debug_switch so it can be used in stab & htab init. */ + bl .ppcdbg_initialize + + /* Get the pointer to the segment table which is used by */ + /* stab_initialize */ + ld r6,PACA(r27) /* Get the base Paca pointer */ + sub r6,r6,r26 /* convert to physical addr */ + ld r4,PACASTABREAL(r6) + ori r3,r4,1 /* turn on valid bit */ + mtasr r3 /* set the stab location */ + + /* Initialize an initial memory mapping and turn on relocation. */ + li r3,0 /* 0 -> include esid 0xC00000000 */ + slbia + bl .stab_initialize + bl .htab_initialize + + LOADADDR(r6,_SDR1) + sub r6,r6,r26 + ld r6,0(r6) /* get the value of _SDR1 */ + mtspr SDR1,r6 /* set the htab location */ + + LOADADDR(r3,.start_here_common) + SET_REG_TO_CONST(r4, MSR_KERNEL) + mtspr SRR0,r3 + mtspr SRR1,r4 + rfid + + /* This is where all platforms converge execution */ +_STATIC(start_here_common) + /* relocation is on at this point */ + + /* Clear out the BSS */ + LOADADDR(r11,_end) + + LOADADDR(r8,__bss_start) + + sub r11,r11,r8 /* bss size */ + addi r11,r11,7 /* round up to an even double word */ + rldicl. r11,r11,61,3 /* shift right by 3 */ + beq 4f + addi r8,r8,-8 + li r0,0 + mtctr r11 /* zero this many doublewords */ +3: stdu r0,8(r8) + bdnz 3b +4: + + /* The following code sets up the SP and TOC now that we are */ + /* running with translation enabled. */ + + /* ptr to current */ + LOADADDR(r3,init_task_union) + + /* set up the stack */ + addi r1,r3,TASK_UNION_SIZE + li r0,0 + stdu r0,-STACK_FRAME_OVERHEAD(r1) + + /* set up the TOC */ + LOADADDR(r2,__toc_start) + addi r2,r2,0x4000 + addi r2,r2,0x4000 + + /* setup the naca pointer */ + LOADADDR(r9,naca) + + SET_REG_TO_CONST(r8, KERNELBASE) + addi r8,r8,0x4000 + std r8,0(r9) /* set the value of the naca ptr */ + + bl .naca_init /* Set initial values in the naca */ + + LOADADDR(r4,naca) /* Get Naca ptr address */ + ld r4,0(r4) /* Get the location of the naca */ + ld r4,PACA(r4) /* Get the base Paca pointer */ + mtspr SPRG3,r4 + + /* ptr to current */ + LOADADDR(r3,init_task_union) + std r3,PACACURRENT(r4) + + std r2,PACATOC(r4) + li r5,0 + std r0,PACAKSAVE(r4) +/* + * Register old definition new definition + * + * SPRG0 temp - used to save gpr reserved for hypervisor + * SPRG1 temp - used to save gpr unused + * SPRG2 0 or kernel stack frame temp - used to save gpr + * SPRG3 Linux thread virt addr of Paca + */ + + /* + * Restore the parms passed in from the bootloader. + */ + mr r3,r31 + mr r4,r30 + mr r5,r29 + mr r6,r28 + mr r7,r27 + + bl .setup_system + + /* Load up the kernel context */ +5: + bl .start_kernel + +/* + * We put a few things here that have to be page-aligned. + * This stuff goes at the beginning of the data segment, + * which is page-aligned. + */ + .data + .align 12 + .globl sdata +sdata: + .globl empty_zero_page +empty_zero_page: + .space 4096 + + .globl swapper_pg_dir +swapper_pg_dir: + .space 4096 + + .globl ioremap_dir +ioremap_dir: + .space 4096 + + .globl bolted_dir +bolted_dir: + .space 4096 + +/* + * This space gets a copy of optional info passed to us by the bootstrap + * Used to pass parameters into the kernel like root=/dev/sda1, etc. + */ + .globl cmd_line +cmd_line: + .space 512 diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/htab.c linuxppc64_2_4/arch/ppc64/kernel/htab.c --- linux-2.4.5-ac18/arch/ppc64/kernel/htab.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/htab.c Mon Jun 18 15:33:06 2001 @@ -0,0 +1,1082 @@ +/* + * + * + * PowerPC64 port by Mike Corrigan and Dave Engebretsen + * {mikejc|engebret}@us.ibm.com + * + * Copyright (c) 2000 Mike Corrigan + * + * Module name: htab.c + * + * Description: + * PowerPC Hashed Page Table functions + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* For iSeries */ +#include + +/* Note: pte --> Linux PTE + * HPTE --> PowerPC Hashed Page Table Entry + * HPTEX --> Hashed Page Table Extension + */ + +HTAB htab_data = {0, 0, 0, 0, 0}; + +static ssize_t ppc_htab_read(struct file * file, char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_htab_write(struct file * file, const char * buffer, + size_t count, loff_t *ppos); +static long ppc_htab_lseek(struct file * file, loff_t offset, int orig); +int proc_dol2crvec(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp); + +void htab_initialize(void); +void make_pte(HPTE * htab, HPTEX * htabx, + unsigned long va, unsigned long pa, int mode, unsigned long hash_mask); +extern unsigned long reloc_offset(void); +extern unsigned long get_kernel_vsid( unsigned long ea ); +extern void cacheable_memzero( void *, unsigned int ); + +extern unsigned long _SDR1; +extern char *klimit; +extern struct Naca *naca; + +extern u32 msChunks[]; +extern unsigned long _ASR; +extern inline void make_ste(unsigned long stab, + unsigned long esid, unsigned long vsid); + +extern pgd_t * bolted_pgd; + +extern char _stext[], _etext[]; + +spinlock_t hash_table_lock = SPIN_LOCK_UNLOCKED; + +#define PTRRELOC(x) ((typeof(x))((unsigned long)(x) - offset)) +#define PTRUNRELOC(x) ((typeof(x))((unsigned long)(x) + offset)) +#define RELOC(x) (*PTRRELOC(&(x))) + +void htab_initialize() { + unsigned long offset = reloc_offset(); + unsigned long table, tablex, htab_size_bytes, htabx_size_bytes, htabsize, + mem_size, rnd_mem_size, pteg_count, map_size, addr, vsid; + struct Naca *reloc_naca = RELOC(naca); + unsigned long start_ro, end_ro; + + /* + * Calculate the required size of the htab. We want the number of + * PTEGs to equal one half the number of real pages. + */ + mem_size = reloc_naca->physicalMemorySize; + + /* round mem_size up to next power of 2 -- htab size must be + a power of 2 */ + rnd_mem_size = 1UL << (unsigned long)__ilog2(mem_size); + if ( rnd_mem_size < mem_size ) + rnd_mem_size <<= 1; + + pteg_count = (rnd_mem_size >> (12 + 1)); /* # pages / 2 */ + + /* For debug, make the HTAB 1/8 as big as it normally would be. */ + if(reloc_naca->debug_switch & PPCDBG_HTABSIZE) { + pteg_count >>= 3; + } + + RELOC(htab_data.htab_num_ptegs) = pteg_count; + RELOC(htab_data.htab_hash_mask) = pteg_count - 1; + htab_size_bytes = (pteg_count << 7); /* pteg_count*128B/PTEG */ + + /* size of HPT extension */ + htabx_size_bytes = (pteg_count*8) * (sizeof(HPTEX)); + + /* move to a boundary = the size */ + table = (unsigned long)RELOC(klimit); + table = (table + (htab_size_bytes -1)) & (~(htab_size_bytes -1)); + RELOC(htab_data.htab) = (HPTE *)table; + + /* assign space for HPT extension */ + tablex = table + htab_size_bytes; + RELOC(htab_data.htabx) = (HPTEX *)tablex; + + /* set the new value of klimit */ + RELOC(klimit) = (void *)(table + htab_size_bytes + htabx_size_bytes); + + /* HTABORG = htab physical addr + * HTABSIZE = # bits from hash used in pg table index + */ + htabsize = __ilog2(pteg_count) - 11; /* encoded htabsize */ + + RELOC(_SDR1) = table + htabsize - offset; + + /* + * Put in PTEs for the first 64MB of RAM. Make the assumption + * that addr&KERNELBASE = 0 (ie they are disjoint). We also + * assume that the va is <= 64 bits. + */ + cacheable_memzero((void *)table - offset, htab_size_bytes); + cacheable_memzero((void *)tablex - offset, htabx_size_bytes); + + RELOC(htab_data.last_kernel_address) = KERNELBASE + mem_size - 1; + + map_size = tablex + htabx_size_bytes - KERNELBASE; + + /* We make a single range of pages readonly. */ + start_ro = 0x6000; + end_ro = (_etext - _stext - PAGE_SIZE + 1) & PAGE_MASK; + + for (addr = 0; addr < map_size; addr += 0x1000) { + vsid = get_kernel_vsid(0xC000000000000000 + addr); + make_pte((HPTE *)(table - offset), + (HPTEX *)(tablex - offset), + (vsid << 28) | (addr & 0xfffffff), // va (NOT the ea) + addr, // pa + (addr >= start_ro && addr < end_ro) ? + _PAGE_ACCESSED | _PAGE_COHERENT | PP_RXRX : + _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX, + pteg_count-1 ); + + } +} + +/* + * Create a pte. Used during initialization only. + * We assume the PTE will fit in the primary PTEG. + */ +void make_pte(HPTE * htab, HPTEX * htabx, + unsigned long va, unsigned long pa, int mode, + unsigned long hash_mask) +{ + HPTE *hpte; + HPTEX *hptex; + unsigned long hash, i; + unsigned long vsid = va >> 28; + unsigned long page = (va >> 12) & 0xFFFF; + + /* 39 bit hash value = (low order 39 bits of VSID) xor (page index) */ + hash = (vsid & 0x7FFFFFFFFF) ^ page; // 39 bit hash value + hpte = htab + ((hash & hash_mask)*HPTES_PER_GROUP); + hptex = htabx + ((hash & hash_mask)*HPTES_PER_GROUP); + + for (i = 0; i < 8; ++i, ++hpte, ++hptex) { + if ( hpte->dw0.dw0.v == 0 ) { /* !valid */ + hpte->dw0.dword0 = ((((va >> 28) & 0xFFFFFFFFFFFFF) << 12) | /* VSID */ + (((va >> 23) & 0x1f) << 7) | /* API */ + 1); /* valid */ + hpte->dw1.dword1 = pa | mode; + hptex->dw1.dw1.flags.kernel = 1; + hptex->dw1.dw1.flags.valid = 1; + hptex->dw1.dw1.flags.bolted = 1; /* Mark bolted */ + break; + } + } +} + +void invalidate_hpte( unsigned long slot ) +{ + HPTEX * hptex = htab_data.htabx + slot; + + + /* Local copy of the second doubleword of the HPTEX */ + union { + unsigned long d; + HPTEX_dword1 h; + } hptex_dw1; + + pte_t * pte; + unsigned long flags; + unsigned long prpn; + unsigned long lpte; + + /* Invalidate the HPTE and get the Ref and Change bits */ + + if ( _machine == _MACH_iSeries ) { + unsigned long refBit, chgBit; + unsigned long status; + + status = HvCallHpt_invalidateSetSwBitsGet( slot, 0, 0 ); + + /* get the bits returned from the hypervisor into the + * positions needed + */ + refBit = ( status >> 40 ) & _PAGE_ACCESSED; + chgBit = ( status >> 33 ) & _PAGE_DIRTY; + + flags = refBit | chgBit; + + } + else { + /* Local copy of the first doubleword of the HPTE */ + union { + unsigned long d; + Hpte_dword0 h; + } hpte_dw0; + + /* Local copy of the second doubleword of the HPTE */ + union { + unsigned long d; + Hpte_dword1 h; + Hpte_dword1_flags f; + } hpte_dw1; + + /* Locate the HPTE */ + HPTE * hpte = htab_data.htab + slot; + + /* Get the first doubleword of the HPTE */ + hpte_dw0.d = hpte->dw0.dword0; + + /* Invalidate the hpte */ + hpte->dw0.dword0 = 0; + + /* Invalidate the tlb */ + { + unsigned long vsid, group, pi, pi_high; + + vsid = hpte_dw0.h.avpn >> 5; + group = slot >> 3; + if(hpte_dw0.h.h) { + group = ~group; + } + pi = (vsid ^ group) & 0x7ff; + pi_high = (hpte_dw0.h.avpn & 0x1f) << 11; + pi |= pi_high; + _tlbie(pi << 12); + } + + /* Get the second doubleword of the HPTE */ + hpte_dw1.d = hpte->dw1.dword1; + /* get just the ref and change bits */ + flags = hpte_dw1.f.flags & ( _PAGE_DIRTY | _PAGE_ACCESSED ); + + } + /* Get the address of the linux pte from the HPTEX */ + pte = hptex->linux_pte; + + /* If there is a linux pte, update it */ + if ( pte ) { + /* get the second doubleword of the HPTEX */ + hptex_dw1.d = hptex->dw1.dword1; + /* get the rpn (physical not absolute) */ + prpn = hptex_dw1.h.rpn; + + /* Update the Linux pte with flags and rpn */ + __asm__ __volatile__ ( "\n\ + 1: ldarx %0,0,%2 /* get old pte */ \n\ + or %0,%0,%4 /* insert C, R */ \n\ + andi. %0,%0,0xfbff /* clear hpte flag */ \n\ + insrdi %0,%5,52,0 /* insert rpn */ \n\ + stdcx. %0,0,%2 /* store back pte */ \n\ + bne- 1b /* try again */ \n" + : "=&r" (lpte), "=m" (*pte) + : "r" (pte), "m" (*pte), + "r" (flags), "r" (prpn) + : "cc" ); + } + + /* Clear the HPTE Extension */ + hptex->linux_pte = 0; + hptex->dw1.dword1 = 0; + +} + + +/* Select an available HPT slot for a new HPTE/HPTEX + * return slot index (if in primary group) + * return -slot index (if in secondary group) + * this function only examines the HPT Extension + * (usuable for native and LPAR) + */ +long select_hpte_slot( unsigned long vpn ) +{ + unsigned long primary_hash; + unsigned long hpteg_slot; + HPTEX * hptex; + unsigned i, k; + + unsigned long vsid = vpn >> 16; + unsigned long page = vpn & 0xffff; + + /* When searching for available slots, the HPTE extension + * is searched rather than the HPTEs themselves. This is + * done because touching the HPTEs is very expensive on + * LPAR systems (requires a hypervisor call) + + * Search the primary group for an available slot + */ + primary_hash = ( vsid & 0x7fffffffff ) ^ page; + hpteg_slot = ( primary_hash & htab_data.htab_hash_mask ) * HPTES_PER_GROUP; + hptex = htab_data.htabx + hpteg_slot; + + for (i=0; idw1.dw1.flags.valid == 0 ) { + /* If an available slot found, return it */ + return hpteg_slot + i; + } + hptex++; + } + + /* No available entry found in primary group */ + + PMC_SW_SYSTEM(htab_primary_overflows); + + /* Search the secondary group */ + + hpteg_slot = ( ~primary_hash & htab_data.htab_hash_mask ) * HPTES_PER_GROUP; + hptex = htab_data.htabx + hpteg_slot; + + for (i=0; idw1.dw1.flags.valid == 0 ) { + /* If an available slot found, return it */ + return -(hpteg_slot + i); + } + hptex++; + } + + /* No available entry found in secondary group */ + + PMC_SW_SYSTEM(htab_capacity_castouts); + + /* Select an entry in the primary group to replace */ + + hpteg_slot = ( primary_hash & htab_data.htab_hash_mask ) * HPTES_PER_GROUP; + hptex = htab_data.htabx + hpteg_slot; + k = htab_data.next_round_robin++ & 0x7; + + for (i=0; i> 12; + + spin_lock( &hash_table_lock ); + + hpte_slot = select_hpte_slot( vpn ); + hash = 0; + if ( hpte_slot < 0 ) { + hash = 1; + hpte_slot = -hpte_slot; + } + create_valid_hpte( hpte_slot, vpn, pa >> 12, hash, ptep, + hpteflags, hptexflags ); + + if ( ptep ) { + /* Get existing pte flags */ + pte = *ptep; + pte_val(pte) &= PAGE_SIZE-1; + + /* Add in the hash_hpte flag */ + pte_val(pte) |= _PAGE_HPTE; + + /* Add in the hpte slot */ + pte_val(pte) |= hpte_slot << PAGE_SHIFT; + + /* Save the new pte. */ + *ptep = pte; + + } + + spin_unlock( &hash_table_lock ); + +} + +/* Create an HPTE/HPTEX and validate it + * It is assumed that the HPT slot currently is invalid. + * The HPTE is set with the vpn, rpn (converted to absolute) + * and flags + * The HPTE Extension is set with the address of the linux pte + * and flags + */ +void create_valid_hpte(unsigned long slot, unsigned long vpn, + unsigned long prpn, unsigned hash, + void * ptep, unsigned hpteflags, + HPTEX_flags hptexflags ) +{ + /* Local copy of HPTE */ + struct { + /* Local copy of first doubleword of HPTE */ + union { + unsigned long d; + Hpte_dword0 h; + } dw0; + /* Local copy of second doubleword of HPTE */ + union { + unsigned long d; + Hpte_dword1 h; + Hpte_dword1_flags f; + } dw1; + } lhpte; + + /* Local copy of second doubleword of HPTEX */ + union { + unsigned long d; + HPTEX_dword1 h; + } hptex_dw1; + + unsigned long avpn = vpn >> 11; + unsigned long arpn = physRpn_to_absRpn( prpn ); + HPTEX * hptex = htab_data.htabx + slot; + + /* Fill in the local HPTE Extension with rpn, and flags */ + + hptex_dw1.d = 0; + hptex_dw1.h.rpn = prpn; + hptex_dw1.h.flags = hptexflags; + + hptex->dw1.dword1 = hptex_dw1.d; + hptex->linux_pte = ptep; + + /* Fill in the local HPTE with absolute rpn, avpn and flags */ + lhpte.dw1.d = 0; + lhpte.dw1.h.rpn = arpn; + lhpte.dw1.f.flags = hpteflags; + + lhpte.dw0.d = 0; + lhpte.dw0.h.avpn = avpn; + lhpte.dw0.h.h = hash; + lhpte.dw0.h.v = 1; + + /* Now fill in the actual HPTE */ + + if ( _machine == _MACH_iSeries ) { + HvCallHpt_addValidate( slot, hash, (HPTE *)&lhpte ); + } + else { + HPTE * hpte = htab_data.htab + slot; + + /* Set the second dword first so that the valid bit + * is the last thing set + */ + + hpte->dw1.dword1 = lhpte.dw1.d; + + /* Guarantee the second dword is visible before + * the valid bit + */ + + __asm__ __volatile__ ("eieio" : : : "memory"); + + /* Now set the first dword including the valid bit */ + hpte->dw0.dword0 = lhpte.dw0.d; + + __asm__ __volatile__ ("ptesync" : : : "memory"); + + } +} + +/* find_linux_pte returns the address of a linux pte for a given + * effective address and directory. If not found, it returns zero. + */ + +pte_t * find_linux_pte( pgd_t * pgdir, unsigned long ea ) +{ + pgd_t *pg; + pmd_t *pm; + pte_t *pt = NULL; + pte_t pte; + pg = pgdir + pgd_index( ea ); + if ( ! pgd_none( *pg ) ) { + + pm = pmd_offset( pg, ea ); + if ( ! pmd_none( *pm ) ) { + pt = pte_offset( pm, ea ); + pte = fetch_pte( pt ); + if ( ! pte_present( pte ) ) + pt = NULL; + } + } + + return pt; + +} + +#define SOFTWARE_CHANGE_MANAGEMENT 1 + +static inline unsigned long computeHptePP( unsigned long pte ) +{ + return ( pte & _PAGE_USER ) | + ( ( ( pte & _PAGE_USER ) >> 1 ) & +#ifdef SOFTWARE_CHANGE_MANAGEMENT + ( (~pte & _PAGE_HWWRITE ) >> 9 ) ); +#else + ( (~pte & _PAGE_RW ) >> 2 ) ); +#endif +} + +static void updateHptePP( long slot, unsigned long newpp, unsigned long va ) +{ +if ( _machine == _MACH_iSeries ) { +HvCallHpt_setPp( slot, newpp ); +} +else { +/* Local copy of first doubleword of HPTE */ +union { +unsigned long d; +Hpte_dword0 h; +} hpte_dw0; + +/* Local copy of second doubleword of HPTE */ +union { +unsigned long d; +Hpte_dword1 h; +Hpte_dword1_flags f; +} hpte_dw1; + +HPTE * hpte = htab_data.htab + slot; + +/* Turn off valid bit in HPTE */ +hpte_dw0.d = hpte->dw0.dword0; +hpte_dw0.h.v = 0; +hpte->dw0.dword0 = hpte_dw0.d; + +/* Ensure it is out of the tlb too */ +_tlbie( va ); + +/* Insert the new pp bits into the HPTE */ +hpte_dw1.d = hpte->dw1.dword1; +hpte_dw1.h.pp = newpp; +hpte->dw1.dword1 = hpte_dw1.d; + +/* Ensure it is visible before validating */ +__asm__ __volatile__ ("eieio" : : : "memory"); + +/* Turn the valid bit back on in HPTE */ +hpte_dw0.h.v = 1; +hpte->dw0.dword0 = hpte_dw0.d; + +__asm__ __volatile__ ("ptesync" : : : "memory"); +} +} + +/* Handle a fault by adding an HPTE + * If the address can't be determined to be valid + * via Linux page tables, return 1. If handled + * return 0 + */ + +int hash_page( unsigned long ea, unsigned long access ) +{ + int rc = 1; + void * pgdir = NULL; + unsigned long va, vsid, vpn; + unsigned long newpp, hash_ind, prpn; + unsigned long hpteflags; + long slot; + struct mm_struct * mm; + pte_t old_pte, new_pte, *ptep; + + union { + unsigned char c; + HPTEX_flags f; + } xflags; + + xflags.c = 0; + + if ( ( ea >= KRANGE_START ) && ( ea <= KRANGE_END ) ) { + vsid = get_kernel_vsid( ea ); + va = ( vsid << 28 ) | ( ea & 0x0fffffff ); + vpn = va >> PAGE_SHIFT; + ptep = 0; + xflags.f.kernel = 1; + pte_val(old_pte) = (__pa(ea) & PAGE_MASK) | + _PAGE_PRESENT | _PAGE_ACCESSED | + _PAGE_SHARED | _PAGE_RW | _PAGE_COHERENT | + _PAGE_DIRTY | _PAGE_HWWRITE; + spin_lock( &hash_table_lock ); + } + else { + /* if in vmalloc range, use the vmalloc page directory */ + if ( ( ea >= VMALLOC_START ) && ( ea <= VMALLOC_END ) ) { + mm = &init_mm; + vsid = get_kernel_vsid( ea ); + } + /* if in ioremap range, use the ioremap page directory */ + else if ( ( ea >= IMALLOC_START ) && ( ea <= IMALLOC_END ) ) { + mm = &ioremap_mm; + vsid = get_kernel_vsid( ea ); + } + /* if in user range, use the current task's page directory */ + else if ( ( ea >= USER_START ) && ( ea <= USER_END ) ) { + mm = current->mm; + if ( mm == NULL ) { + PPCDBG(PPCDBG_MM, "hash_page returning; mm = 0\n"); + return 1; + } + vsid = get_vsid(mm->context, ea ); + } + /* if not in a valid range, send the problem up to do_page_fault */ + else + return 1; + + /* Search the Linux page table for a match with va */ + va = ( vsid << 28 ) | ( ea & 0x0fffffff ); + vpn = va >> PAGE_SHIFT; + pgdir = mm->pgd; + PPCDBG(PPCDBG_MM, "hash_page ea = 0x%16.16lx, va = 0x%16.16lx\n current = 0x%16.16lx, access = %lx\n", ea, va, current, access); + if ( pgdir == NULL ) { + return 1; + } + + /* Lock the Linux page table to prevent mmap and kswapd + * from modifying entries while we search and update + */ + + spin_lock( &mm->page_table_lock ); + + ptep = find_linux_pte( pgdir, ea ); + /* If no pte found, send the problem up to do_page_fault */ + if ( ! ptep ) { + spin_unlock( &mm->page_table_lock ); + return 1; + } + + /* Acquire the hash table lock to guarantee that the linux + * pte we fetch will not change + */ + spin_lock( &hash_table_lock ); + + /* We fetch the linux pte, but without fetch_pte. The linux pte may have + * _PAGE_HPTE and therefore may not have the rpn + */ + old_pte = *ptep; + + /* If the pte is not "present" (valid), send the problem up to do_page_fault */ + if ( ! pte_present( old_pte ) ) { + spin_unlock( &hash_table_lock ); + spin_unlock( &mm->page_table_lock ); + return 1; + } + + /* At this point we have found a pte (which was present). + * The spinlocks prevent this status from changing + * The hash_table_lock prevents the _PAGE_HPTE status + * from changing (RPN/HPTEX ptr, DIRTY and ACCESSED too) + * The page_table_lock prevents the pte from being + * invalidated or modified + */ + + } + +/* At this point, we have a pte (old_pte) which can be used to build or update + * an HPTE/HPTEX. There are 5 cases: + * + * 1. There is a valid (present) pte with no associated HPTE/HPTEX (this is + * the most common case) + * 2. There is a valid (present) pte with an associated HPTE/HPTEX. The + * current values of the pp bits in the HPTE prevent access because the + * user doesn't have appropriate access rights. + * 3. There is a valid (present) pte with an associated HPTE/HPTEX. The + * current values of the pp bits in the HPTE prevent access because we + * doing software DIRTY bit management and the page is currently not DIRTY. + * 4. This is a Kernel address (0xC---) for which there is no page directory. + * There is an HPTE/HPTEX for this page, but the pp bits prevent access. + * Since we always set up kernel pages with R/W access for the kernel + * this case only comes about for users trying to access the kernel. + * This case is always an error and is not dealt with further here. + * 5. This is a Kernel address (0xC---) for which there is no page directory. + * There is no HPTE/HPTEX for this page. + + * Check the user's access rights to the page. If access should be prevented + * then send the problem up to do_page_fault. + */ + + access |= _PAGE_PRESENT; + if ( 0 == ( access & ~(pte_val(old_pte)) ) ) { + /* User has appropriate access rights. */ + new_pte = old_pte; + /* If the attempted access was a store */ + if ( access & _PAGE_RW ) + pte_val(new_pte) |= _PAGE_ACCESSED | + _PAGE_DIRTY | + _PAGE_HWWRITE; + else + pte_val(new_pte) |= _PAGE_ACCESSED; + + /* Only cases 1, 3 and 5 still in play */ + + newpp = computeHptePP( pte_val(new_pte) ); + + /* Check if pte already has an hpte (case 3) */ + if ( pte_has_hpte( old_pte ) ) { + /* For this case we need to update the + * PP bits in the HPTE. If we are doing + * software change management, we also + * need to update the linux pte with + * the DIRTY bit. With hardware change + * bit management we do not need to update + * the linux pte. + * + * Get the HPTE/HPTEX slot from the linux pte + */ + slot = ( pte_val( old_pte ) >> PAGE_SHIFT ); + + /* Update the pp bits in the HPTE */ + updateHptePP( slot, newpp, va ); + +#ifdef SOFTWARE_CHANGE_MANAGEMENT + if ( pte_val(new_pte) != pte_val(old_pte) ) + *ptep = new_pte; +#endif + + } + /* Cases 1 and 5 */ + else { + /* For these cases we need to create a new + * HPTE/HPTEX and update the linux pte (for + * case 1). For case 5 there is no linux pte. + * + * Find an available HPTE/HPTEX slot + */ + slot = select_hpte_slot( vpn ); + hash_ind = 0; + if ( slot < 0 ) { + slot = -slot; + hash_ind = 1; + } + + /* setup the HPTEX flags (valid and possibly kernel */ + + xflags.f.valid = 1; + + /* Set the physical address */ + prpn = pte_val(old_pte) >> PAGE_SHIFT; + + if ( ptep ) { + /* Update the linux pte with the HPTE/HPTEX slot */ + pte_val(new_pte) |= _PAGE_HPTE; + pte_val(new_pte) = (slot << PAGE_SHIFT) | + (pte_val(new_pte) & (PAGE_SIZE-1)); + /* No need to use ldarx/stdcx here because all + * who might be updating the pte will hold the page_table_lock + * or the hash_table_lock (we hold both) + */ + *ptep = new_pte; + } + + /* copy appropriate flags from linux pte */ + hpteflags = (pte_val(new_pte) & 0x1f8) | newpp; + + /* Create the HPTE/HPTEX */ + create_valid_hpte( slot, vpn, prpn, hash_ind, ptep, hpteflags, xflags.f ); + + } + + + /* Indicate success */ + rc = 0; + + } + + spin_unlock( &hash_table_lock ); + if (ptep) + spin_unlock( &mm->page_table_lock ); + + return rc; +} + +pte_t __fetch_pte( pte_t * ptep) { + pte_t raw_pte; + HPTEX * hpteExt; + unsigned long hpte_index, flags; + + /* Get raw Linux pte */ + raw_pte = *(ptep); + /* Check if it has an hpte + * If not, just return it + */ + if ( pte_has_hpte( raw_pte ) ) { + /* hash_table_lock prevents changes to hpte status */ + spin_lock_irqsave(&hash_table_lock, flags); + /* refetch the Linux pte in case it changed outside the lock */ + raw_pte = *(ptep); + /* Check if it still has an hpte */ + if ( pte_has_hpte( raw_pte ) ) { + /* Compute address of the hpte extension */ + hpte_index = pte_pagenr(raw_pte); + hpteExt = htab_data.htabx + hpte_index; + /* Create a pte from the raw pte and the extension */ + pte_val(raw_pte) = + (pte_val(raw_pte) & + (PAGE_SIZE-1) & + (~_PAGE_HPTE)) | + (hpteExt->dw1.dw1.rpn << PAGE_SHIFT ); + } + spin_unlock_irqrestore(&hash_table_lock, flags); + } + + return raw_pte; +} + +/* + * It would be more efficient to invalidate the hpte & then revalidate it + * with the new data right here. Rather we simply invalidate the hpte and + * then fault in the new value with a DSI. + */ +void __set_pte( pte_t * ptep, pte_t new_pte ) +{ + pte_t pte; + unsigned long hpte_index, flags; + + __save_and_cli(flags); + spin_lock( &hash_table_lock ); + pte = *ptep; + if(pte_has_hpte(pte)) { + hpte_index = pte_pagenr(pte); + invalidate_hpte(hpte_index); + } + *ptep = new_pte; + spin_unlock( &hash_table_lock ); + __restore_flags(flags); +} + +unsigned long ___pa(unsigned long ea) +{ + unsigned long pa = 0; + struct mm_struct *mm; + void *pgdir = NULL; + pte_t *ptep; + + switch( REGION_ID(ea) ) { + case KERNEL_REGION_ID: + pa = ea - PAGE_OFFSET; + break; + case VMALLOC_REGION_ID: + mm = &init_mm; + pgdir = mm->pgd; + ptep = find_linux_pte(pgdir, ea); + pa = pte_pagenr(fetch_pte(ptep)) << PAGE_SHIFT; + break; + case IO_REGION_ID: + mm = &ioremap_mm; + pgdir = mm->pgd; + ptep = find_linux_pte(pgdir, ea); + pa = pte_pagenr(fetch_pte(ptep)) << PAGE_SHIFT; + break; + case BOLTED_REGION_ID: + pgdir = bolted_pgd; + ptep = find_linux_pte(pgdir, ea); + pa = pte_pagenr(fetch_pte(ptep)) << PAGE_SHIFT; + break; + default: + } + + pa |= (ea & (PAGE_SIZE-1)); + + return(pa); +} + +int proc_dol2crvec(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + int vleft, first=1, len, left, val; +#define TMPBUFLEN 256 + char buf[TMPBUFLEN], *p; + static const char *sizestrings[4] = { + "2MB", "256KB", "512KB", "1MB" + }; + static const char *clockstrings[8] = { + "clock disabled", "+1 clock", "+1.5 clock", "reserved(3)", + "+2 clock", "+2.5 clock", "+3 clock", "reserved(7)" + }; + static const char *typestrings[4] = { + "flow-through burst SRAM", "reserved SRAM", + "pipelined burst SRAM", "pipelined late-write SRAM" + }; + static const char *holdstrings[4] = { + "0.5", "1.0", "(reserved2)", "(reserved3)" + }; + + if ( ((_get_PVR() >> 16) != 8) && ((_get_PVR() >> 16) != 12)) + return -EFAULT; + + if ( /*!table->maxlen ||*/ (filp->f_pos && !write)) { + *lenp = 0; + return 0; + } + + vleft = table->maxlen / sizeof(int); + left = *lenp; + + for (; left /*&& vleft--*/; first=0) { + if (write) { + while (left) { + char c; + if(get_user(c,(char *) buffer)) + return -EFAULT; + if (!isspace(c)) + break; + left--; + ((char *) buffer)++; + } + if (!left) + break; + len = left; + if (len > TMPBUFLEN-1) + len = TMPBUFLEN-1; + if(copy_from_user(buf, buffer, len)) + return -EFAULT; + buf[len] = 0; + p = buf; + if (*p < '0' || *p > '9') + break; + val = simple_strtoul(p, &p, 0); + len = p-buf; + if ((len < left) && *p && !isspace(*p)) + break; + buffer += len; + left -= len; +#if 0 + /* DRENG need a def */ + _set_L2CR(0); + _set_L2CR(val); + while ( _get_L2CR() & 0x1 ) + /* wait for invalidate to finish */; +#endif + + } else { + p = buf; + if (!first) + *p++ = '\t'; +#if 0 + /* DRENG need a def */ + val = _get_L2CR(); +#endif + p += sprintf(p, "0x%08x: ", val); + p += sprintf(p, " %s", (val >> 31) & 1 ? "enabled" : + "disabled"); + p += sprintf(p, ", %sparity", (val>>30)&1 ? "" : "no "); + p += sprintf(p, ", %s", sizestrings[(val >> 28) & 3]); + p += sprintf(p, ", %s", clockstrings[(val >> 25) & 7]); + p += sprintf(p, ", %s", typestrings[(val >> 23) & 2]); + p += sprintf(p, "%s", (val>>22)&1 ? ", data only" : ""); + p += sprintf(p, "%s", (val>>20)&1 ? ", ZZ enabled": ""); + p += sprintf(p, ", %s", (val>>19)&1 ? "write-through" : + "copy-back"); + p += sprintf(p, "%s", (val>>18)&1 ? ", testing" : ""); + p += sprintf(p, ", %sns hold",holdstrings[(val>>16)&3]); + p += sprintf(p, "%s", (val>>15)&1 ? ", DLL slow" : ""); + p += sprintf(p, "%s", (val>>14)&1 ? ", diff clock" :""); + p += sprintf(p, "%s", (val>>13)&1 ? ", DLL bypass" :""); + + p += sprintf(p,"\n"); + + len = strlen(buf); + if (len > left) + len = left; + if(copy_to_user(buffer, buf, len)) + return -EFAULT; + left -= len; + buffer += len; + break; + } + } + + if (!write && !first && left) { + if(put_user('\n', (char *) buffer)) + return -EFAULT; + left--, buffer++; + } + if (write) { + p = (char *) buffer; + while (left) { + char c; + if(get_user(c, p++)) + return -EFAULT; + if (!isspace(c)) + break; + left--; + } + } + if (write && first) + return -EINVAL; + *lenp -= left; + filp->f_pos += *lenp; + return 0; +} + +static long +ppc_htab_lseek(struct file * file, loff_t 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); + } +} + +/* + * Allow user to define performance counters and resize the hash table + */ +static ssize_t ppc_htab_write(struct file * file, const char * buffer, + size_t count, loff_t *ppos) +{ +} + +/* + * 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 ssize_t ppc_htab_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/hvCall.S linuxppc64_2_4/arch/ppc64/kernel/hvCall.S --- linux-2.4.5-ac18/arch/ppc64/kernel/hvCall.S Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/hvCall.S Mon May 7 12:44:10 2001 @@ -0,0 +1,61 @@ +/* + * arch/ppc64/kernel/hvCall.S + * + * + * This file contains the code to perform calls to the + * iSeries LPAR hypervisor + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include "ppc_asm.h" +#include +#include + + .text + +/* + * Hypervisor call + * + * Invoke the iSeries hypervisor via the System Call instruction + * Parameters are passed to this routine in registers r3 - r10 + * + * r3 contains the HV function to be called + * r4-r10 contain the operands to the hypervisor function + * + */ + +_GLOBAL(HvCall) +_GLOBAL(HvCall0) +_GLOBAL(HvCall1) +_GLOBAL(HvCall2) +_GLOBAL(HvCall3) +_GLOBAL(HvCall4) +_GLOBAL(HvCall5) +_GLOBAL(HvCall6) +_GLOBAL(HvCall7) + + + mfcr r0 + std r0,-8(r1) + stdu r1,-(STACK_FRAME_OVERHEAD+16)(r1) + + /* r0 = 0xffffffffffffffff indicates a hypervisor call */ + + li r0,-1 + + /* Invoke the hypervisor */ + + sc + + ld r1,0(r1) + ld r0,-8(r1) + mtcrf 0xff,r0 + + /* return to caller, return value in r3 */ + + blr + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/i8259.c linuxppc64_2_4/arch/ppc64/kernel/i8259.c --- linux-2.4.5-ac18/arch/ppc64/kernel/i8259.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/i8259.c Fri Jun 15 13:13:51 2001 @@ -0,0 +1,163 @@ +/* + * c 2001 PPC64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include "i8259.h" +#include +#include + +unsigned char cached_8259[2] = { 0xff, 0xff }; +#define cached_A1 (cached_8259[0]) +#define cached_21 (cached_8259[1]) + +static spinlock_t i8259_lock = SPIN_LOCK_UNLOCKED; + +int i8259_pic_irq_offset; + +int i8259_irq(int cpu) +{ + int irq; + + spin_lock/*_irqsave*/(&i8259_lock/*, flags*/); + /* + * Perform an interrupt acknowledge cycle on controller 1 + */ + outb(0x0C, 0x20); + irq = inb(0x20) & 7; + if (irq == 2) + { + /* + * Interrupt is cascaded so perform interrupt + * acknowledge on controller 2 + */ + outb(0x0C, 0xA0); + irq = (inb(0xA0) & 7) + 8; + } + else if (irq==7) + { + /* + * This may be a spurious interrupt + * + * Read the interrupt status register. If the most + * significant bit is not set then there is no valid + * interrupt + */ + outb(0x0b, 0x20); + if(~inb(0x20)&0x80) { + spin_unlock/*_irqrestore*/(&i8259_lock/*, flags*/); + return -1; + } + } + spin_unlock/*_irqrestore*/(&i8259_lock/*, flags*/); + return irq; +} + +static void i8259_mask_and_ack_irq(unsigned int irq_nr) +{ + unsigned long flags; + + spin_lock_irqsave(&i8259_lock, flags); + if ( irq_nr >= i8259_pic_irq_offset ) + irq_nr -= i8259_pic_irq_offset; + + if (irq_nr > 7) { + cached_A1 |= 1 << (irq_nr-8); + inb(0xA1); /* DUMMY */ + outb(cached_A1,0xA1); + outb(0x20,0xA0); /* Non-specific EOI */ + outb(0x20,0x20); /* Non-specific EOI to cascade */ + } else { + cached_21 |= 1 << irq_nr; + inb(0x21); /* DUMMY */ + outb(cached_21,0x21); + outb(0x20,0x20); /* Non-specific EOI */ + } + spin_unlock_irqrestore(&i8259_lock, flags); +} + +static void i8259_set_irq_mask(int irq_nr) +{ + outb(cached_A1,0xA1); + outb(cached_21,0x21); +} + +static void i8259_mask_irq(unsigned int irq_nr) +{ + unsigned long flags; + + spin_lock_irqsave(&i8259_lock, flags); + if ( irq_nr >= i8259_pic_irq_offset ) + irq_nr -= i8259_pic_irq_offset; + if ( irq_nr < 8 ) + cached_21 |= 1 << irq_nr; + else + cached_A1 |= 1 << (irq_nr-8); + i8259_set_irq_mask(irq_nr); + spin_unlock_irqrestore(&i8259_lock, flags); +} + +static void i8259_unmask_irq(unsigned int irq_nr) +{ + unsigned long flags; + + spin_lock_irqsave(&i8259_lock, flags); + if ( irq_nr >= i8259_pic_irq_offset ) + irq_nr -= i8259_pic_irq_offset; + if ( irq_nr < 8 ) + cached_21 &= ~(1 << irq_nr); + else + cached_A1 &= ~(1 << (irq_nr-8)); + i8259_set_irq_mask(irq_nr); + spin_unlock_irqrestore(&i8259_lock, flags); +} + +static void i8259_end_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + i8259_unmask_irq(irq); +} + +struct hw_interrupt_type i8259_pic = { + " i8259 ", + NULL, + NULL, + i8259_unmask_irq, + i8259_mask_irq, + i8259_mask_and_ack_irq, + i8259_end_irq, + NULL +}; + +void __init i8259_init(void) +{ + unsigned long flags; + + spin_lock_irqsave(&i8259_lock, flags); + /* init master interrupt controller */ + outb(0x11, 0x20); /* Start init sequence */ + outb(0x00, 0x21); /* Vector base */ + outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */ + outb(0x01, 0x21); /* Select 8086 mode */ + outb(0xFF, 0x21); /* Mask all */ + /* init slave interrupt controller */ + outb(0x11, 0xA0); /* Start init sequence */ + outb(0x08, 0xA1); /* Vector base */ + outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */ + outb(0x01, 0xA1); /* Select 8086 mode */ + outb(0xFF, 0xA1); /* Mask all */ + outb(cached_A1, 0xA1); + outb(cached_21, 0x21); + spin_unlock_irqrestore(&i8259_lock, flags); + request_irq( i8259_pic_irq_offset + 2, no_action, SA_INTERRUPT, + "82c59 secondary cascade", NULL ); + +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/i8259.h linuxppc64_2_4/arch/ppc64/kernel/i8259.h --- linux-2.4.5-ac18/arch/ppc64/kernel/i8259.h Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/i8259.h Mon May 7 12:44:10 2001 @@ -0,0 +1,19 @@ +/* + * c 2001 PPC 64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef _PPC_KERNEL_i8259_H +#define _PPC_KERNEL_i8259_H + +#include "local_irq.h" + +extern struct hw_interrupt_type i8259_pic; + +void i8259_init(void); +int i8259_irq(int); + +#endif /* _PPC_KERNEL_i8259_H */ diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/iSeries_dma.c linuxppc64_2_4/arch/ppc64/kernel/iSeries_dma.c --- linux-2.4.5-ac18/arch/ppc64/kernel/iSeries_dma.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/iSeries_dma.c Fri Jul 13 10:05:49 2001 @@ -0,0 +1,1089 @@ +/* + * iSeries_dma.c + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * Dynamic DMA mapping support. + * + * Manages the TCE space assigned to this partition + * + * modeled from pci-dma.c + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*#define DEBUG_TCE 1 */ + +/*HACK HACK HACK */ + +u8 iSeries_Get_Bus( struct pci_dev * dv ) +{ + return 0; +} + +/* HACK HACK HACK */ + +unsigned long phb_tce_table_init( void * x ) +{ + return (unsigned long)x; +} + + +struct TceTable virtBusTceTable; /* Tce table for virtual bus */ + +struct TceTable * tceTables[256]; /* Tce tables for 256 busses + * Bus 255 is the virtual bus + * zero indicates no bus defined + */ + /* allocates a contiguous range of tces (power-of-2 size) */ +static long alloc_tce_range( struct TceTable *, + unsigned order ); + /* allocates a contiguous range of tces (power-of-2 size) + * assumes lock already held + */ +static long alloc_tce_range_nolock( struct TceTable *, + unsigned order ); + /* frees a contiguous range of tces (power-of-2 size) */ +static void free_tce_range( struct TceTable *, + long tcenum, + unsigned order ); + /* frees a contiguous rnage of tces (power-of-2 size) + * assumes lock already held + */ +static void free_tce_range_nolock( struct TceTable *, + long tcenum, + unsigned order ); + /* allocates a range of tces and sets them to the pages */ +static dma_addr_t get_tces( struct TceTable *, + unsigned order, + void *page, + unsigned numPages, + int tceType, + int direction ); +static void free_tces( struct TceTable *, + dma_addr_t tce, + unsigned order, + unsigned numPages ); +static long test_tce_range( struct TceTable *, + long tcenum, + unsigned order ); + +static unsigned fill_scatterlist_sg( struct scatterlist *sg, int nents, + dma_addr_t dma_addr, unsigned long numTces ); + +static unsigned long num_tces_sg( struct scatterlist *sg, + int nents ); + +static dma_addr_t create_tces_sg( struct TceTable *tbl, + struct scatterlist *sg, + int nents, + unsigned numTces, + int tceType, + int direction ); + +static unsigned __inline__ count_leading_zeros32( unsigned long x ) +{ + unsigned lz; + asm("cntlzw %0,%1" : "=r"(lz) : "r"(x)); + return lz; +} + +static void __inline__ build_tce( struct TceTable * tbl, long tcenum, + unsigned long uaddr, int tceType, int direction ) +{ + u64 setTceRc; + union Tce tce; + + tce.wholeTce = 0; + tce.tceBits.rpn = (virt_to_absolute(uaddr)) >> PAGE_SHIFT; + /* If for virtual bus */ + if ( tceType == TCE_VB ) { + tce.tceBits.valid = 1; + tce.tceBits.allIo = 1; + if ( direction != PCI_DMA_TODEVICE ) + tce.tceBits.readWrite = 1; + } + /* If for PCI bus */ + else { + tce.tceBits.readWrite = 1; // Read allowed + if ( direction != PCI_DMA_TODEVICE ) + tce.tceBits.pciWrite = 1; + } + setTceRc = HvCallXm_setTce( (u64)tbl->index, (u64)tcenum, tce.wholeTce ); + if ( setTceRc ) { + printk("build_tce: HvCallXm_setTce failed, rc=%ld, index=%ld, tcenum=%0lx, tce=%016lx\n", + setTceRc, (u64)tbl->index, (u64)tcenum, tce.wholeTce ); + } + +} + + + +/* Build a TceTable structure. This contains a multi-level bit map which + * is used to manage allocation of the tce space. + */ + +struct TceTable * build_tce_table( struct HvTceTableManagerCB * tceTableParms, + struct TceTable * tbl ) +{ + unsigned long bits, bytes, totalBytes; + unsigned long numBits[NUM_TCE_LEVELS], numBytes[NUM_TCE_LEVELS]; + unsigned i, k, m; + unsigned char * pos, * p, b; + + tbl->size = tceTableParms->size; + tbl->busNumber = tceTableParms->busNumber; + tbl->startOffset = tceTableParms->startOffset; + tbl->index = tceTableParms->index; + spin_lock_init( &(tbl->lock) ); + + tbl->mlbm.maxLevel = 0; + + /* Compute number of bits and bytes for each level of the + * multi-level bit map + */ + totalBytes = 0; + bits = tbl->size * (PAGE_SIZE / sizeof( union Tce )); + + for ( i=0; imlbm.level[i].map = pos; + tbl->mlbm.maxLevel = i; + + if ( numBits[i] & 1 ) { + p = pos + numBytes[i] - 1; + m = (( numBits[i] % 8) - 1) & 7; + *p = 0x80 >> m; +#ifdef DEBUG_TCE + printk("build_tce_table: level %d last bit %x\n", i, 0x80>>m ); +#endif + } + } + else + tbl->mlbm.level[i].map = 0; + pos += numBytes[i]; + tbl->mlbm.level[i].numBits = numBits[i]; + tbl->mlbm.level[i].numBytes = numBytes[i]; + + } + + /* For the highest level, turn on all the bits */ + + i = tbl->mlbm.maxLevel; + p = tbl->mlbm.level[i].map; + m = numBits[i]; +#ifdef DEBUG_TCE + printk("build_tce_table: highest level (%d) has all bits set\n", i); +#endif + for (k=0; k= 8 ) { + /* handle full bytes */ + *p++ = 0xff; + m -= 8; + } + else { + /* handle the last partial byte */ + b = 0x80; + *p = 0; + while (m) { + *p |= b; + b >>= 1; + --m; + } + } + } + + return tbl; + +} + +static long alloc_tce_range( struct TceTable *tbl, unsigned order ) +{ + long retval; + unsigned long flags; + + /* Lock the tce allocation bitmap */ + spin_lock_irqsave( &(tbl->lock), flags ); + + /* Do the actual work */ + retval = alloc_tce_range_nolock( tbl, order ); + + /* Unlock the tce allocation bitmap */ + spin_unlock_irqrestore( &(tbl->lock), flags ); + + return retval; +} + +static long alloc_tce_range_nolock( struct TceTable *tbl, unsigned order ) +{ + unsigned long numBits, numBytes; + unsigned long i, bit, block, mask; + long tcenum; + unsigned char * map; + + /* If the order (power of 2 size) requested is larger than our + * biggest, indicate failure + */ + if ( order > tbl->mlbm.maxLevel ) { + printk("alloc_tce_range_nolock: invalid order requested, order = %d\n", order ); + return -1; + } + + numBits = tbl->mlbm.level[order].numBits; + numBytes = tbl->mlbm.level[order].numBytes; + map = tbl->mlbm.level[order].map; + + /* Initialize return value to -1 (failure) */ + tcenum = -1; + + /* Loop through the bytes of the bitmap */ + for (i=0; i> bit); + *map &= mask; + /* compute the index into our tce table for + * the first tce in the block + */ +#ifdef DEBUG_TCE + printk("alloc_tce_range_nolock: allocating block %ld, (byte=%ld, bit=%ld) order %d\n", block, i, bit, order ); +#endif + tcenum = block << order; + break; + } + ++map; + } + +#ifdef DEBUG_TCE + if ( tcenum == -1 ) { + printk("alloc_tce_range_nolock: no available blocks of order = %d\n", order ); + if ( order < tbl->mlbm.maxLevel ) + printk("alloc_tce_range_nolock: trying next bigger size\n" ); + else + printk("alloc_tce_range_nolock: maximum size reached...failing\n"); + } +#endif + + /* If no block of the requested size was found, try the next + * size bigger. If one of those is found, return the second + * half of the block to freespace and keep the first half + */ + if ( ( tcenum == -1 ) && ( order < tbl->mlbm.maxLevel ) ) { + tcenum = alloc_tce_range_nolock( tbl, order+1 ); + if ( tcenum != -1 ) { + free_tce_range_nolock( tbl, tcenum+(1<lock), flags ); + + /* Do the actual work */ + free_tce_range_nolock( tbl, tcenum, order ); + + /* Unlock the tce allocation bitmap */ + spin_unlock_irqrestore( &(tbl->lock), flags ); + +} + +static void free_tce_range_nolock( struct TceTable *tbl, long tcenum, unsigned order ) +{ + unsigned long block; + unsigned byte, bit, mask, b; + unsigned char * map, * bytep; + + if ( order > tbl->mlbm.maxLevel ) { + printk("free_tce_range: order too large, order = %d, tcenum = %ld\n", order, tcenum ); + return; + } + + block = tcenum >> order; + if ( tcenum != (block << order ) ) { + printk("free_tce_range: tcenum %lx is not on appropriate boundary for order %x\n", tcenum, order ); + return; + } + if ( block >= tbl->mlbm.level[order].numBits ) { + printk("free_tce_range: tcenum %lx is outside the range of this map (order %x, numBits %lx\n", tcenum, order, tbl->mlbm.level[order].numBits ); + return; + } +#ifdef DEBUG_TCE + if ( test_tce_range( tbl, tcenum, order ) ) { + printk("free_tce_range: freeing range not completely allocated.\n"); + printk("free_tce_range: TceTable %p, tcenum %lx, order %x\n", tbl, tcenum, order ); + } +#endif + map = tbl->mlbm.level[order].map; + byte = block / 8; + bit = block % 8; + mask = 0x80 >> bit; + bytep = map + byte; +#ifdef DEBUG_TCE + printk("free_tce_range_nolock: freeing block %ld (byte=%d, bit=%d) of order %d\n",block, byte, bit, order); + if ( *bytep & mask ) + printk("free_tce_range: already free: TceTable %p, tcenum %lx, order %x\n", tbl, tcenum, order ); +#endif + *bytep |= mask; + + /* If there is a higher level in the bit map than this we may be + * able to buddy up this block with its partner. + * If this is the highest level we can't buddy up + * If this level has an odd number of bits and + * we are freeing the last block we can't buddy up + */ + if ( ( order < tbl->mlbm.maxLevel ) && + ( ( 0 == ( tbl->mlbm.level[order].numBits & 1 ) ) || + ( block < tbl->mlbm.level[order].numBits-1 ) ) ) { + + /* See if we can buddy up the block we just freed */ + bit &= 6; /* get to the first of the buddy bits */ + mask = 0xc0 >> bit; /* build two bit mask */ + b = *bytep & mask; /* Get the two bits */ + if ( 0 == (b ^ mask) ) { /* If both bits are on */ + /* both of the buddy blocks are free we can combine them */ + *bytep ^= mask; /* turn off the two bits */ + block = ( byte * 8 ) + bit; /* block of first of buddies */ + tcenum = block << order; + /* free the buddied block */ +#ifdef DEBUG_TCE + printk("free_tce_range: buddying up block %ld and block %ld\n", block, block+1); +#endif + free_tce_range_nolock( tbl, tcenum, order+1 ); + } + } +} + +static long test_tce_range( struct TceTable *tbl, long tcenum, unsigned order ) +{ + unsigned long block; + unsigned byte, bit, mask, b; + long retval, retLeft, retRight; + unsigned char * map; + + map = tbl->mlbm.level[order].map; + block = tcenum >> order; + byte = block / 8; /* Byte within bitmap */ + bit = block % 8; /* Bit within byte */ + mask = 0x80 >> bit; + b = (*(map+byte) & mask ); /* 0 if block is allocated, else free */ + if ( b ) + retval = 1; /* 1 == block is free */ + else + retval = 0; /* 0 == block is allocated */ + /* Test bits at all levels below this to ensure that all agree */ + + if (order) { + retLeft = test_tce_range( tbl, tcenum, order-1 ); + retRight = test_tce_range( tbl, tcenum+(1<<(order-1)), order-1 ); + if ( retLeft || retRight ) { + retval = 2; + } + } + + /* Test bits at all levels above this to ensure that all agree */ + + return retval; +} + +static dma_addr_t get_tces( struct TceTable *tbl, unsigned order, void *page, unsigned numPages, int tceType, int direction ) +{ + long tcenum; + unsigned long uaddr; + unsigned i; + dma_addr_t retTce = NO_TCE; + + uaddr = (unsigned long)page & PAGE_MASK; + + /* Allocate a range of tces */ + tcenum = alloc_tce_range( tbl, order ); + if ( tcenum != -1 ) { + /* We got the tces we wanted */ + tcenum += tbl->startOffset; /* Offset into real TCE table */ + retTce = tcenum << PAGE_SHIFT; /* Set the return dma address */ + /* Setup a tce for each page */ + for (i=0; isize * (PAGE_SIZE / sizeof(union Tce))) - 1; + + tcenum = dma_addr >> PAGE_SHIFT; + tcenum -= tbl->startOffset; + + if ( tcenum > maxTcenum ) { + printk("free_tces: tcenum > maxTcenum, tcenum = %ld, maxTcenum = %ld\n", + tcenum, maxTcenum ); + printk("free_tces: TCE Table at %16lx\n", (unsigned long)tbl ); + printk("free_tces: bus# %lu\n", (unsigned long)tbl->busNumber ); + printk("free_tces: size %lu\n", (unsigned long)tbl->size ); + printk("free_tces: startOff %lu\n", (unsigned long)tbl->startOffset ); + printk("free_tces: index %lu\n", (unsigned long)tbl->index ); + return; + } + + freeTce = tcenum; + + for (i=0; iindex, (u64)tcenum, tce.wholeTce ); + if ( setTceRc ) { + printk("free_tces: HvCallXm_setTce failed, rc=%ld, index=%ld, tcenum=%0lx, tce=%016lx\n", + setTceRc, (u64)tbl->index, (u64)tcenum, tce.wholeTce ); + } + + ++tcenum; + } + + free_tce_range( tbl, freeTce, order ); + +} + +void __init create_virtual_bus_tce_table(void) +{ + struct TceTable * t; + struct HvTceTableManagerCB virtBusTceTableParms; + u64 absParmsPtr; + + virtBusTceTableParms.busNumber = 255; /* Bus 255 is the virtual bus */ + virtBusTceTableParms.virtualBusFlag = 0xff; /* Ask for virtual bus */ + + absParmsPtr = virt_to_absolute( (u64)&virtBusTceTableParms ); + HvCallXm_getTceTableParms( absParmsPtr ); + + t = build_tce_table( &virtBusTceTableParms, &virtBusTceTable ); + if ( t ) { + tceTables[255] = t; + printk("Virtual Bus TCE table built successfully.\n"); + printk(" TCE table size = %ld entries\n", + (unsigned long)t->size*(PAGE_SIZE/sizeof(union Tce)) ); + printk(" TCE table token = %d\n", + (unsigned)t->index ); + printk(" TCE table start entry = 0x%lx\n", + (unsigned long)t->startOffset ); + } + else + printk("Virtual Bus TCE table failed.\n"); +} + +void __init create_pci_bus_tce_table( unsigned busNumber ) +{ + struct TceTable * t; + struct TceTable * newTceTable; + struct HvTceTableManagerCB pciBusTceTableParms; + u64 absParmsPtr; + + if ( busNumber > 254 ) { + printk("PCI Bus TCE table failed.\n"); + printk(" Invalid bus number %u\n", busNumber ); + return; + } + + newTceTable = kmalloc( sizeof(struct TceTable), GFP_KERNEL ); + + pciBusTceTableParms.busNumber = busNumber; + pciBusTceTableParms.virtualBusFlag = 0; + + absParmsPtr = virt_to_absolute( (u64)&pciBusTceTableParms ); + HvCallXm_getTceTableParms( absParmsPtr ); + + t = build_tce_table( &pciBusTceTableParms, newTceTable ); + if ( t ) { + tceTables[busNumber] = t; + printk("PCI Bus TCE table built successfully.\n"); + printk(" TCE table size = %ld entries\n", + (unsigned long)t->size*(PAGE_SIZE/sizeof(union Tce)) ); + printk(" TCE table token = %d\n", + (unsigned)t->index ); + printk(" TCE table start entry = 0x%lx\n", + (unsigned long)t->startOffset ); + } + else { + kfree( newTceTable ); + printk("PCI Bus TCE table failed.\n"); + } +} + + +/* Allocates a contiguous real buffer and creates TCEs over it. + * Returns the virtual address of the buffer and sets dma_handle + * to the dma address (tce) of the first page. + */ +void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle) +{ + struct TceTable * tbl; + void *ret = NULL; + unsigned order, nPages, bus; + dma_addr_t tce; + int tceType; + + size = PAGE_ALIGN(size); + order = get_order(size); + nPages = 1 << order; + + /* If no pci_dev then use virtual bus */ + if (hwdev == NULL ) { + bus = 255; + tceType = TCE_VB; + } + else { +#ifdef CONFIG_PCI + /* Get the iSeries bus # to use as an index + * into the TCE table array + */ + bus = ISERIES_GET_BUS( hwdev ); + tceType = TCE_PCI; +#else + BUG(); + return NULL; +#endif /* CONFIG_PCI */ + } + + tbl = tceTables[bus]; + if ( tbl ) { + /* Alloc enough pages (and possibly more) */ + ret = (void *)__get_free_pages( GFP_ATOMIC, order ); + if ( ret ) { + /* Page allocation succeeded */ + memset(ret, 0, nPages << PAGE_SHIFT); + /* Set up tces to cover the allocated range */ + tce = get_tces( tbl, order, ret, nPages, tceType, + PCI_DMA_BIDIRECTIONAL ); + if ( tce == NO_TCE ) { +/*#ifdef DEBUG_TCE */ + printk("pci_alloc_consistent: get_tces failed\n" ); +/*#endif */ + free_pages( (unsigned long)ret, order ); + ret = NULL; + } + else + { + *dma_handle = tce; + } + } +/*#ifdef DEBUG_TCE */ + else + printk("pci_alloc_consistent: __get_free_pages failed for order = %d\n", order); +/*#endif*/ + } + + return ret; +} + +void pci_free_consistent(struct pci_dev *hwdev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + struct TceTable * tbl; + unsigned order, nPages, bus; + + size = PAGE_ALIGN(size); + order = get_order(size); + nPages = 1 << order; + + if ( order > 10 ) + printk("pci_free_consistent: order=%d, size=%ld, nPages=%d, dma_handle=%016lx, vaddr=%016lx\n", + order, size, nPages, (unsigned long)dma_handle, (unsigned long)vaddr ); + + /* If no pci_dev then use virtual bus */ + if (hwdev == NULL ) + bus = 255; + else { +#ifdef CONFIG_PCI + /* Get the iSeries bus # to use as an index + * into the TCE table array + */ + bus = ISERIES_GET_BUS( hwdev ); +#else + BUG(); + return; +#endif /* CONFIG_PCI */ + } + + if ( bus > 255 ) { + printk("pci_free_consistent: invalid bus # %d\n", bus ); + printk("pci_free_consistent: hwdev = %08lx\n", (unsigned long)hwdev ); + } + + tbl = tceTables[bus]; + + if ( tbl ) { + free_tces( tbl, dma_handle, order, nPages ); + free_pages( (unsigned long)vaddr, order ); + } +} + +/* Creates TCEs for a user provided buffer. The user buffer must be + * contiguous real kernel storage (not vmalloc). The address of the buffer + * passed here is the kernel (virtual) address of the buffer. The buffer + * need not be page aligned, the dma_addr_t returned will point to the same + * byte within the page as vaddr. + */ +dma_addr_t pci_map_single( struct pci_dev *hwdev, void *vaddr, size_t size, int direction ) +{ + struct TceTable * tbl; + dma_addr_t dma_handle; + unsigned long uaddr; + unsigned order, nPages, bus; + int tceType; + + if ( direction == PCI_DMA_NONE ) + BUG(); + + dma_handle = NO_TCE; + + uaddr = (unsigned long)vaddr; + nPages = PAGE_ALIGN( uaddr + size ) - ( uaddr & PAGE_MASK ); + order = get_order( nPages & PAGE_MASK ); + nPages >>= PAGE_SHIFT; + + /* If no pci_dev then use virtual bus */ + if (hwdev == NULL ) { + bus = 255; + tceType = TCE_VB; + } + else { +#ifdef CONFIG_PCI + /* Get the iSeries bus # to use as an index + * into the TCE table array + */ + bus = ISERIES_GET_BUS( hwdev ); + tceType = TCE_PCI; +#else + BUG(); + return NO_TCE; +#endif /* CONFIG_PCI */ + + } + + tbl = tceTables[bus]; + + if ( tbl ) { + dma_handle = get_tces( tbl, order, vaddr, nPages, tceType, + direction ); + dma_handle |= ( uaddr & ~PAGE_MASK ); + } + + return dma_handle; +} + +void pci_unmap_single( struct pci_dev *hwdev, dma_addr_t dma_handle, size_t size, int direction ) +{ + struct TceTable * tbl; + unsigned order, nPages, bus; + + if ( direction == PCI_DMA_NONE ) + BUG(); + + nPages = PAGE_ALIGN( dma_handle + size ) - ( dma_handle & PAGE_MASK ); + order = get_order( nPages & PAGE_MASK ); + nPages >>= PAGE_SHIFT; + + if ( order > 10 ) + printk("pci_unmap_single: order=%d, size=%ld, nPages=%d, dma_handle=%016lx\n", + order, size, nPages, (unsigned long)dma_handle ); + + /* If no pci_dev then use virtual bus */ + if (hwdev == NULL ) + bus = 255; + else { +#ifdef CONFIG_PCI + /* Get the iSeries bus # to use as an index + * into the TCE table array + */ + bus = ISERIES_GET_BUS( hwdev ); +#else + BUG(); + return; +#endif /* CONFIG_PCI */ + } + + if ( bus > 255 ) { + printk("pci_unmap_single: invalid bus # %d\n", bus ); + printk("pci_unmap_single: hwdev = %08lx\n", (unsigned long)hwdev ); + } + + tbl = tceTables[bus]; + + if ( tbl ) + free_tces( tbl, dma_handle, order, nPages ); + +} + +/* Figure out how many TCEs are actually going to be required + * to map this scatterlist. This code is not optimal. It + * takes into account the case where entry n ends in the same + * page in which entry n+1 starts. It does not handle the + * general case of entry n ending in the same page in which + * entry m starts. + */ +static unsigned long num_tces_sg( struct scatterlist *sg, int nents ) +{ + unsigned long nTces, numPages, startPage, endPage, prevEndPage; + unsigned i; + + prevEndPage = 0; + nTces = 0; + + for (i=0; iaddress >> PAGE_SHIFT; + endPage = ((unsigned long)sg->address + sg->length - 1) >> PAGE_SHIFT; + numPages = endPage - startPage + 1; + /* Simple optimization: if the previous entry ended + * in the same page in which this entry starts + * then we can reduce the required pages by one. + * This matches assumptions in fill_scatterlist_sg and + * create_tces_sg + */ + if ( startPage == prevEndPage ) + --numPages; + nTces += numPages; + prevEndPage = endPage; + sg++; + } + return nTces; +} + +/* Fill in the dma data in the scatterlist + * return the number of dma sg entries created + */ +static unsigned fill_scatterlist_sg( struct scatterlist *sg, int nents, + dma_addr_t dma_addr , unsigned long numTces) +{ + struct scatterlist *dma_sg; + u32 cur_start_dma; + unsigned long cur_len_dma, cur_end_virt, uaddr; + unsigned num_dma_ents; + + dma_sg = sg; + num_dma_ents = 1; + + /* Process the first sg entry */ + cur_start_dma = dma_addr + ((unsigned long)sg->address & (~PAGE_MASK)); + cur_len_dma = sg->length; + /* cur_end_virt holds the address of the byte immediately after the + * end of the current buffer. + */ + cur_end_virt = (unsigned long)sg->address + cur_len_dma; + /* Later code assumes that unused sg->dma_address and sg->dma_length + * fields will be zero. Other archs seem to assume that the user + * (device driver) guarantees that...I don't want to depend on that + */ + sg->dma_address = sg->dma_length = 0; + + /* Process the rest of the sg entries */ + while (--nents) { + ++sg; + /* Clear possibly unused fields. Note: sg >= dma_sg so + * this can't be clearing a field we've already set + */ + sg->dma_address = sg->dma_length = 0; + + /* Check if it is possible to make this next entry + * contiguous (in dma space) with the previous entry. + */ + + /* The entries can be contiguous in dma space if + * the previous entry ends immediately before the + * start of the current entry (in virtual space) + * or if the previous entry ends at a page boundary + * and the current entry starts at a page boundary. + */ + uaddr = (unsigned long)sg->address; + if ( ( uaddr != cur_end_virt ) && + ( ( ( uaddr | cur_end_virt ) & (~PAGE_MASK) ) || + ( ( uaddr & PAGE_MASK ) == ( ( cur_end_virt-1 ) & PAGE_MASK ) ) ) ) { + /* This entry can not be contiguous in dma space. + * save the previous dma entry and start a new one + */ + dma_sg->dma_address = cur_start_dma; + dma_sg->dma_length = cur_len_dma; + + ++dma_sg; + ++num_dma_ents; + + cur_start_dma += cur_len_dma-1; + /* If the previous entry ends and this entry starts + * in the same page then they share a tce. In that + * case don't bump cur_start_dma to the next page + * in dma space. This matches assumptions made in + * num_tces_sg and create_tces_sg. + */ + if ((uaddr & PAGE_MASK) == ((cur_end_virt-1) & PAGE_MASK)) + cur_start_dma &= PAGE_MASK; + else + cur_start_dma = PAGE_ALIGN(cur_start_dma+1); + cur_start_dma += ( uaddr & (~PAGE_MASK) ); + cur_len_dma = 0; + } + /* Accumulate the length of this entry for the next + * dma entry + */ + cur_len_dma += sg->length; + cur_end_virt = uaddr + sg->length; + } + /* Fill in the last dma entry */ + dma_sg->dma_address = cur_start_dma; + dma_sg->dma_length = cur_len_dma; + + if ((((cur_start_dma +cur_len_dma - 1)>> PAGE_SHIFT) - (dma_addr >> PAGE_SHIFT) + 1) != numTces) + { + printk("fill_scatterlist_sg: numTces %ld, used tces %d\n", + numTces, + (unsigned)(((cur_start_dma + cur_len_dma - 1) >> PAGE_SHIFT) - (dma_addr >> PAGE_SHIFT) + 1)); + } + + + return num_dma_ents; +} + +/* Call the hypervisor to create the TCE entries. + * return the number of TCEs created + */ +static dma_addr_t create_tces_sg( struct TceTable *tbl, struct scatterlist *sg, + int nents, unsigned numTces, int tceType, int direction ) +{ + unsigned order, i, j; + unsigned long startPage, endPage, prevEndPage, numPages, uaddr; + long tcenum, starttcenum; + dma_addr_t dmaAddr; + + dmaAddr = NO_TCE; + + order = get_order( numTces << PAGE_SHIFT ); + /* allocate a block of tces */ + tcenum = alloc_tce_range( tbl, order ); + if ( tcenum != -1 ) { + tcenum += tbl->startOffset; + starttcenum = tcenum; + dmaAddr = tcenum << PAGE_SHIFT; + prevEndPage = 0; + for (j=0; jaddress >> PAGE_SHIFT; + endPage = ((unsigned long)sg->address + sg->length - 1) >> PAGE_SHIFT; + numPages = endPage - startPage + 1; + + uaddr = (unsigned long)sg->address; + + /* If the previous entry ended in the same page that + * the current page starts then they share that + * tce and we reduce the number of tces we need + * by one. This matches assumptions made in + * num_tces_sg and fill_scatterlist_sg + */ + if ( startPage == prevEndPage ) { + --numPages; + uaddr += PAGE_SIZE; + } + + for (i=0; idma_address = pci_map_single( hwdev, sg->address, + sg->length, direction ); + sg->dma_length = sg->length; + return 1; + } + + if ( direction == PCI_DMA_NONE ) + BUG(); + + /* If no pci_dev then use virtual bus */ + if (hwdev == NULL ) { + bus = 255; + tceType = TCE_VB; + } + else { +#ifdef CONFIG_PCI + /* Get the iSeries bus # to use as an index + * into the TCE table array + */ + bus = ISERIES_GET_BUS( hwdev ); + tceType = TCE_PCI; +#else + BUG(); + return 0; +#endif /* CONFIG_PCI */ + } + + tbl = tceTables[bus]; + + if ( tbl ) { + /* Compute the number of tces required */ + numTces = num_tces_sg( sg, nents ); + /* Create the tces and get the dma address */ + dma_handle = create_tces_sg( tbl, sg, nents, numTces, + tceType, direction ); + + /* Fill in the dma scatterlist */ + num_dma = fill_scatterlist_sg( sg, nents, dma_handle, numTces ); + } + + return num_dma; +} + +void pci_unmap_sg( struct pci_dev *hwdev, struct scatterlist *sg, int nelms, int direction ) +{ + struct TceTable * tbl; + unsigned order, numTces, bus, i; + dma_addr_t dma_end_page, dma_start_page; + + if ( direction == PCI_DMA_NONE ) + BUG(); + + dma_start_page = sg->dma_address & PAGE_MASK; + for ( i=nelms; i>0; --i ) { + unsigned k = i - 1; + if ( sg[k].dma_length ) { + dma_end_page = ( sg[k].dma_address + + sg[k].dma_length - 1 ) & PAGE_MASK; + break; + } + } + + numTces = ((dma_end_page - dma_start_page ) >> PAGE_SHIFT) + 1; + order = get_order( numTces << PAGE_SHIFT ); + + if ( order > 10 ) + printk("pci_unmap_sg: order=%d, numTces=%d, nelms=%d, dma_start_page=%016lx, dma_end_page=%016lx\n", + order, numTces, nelms, (unsigned long)dma_start_page, (unsigned long)dma_end_page ); + + + /* If no pci_dev then use virtual bus */ + if (hwdev == NULL ) + bus = 255; + else { +#ifdef CONFIG_PCI + /* Get the iSeries bus # to use as an index + * into the TCE table array + */ + bus = ISERIES_GET_BUS( hwdev ); +#else + BUG(); + return; +#endif /* CONFIG_PCI */ + } + + if ( bus > 255 ) { + printk("pci_unmap_sg: invalid bus # %d\n", bus ); + printk("pci_unmap_sg: hwdev = %08lx\n", (unsigned long)hwdev ); + } + + + tbl = tceTables[bus]; + + if ( tbl ) + free_tces( tbl, dma_start_page, order, numTces ); + +} + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/iSeries_pci.c linuxppc64_2_4/arch/ppc64/kernel/iSeries_pci.c --- linux-2.4.5-ac18/arch/ppc64/kernel/iSeries_pci.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/iSeries_pci.c Mon Jun 4 09:33:22 2001 @@ -0,0 +1,144 @@ +/* + * iSeries_pci.c + * + * Copyright (C) 2001 Allan Trautman, IBM Corporation + * + * iSeries specific routines for PCI. + * + * Based on code from pci.c and iSeries_pci.c 32bit + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xics.h" +#include "open_pic.h" +#include "pci.h" + +#include "iSeries_pci.h" + + +extern struct pci_controller* hose_head; +extern struct pci_controller** hose_tail; +extern struct Naca *naca; +extern struct pci_ops iSeries_pci_ops; + +struct iSeries_Device; +struct pci_dev; + +/* Forward declares of prototypes. */ +unsigned long find_and_init_phbs(void); +void fixup_resources(struct pci_dev *dev); +void iSeries_pcibios_fixup(void); +iSeries_Device* find_iSeries_Device(struct pci_dev* PciDev); +void iSeries_Pci_Log(char*, iSeries_Device*); + +/************************************************************************/ +/* PCI Config Space Access functions for IBM iSeries Systems */ +/* */ +/* Modeled after the IBM Python */ +/* IBM_ISERIES_PCI_OP(read,byte,u8*,Load8) */ +/* int pci_read_config_word(struct pci_dev*, int Offset, u16* ValuePtr) */ +/************************************************************************/ +#define IBM_ISERIES_PCI_READ_OP(size, datatype, hvop) \ +int iSeries_pci_##type##_config_##size##(struct pci_dev* PciDev, int Offset, ##datatype##* ValuePtr) { \ + iSeries_Device* Device = find_iSeries_Device(PciDev); \ + if(Device == NULL) return 0x301; \ + Device->RCode = HvCallPci_config##hvop##(Device->BusNumber, Device->SubBus, Device->DevFn, Offset, ValuePtr); \ + if(Device->RCode > 0 || PciTraceFlag > 0) { \ + iSeries_Pci_Log("##hvop##",Device); \ + return Device->RCode; \ +} \ +IBM_ISERIES_PCI_OP(read,byte, u8*,Load8) +IBM_ISERIES_PCI_OP(read,word, u16*,Load16) +IBM_ISERIES_PCI_OP(read,dword, u32*,Load32) +IBM_ISERIES_PCI_OP(write,byte, u8, Load8) +IBM_ISERIES_PCI_OP(write,word, u16, Load16) +IBM_ISERIES_PCI_OP(write,dword,u32, Load32) + +/************************************************************************/ +/* PCI Config Ops Branch Table */ +/************************************************************************/ +struct pci_ops iSeries_pci_ops = { + iSeries_pci_read_config_byte, + iSeries_pci_read_config_word, + iSeries_pci_read_config_dword, + iSeries_pci_write_config_byte, + iSeries_pci_write_config_word, + iSeries_pci_write_config_dword +}; + + +/************************************************************************/ + * + * unsigned int __init find_and_init_phbs(void) + * + * Description: + * This function checks for all possible system PCI host bridges that connect + * PCI buses. The system hypervisor is queried as to the guest partition + * ownership status. A pci_controller is build for any bus which is partially + * owned or fully owned by this guest partition. + ************************************************************************/ +unsigned int __init find_and_init_phbs(void) { + struct pci_controller* hose; + HvBusNumber BusNumber; + int LxBusNumber = 0; /* Linux Bus number for grins */ + + /* Check to make sure the device probing will work on this iSeries Release. */ + if(hvReleaseData.xVrmIndex !=3) { + printk("PCI: iSeries Lpar and Linux native PCI I/O code is incompatible.\n"); + printk("PCI: A newer version of the Linux kernel is need for this iSeries release.\n"); + return -1; + } + /* Check all possible buses. */ + for (BusNumber = 0; BusNumber < 256; BusNumber++) { + if (HvCallXm_testBus(BusNumber) == 0) { + hose = pci_alloc_pci_controller("PHB HV",phb_type_hypervisor); + if(hose == NULL) return -1; /* Problems? */ + hose->local_number = BusNumber; /* Stuff HV bus number away. */ + hose->first_busno = LxBusNumber; /* This just for debug. pcibios will */ + hose->last_busno = 0xff; /* assign the bus numbers later. */ + hose->ops = &iSeries_pci_ops; /* Cnfg Reg Ops routines. */ + LxBusNumber += 1; /* Keep track for debug. */ + } + } + return 0; +} +/* + * find_iSeries_Device(PciDev); + * + * iSeries_Device_SaveConfigRegs, + * iSeries_Device_ToggleReset + * iSeries_Device_RestoreConfigRegs + */ diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/iSeries_proc.c linuxppc64_2_4/arch/ppc64/kernel/iSeries_proc.c --- linux-2.4.5-ac18/arch/ppc64/kernel/iSeries_proc.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/iSeries_proc.c Mon May 7 12:44:10 2001 @@ -0,0 +1,154 @@ +/* + * iSeries_proc.c + * Copyright (C) 2001 Kyle A. Lucke IBM Corporation + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* Change Activity: */ +/* End Change Activity */ + +#include +#include +#ifndef _ISERIES_PROC_H +#include +#endif + + +static struct proc_dir_entry * iSeries_proc_root = NULL; +static int iSeries_proc_initializationDone = 0; +static spinlock_t iSeries_proc_lock; + +struct iSeries_proc_registration +{ + struct iSeries_proc_registration *next; + iSeriesProcFunction functionMember; +}; + + +struct iSeries_proc_registration preallocated[16]; +#define MYQUEUETYPE(T) struct MYQueue##T +#define MYQUEUE(T) \ +MYQUEUETYPE(T) \ +{ \ +struct T *head; \ +struct T *tail; \ +} +#define MYQUEUECTOR(q) do { (q)->head = NULL; (q)->tail = NULL; } while(0) +#define MYQUEUEENQ(q, p) \ +do { \ +(p)->next = NULL; \ +if ((q)->head != NULL) \ +{ \ +(q)->head->next = (p); \ +(q)->head = (p); \ +} \ +else \ +{ \ +(q)->tail = (q)->head = (p); \ +} \ +} while(0) + +#define MYQUEUEDEQ(q,p) \ +do { \ +(p) = (q)->tail; \ +if ((p) != NULL) \ +{ \ +(q)->tail = (p)->next; \ +(p)->next = NULL; \ +} \ +if ((q)->tail == NULL) \ +(q)->head = NULL; \ +} while(0) +MYQUEUE(iSeries_proc_registration); +typedef MYQUEUETYPE(iSeries_proc_registration) aQueue; + + +aQueue iSeries_free; +aQueue iSeries_queued; + +void iSeries_proc_early_init(void) +{ + int i = 0; + unsigned long flags; + iSeries_proc_initializationDone = 0; + spin_lock_init(&iSeries_proc_lock); + MYQUEUECTOR(&iSeries_free); + MYQUEUECTOR(&iSeries_queued); + + spin_lock_irqsave(&iSeries_proc_lock, flags); + for (i = 0; i < 16; ++i) + { + MYQUEUEENQ(&iSeries_free, preallocated+i); + } + spin_unlock_irqrestore(&iSeries_proc_lock, flags); +} + +void iSeries_proc_create(void) +{ + unsigned long flags; + struct iSeries_proc_registration *reg = NULL; + spin_lock_irqsave(&iSeries_proc_lock, flags); + printk("iSeries_proc: Creating /proc/iSeries\n"); + + iSeries_proc_root = proc_mkdir("iSeries", 0); + if (!iSeries_proc_root) return; + + MYQUEUEDEQ(&iSeries_queued, reg); + + while (reg != NULL) + { + (*(reg->functionMember))(iSeries_proc_root); + + MYQUEUEDEQ(&iSeries_queued, reg); + } + + iSeries_proc_initializationDone = 1; + spin_unlock_irqrestore(&iSeries_proc_lock, flags); +} + +void iSeries_proc_callback(iSeriesProcFunction initFunction) +{ + unsigned long flags; + spin_lock_irqsave(&iSeries_proc_lock, flags); + + if (iSeries_proc_initializationDone) + { + (*initFunction)(iSeries_proc_root); + } + else + { + struct iSeries_proc_registration *reg = NULL; + + MYQUEUEDEQ(&iSeries_free, reg); + + if (reg != NULL) + { +/* printk("Registering %p in reg %p\n", initFunction, reg); */ + reg->functionMember = initFunction; + + MYQUEUEENQ(&iSeries_queued, reg); + } + else + { + printk("Couldn't get a queue entry\n"); + } + } + + spin_unlock_irqrestore(&iSeries_proc_lock, flags); +} + + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/iSeries_rtc.c linuxppc64_2_4/arch/ppc64/kernel/iSeries_rtc.c --- linux-2.4.5-ac18/arch/ppc64/kernel/iSeries_rtc.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/iSeries_rtc.c Fri May 4 17:13:59 2001 @@ -0,0 +1,265 @@ +/* + * Real Time Clock interface for IBM iSeries + * + * Based on rtc.c by Paul Gortmaker + * + * This driver allows use of the real time clock + * from user space. It exports the /dev/rtc + * interface supporting various ioctl() and also the + * /proc/driver/rtc pseudo-file for status information. + * + * iSeries does not support RTC interrupts nor an alarm. + * + * 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. + * + * 1.0 Mike Corrigan: IBM iSeries rtc support + */ + +#define RTC_VERSION "1.0" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +/* + * We sponge a minor off of the misc major. No need slurping + * up another valuable major dev number for this. If you add + * an ioctl, make sure you don't conflict with SPARC's RTC + * ioctls. + */ + +static loff_t rtc_llseek(struct file *file, loff_t offset, int origin); + +static ssize_t rtc_read(struct file *file, char *buf, + size_t count, loff_t *ppos); + +static int rtc_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); + +static void get_rtc_time (struct rtc_time *rtc_tm); + +static int rtc_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data); + +/* + * If this driver ever becomes modularised, it will be really nice + * to make the epoch retain its value across module reload... + */ + +static unsigned long epoch = 1900; /* year corresponding to 0x00 */ + +static const unsigned char days_in_mo[] = +{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +/* + * Now all the various file operations that we export. + */ + +static loff_t rtc_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +static ssize_t rtc_read(struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + return -EIO; +} + +static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct rtc_time wtime; + + switch (cmd) { + case RTC_RD_TIME: /* Read the time/date from RTC */ + { + get_rtc_time(&wtime); + break; + } + case RTC_SET_TIME: /* Set the RTC */ + { + struct rtc_time rtc_tm; + unsigned char mon, day, hrs, min, sec, leap_yr; + unsigned int yrs; + + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, + sizeof(struct rtc_time))) + return -EFAULT; + + yrs = rtc_tm.tm_year; + mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */ + day = rtc_tm.tm_mday; + hrs = rtc_tm.tm_hour; + min = rtc_tm.tm_min; + sec = rtc_tm.tm_sec; + + if (yrs < 70) + return -EINVAL; + + leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400)); + + if ((mon > 12) || (day == 0)) + return -EINVAL; + + if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr))) + return -EINVAL; + + if ((hrs >= 24) || (min >= 60) || (sec >= 60)) + return -EINVAL; + + if ( yrs > 169 ) + return -EINVAL; + + mf_setRtc( &rtc_tm ); + + return 0; + } + case RTC_EPOCH_READ: /* Read the epoch. */ + { + return put_user (epoch, (unsigned long *)arg); + } + case RTC_EPOCH_SET: /* Set the epoch. */ + { + /* + * There were no RTC clocks before 1900. + */ + if (arg < 1900) + return -EINVAL; + + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + epoch = arg; + return 0; + } + default: + return -EINVAL; + } + return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0; +} + +static int rtc_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int rtc_release(struct inode *inode, struct file *file) +{ + return 0; +} + +/* + * The various file operations we support. + */ + +static struct file_operations rtc_fops = { + owner: THIS_MODULE, + llseek: rtc_llseek, + read: rtc_read, + ioctl: rtc_ioctl, + open: rtc_open, + release: rtc_release, +}; + +static struct miscdevice rtc_dev= +{ + RTC_MINOR, + "rtc", + &rtc_fops +}; + +static int __init rtc_init(void) +{ + misc_register(&rtc_dev); + create_proc_read_entry ("driver/rtc", 0, 0, rtc_read_proc, NULL); + + printk(KERN_INFO "iSeries Real Time Clock Driver v" RTC_VERSION "\n"); + + return 0; +} + +static void __exit rtc_exit (void) +{ + remove_proc_entry ("driver/rtc", NULL); + misc_deregister(&rtc_dev); +} + +module_init(rtc_init); +module_exit(rtc_exit); +EXPORT_NO_SYMBOLS; + +/* + * Info exported via "/proc/driver/rtc". + */ + +static int rtc_proc_output (char *buf) +{ + + char *p; + struct rtc_time tm; + + p = buf; + + get_rtc_time(&tm); + + /* + * There is no way to tell if the luser has the RTC set for local + * time or for Universal Standard Time (GMT). Probably local though. + */ + p += sprintf(p, + "rtc_time\t: %02d:%02d:%02d\n" + "rtc_date\t: %04d-%02d-%02d\n" + "rtc_epoch\t: %04lu\n", + tm.tm_hour, tm.tm_min, tm.tm_sec, + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, epoch); + + p += sprintf(p, + "DST_enable\t: no\n" + "BCD\t\t: yes\n" + "24hr\t\t: yes\n" ); + + return p - buf; +} + +static int rtc_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = rtc_proc_output (page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +static void get_rtc_time(struct rtc_time *rtc_tm) +{ + mf_getRtc( rtc_tm ); + + rtc_tm->tm_mon--; +} + + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/iSeries_setup.c linuxppc64_2_4/arch/ppc64/kernel/iSeries_setup.c --- linux-2.4.5-ac18/arch/ppc64/kernel/iSeries_setup.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/iSeries_setup.c Fri Jun 15 13:22:13 2001 @@ -0,0 +1,758 @@ +/* + * + * + * Copyright (c) 2000 Mike Corrigan + * Copyright (c) 1999-2000 Grant Erickson + * + * Module name: iSeries_setup.c + * + * Description: + * Architecture- / platform-specific boot-time initialization code for + * the IBM iSeries LPAR. Adapted from original code by Grant Erickson and + * code by Gary Thomas, Cort Dougan , and Dan Malek + * . + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include "iSeries_setup.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Function Prototypes */ + +extern void abort(void); +static void build_iSeries_Memory_Map( void ); +static void setup_iSeries_cache_sizes( void ); +static void iSeries_build_hptex( unsigned long pages_to_map ); +/* static void init_bolted_stacks( void ); */ + +/* Global Variables */ + +unsigned long procFreqHz = 0; +unsigned long procFreqMhz = 0; +unsigned long procFreqMhzHundreths = 0; + +unsigned long tbFreqHz = 0; +unsigned long tbFreqMhz = 0; +unsigned long tbFreqMhzHundreths = 0; + +int piranha_simulator = 0; + +/* extern char _start_boltedStacks[]; */ +/* extern char _end_boltedStacks[]; */ + +extern struct Naca *naca; +extern int rd_size; /* Defined in drivers/block/rd.c */ +extern char * klimit; + +#define VIODASD_MAJOR 156 + +/* + * void __init iSeries_init_early() + */ + +void __init +iSeries_init_early(void) +{ + +#if defined(CONFIG_BLK_DEV_INITRD) + /* + * If the init RAM disk has been configured and there is + * a non-zero starting address for it, set it up + */ + + if ( naca->xRamDisk ) { + initrd_start = (unsigned long)(naca->xRamDisk) + KERNELBASE; + initrd_end = initrd_start + naca->xRamDiskSize * PAGE_SIZE; + initrd_below_start_ok = 1; // ramdisk in kernel space + ROOT_DEV = MKDEV( RAMDISK_MAJOR, 0 ); + + if ( ((rd_size*1024)/PAGE_SIZE) < naca->xRamDiskSize ) + rd_size = (naca->xRamDiskSize*PAGE_SIZE)/1024; + } else + +#endif /* CONFIG_BLK_DEV_INITRD */ + { + + ROOT_DEV = MKDEV( VIODASD_MAJOR, 1 ); + } + + /* Initialize the table which translate Linux physical addresses to + * AS/400 absolute addresses + */ + + build_iSeries_Memory_Map(); + + setup_iSeries_cache_sizes(); + + return; + +} + +/* + * void __init iSeries_init() + */ + +void __init +iSeries_init(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + + /* Initialize machine-dependency vectors */ + + ppc_md.setup_arch = iSeries_setup_arch; + ppc_md.setup_residual = iSeries_setup_residual; + ppc_md.get_cpuinfo = iSeries_get_cpuinfo; + ppc_md.irq_cannonicalize = NULL; + ppc_md.init_IRQ = iSeries_init_IRQ; + ppc_md.get_irq = iSeries_get_irq; + ppc_md.init = NULL; + + ppc_md.restart = iSeries_restart; + ppc_md.power_off = iSeries_power_off; + ppc_md.halt = iSeries_halt; + + ppc_md.time_init = NULL; + ppc_md.set_rtc_time = iSeries_set_rtc_time; + ppc_md.get_rtc_time = iSeries_get_rtc_time; + ppc_md.calibrate_decr = iSeries_calibrate_decr; + ppc_md.progress = iSeries_progress; + + ppc_md.kbd_setkeycode = NULL; + ppc_md.kbd_getkeycode = NULL; + ppc_md.kbd_translate = NULL; + ppc_md.kbd_unexpected_up = NULL; + ppc_md.kbd_leds = NULL; + ppc_md.kbd_init_hw = NULL; + +#if defined(CONFIG_MAGIC_SYSRQ) + ppc_md.ppc_kbd_sysrq_xlate = NULL; +#endif + + if ( itLpNaca.xPirEnvironMode == 0 ) + piranha_simulator = 1; + + /* Associate Lp Event Queue 0 with processor 0 */ + HvCallEvent_setLpEventQueueInterruptProc( 0, 0 ); + + { + /* copy the command line parameter from the primary VSP */ + char *p, *q; + HvCallEvent_dmaToSp( cmd_line, + 2*64*1024, + 256, + HvLpDma_Direction_RemoteToLocal ); + + p = q = cmd_line + 255; + while( p > cmd_line ) { + if ((*p == 0) || (*p == ' ') || (*p == '\n')) + --p; + else + break; + } + if ( p < q ) + *(p+1) = 0; + } + + iSeries_proc_early_init(); + mf_init(); + + iSeries_proc_callback( &pmc_proc_init ); + + viopath_init(); + + return; +} + + +/* + * The iSeries may have very large memories ( > 128 GB ) and a partition + * may get memory in "chunks" that may be anywhere in the 2**52 real + * address space. The chunks are 256K in size. To map this to the + * memory model Linux expects, the AS/400 specific code builds a + * translation table to translate what Linux thinks are "physical" + * addresses to the actual real addresses. This allows us to make + * it appear to Linux that we have contiguous memory starting at + * physical address zero while in fact this could be far from the truth. + * To avoid confusion, I'll let the words physical and/or real address + * apply to the Linux addresses while I'll use "absolute address" to + * refer to the actual hardware real address. + * + * build_iSeries_Memory_Map gets information from the Hypervisor and + * looks at the Main Store VPD to determine the absolute addresses + * of the memory that has been assigned to our partition and builds + * a table used to translate Linux's physical addresses to these + * absolute addresses. Absolute addresses are needed when + * communicating with the hypervisor (e.g. to build HPT entries) + */ + +static void __init build_iSeries_Memory_Map(void) +{ + u32 loadAreaFirstChunk, loadAreaLastChunk, loadAreaSize; + u32 nextPhysChunk; + u32 hptFirstChunk, hptLastChunk, hptSizeChunks, hptSizePages; + u32 num_ptegs; + u32 holeFirstChunk, holeSizeChunks; + u32 totalChunks,moreChunks; + u32 currChunk, thisChunk, absChunk; + u32 currDword; + u32 chunkBit; + u64 holeStart, holeEnd, holeSize; + u64 map; + unsigned long htabx; + unsigned long htabx_size_bytes; + struct IoHriMainStoreSegment4 * msVpd; + + /* Get absolute address of our load area + * and map it to physical address 0 + * This guarantees that the loadarea ends up at physical 0 + * otherwise, it might not be returned by PLIC as the first + * chunks + */ + + loadAreaFirstChunk = (u32)(itLpNaca.xLoadAreaAddr >> 18); + loadAreaSize = itLpNaca.xLoadAreaChunks; + + /* Only add the pages already mapped here. + * Otherwise we might add the hpt pages + * The rest of the pages of the load area + * aren't in the HPT yet and can still + * be assigned an arbitrary physical address + */ + if ( (loadAreaSize * 64) > HvPagesToMap ) + loadAreaSize = HvPagesToMap / 64; + + loadAreaLastChunk = loadAreaFirstChunk + loadAreaSize - 1; + + printk( "Mapping load area - physical addr = 0000000000000000\n" + " absolute addr = %016x\n", + loadAreaFirstChunk << 18 ); + printk( "Load area size %dK\n", loadAreaSize*256 ); + + for ( nextPhysChunk = 0; + nextPhysChunk < loadAreaSize; + ++nextPhysChunk ) { + msChunks[nextPhysChunk] = loadAreaFirstChunk+nextPhysChunk; + } + + /* Get absolute address of our HPT and remember it so + * we won't map it to any physical address + */ + + hptFirstChunk = (u32)(HvCallHpt_getHptAddress() >> 18 ); + hptSizePages = (u32)(HvCallHpt_getHptPages()); + hptSizeChunks = hptSizePages >> 6; + hptLastChunk = hptFirstChunk + hptSizeChunks - 1; + + printk( "HPT absolute addr = %016x, size = %dK\n", + hptFirstChunk << 18, hptSizeChunks*256 ); + + /* Fill in the htab_data structure */ + + /* Fill in size of hashed page table */ + num_ptegs = hptSizePages * (PAGE_SIZE/(sizeof(HPTE)*HPTES_PER_GROUP)); + htab_data.htab_num_ptegs = num_ptegs; + htab_data.htab_hash_mask = num_ptegs - 1; + + /* The actual hashed page table is in the hypervisor, we have no direct access */ + htab_data.htab = NULL; + + /* Fill in size of the hashed page table extension */ + htabx_size_bytes = (num_ptegs * HPTES_PER_GROUP * sizeof(HPTEX)); + + /* Allocate space for the hashed page table extension by adjusting + * klimit + */ + htabx = ((unsigned long)klimit + (PAGE_SIZE-1) ) & (~(PAGE_SIZE-1)); + htab_data.htabx = (HPTEX *)htabx; + klimit = (char *)(htabx + htabx_size_bytes); + cacheable_memzero((void *)htabx, htabx_size_bytes); + + iSeries_build_hptex( loadAreaSize * 64 ); + + /* Determine if absolute memory has any + * holes so that we can interpret the + * access map we get back from the hypervisor + * correctly. + */ + + msVpd = (struct IoHriMainStoreSegment4 *)xMsVpd; + holeStart = msVpd->nonInterleavedBlocksStartAdr; + holeEnd = msVpd->nonInterleavedBlocksEndAdr; + holeSize = holeEnd - holeStart; + + if ( holeSize ) { + holeStart = holeStart & 0x000fffffffffffff; + holeStart = holeStart >> 18; + holeFirstChunk = (u32)holeStart; + holeSize = holeSize >> 18; + holeSizeChunks = (u32)holeSize; + printk( "Main store hole: start chunk = %0x, size = %0x chunks\n", + holeFirstChunk, holeSizeChunks ); + } + else { + holeFirstChunk = 0xffffffff; + holeSizeChunks = 0; + } + + /* Process the main store access map from the hypervisor + * to build up our physical -> absolute translation table + */ + + totalChunks = (u32)HvLpConfig_getMsChunks(); + currChunk = 0; + currDword = 0; + moreChunks = totalChunks; + + while ( moreChunks ) { + map = HvCallSm_get64BitsOfAccessMap( itLpNaca.xLpIndex, + currDword ); + thisChunk = currChunk; + while ( map ) { + chunkBit = map >> 63; + map <<= 1; + if ( chunkBit ) { + --moreChunks; + absChunk = thisChunk; + if ( absChunk >= holeFirstChunk ) + absChunk += holeSizeChunks; + if ( ( ( absChunk < hptFirstChunk ) || + ( absChunk > hptLastChunk ) ) && + ( ( absChunk < loadAreaFirstChunk ) || + ( absChunk > loadAreaLastChunk ) ) ) { +/* printk( "Mapping physical = %0x to absolute %0x for 256K\n", nextPhysChunk << 18, absChunk << 18 ); */ + msChunks[nextPhysChunk] = absChunk; + ++nextPhysChunk; + } + } + ++thisChunk; + } + ++currDword; + currChunk += 64; + } + + /* main store size (in chunks) is + * totalChunks - hptSizeChunks + * which should be equal to + * nextPhysChunk + */ + + totalLpChunks = nextPhysChunk; + + naca->physicalMemorySize = totalLpChunks << 18; +/* htab_data.last_kernel_address = KERNELBASE + (( totalLpChunks << 18 ) - 1); */ + +} + +/* + * Set up the variables that describe the cache line sizes + * for this machine. + */ + +static void __init setup_iSeries_cache_sizes(void) +{ + unsigned i,n; + naca->iCacheL1LineSize = xIoHriProcessorVpd[0].xInstCacheOperandSize; + naca->dCacheL1LineSize = xIoHriProcessorVpd[0].xDataCacheOperandSize; + naca->iCacheL1LinesPerPage = PAGE_SIZE / naca->iCacheL1LineSize; + naca->dCacheL1LinesPerPage = PAGE_SIZE / naca->dCacheL1LineSize; + i = naca->iCacheL1LineSize; + n = 0; + while ((i=(i/2))) ++n; + naca->iCacheL1LogLineSize = n; + i = naca->dCacheL1LineSize; + n = 0; + while ((i=(i/2))) ++n; + naca->dCacheL1LogLineSize = n; + + printk( "D-cache line size = %d (log = %d)\n", + (unsigned)naca->dCacheL1LineSize, + (unsigned)naca->dCacheL1LogLineSize ); + printk( "I-cache line size = %d (log = %d)\n", + (unsigned)naca->iCacheL1LineSize, + (unsigned)naca->iCacheL1LogLineSize ); + +} + +/* + * Put entries into the hptex for the pages already mapped + * by the hypervisor. + */ + +static void __init iSeries_build_hptex( unsigned long pages_to_map ) +{ + unsigned long vsid, va, page, hash, i, k; + HPTEX * hptex; + + for ( i=0; i> 12 ) & 0xffff; + hash = ( vsid & 0x7fffffffff ) ^ page; + hptex = htab_data.htabx + (( hash & htab_data.htab_hash_mask ) * HPTES_PER_GROUP ); + + for ( k=0; k<8; ++k, ++hptex ) { + if ( hptex->dw1.dw1.flags.valid == 0 ) { + hptex->dw1.dw1.flags.kernel = 1; + hptex->dw1.dw1.flags.valid = 1; + hptex->dw1.dw1.flags.bolted = 1; + break; + } + } + } +} + + + +/* +static void __init init_bolted_stacks(void) +{ + struct BoltedStack * bs; + struct BoltedStack * b1, * b2; + unsigned i; + + bs = (struct BoltedStack *)_start_boltedStacks; + + for ( i=0; i= (unsigned long)_end_boltedStacks ) + break; + b1 = bs++; + b2 = bs++; + b1->head.next = b2; + b2->head.next = NULL; + xPaca[i].xBoltedStack = b1; + } +} +*/ + +/* + * Document me. + */ +void __init +iSeries_setup_arch(void) +{ + void * eventStack; +/* u32 procFreq; + u32 tbFreq; + u32 evStackContigReal; + u64 evStackReal; +*/ + + /* Setup the Lp Event Queue */ + + /* Associate Lp Event Queue 0 with processor 0 */ + +/* HvCallEvent_setLpEventQueueInterruptProc( 0, 0 ); */ + + /* Allocate a page for the Event Stack + * The hypervisor wants the absolute real address, so + * we subtract out the KERNELBASE and add in the + * absolute real address of the kernel load area + */ + + eventStack = alloc_bootmem_pages( LpEventStackSize ); + + memset( eventStack, 0, LpEventStackSize ); + + /* Invoke the hypervisor to initialize the event stack */ + + HvCallEvent_setLpEventStack( 0, eventStack, LpEventStackSize ); + + /* Initialize fields in our Lp Event Queue */ + + xItLpQueue.xSlicEventStackPtr = (char *)eventStack; + xItLpQueue.xSlicCurEventPtr = (char *)eventStack; + xItLpQueue.xSlicLastValidEventPtr = (char *)eventStack + + (LpEventStackSize - LpEventMaxSize); + xItLpQueue.xIndex = 0; + + /* Compute processor frequency */ + procFreqHz = (((1UL<<34) * 1000000) / xIoHriProcessorVpd[0].xProcFreq ); + procFreqMhz = procFreqHz / 1000000; + procFreqMhzHundreths = (procFreqHz/10000) - (procFreqMhz*100); +/* procFreq = ( 0x40000000 / (xIoHriProcessorVpd[0].xProcFreq / 1600) ); + procFreqHz = procFreq * 10000; + procFreqMhz = procFreq / 100; + procFreqMhzHundreths = procFreq - (procFreqMhz * 100 ); +*/ + /* Compute time base frequency */ + tbFreqHz = (((1UL<<32) * 1000000) / xIoHriProcessorVpd[0].xTimeBaseFreq ); + tbFreqMhz = tbFreqHz / 1000000; + tbFreqMhzHundreths = (tbFreqHz/10000) - (tbFreqMhz*100); +/* tbFreq = ( 0x40000000 / (xIoHriProcessorVpd[0].xTimeBaseFreq / 400) ); + tbFreqHz = tbFreq * 10000; + tbFreqMhz = tbFreq / 100; + tbFreqMhzHundreths = tbFreq - (tbFreqMhz * 100 ); +*/ + + printk("Max logical processors = %d\n", + itVpdAreas.xSlicMaxLogicalProcs ); + printk("Max physical processors = %d\n", + itVpdAreas.xSlicMaxPhysicalProcs ); + printk("Processor frequency = %lu.%02lu\n", + procFreqMhz, + procFreqMhzHundreths ); + printk("Time base frequency = %lu.%02lu\n", + tbFreqMhz, + tbFreqMhzHundreths ); + printk("Processor version = %x\n", + xIoHriProcessorVpd[0].xPVR ); + + +/* + // copy the command line parameter from the primary VSP + HvCallEvent_dmaToSp( cmd_line, + 2*64*1024, + 256, + HvLpDma_Direction_RemoteToLocal ); + + mf_init(); + viopath_init(); +*/ +} + +/* + * int as400_setup_residual() + * + * Description: + * This routine pretty-prints CPU information gathered from the VPD + * for use in /proc/cpuinfo + * + * Input(s): + * *buffer - Buffer into which CPU data is to be printed. + * + * Output(s): + * *buffer - Buffer with CPU data. + * + * Returns: + * The number of bytes copied into 'buffer' if OK, otherwise zero or less + * on error. + */ +int +iSeries_setup_residual(char *buffer) +{ + int len = 0; + + len += sprintf(len+buffer,"clock\t\t: %lu.%02luMhz\n", + procFreqMhz, procFreqMhzHundreths ); + len += sprintf(len+buffer,"time base\t: %lu.%02luMHz\n", + tbFreqMhz, tbFreqMhzHundreths ); + len += sprintf(len+buffer,"i-cache\t\t: %d\n", + naca->iCacheL1LineSize); + len += sprintf(len+buffer,"d-cache\t\t: %d\n", + naca->dCacheL1LineSize); + + + return (len); +} + +int iSeries_get_cpuinfo(char *buffer) +{ + int len = 0; + + len += sprintf(len+buffer,"machine\t\t: 64-bit iSeries Logical Partition\n"); + + return len; +} + +static char * lpEventTypes[9]= { +/* 0123456789012345678901234567890 */ + "Hypervisor\t\t", + "Machine Facilities\t", + "Session Manager\t\t", + "SPD I/O\t\t\t", + "Virtual Bus\t\t", + "PCI I/O\t\t\t", + "RIO I/O\t\t\t", + "Virtual Lan\t\t", + "Virtual I/O\t\t" + }; + +int get_ioevent_data(char *buffer) +{ + int len = 0; + unsigned i; + + len += sprintf(len+buffer,"LpEventQueue 0\n"); + len += sprintf(len+buffer," events processed:\t%lu\n", + (unsigned long)xItLpQueue.xLpIntCount ); + /* display event counts by type */ + for (i=0; i + * Copyright (c) 1999-2000 Grant Erickson + * + * Module name: as400_setup.h + * + * Description: + * Architecture- / platform-specific boot-time initialization code for + * the IBM AS/400 LPAR. Adapted from original code by Grant Erickson and + * code by Gary Thomas, Cort Dougan , and Dan Malek + * . + * + * 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. + */ + +#ifndef __ISERIES_SETUP_H__ +#define __ISERIES_SETUP_H__ + + +#ifdef __cplusplus +extern "C" { +#endif + +extern void iSeries_init_early(void); +extern void iSeries_init(unsigned long r3, + unsigned long ird_start, + unsigned long ird_end, + unsigned long cline_start, + unsigned long cline_end); +extern void iSeries_setup_arch(void); +extern int iSeries_setup_residual(char *buffer); +extern int iSeries_get_cpuinfo(char *buffer); +extern void iSeries_init_IRQ(void); +extern int iSeries_get_irq(struct pt_regs *regs); +extern void iSeries_restart(char *cmd); +extern void iSeries_power_off(void); +extern void iSeries_halt(void); +extern void iSeries_time_init(void); +extern int iSeries_set_rtc_time(unsigned long now); +extern unsigned long iSeries_get_rtc_time(void); +extern void iSeries_calibrate_decr(void); +extern void iSeries_progress( char *, unsigned short ); + + +#ifdef __cplusplus +} +#endif + +#endif /* __ISERIES_SETUP_H__ */ diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/idle.c linuxppc64_2_4/arch/ppc64/kernel/idle.c --- linux-2.4.5-ac18/arch/ppc64/kernel/idle.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/idle.c Mon Jun 18 13:47:13 2001 @@ -0,0 +1,84 @@ +/* + * + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static void power_save(void) +{ + /* Implement me */ ; +} + +int idled(void) +{ + int do_power_save = 0; + + /* endless loop with no priority at all */ + current->nice = 20; + current->counter = -100; + init_idle(); + + for (;;) { +#ifdef CONFIG_SMP + int oldval; + + if (!do_power_save) { + /* + * Deal with another CPU just having chosen a thread to + * run here: + */ + oldval = xchg(¤t->need_resched, -1); + + if (!oldval) { + while(current->need_resched == -1) + ; /* Do Nothing */ + } + } +#endif + if (do_power_save && !current->need_resched) + power_save(); + + if (current->need_resched) { + schedule(); + check_pgt_cache(); + } + } + return 0; +} + +/* + * SMP entry into the idle task - calls the same thing as the + * non-smp versions. -- Cort + */ +int cpu_idle(void) +{ + idled(); + return 0; +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/ioctl32.c linuxppc64_2_4/arch/ppc64/kernel/ioctl32.c --- linux-2.4.5-ac18/arch/ppc64/kernel/ioctl32.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/ioctl32.c Thu Jul 12 00:50:47 2001 @@ -0,0 +1,3803 @@ +/* + * ioctl32.c: Conversion between 32bit and 64bit native ioctls. + * + * Based on sparc64 ioctl32.c by: + * + * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com) + * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) + * + * ppc64 changes: + * + * Copyright (C) 2000 Ken Aaker (kdaaker@rchland.vnet.ibm.com) + * Copyright (C) 2001 Anton Blanchard (antonb@au.ibm.com) + * + * These routines maintain argument size conversion between 32bit and 64bit + * ioctls. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE) +/* Ugh. This header really is not clean */ +#define min min +#define max max +#include +#endif /* LVM */ + +#include +/* Ugly hack. */ +#undef __KERNEL__ +#include +#define __KERNEL__ +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Use this to get at 32-bit user passed pointers. + See sys_sparc32.c for description about these. */ +#define A(__x) ((unsigned long)(__x)) +#define AA(__x) \ +({ unsigned long __ret; \ + __asm__ ("clrldi %0, %0, 32" \ + : "=r" (__ret) \ + : "0" (__x)); \ + __ret; \ +}) + +/* Aiee. Someone does not find a difference between int and long */ +#define EXT2_IOC32_GETFLAGS _IOR('f', 1, int) +#define EXT2_IOC32_SETFLAGS _IOW('f', 2, int) +#define EXT2_IOC32_GETVERSION _IOR('v', 1, int) +#define EXT2_IOC32_SETVERSION _IOW('v', 2, int) + +extern asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg); + +static int w_long(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + int err; + unsigned long val; + + set_fs (KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&val); + set_fs (old_fs); + if (!err && put_user(val, (u32 *)arg)) + return -EFAULT; + return err; +} + +static int rw_long(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + int err; + unsigned long val; + + if(get_user(val, (u32 *)arg)) + return -EFAULT; + set_fs (KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&val); + set_fs (old_fs); + if (!err && put_user(val, (u32 *)arg)) + return -EFAULT; + return err; +} + +static int do_ext2_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + /* These are just misnamed, they actually get/put from/to user an int */ + switch (cmd) { + case EXT2_IOC32_GETFLAGS: cmd = EXT2_IOC_GETFLAGS; break; + case EXT2_IOC32_SETFLAGS: cmd = EXT2_IOC_SETFLAGS; break; + case EXT2_IOC32_GETVERSION: cmd = EXT2_IOC_GETVERSION; break; + case EXT2_IOC32_SETVERSION: cmd = EXT2_IOC_SETVERSION; break; + } + return sys_ioctl(fd, cmd, arg); +} + +struct video_tuner32 { + s32 tuner; + u8 name[32]; + u32 rangelow, rangehigh; + u32 flags; + u16 mode, signal; +}; + +static int get_video_tuner32(struct video_tuner *kp, struct video_tuner32 *up) +{ + int i; + + if(get_user(kp->tuner, &up->tuner)) + return -EFAULT; + for(i = 0; i < 32; i++) + __get_user(kp->name[i], &up->name[i]); + __get_user(kp->rangelow, &up->rangelow); + __get_user(kp->rangehigh, &up->rangehigh); + __get_user(kp->flags, &up->flags); + __get_user(kp->mode, &up->mode); + __get_user(kp->signal, &up->signal); + return 0; +} + +static int put_video_tuner32(struct video_tuner *kp, struct video_tuner32 *up) +{ + int i; + + if(put_user(kp->tuner, &up->tuner)) + return -EFAULT; + for(i = 0; i < 32; i++) + __put_user(kp->name[i], &up->name[i]); + __put_user(kp->rangelow, &up->rangelow); + __put_user(kp->rangehigh, &up->rangehigh); + __put_user(kp->flags, &up->flags); + __put_user(kp->mode, &up->mode); + __put_user(kp->signal, &up->signal); + return 0; +} + +struct video_buffer32 { + /* void * */ u32 base; + s32 height, width, depth, bytesperline; +}; + +static int get_video_buffer32(struct video_buffer *kp, struct video_buffer32 *up) +{ + u32 tmp; + + if(get_user(tmp, &up->base)) + return -EFAULT; + kp->base = (void *) ((unsigned long)tmp); + __get_user(kp->height, &up->height); + __get_user(kp->width, &up->width); + __get_user(kp->depth, &up->depth); + __get_user(kp->bytesperline, &up->bytesperline); + return 0; +} + +static int put_video_buffer32(struct video_buffer *kp, struct video_buffer32 *up) +{ + u32 tmp = (u32)((unsigned long)kp->base); + + if(put_user(tmp, &up->base)) + return -EFAULT; + __put_user(kp->height, &up->height); + __put_user(kp->width, &up->width); + __put_user(kp->depth, &up->depth); + __put_user(kp->bytesperline, &up->bytesperline); + return 0; +} + +struct video_clip32 { + s32 x, y, width, height; + /* struct video_clip32 * */ u32 next; +}; + +struct video_window32 { + u32 x, y, width, height, chromakey, flags; + /* struct video_clip32 * */ u32 clips; + s32 clipcount; +}; + +static void free_kvideo_clips(struct video_window *kp) +{ + struct video_clip *cp; + + cp = kp->clips; + if(cp != NULL) + kfree(cp); +} + +static int get_video_window32(struct video_window *kp, struct video_window32 *up) +{ + struct video_clip32 *ucp; + struct video_clip *kcp; + int nclips, err, i; + u32 tmp; + + if(get_user(kp->x, &up->x)) + return -EFAULT; + __get_user(kp->y, &up->y); + __get_user(kp->width, &up->width); + __get_user(kp->height, &up->height); + __get_user(kp->chromakey, &up->chromakey); + __get_user(kp->flags, &up->flags); + __get_user(kp->clipcount, &up->clipcount); + __get_user(tmp, &up->clips); + ucp = (struct video_clip32 *)A(tmp); + kp->clips = NULL; + + nclips = kp->clipcount; + if(nclips == 0) + return 0; + + if(ucp == 0) + return -EINVAL; + + /* Peculiar interface... */ + if(nclips < 0) + nclips = VIDEO_CLIPMAP_SIZE; + + kcp = kmalloc(nclips * sizeof(struct video_clip), GFP_KERNEL); + err = -ENOMEM; + if(kcp == NULL) + goto cleanup_and_err; + + kp->clips = kcp; + for(i = 0; i < nclips; i++) { + __get_user(kcp[i].x, &ucp[i].x); + __get_user(kcp[i].y, &ucp[i].y); + __get_user(kcp[i].width, &ucp[i].width); + __get_user(kcp[i].height, &ucp[i].height); + kcp[nclips].next = NULL; + } + + return 0; + +cleanup_and_err: + free_kvideo_clips(kp); + return err; +} + +/* You get back everything except the clips... */ +static int put_video_window32(struct video_window *kp, struct video_window32 *up) +{ + if(put_user(kp->x, &up->x)) + return -EFAULT; + __put_user(kp->y, &up->y); + __put_user(kp->width, &up->width); + __put_user(kp->height, &up->height); + __put_user(kp->chromakey, &up->chromakey); + __put_user(kp->flags, &up->flags); + __put_user(kp->clipcount, &up->clipcount); + return 0; +} + +#define VIDIOCGTUNER32 _IOWR('v',4, struct video_tuner32) +#define VIDIOCSTUNER32 _IOW('v',5, struct video_tuner32) +#define VIDIOCGWIN32 _IOR('v',9, struct video_window32) +#define VIDIOCSWIN32 _IOW('v',10, struct video_window32) +#define VIDIOCGFBUF32 _IOR('v',11, struct video_buffer32) +#define VIDIOCSFBUF32 _IOW('v',12, struct video_buffer32) +#define VIDIOCGFREQ32 _IOR('v',14, u32) +#define VIDIOCSFREQ32 _IOW('v',15, u32) + +static int do_video_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + union { + struct video_tuner vt; + struct video_buffer vb; + struct video_window vw; + unsigned long vx; + } karg; + mm_segment_t old_fs = get_fs(); + void *up = (void *)arg; + int err = 0; + + /* First, convert the command. */ + switch(cmd) { + case VIDIOCGTUNER32: cmd = VIDIOCGTUNER; break; + case VIDIOCSTUNER32: cmd = VIDIOCSTUNER; break; + case VIDIOCGWIN32: cmd = VIDIOCGWIN; break; + case VIDIOCSWIN32: cmd = VIDIOCSWIN; break; + case VIDIOCGFBUF32: cmd = VIDIOCGFBUF; break; + case VIDIOCSFBUF32: cmd = VIDIOCSFBUF; break; + case VIDIOCGFREQ32: cmd = VIDIOCGFREQ; break; + case VIDIOCSFREQ32: cmd = VIDIOCSFREQ; break; + }; + + switch(cmd) { + case VIDIOCSTUNER: + case VIDIOCGTUNER: + err = get_video_tuner32(&karg.vt, up); + break; + + case VIDIOCSWIN: + err = get_video_window32(&karg.vw, up); + break; + + case VIDIOCSFBUF: + err = get_video_buffer32(&karg.vb, up); + break; + + case VIDIOCSFREQ: + err = get_user(karg.vx, (u32 *)up); + break; + }; + if(err) + goto out; + + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&karg); + set_fs(old_fs); + + if(cmd == VIDIOCSWIN) + free_kvideo_clips(&karg.vw); + + if(err == 0) { + switch(cmd) { + case VIDIOCGTUNER: + err = put_video_tuner32(&karg.vt, up); + break; + + case VIDIOCGWIN: + err = put_video_window32(&karg.vw, up); + break; + + case VIDIOCGFBUF: + err = put_video_buffer32(&karg.vb, up); + break; + + case VIDIOCGFREQ: + err = put_user(((u32)karg.vx), (u32 *)up); + break; + }; + } +out: + return err; +} + +struct timeval32 { + int tv_sec; + int tv_usec; +}; + +static int do_siocgstamp(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct timeval32 *up = (struct timeval32 *)arg; + struct timeval ktv; + mm_segment_t old_fs = get_fs(); + int err; + + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&ktv); + set_fs(old_fs); + if(!err) { + err = put_user(ktv.tv_sec, &up->tv_sec); + err |= __put_user(ktv.tv_usec, &up->tv_usec); + } + return err; +} + +struct ifmap32 { + u32 mem_start; + u32 mem_end; + unsigned short base_addr; + unsigned char irq; + unsigned char dma; + unsigned char port; +}; + +struct ifreq32 { +#define IFHWADDRLEN 6 +#define IFNAMSIZ 16 + union { + char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + } ifr_ifrn; + union { + struct sockaddr ifru_addr; + struct sockaddr ifru_dstaddr; + struct sockaddr ifru_broadaddr; + struct sockaddr ifru_netmask; + struct sockaddr ifru_hwaddr; + short ifru_flags; + int ifru_ivalue; + int ifru_mtu; + struct ifmap32 ifru_map; + char ifru_slave[IFNAMSIZ]; /* Just fits the size */ + char ifru_newname[IFNAMSIZ]; + __kernel_caddr_t32 ifru_data; + } ifr_ifru; +}; + +struct ifconf32 { + int ifc_len; /* size of buffer */ + __kernel_caddr_t32 ifcbuf; +}; + +static int dev_ifname32(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct net_device *dev; + struct ifreq32 ifr32; + int err; + + if (copy_from_user(&ifr32, (struct ifreq32 *)arg, sizeof(struct ifreq32))) + return -EFAULT; + + dev = dev_get_by_index(ifr32.ifr_ifindex); + if (!dev) + return -ENODEV; + + strcpy(ifr32.ifr_name, dev->name); + + err = copy_to_user((struct ifreq32 *)arg, &ifr32, sizeof(struct ifreq32)); + return (err ? -EFAULT : 0); +} + +static int dev_ifconf(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct ifconf32 ifc32; + struct ifconf ifc; + struct ifreq32 *ifr32; + struct ifreq *ifr; + mm_segment_t old_fs; + unsigned int i, j; + int err; + + if (copy_from_user(&ifc32, (struct ifconf32 *)arg, sizeof(struct ifconf32))) + return -EFAULT; + + if(ifc32.ifcbuf == 0) { + ifc32.ifc_len = 0; + ifc.ifc_len = 0; + ifc.ifc_buf = NULL; + } else { + 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; + } + ifr = ifc.ifc_req; + ifr32 = (struct ifreq32 *)A(ifc32.ifcbuf); + for (i = 0; i < ifc32.ifc_len; i += sizeof (struct ifreq32)) { + if (copy_from_user(ifr++, ifr32++, sizeof (struct ifreq32))) { + kfree (ifc.ifc_buf); + return -EFAULT; + } + } + old_fs = get_fs(); set_fs (KERNEL_DS); + err = sys_ioctl (fd, SIOCGIFCONF, (unsigned long)&ifc); + set_fs (old_fs); + if (!err) { + ifr = ifc.ifc_req; + ifr32 = (struct ifreq32 *)A(ifc32.ifcbuf); + for (i = 0, j = 0; i < ifc32.ifc_len && j < ifc.ifc_len; + i += sizeof (struct ifreq32), j += sizeof (struct ifreq)) { + if (copy_to_user(ifr32++, ifr++, sizeof (struct ifreq32))) { + err = -EFAULT; + break; + } + } + if (!err) { + if (ifc32.ifcbuf == 0) { + /* Translate from 64-bit structure multiple to + * a 32-bit one. + */ + i = ifc.ifc_len; + i = ((i / sizeof(struct ifreq)) * sizeof(struct ifreq32)); + ifc32.ifc_len = i; + } else { + if (i <= ifc32.ifc_len) + ifc32.ifc_len = i; + else + ifc32.ifc_len = i - sizeof (struct ifreq32); + } + if (copy_to_user((struct ifconf32 *)arg, &ifc32, sizeof(struct ifconf32))) + err = -EFAULT; + } + } + if(ifc.ifc_buf != NULL) + kfree (ifc.ifc_buf); + return err; +} + +static int ethtool_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct ifreq ifr; + mm_segment_t old_fs; + int err, len; + u32 data, ethcmd; + + if (copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(struct ifreq32))) + return -EFAULT; + ifr.ifr_data = (__kernel_caddr_t)get_free_page(GFP_KERNEL); + if (!ifr.ifr_data) + return -EAGAIN; + + __get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data)); + + if (get_user(ethcmd, (u32 *)A(data))) { + err = -EFAULT; + goto out; + } + switch (ethcmd) { + case ETHTOOL_GDRVINFO: len = sizeof(struct ethtool_drvinfo); break; + case ETHTOOL_GSET: + case ETHTOOL_SSET: + default: len = sizeof(struct ethtool_cmd); break; + } + + if (copy_from_user(ifr.ifr_data, (char *)A(data), len)) { + err = -EFAULT; + goto out; + } + + old_fs = get_fs(); + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&ifr); + set_fs (old_fs); + if (!err) { + u32 data; + + __get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data)); + len = copy_to_user((char *)A(data), ifr.ifr_data, len); + if (len) + err = -EFAULT; + } + +out: + free_page((unsigned long)ifr.ifr_data); + return err; +} + +static int dev_ifsioc(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct ifreq ifr; + mm_segment_t old_fs; + int err; + + switch (cmd) { + case SIOCSIFMAP: + err = copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(ifr.ifr_name)); + err |= __get_user(ifr.ifr_map.mem_start, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.mem_start)); + err |= __get_user(ifr.ifr_map.mem_end, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.mem_end)); + err |= __get_user(ifr.ifr_map.base_addr, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.base_addr)); + err |= __get_user(ifr.ifr_map.irq, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.irq)); + err |= __get_user(ifr.ifr_map.dma, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.dma)); + err |= __get_user(ifr.ifr_map.port, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.port)); + if (err) + return -EFAULT; + break; + case SIOCGPPPSTATS: + case SIOCGPPPCSTATS: + case SIOCGPPPVER: + default: + if (copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(struct ifreq32))) + return -EFAULT; + break; + } + old_fs = get_fs(); + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&ifr); + set_fs (old_fs); + if (!err) { + switch (cmd) { + case SIOCGIFFLAGS: + case SIOCGIFMETRIC: + case SIOCGIFMTU: + case SIOCGIFMEM: + case SIOCGIFHWADDR: + case SIOCGIFINDEX: + case SIOCGIFADDR: + case SIOCGIFBRDADDR: + case SIOCGIFDSTADDR: + case SIOCGIFNETMASK: + case SIOCGIFTXQLEN: + if (copy_to_user((struct ifreq32 *)arg, &ifr, sizeof(struct ifreq32))) + return -EFAULT; + break; + case SIOCGPPPSTATS: + case SIOCGPPPCSTATS: + case SIOCGPPPVER: + case SIOCGIFMAP: + err = copy_to_user((struct ifreq32 *)arg, &ifr, sizeof(ifr.ifr_name)); + err |= __put_user(ifr.ifr_map.mem_start, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.mem_start)); + err |= __put_user(ifr.ifr_map.mem_end, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.mem_end)); + err |= __put_user(ifr.ifr_map.base_addr, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.base_addr)); + err |= __put_user(ifr.ifr_map.irq, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.irq)); + err |= __put_user(ifr.ifr_map.dma, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.dma)); + err |= __put_user(ifr.ifr_map.port, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_map.port)); + if (err) + err = -EFAULT; + break; + } + } + return err; +} + +struct rtentry32 { + u32 rt_pad1; + struct sockaddr rt_dst; /* target address */ + struct sockaddr rt_gateway; /* gateway addr (RTF_GATEWAY) */ + struct sockaddr rt_genmask; /* target network mask (IP) */ + unsigned short rt_flags; + short rt_pad2; + u32 rt_pad3; + unsigned char rt_tos; + unsigned char rt_class; + short rt_pad4; + short rt_metric; /* +1 for binary compatibility! */ + /* char * */ u32 rt_dev; /* forcing the device at add */ + u32 rt_mtu; /* per route MTU/Window */ + u32 rt_window; /* Window clamping */ + unsigned short rt_irtt; /* Initial RTT */ + +}; + +struct in6_rtmsg32 { + struct in6_addr rtmsg_dst; + struct in6_addr rtmsg_src; + struct in6_addr rtmsg_gateway; + u32 rtmsg_type; + u16 rtmsg_dst_len; + u16 rtmsg_src_len; + u32 rtmsg_metric; + u32 rtmsg_info; + u32 rtmsg_flags; + s32 rtmsg_ifindex; +}; + +extern struct socket *sockfd_lookup(int fd, int *err); + +static int routing_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + int ret; + void *r = NULL; + struct in6_rtmsg r6; + struct rtentry r4; + char devname[16]; + u32 rtdev; + mm_segment_t old_fs = get_fs(); + + struct socket *mysock = sockfd_lookup(fd, &ret); + + if (mysock && mysock->sk && mysock->sk->family == AF_INET6) { /* ipv6 */ + ret = copy_from_user (&r6.rtmsg_dst, &(((struct in6_rtmsg32 *)arg)->rtmsg_dst), + 3 * sizeof(struct in6_addr)); + ret |= __get_user (r6.rtmsg_type, &(((struct in6_rtmsg32 *)arg)->rtmsg_type)); + ret |= __get_user (r6.rtmsg_dst_len, &(((struct in6_rtmsg32 *)arg)->rtmsg_dst_len)); + ret |= __get_user (r6.rtmsg_src_len, &(((struct in6_rtmsg32 *)arg)->rtmsg_src_len)); + ret |= __get_user (r6.rtmsg_metric, &(((struct in6_rtmsg32 *)arg)->rtmsg_metric)); + ret |= __get_user (r6.rtmsg_info, &(((struct in6_rtmsg32 *)arg)->rtmsg_info)); + ret |= __get_user (r6.rtmsg_flags, &(((struct in6_rtmsg32 *)arg)->rtmsg_flags)); + ret |= __get_user (r6.rtmsg_ifindex, &(((struct in6_rtmsg32 *)arg)->rtmsg_ifindex)); + + r = (void *) &r6; + } else { /* ipv4 */ + ret = copy_from_user (&r4.rt_dst, &(((struct rtentry32 *)arg)->rt_dst), 3 * sizeof(struct sockaddr)); + ret |= __get_user (r4.rt_flags, &(((struct rtentry32 *)arg)->rt_flags)); + ret |= __get_user (r4.rt_metric, &(((struct rtentry32 *)arg)->rt_metric)); + ret |= __get_user (r4.rt_mtu, &(((struct rtentry32 *)arg)->rt_mtu)); + ret |= __get_user (r4.rt_window, &(((struct rtentry32 *)arg)->rt_window)); + ret |= __get_user (r4.rt_irtt, &(((struct rtentry32 *)arg)->rt_irtt)); + ret |= __get_user (rtdev, &(((struct rtentry32 *)arg)->rt_dev)); + if (rtdev) { + ret |= copy_from_user (devname, (char *)A(rtdev), 15); + r4.rt_dev = devname; devname[15] = 0; + } else + r4.rt_dev = 0; + + r = (void *) &r4; + } + + if (ret) + return -EFAULT; + + set_fs (KERNEL_DS); + ret = sys_ioctl (fd, cmd, (long) r); + set_fs (old_fs); + + return ret; +} + +struct hd_geometry32 { + unsigned char heads; + unsigned char sectors; + unsigned short cylinders; + u32 start; +}; + +static int hdio_getgeo(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct hd_geometry geo; + int err; + + set_fs (KERNEL_DS); + err = sys_ioctl(fd, HDIO_GETGEO, (unsigned long)&geo); + set_fs (old_fs); + if (!err) { + err = copy_to_user ((struct hd_geometry32 *)arg, &geo, 4); + err |= __put_user (geo.start, &(((struct hd_geometry32 *)arg)->start)); + } + return err ? -EFAULT : 0; +} + + +static int hdio_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t 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 *)arg; + if(put_user(kval, uvp)) + error = -EFAULT; + } + return error; +} + +struct floppy_struct32 { + unsigned int size; + unsigned int sect; + unsigned int head; + unsigned int track; + unsigned int stretch; + unsigned char gap; + unsigned char rate; + unsigned char spec1; + unsigned char fmt_gap; + const __kernel_caddr_t32 name; +}; + +struct floppy_drive_params32 { + char cmos; + u32 max_dtr; + u32 hlt; + u32 hut; + u32 srt; + u32 spinup; + u32 spindown; + unsigned char spindown_offset; + unsigned char select_delay; + unsigned char rps; + unsigned char tracks; + u32 timeout; + unsigned char interleave_sect; + struct floppy_max_errors max_errors; + char flags; + char read_track; + short autodetect[8]; + int checkfreq; + int native_format; +}; + +struct floppy_drive_struct32 { + signed char flags; + u32 spinup_date; + u32 select_date; + u32 first_read_date; + short probed_format; + short track; + short maxblock; + short maxtrack; + int generation; + int keep_data; + int fd_ref; + int fd_device; + int last_checked; + __kernel_caddr_t32 dmabuf; + int bufblocks; +}; + +struct floppy_fdc_state32 { + int spec1; + int spec2; + int dtr; + unsigned char version; + unsigned char dor; + u32 address; + unsigned int rawcmd:2; + unsigned int reset:1; + unsigned int need_configure:1; + unsigned int perp_mode:2; + unsigned int has_fifo:1; + unsigned int driver_version; + unsigned char track[4]; +}; + +struct floppy_write_errors32 { + unsigned int write_errors; + u32 first_error_sector; + int first_error_generation; + u32 last_error_sector; + int last_error_generation; + unsigned int badness; +}; + +#define FDSETPRM32 _IOW(2, 0x42, struct floppy_struct32) +#define FDDEFPRM32 _IOW(2, 0x43, struct floppy_struct32) +#define FDGETPRM32 _IOR(2, 0x04, struct floppy_struct32) +#define FDSETDRVPRM32 _IOW(2, 0x90, struct floppy_drive_params32) +#define FDGETDRVPRM32 _IOR(2, 0x11, struct floppy_drive_params32) +#define FDGETDRVSTAT32 _IOR(2, 0x12, struct floppy_drive_struct32) +#define FDPOLLDRVSTAT32 _IOR(2, 0x13, struct floppy_drive_struct32) +#define FDGETFDCSTAT32 _IOR(2, 0x15, struct floppy_fdc_state32) +#define FDWERRORGET32 _IOR(2, 0x17, struct floppy_write_errors32) + +static struct { + unsigned int cmd32; + unsigned int cmd; +} fd_ioctl_trans_table[] = { + { FDSETPRM32, FDSETPRM }, + { FDDEFPRM32, FDDEFPRM }, + { FDGETPRM32, FDGETPRM }, + { FDSETDRVPRM32, FDSETDRVPRM }, + { FDGETDRVPRM32, FDGETDRVPRM }, + { FDGETDRVSTAT32, FDGETDRVSTAT }, + { FDPOLLDRVSTAT32, FDPOLLDRVSTAT }, + { FDGETFDCSTAT32, FDGETFDCSTAT }, + { FDWERRORGET32, FDWERRORGET } +}; + +#define NR_FD_IOCTL_TRANS (sizeof(fd_ioctl_trans_table)/sizeof(fd_ioctl_trans_table[0])) + +static int fd_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + void *karg = NULL; + unsigned int kcmd = 0; + int i, err; + + for (i = 0; i < NR_FD_IOCTL_TRANS; i++) + if (cmd == fd_ioctl_trans_table[i].cmd32) { + kcmd = fd_ioctl_trans_table[i].cmd; + break; + } + if (!kcmd) + return -EINVAL; + + switch (cmd) { + case FDSETPRM32: + case FDDEFPRM32: + case FDGETPRM32: + { + struct floppy_struct *f; + + f = karg = kmalloc(sizeof(struct floppy_struct), GFP_KERNEL); + if (!karg) + return -ENOMEM; + if (cmd == FDGETPRM32) + break; + err = __get_user(f->size, &((struct floppy_struct32 *)arg)->size); + err |= __get_user(f->sect, &((struct floppy_struct32 *)arg)->sect); + err |= __get_user(f->head, &((struct floppy_struct32 *)arg)->head); + err |= __get_user(f->track, &((struct floppy_struct32 *)arg)->track); + err |= __get_user(f->stretch, &((struct floppy_struct32 *)arg)->stretch); + err |= __get_user(f->gap, &((struct floppy_struct32 *)arg)->gap); + err |= __get_user(f->rate, &((struct floppy_struct32 *)arg)->rate); + err |= __get_user(f->spec1, &((struct floppy_struct32 *)arg)->spec1); + err |= __get_user(f->fmt_gap, &((struct floppy_struct32 *)arg)->fmt_gap); + err |= __get_user((u64)f->name, &((struct floppy_struct32 *)arg)->name); + if (err) { + err = -EFAULT; + goto out; + } + break; + } + case FDSETDRVPRM32: + case FDGETDRVPRM32: + { + struct floppy_drive_params *f; + + f = karg = kmalloc(sizeof(struct floppy_drive_params), GFP_KERNEL); + if (!karg) + return -ENOMEM; + if (cmd == FDGETDRVPRM32) + break; + err = __get_user(f->cmos, &((struct floppy_drive_params32 *)arg)->cmos); + err |= __get_user(f->max_dtr, &((struct floppy_drive_params32 *)arg)->max_dtr); + err |= __get_user(f->hlt, &((struct floppy_drive_params32 *)arg)->hlt); + err |= __get_user(f->hut, &((struct floppy_drive_params32 *)arg)->hut); + err |= __get_user(f->srt, &((struct floppy_drive_params32 *)arg)->srt); + err |= __get_user(f->spinup, &((struct floppy_drive_params32 *)arg)->spinup); + err |= __get_user(f->spindown, &((struct floppy_drive_params32 *)arg)->spindown); + err |= __get_user(f->spindown_offset, &((struct floppy_drive_params32 *)arg)->spindown_offset); + err |= __get_user(f->select_delay, &((struct floppy_drive_params32 *)arg)->select_delay); + err |= __get_user(f->rps, &((struct floppy_drive_params32 *)arg)->rps); + err |= __get_user(f->tracks, &((struct floppy_drive_params32 *)arg)->tracks); + err |= __get_user(f->timeout, &((struct floppy_drive_params32 *)arg)->timeout); + err |= __get_user(f->interleave_sect, &((struct floppy_drive_params32 *)arg)->interleave_sect); + err |= __copy_from_user(&f->max_errors, &((struct floppy_drive_params32 *)arg)->max_errors, sizeof(f->max_errors)); + err |= __get_user(f->flags, &((struct floppy_drive_params32 *)arg)->flags); + err |= __get_user(f->read_track, &((struct floppy_drive_params32 *)arg)->read_track); + err |= __copy_from_user(f->autodetect, ((struct floppy_drive_params32 *)arg)->autodetect, sizeof(f->autodetect)); + err |= __get_user(f->checkfreq, &((struct floppy_drive_params32 *)arg)->checkfreq); + err |= __get_user(f->native_format, &((struct floppy_drive_params32 *)arg)->native_format); + if (err) { + err = -EFAULT; + goto out; + } + break; + } + case FDGETDRVSTAT32: + case FDPOLLDRVSTAT32: + karg = kmalloc(sizeof(struct floppy_drive_struct), GFP_KERNEL); + if (!karg) + return -ENOMEM; + break; + case FDGETFDCSTAT32: + karg = kmalloc(sizeof(struct floppy_fdc_state), GFP_KERNEL); + if (!karg) + return -ENOMEM; + break; + case FDWERRORGET32: + karg = kmalloc(sizeof(struct floppy_write_errors), GFP_KERNEL); + if (!karg) + return -ENOMEM; + break; + default: + return -EINVAL; + } + set_fs (KERNEL_DS); + err = sys_ioctl (fd, kcmd, (unsigned long)karg); + set_fs (old_fs); + if (err) + goto out; + switch (cmd) { + case FDGETPRM32: + { + struct floppy_struct *f = karg; + + err = __put_user(f->size, &((struct floppy_struct32 *)arg)->size); + err |= __put_user(f->sect, &((struct floppy_struct32 *)arg)->sect); + err |= __put_user(f->head, &((struct floppy_struct32 *)arg)->head); + err |= __put_user(f->track, &((struct floppy_struct32 *)arg)->track); + err |= __put_user(f->stretch, &((struct floppy_struct32 *)arg)->stretch); + err |= __put_user(f->gap, &((struct floppy_struct32 *)arg)->gap); + err |= __put_user(f->rate, &((struct floppy_struct32 *)arg)->rate); + err |= __put_user(f->spec1, &((struct floppy_struct32 *)arg)->spec1); + err |= __put_user(f->fmt_gap, &((struct floppy_struct32 *)arg)->fmt_gap); + err |= __put_user((u64)f->name, &((struct floppy_struct32 *)arg)->name); + break; + } + case FDGETDRVPRM32: + { + struct floppy_drive_params *f = karg; + + err = __put_user(f->cmos, &((struct floppy_drive_params32 *)arg)->cmos); + err |= __put_user(f->max_dtr, &((struct floppy_drive_params32 *)arg)->max_dtr); + err |= __put_user(f->hlt, &((struct floppy_drive_params32 *)arg)->hlt); + err |= __put_user(f->hut, &((struct floppy_drive_params32 *)arg)->hut); + err |= __put_user(f->srt, &((struct floppy_drive_params32 *)arg)->srt); + err |= __put_user(f->spinup, &((struct floppy_drive_params32 *)arg)->spinup); + err |= __put_user(f->spindown, &((struct floppy_drive_params32 *)arg)->spindown); + err |= __put_user(f->spindown_offset, &((struct floppy_drive_params32 *)arg)->spindown_offset); + err |= __put_user(f->select_delay, &((struct floppy_drive_params32 *)arg)->select_delay); + err |= __put_user(f->rps, &((struct floppy_drive_params32 *)arg)->rps); + err |= __put_user(f->tracks, &((struct floppy_drive_params32 *)arg)->tracks); + err |= __put_user(f->timeout, &((struct floppy_drive_params32 *)arg)->timeout); + err |= __put_user(f->interleave_sect, &((struct floppy_drive_params32 *)arg)->interleave_sect); + err |= __copy_to_user(&((struct floppy_drive_params32 *)arg)->max_errors, &f->max_errors, sizeof(f->max_errors)); + err |= __put_user(f->flags, &((struct floppy_drive_params32 *)arg)->flags); + err |= __put_user(f->read_track, &((struct floppy_drive_params32 *)arg)->read_track); + err |= __copy_to_user(((struct floppy_drive_params32 *)arg)->autodetect, f->autodetect, sizeof(f->autodetect)); + err |= __put_user(f->checkfreq, &((struct floppy_drive_params32 *)arg)->checkfreq); + err |= __put_user(f->native_format, &((struct floppy_drive_params32 *)arg)->native_format); + break; + } + case FDGETDRVSTAT32: + case FDPOLLDRVSTAT32: + { + struct floppy_drive_struct *f = karg; + + err = __put_user(f->flags, &((struct floppy_drive_struct32 *)arg)->flags); + err |= __put_user(f->spinup_date, &((struct floppy_drive_struct32 *)arg)->spinup_date); + err |= __put_user(f->select_date, &((struct floppy_drive_struct32 *)arg)->select_date); + err |= __put_user(f->first_read_date, &((struct floppy_drive_struct32 *)arg)->first_read_date); + err |= __put_user(f->probed_format, &((struct floppy_drive_struct32 *)arg)->probed_format); + err |= __put_user(f->track, &((struct floppy_drive_struct32 *)arg)->track); + err |= __put_user(f->maxblock, &((struct floppy_drive_struct32 *)arg)->maxblock); + err |= __put_user(f->maxtrack, &((struct floppy_drive_struct32 *)arg)->maxtrack); + err |= __put_user(f->generation, &((struct floppy_drive_struct32 *)arg)->generation); + err |= __put_user(f->keep_data, &((struct floppy_drive_struct32 *)arg)->keep_data); + err |= __put_user(f->fd_ref, &((struct floppy_drive_struct32 *)arg)->fd_ref); + err |= __put_user(f->fd_device, &((struct floppy_drive_struct32 *)arg)->fd_device); + err |= __put_user(f->last_checked, &((struct floppy_drive_struct32 *)arg)->last_checked); + err |= __put_user((u64)f->dmabuf, &((struct floppy_drive_struct32 *)arg)->dmabuf); + err |= __put_user((u64)f->bufblocks, &((struct floppy_drive_struct32 *)arg)->bufblocks); + break; + } + case FDGETFDCSTAT32: + { + struct floppy_fdc_state *f = karg; + + err = __put_user(f->spec1, &((struct floppy_fdc_state32 *)arg)->spec1); + err |= __put_user(f->spec2, &((struct floppy_fdc_state32 *)arg)->spec2); + err |= __put_user(f->dtr, &((struct floppy_fdc_state32 *)arg)->dtr); + err |= __put_user(f->version, &((struct floppy_fdc_state32 *)arg)->version); + err |= __put_user(f->dor, &((struct floppy_fdc_state32 *)arg)->dor); + err |= __put_user(f->address, &((struct floppy_fdc_state32 *)arg)->address); + err |= __copy_to_user((char *)&((struct floppy_fdc_state32 *)arg)->address + + sizeof(((struct floppy_fdc_state32 *)arg)->address), + (char *)&f->address + sizeof(f->address), sizeof(int)); + err |= __put_user(f->driver_version, &((struct floppy_fdc_state32 *)arg)->driver_version); + err |= __copy_to_user(((struct floppy_fdc_state32 *)arg)->track, f->track, sizeof(f->track)); + break; + } + case FDWERRORGET32: + { + struct floppy_write_errors *f = karg; + + err = __put_user(f->write_errors, &((struct floppy_write_errors32 *)arg)->write_errors); + err |= __put_user(f->first_error_sector, &((struct floppy_write_errors32 *)arg)->first_error_sector); + err |= __put_user(f->first_error_generation, &((struct floppy_write_errors32 *)arg)->first_error_generation); + err |= __put_user(f->last_error_sector, &((struct floppy_write_errors32 *)arg)->last_error_sector); + err |= __put_user(f->last_error_generation, &((struct floppy_write_errors32 *)arg)->last_error_generation); + err |= __put_user(f->badness, &((struct floppy_write_errors32 *)arg)->badness); + break; + } + default: + break; + } + if (err) + err = -EFAULT; + +out: if (karg) kfree(karg); + return err; +} + +struct ppp_option_data32 { + __kernel_caddr_t32 ptr; + __u32 length; + int transmit; +}; +#define PPPIOCSCOMPRESS32 _IOW('t', 77, struct ppp_option_data32) + +struct ppp_idle32 { + __kernel_time_t32 xmit_idle; + __kernel_time_t32 recv_idle; +}; +#define PPPIOCGIDLE32 _IOR('t', 63, struct ppp_idle32) + +static int ppp_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct ppp_option_data32 data32; + struct ppp_option_data data; + struct ppp_idle32 idle32; + struct ppp_idle idle; + unsigned int kcmd; + void *karg; + int err = 0; + + switch (cmd) { + case PPPIOCGIDLE32: + kcmd = PPPIOCGIDLE; + karg = &idle; + break; + case PPPIOCSCOMPRESS32: + if (copy_from_user(&data32, (struct ppp_option_data32 *)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)) { + kfree(data.ptr); + return -EFAULT; + } + data.length = data32.length; + data.transmit = data32.transmit; + kcmd = PPPIOCSCOMPRESS; + karg = &data; + break; + default: + do { + static int count = 0; + if (++count <= 20) + printk("ppp_ioctl: Unknown cmd fd(%d) " + "cmd(%08x) arg(%08x)\n", + (int)fd, (unsigned int)cmd, (unsigned int)arg); + } while(0); + return -EINVAL; + } + set_fs (KERNEL_DS); + err = sys_ioctl (fd, kcmd, (unsigned long)karg); + set_fs (old_fs); + switch (cmd) { + case PPPIOCGIDLE32: + if (err) + return err; + idle32.xmit_idle = idle.xmit_idle; + idle32.recv_idle = idle.recv_idle; + if (copy_to_user((struct ppp_idle32 *)arg, &idle32, sizeof(struct ppp_idle32))) + return -EFAULT; + break; + case PPPIOCSCOMPRESS32: + kfree(data.ptr); + break; + default: + break; + } + return err; +} + + +struct mtget32 { + __u32 mt_type; + __u32 mt_resid; + __u32 mt_dsreg; + __u32 mt_gstat; + __u32 mt_erreg; + __kernel_daddr_t32 mt_fileno; + __kernel_daddr_t32 mt_blkno; +}; +#define MTIOCGET32 _IOR('m', 2, struct mtget32) + +struct mtpos32 { + __u32 mt_blkno; +}; +#define MTIOCPOS32 _IOR('m', 3, struct mtpos32) + +struct mtconfiginfo32 { + __u32 mt_type; + __u32 ifc_type; + __u16 irqnr; + __u16 dmanr; + __u16 port; + __u32 debug; + __u32 have_dens:1; + __u32 have_bsf:1; + __u32 have_fsr:1; + __u32 have_bsr:1; + __u32 have_eod:1; + __u32 have_seek:1; + __u32 have_tell:1; + __u32 have_ras1:1; + __u32 have_ras2:1; + __u32 have_ras3:1; + __u32 have_qfa:1; + __u32 pad1:5; + char reserved[10]; +}; +#define MTIOCGETCONFIG32 _IOR('m', 4, struct mtconfiginfo32) +#define MTIOCSETCONFIG32 _IOW('m', 5, struct mtconfiginfo32) + +static int mt_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct mtconfiginfo info; + struct mtget get; + struct mtpos pos; + unsigned long kcmd; + void *karg; + int err = 0; + + switch(cmd) { + case MTIOCPOS32: + kcmd = MTIOCPOS; + karg = &pos; + break; + case MTIOCGET32: + kcmd = MTIOCGET; + karg = &get; + break; + case MTIOCGETCONFIG32: + kcmd = MTIOCGETCONFIG; + karg = &info; + break; + case MTIOCSETCONFIG32: + kcmd = MTIOCSETCONFIG; + karg = &info; + err = __get_user(info.mt_type, &((struct mtconfiginfo32 *)arg)->mt_type); + err |= __get_user(info.ifc_type, &((struct mtconfiginfo32 *)arg)->ifc_type); + err |= __get_user(info.irqnr, &((struct mtconfiginfo32 *)arg)->irqnr); + err |= __get_user(info.dmanr, &((struct mtconfiginfo32 *)arg)->dmanr); + err |= __get_user(info.port, &((struct mtconfiginfo32 *)arg)->port); + err |= __get_user(info.debug, &((struct mtconfiginfo32 *)arg)->debug); + err |= __copy_from_user((char *)&info.debug + sizeof(info.debug), + (char *)&((struct mtconfiginfo32 *)arg)->debug + + sizeof(((struct mtconfiginfo32 *)arg)->debug), sizeof(__u32)); + if (err) + return -EFAULT; + break; + default: + do { + static int count = 0; + if (++count <= 20) + printk("mt_ioctl: Unknown cmd fd(%d) " + "cmd(%08x) arg(%08x)\n", + (int)fd, (unsigned int)cmd, (unsigned int)arg); + } while(0); + return -EINVAL; + } + set_fs (KERNEL_DS); + err = sys_ioctl (fd, kcmd, (unsigned long)karg); + set_fs (old_fs); + if (err) + return err; + switch (cmd) { + case MTIOCPOS32: + err = __put_user(pos.mt_blkno, &((struct mtpos32 *)arg)->mt_blkno); + break; + case MTIOCGET32: + err = __put_user(get.mt_type, &((struct mtget32 *)arg)->mt_type); + err |= __put_user(get.mt_resid, &((struct mtget32 *)arg)->mt_resid); + err |= __put_user(get.mt_dsreg, &((struct mtget32 *)arg)->mt_dsreg); + err |= __put_user(get.mt_gstat, &((struct mtget32 *)arg)->mt_gstat); + err |= __put_user(get.mt_erreg, &((struct mtget32 *)arg)->mt_erreg); + err |= __put_user(get.mt_fileno, &((struct mtget32 *)arg)->mt_fileno); + err |= __put_user(get.mt_blkno, &((struct mtget32 *)arg)->mt_blkno); + break; + case MTIOCGETCONFIG32: + err = __put_user(info.mt_type, &((struct mtconfiginfo32 *)arg)->mt_type); + err |= __put_user(info.ifc_type, &((struct mtconfiginfo32 *)arg)->ifc_type); + err |= __put_user(info.irqnr, &((struct mtconfiginfo32 *)arg)->irqnr); + err |= __put_user(info.dmanr, &((struct mtconfiginfo32 *)arg)->dmanr); + err |= __put_user(info.port, &((struct mtconfiginfo32 *)arg)->port); + err |= __put_user(info.debug, &((struct mtconfiginfo32 *)arg)->debug); + err |= __copy_to_user((char *)&((struct mtconfiginfo32 *)arg)->debug + + sizeof(((struct mtconfiginfo32 *)arg)->debug), + (char *)&info.debug + sizeof(info.debug), sizeof(__u32)); + break; + case MTIOCSETCONFIG32: + break; + } + return err ? -EFAULT: 0; +} + +struct cdrom_read32 { + int cdread_lba; + __kernel_caddr_t32 cdread_bufaddr; + int cdread_buflen; +}; + +struct cdrom_read_audio32 { + union cdrom_addr addr; + u_char addr_format; + int nframes; + __kernel_caddr_t32 buf; +}; + +struct cdrom_generic_command32 { + unsigned char cmd[CDROM_PACKET_SIZE]; + __kernel_caddr_t32 buffer; + unsigned int buflen; + int stat; + __kernel_caddr_t32 sense; + __kernel_caddr_t32 reserved[3]; +}; + +static int cdrom_ioctl_trans(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct cdrom_read cdread; + struct cdrom_read_audio cdreadaudio; + struct cdrom_generic_command cgc; + __kernel_caddr_t32 addr; + char *data = 0; + void *karg; + int err = 0; + + switch(cmd) { + case CDROMREADMODE2: + case CDROMREADMODE1: + case CDROMREADRAW: + case CDROMREADCOOKED: + karg = &cdread; + err = __get_user(cdread.cdread_lba, &((struct cdrom_read32 *)arg)->cdread_lba); + err |= __get_user(addr, &((struct cdrom_read32 *)arg)->cdread_bufaddr); + err |= __get_user(cdread.cdread_buflen, &((struct cdrom_read32 *)arg)->cdread_buflen); + if (err) + return -EFAULT; + data = kmalloc(cdread.cdread_buflen, GFP_KERNEL); + if (!data) + return -ENOMEM; + cdread.cdread_bufaddr = data; + break; + case CDROMREADAUDIO: + karg = &cdreadaudio; + err = copy_from_user(&cdreadaudio.addr, &((struct cdrom_read_audio32 *)arg)->addr, sizeof(cdreadaudio.addr)); + err |= __get_user(cdreadaudio.addr_format, &((struct cdrom_read_audio32 *)arg)->addr_format); + err |= __get_user(cdreadaudio.nframes, &((struct cdrom_read_audio32 *)arg)->nframes); + err |= __get_user(addr, &((struct cdrom_read_audio32 *)arg)->buf); + if (err) + return -EFAULT; + data = kmalloc(cdreadaudio.nframes * 2352, GFP_KERNEL); + if (!data) + return -ENOMEM; + cdreadaudio.buf = data; + break; + case CDROM_SEND_PACKET: + karg = &cgc; + err = copy_from_user(cgc.cmd, &((struct cdrom_generic_command32 *)arg)->cmd, sizeof(cgc.cmd)); + err |= __get_user(addr, &((struct cdrom_generic_command32 *)arg)->buffer); + err |= __get_user(cgc.buflen, &((struct cdrom_generic_command32 *)arg)->buflen); + if (err) + return -EFAULT; + if ((data = kmalloc(cgc.buflen, GFP_KERNEL)) == NULL) + return -ENOMEM; + cgc.buffer = data; + break; + default: + do { + static int count = 0; + if (++count <= 20) + printk("cdrom_ioctl: Unknown cmd fd(%d) " + "cmd(%08x) arg(%08x)\n", + (int)fd, (unsigned int)cmd, (unsigned int)arg); + } while(0); + return -EINVAL; + } + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)karg); + set_fs (old_fs); + if (err) + goto out; + switch (cmd) { + case CDROMREADMODE2: + case CDROMREADMODE1: + case CDROMREADRAW: + case CDROMREADCOOKED: + err = copy_to_user((char *)A(addr), data, cdread.cdread_buflen); + break; + case CDROMREADAUDIO: + err = copy_to_user((char *)A(addr), data, cdreadaudio.nframes * 2352); + break; + case CDROM_SEND_PACKET: + err = copy_to_user((char *)A(addr), data, cgc.buflen); + break; + default: + break; + } +out: if (data) + kfree(data); + return err ? -EFAULT : 0; +} + +struct loop_info32 { + int lo_number; /* ioctl r/o */ + __kernel_dev_t32 lo_device; /* ioctl r/o */ + unsigned int lo_inode; /* ioctl r/o */ + __kernel_dev_t32 lo_rdevice; /* ioctl r/o */ + int lo_offset; + int lo_encrypt_type; + int lo_encrypt_key_size; /* ioctl w/o */ + int lo_flags; /* ioctl r/o */ + char lo_name[LO_NAME_SIZE]; + unsigned char lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */ + unsigned int lo_init[2]; + char reserved[4]; +}; + +static int loop_status(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct loop_info l; + int err = -EINVAL; + + switch(cmd) { + case LOOP_SET_STATUS: + err = get_user(l.lo_number, &((struct loop_info32 *)arg)->lo_number); + err |= __get_user(l.lo_device, &((struct loop_info32 *)arg)->lo_device); + err |= __get_user(l.lo_inode, &((struct loop_info32 *)arg)->lo_inode); + err |= __get_user(l.lo_rdevice, &((struct loop_info32 *)arg)->lo_rdevice); + err |= __copy_from_user((char *)&l.lo_offset, (char *)&((struct loop_info32 *)arg)->lo_offset, + 8 + (unsigned long)l.lo_init - (unsigned long)&l.lo_offset); + if (err) { + err = -EFAULT; + } else { + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&l); + set_fs (old_fs); + } + break; + case LOOP_GET_STATUS: + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&l); + set_fs (old_fs); + if (!err) { + err = put_user(l.lo_number, &((struct loop_info32 *)arg)->lo_number); + err |= __put_user(l.lo_device, &((struct loop_info32 *)arg)->lo_device); + err |= __put_user(l.lo_inode, &((struct loop_info32 *)arg)->lo_inode); + err |= __put_user(l.lo_rdevice, &((struct loop_info32 *)arg)->lo_rdevice); + err |= __copy_to_user((char *)&((struct loop_info32 *)arg)->lo_offset, + (char *)&l.lo_offset, (unsigned long)l.lo_init - (unsigned long)&l.lo_offset); + if (err) + err = -EFAULT; + } + break; + default: { + static int count = 0; + if (++count <= 20) + printk("%s: Unknown loop ioctl cmd, fd(%d) " + "cmd(%08x) arg(%08lx)\n", + __FUNCTION__, fd, cmd, arg); + } + } + return err; +} + +extern int tty_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg); + +#ifdef CONFIG_VT +static int vt_check(struct file *file) +{ + struct tty_struct *tty; + struct inode *inode = file->f_dentry->d_inode; + + if (file->f_op->ioctl != tty_ioctl) + return -EINVAL; + + tty = (struct tty_struct *)file->private_data; + if (tty_paranoia_check(tty, inode->i_rdev, "tty_ioctl")) + return -EINVAL; + + if (tty->driver.ioctl != vt_ioctl) + return -EINVAL; + + /* + * To have permissions to do most of the vt ioctls, we either have + * to be the owner of the tty, or super-user. + */ + if (current->tty == tty || suser()) + return 1; + return 0; +} + +struct consolefontdesc32 { + unsigned short charcount; /* characters in font (256 or 512) */ + unsigned short charheight; /* scan lines per character (1-32) */ + u32 chardata; /* font data in expanded form */ +}; + +static int do_fontx_ioctl(unsigned int fd, int cmd, struct consolefontdesc32 *user_cfd, struct file *file) +{ + struct consolefontdesc cfdarg; + struct console_font_op op; + int i, perm; + + perm = vt_check(file); + if (perm < 0) return perm; + + if (copy_from_user(&cfdarg, user_cfd, sizeof(struct consolefontdesc32))) + return -EFAULT; + + cfdarg.chardata = (unsigned char *)A(((struct consolefontdesc32 *)&cfdarg)->chardata); + + switch (cmd) { + case PIO_FONTX: + if (!perm) + return -EPERM; + op.op = KD_FONT_OP_SET; + op.flags = 0; + op.width = 8; + op.height = cfdarg.charheight; + op.charcount = cfdarg.charcount; + op.data = cfdarg.chardata; + return con_font_op(fg_console, &op); + case GIO_FONTX: + if (!cfdarg.chardata) + return 0; + op.op = KD_FONT_OP_GET; + op.flags = 0; + op.width = 8; + op.height = cfdarg.charheight; + op.charcount = cfdarg.charcount; + op.data = cfdarg.chardata; + i = con_font_op(fg_console, &op); + if (i) + return i; + cfdarg.charheight = op.height; + cfdarg.charcount = op.charcount; + ((struct consolefontdesc32 *)&cfdarg)->chardata = (unsigned long)cfdarg.chardata; + if (copy_to_user(user_cfd, &cfdarg, sizeof(struct consolefontdesc32))) + return -EFAULT; + return 0; + } + return -EINVAL; +} + +struct console_font_op32 { + unsigned int op; /* operation code KD_FONT_OP_* */ + unsigned int flags; /* KD_FONT_FLAG_* */ + unsigned int width, height; /* font size */ + unsigned int charcount; + u32 data; /* font data with height fixed to 32 */ +}; + +static int do_kdfontop_ioctl(unsigned int fd, unsigned int cmd, struct console_font_op32 *fontop, struct file *file) +{ + struct console_font_op op; + int perm = vt_check(file), i; + struct vt_struct *vt; + + if (perm < 0) return perm; + + if (copy_from_user(&op, (void *) fontop, sizeof(struct console_font_op32))) + return -EFAULT; + if (!perm && op.op != KD_FONT_OP_GET) + return -EPERM; + op.data = (unsigned char *)A(((struct console_font_op32 *)&op)->data); + op.flags |= KD_FONT_FLAG_OLD; + vt = (struct vt_struct *)((struct tty_struct *)file->private_data)->driver_data; + i = con_font_op(vt->vc_num, &op); + if (i) return i; + ((struct console_font_op32 *)&op)->data = (unsigned long)op.data; + if (copy_to_user((void *) fontop, &op, sizeof(struct console_font_op32))) + return -EFAULT; + return 0; +} + +struct fb_fix_screeninfo32 { + char id[16]; /* identification string eg "TT Builtin" */ + unsigned int smem_start; /* Start of frame buffer mem */ + /* (physical address) */ + __u32 smem_len; /* Length of frame buffer mem */ + __u32 type; /* see FB_TYPE_* */ + __u32 type_aux; /* Interleave for interleaved Planes */ + __u32 visual; /* see FB_VISUAL_* */ + __u16 xpanstep; /* zero if no hardware panning */ + __u16 ypanstep; /* zero if no hardware panning */ + __u16 ywrapstep; /* zero if no hardware ywrap */ + __u32 line_length; /* length of a line in bytes */ + unsigned int mmio_start; /* Start of Memory Mapped I/O */ + /* (physical address) */ + __u32 mmio_len; /* Length of Memory Mapped I/O */ + __u32 accel; /* Type of acceleration available */ + __u16 reserved[3]; /* Reserved for future compatibility */ +}; + +static int do_fbioget_fscreeninfo_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct fb_fix_screeninfo fix; + int err; + + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (long)&fix); + set_fs(old_fs); + + if(err == 0) { + unsigned int smem_start = fix.smem_start; /* lose top 32 bits */ + unsigned int mmio_start = fix.mmio_start; /* lose top 32 bits */ + int i; + + err = put_user(fix.id[0], &((struct fb_fix_screeninfo32 *)arg)->id[0]); + for (i=1; i<16; i++) { + err |= __put_user(fix.id[i], &((struct fb_fix_screeninfo32 *)arg)->id[i]); + } + err |= __put_user(smem_start, &((struct fb_fix_screeninfo32 *)arg)->smem_start); + err |= __put_user(fix.smem_len, &((struct fb_fix_screeninfo32 *)arg)->smem_len); + err |= __put_user(fix.type, &((struct fb_fix_screeninfo32 *)arg)->type); + err |= __put_user(fix.type_aux, &((struct fb_fix_screeninfo32 *)arg)->type_aux); + err |= __put_user(fix.visual, &((struct fb_fix_screeninfo32 *)arg)->visual); + err |= __put_user(fix.xpanstep, &((struct fb_fix_screeninfo32 *)arg)->xpanstep); + err |= __put_user(fix.ypanstep, &((struct fb_fix_screeninfo32 *)arg)->ypanstep); + err |= __put_user(fix.ywrapstep, &((struct fb_fix_screeninfo32 *)arg)->ywrapstep); + err |= __put_user(fix.line_length, &((struct fb_fix_screeninfo32 *)arg)->line_length); + err |= __put_user(mmio_start, &((struct fb_fix_screeninfo32 *)arg)->mmio_start); + err |= __put_user(fix.mmio_len, &((struct fb_fix_screeninfo32 *)arg)->mmio_len); + err |= __put_user(fix.accel, &((struct fb_fix_screeninfo32 *)arg)->accel); + err |= __put_user(fix.reserved[0], &((struct fb_fix_screeninfo32 *)arg)->reserved[0]); + err |= __put_user(fix.reserved[1], &((struct fb_fix_screeninfo32 *)arg)->reserved[1]); + err |= __put_user(fix.reserved[2], &((struct fb_fix_screeninfo32 *)arg)->reserved[2]); + if (err) + err = -EFAULT; + } + return err; +} + +struct fb_cmap32 { + __u32 start; /* First entry */ + __u32 len; /* Number of entries */ + __u32 redptr; /* Red values */ + __u32 greenptr; + __u32 blueptr; + __u32 transpptr; /* transparency, can be NULL */ +}; + +static int do_fbiogetcmap_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct fb_cmap cmap; + int err; + + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (long)&cmap); + set_fs(old_fs); + + if(err == 0) { + __u32 redptr = (__u32)cmap.red; + __u32 greenptr = (__u32)cmap.green; + __u32 blueptr = (__u32)cmap.blue; + __u32 transpptr = (__u32)cmap.transp; + + err = put_user(cmap.start, &((struct fb_cmap32 *)arg)->start); + err |= __put_user(cmap.len, &((struct fb_cmap32 *)arg)->len); + err |= __put_user(redptr, &((struct fb_cmap32 *)arg)->redptr); + err |= __put_user(greenptr, &((struct fb_cmap32 *)arg)->greenptr); + err |= __put_user(blueptr, &((struct fb_cmap32 *)arg)->blueptr); + err |= __put_user(transpptr, &((struct fb_cmap32 *)arg)->transpptr); + if (err) + err = -EFAULT; + } + return err; +} + +static int do_fbioputcmap_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + struct fb_cmap cmap; + __u32 redptr, greenptr, blueptr, transpptr; + int err; + + err = get_user(cmap.start, &((struct fb_cmap32 *)arg)->start); + err |= __get_user(cmap.len, &((struct fb_cmap32 *)arg)->len); + err |= __get_user(redptr, &((struct fb_cmap32 *)arg)->redptr); + err |= __get_user(greenptr, &((struct fb_cmap32 *)arg)->greenptr); + err |= __get_user(blueptr, &((struct fb_cmap32 *)arg)->blueptr); + err |= __get_user(transpptr, &((struct fb_cmap32 *)arg)->transpptr); + + if (err) { + err = -EFAULT; + } else { + cmap.red = (__u16 *)redptr; + cmap.green = (__u16 *)greenptr; + cmap.blue = (__u16 *)blueptr; + cmap.transp = (__u16 *)transpptr; + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&cmap); + set_fs (old_fs); + } + return err; +} + +struct unimapdesc32 { + unsigned short entry_ct; + u32 entries; +}; + +static int do_unimap_ioctl(unsigned int fd, unsigned int cmd, struct unimapdesc32 *user_ud, struct file *file) +{ + struct unimapdesc32 tmp; + int perm = vt_check(file); + + if (perm < 0) return perm; + if (copy_from_user(&tmp, user_ud, sizeof tmp)) + return -EFAULT; + switch (cmd) { + case PIO_UNIMAP: + if (!perm) return -EPERM; + return con_set_unimap(fg_console, tmp.entry_ct, (struct unipair *)A(tmp.entries)); + case GIO_UNIMAP: + return con_get_unimap(fg_console, tmp.entry_ct, &(user_ud->entry_ct), (struct unipair *)A(tmp.entries)); + } + return 0; +} +#endif /* CONFIG_VT */ +static int do_smb_getmountuid(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + __kernel_uid_t kuid; + int err; + + cmd = SMB_IOC_GETMOUNTUID; + + set_fs(KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&kuid); + set_fs(old_fs); + + if (err >= 0) + err = put_user(kuid, (__kernel_uid_t32 *)arg); + + return err; +} + +struct atmif_sioc32 { + int number; + int length; + __kernel_caddr_t32 arg; +}; + +struct atm_iobuf32 { + int length; + __kernel_caddr_t32 buffer; +}; + +#define ATM_GETLINKRATE32 _IOW('a', ATMIOC_ITF+1, struct atmif_sioc32) +#define ATM_GETNAMES32 _IOW('a', ATMIOC_ITF+3, struct atm_iobuf32) +#define ATM_GETTYPE32 _IOW('a', ATMIOC_ITF+4, struct atmif_sioc32) +#define ATM_GETESI32 _IOW('a', ATMIOC_ITF+5, struct atmif_sioc32) +#define ATM_GETADDR32 _IOW('a', ATMIOC_ITF+6, struct atmif_sioc32) +#define ATM_RSTADDR32 _IOW('a', ATMIOC_ITF+7, struct atmif_sioc32) +#define ATM_ADDADDR32 _IOW('a', ATMIOC_ITF+8, struct atmif_sioc32) +#define ATM_DELADDR32 _IOW('a', ATMIOC_ITF+9, struct atmif_sioc32) +#define ATM_GETCIRANGE32 _IOW('a', ATMIOC_ITF+10, struct atmif_sioc32) +#define ATM_SETCIRANGE32 _IOW('a', ATMIOC_ITF+11, struct atmif_sioc32) +#define ATM_SETESI32 _IOW('a', ATMIOC_ITF+12, struct atmif_sioc32) +#define ATM_SETESIF32 _IOW('a', ATMIOC_ITF+13, struct atmif_sioc32) +#define ATM_GETSTAT32 _IOW('a', ATMIOC_SARCOM+0, struct atmif_sioc32) +#define ATM_GETSTATZ32 _IOW('a', ATMIOC_SARCOM+1, struct atmif_sioc32) +#define ATM_GETLOOP32 _IOW('a', ATMIOC_SARCOM+2, struct atmif_sioc32) +#define ATM_SETLOOP32 _IOW('a', ATMIOC_SARCOM+3, struct atmif_sioc32) +#define ATM_QUERYLOOP32 _IOW('a', ATMIOC_SARCOM+4, struct atmif_sioc32) + +static struct { + unsigned int cmd32; + unsigned int cmd; +} atm_ioctl_map[] = { + { ATM_GETLINKRATE32, ATM_GETLINKRATE }, + { ATM_GETNAMES32, ATM_GETNAMES }, + { ATM_GETTYPE32, ATM_GETTYPE }, + { ATM_GETESI32, ATM_GETESI }, + { ATM_GETADDR32, ATM_GETADDR }, + { ATM_RSTADDR32, ATM_RSTADDR }, + { ATM_ADDADDR32, ATM_ADDADDR }, + { ATM_DELADDR32, ATM_DELADDR }, + { ATM_GETCIRANGE32, ATM_GETCIRANGE }, + { ATM_SETCIRANGE32, ATM_SETCIRANGE }, + { ATM_SETESI32, ATM_SETESI }, + { ATM_SETESIF32, ATM_SETESIF }, + { ATM_GETSTAT32, ATM_GETSTAT }, + { ATM_GETSTATZ32, ATM_GETSTATZ }, + { ATM_GETLOOP32, ATM_GETLOOP }, + { ATM_SETLOOP32, ATM_SETLOOP }, + { ATM_QUERYLOOP32, ATM_QUERYLOOP } +}; + +#define NR_ATM_IOCTL (sizeof(atm_ioctl_map)/sizeof(atm_ioctl_map[0])) + + +static int do_atm_iobuf(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct atm_iobuf32 iobuf32; + struct atm_iobuf iobuf = { 0, NULL }; + mm_segment_t old_fs; + int err; + + err = copy_from_user(&iobuf32, (struct atm_iobuf32*)arg, + sizeof(struct atm_iobuf32)); + if (err) + return -EFAULT; + + iobuf.length = iobuf32.length; + + if (iobuf32.buffer == (__kernel_caddr_t32) NULL || iobuf32.length == 0) { + iobuf.buffer = (void*)(unsigned long)iobuf32.buffer; + } else { + iobuf.buffer = kmalloc(iobuf.length, GFP_KERNEL); + if (iobuf.buffer == NULL) { + err = -ENOMEM; + goto out; + } + + err = copy_from_user(iobuf.buffer, A(iobuf32.buffer), iobuf.length); + if (err) { + err = -EFAULT; + goto out; + } + } + + old_fs = get_fs(); set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&iobuf); + set_fs (old_fs); + if(err) + goto out; + + if(iobuf.buffer && iobuf.length > 0) { + err = copy_to_user(A(iobuf32.buffer), iobuf.buffer, iobuf.length); + if (err) { + err = -EFAULT; + goto out; + } + } + err = __put_user(iobuf.length, &(((struct atm_iobuf32*)arg)->length)); + + out: + if(iobuf32.buffer && iobuf32.length > 0) + kfree(iobuf.buffer); + + return err; +} + + +static int do_atmif_sioc(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct atmif_sioc32 sioc32; + struct atmif_sioc sioc = { 0, 0, NULL }; + mm_segment_t old_fs; + int err; + + err = copy_from_user(&sioc32, (struct atmif_sioc32*)arg, + sizeof(struct atmif_sioc32)); + if (err) + return -EFAULT; + + sioc.number = sioc32.number; + sioc.length = sioc32.length; + + if (sioc32.arg == (__kernel_caddr_t32) NULL || sioc32.length == 0) { + sioc.arg = (void*)(unsigned long)sioc32.arg; + } else { + sioc.arg = kmalloc(sioc.length, GFP_KERNEL); + if (sioc.arg == NULL) { + err = -ENOMEM; + goto out; + } + + err = copy_from_user(sioc.arg, A(sioc32.arg), sioc32.length); + if (err) { + err = -EFAULT; + goto out; + } + } + + old_fs = get_fs(); set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&sioc); + set_fs (old_fs); + if(err) { + goto out; + } + + if(sioc.arg && sioc.length > 0) { + err = copy_to_user(A(sioc32.arg), sioc.arg, sioc.length); + if (err) { + err = -EFAULT; + goto out; + } + } + err = __put_user(sioc.length, &(((struct atmif_sioc32*)arg)->length)); + + out: + if(sioc32.arg && sioc32.length > 0) + kfree(sioc.arg); + + return err; +} + + +static int do_atm_ioctl(unsigned int fd, unsigned int cmd32, unsigned long arg) +{ + int i; + unsigned int cmd = 0; + + switch (cmd32) { + case SONET_GETSTAT: + case SONET_GETSTATZ: + case SONET_GETDIAG: + case SONET_SETDIAG: + case SONET_CLRDIAG: + case SONET_SETFRAMING: + case SONET_GETFRAMING: + case SONET_GETFRSENSE: + return do_atmif_sioc(fd, cmd32, arg); + } + + for (i = 0; i < NR_ATM_IOCTL; i++) { + if (cmd32 == atm_ioctl_map[i].cmd32) { + cmd = atm_ioctl_map[i].cmd; + break; + } + } + if (i == NR_ATM_IOCTL) { + return -EINVAL; + } + + switch (cmd) { + case ATM_GETNAMES: + return do_atm_iobuf(fd, cmd, arg); + + case ATM_GETLINKRATE: + case ATM_GETTYPE: + case ATM_GETESI: + case ATM_GETADDR: + case ATM_RSTADDR: + case ATM_ADDADDR: + case ATM_DELADDR: + case ATM_GETCIRANGE: + case ATM_SETCIRANGE: + case ATM_SETESI: + case ATM_SETESIF: + case ATM_GETSTAT: + case ATM_GETSTATZ: + case ATM_GETLOOP: + case ATM_SETLOOP: + case ATM_QUERYLOOP: + return do_atmif_sioc(fd, cmd, arg); + } + + return -EINVAL; +} + +#if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE) +/* Ugh, LVM. Pitty it was not cleaned up before accepted :((. */ +typedef struct { + uint8_t vg_name[NAME_LEN]; + uint32_t vg_number; + uint32_t vg_access; + uint32_t vg_status; + uint32_t lv_max; + uint32_t lv_cur; + uint32_t lv_open; + uint32_t pv_max; + uint32_t pv_cur; + uint32_t pv_act; + uint32_t dummy; + uint32_t vgda; + uint32_t pe_size; + uint32_t pe_total; + uint32_t pe_allocated; + uint32_t pvg_total; + u32 proc; + u32 pv[ABS_MAX_PV + 1]; + u32 lv[ABS_MAX_LV + 1]; + uint8_t vg_uuid[UUID_LEN+1]; /* volume group UUID */ +} vg32_t; + +typedef struct { + uint8_t id[2]; + uint16_t version; + lvm_disk_data_t pv_on_disk; + lvm_disk_data_t vg_on_disk; + lvm_disk_data_t pv_namelist_on_disk; + lvm_disk_data_t lv_on_disk; + lvm_disk_data_t pe_on_disk; + uint8_t pv_name[NAME_LEN]; + uint8_t vg_name[NAME_LEN]; + uint8_t system_id[NAME_LEN]; + kdev_t pv_dev; + uint32_t pv_number; + uint32_t pv_status; + uint32_t pv_allocatable; + uint32_t pv_size; + uint32_t lv_cur; + uint32_t pe_size; + uint32_t pe_total; + uint32_t pe_allocated; + uint32_t pe_stale; + u32 pe; + u32 inode; + uint8_t pv_uuid[UUID_LEN+1]; +} pv32_t; + +typedef struct { + char lv_name[NAME_LEN]; + u32 lv; +} lv_req32_t; + +typedef struct { + u32 lv_index; + u32 lv; + /* Transfer size because user space and kernel space differ */ + uint16_t size; +} lv_status_byindex_req32_t; + +typedef struct { + dev_t dev; + u32 lv; +} lv_status_bydev_req32_t; + +typedef struct { + uint8_t lv_name[NAME_LEN]; + kdev_t old_dev; + kdev_t new_dev; + u32 old_pe; + u32 new_pe; +} le_remap_req32_t; + +typedef struct { + char pv_name[NAME_LEN]; + u32 pv; +} pv_status_req32_t; + +typedef struct { + uint8_t lv_name[NAME_LEN]; + uint8_t vg_name[NAME_LEN]; + uint32_t lv_access; + uint32_t lv_status; + uint32_t lv_open; + kdev_t lv_dev; + uint32_t lv_number; + uint32_t lv_mirror_copies; + uint32_t lv_recovery; + uint32_t lv_schedule; + uint32_t lv_size; + u32 lv_current_pe; + uint32_t lv_current_le; + uint32_t lv_allocated_le; + uint32_t lv_stripes; + uint32_t lv_stripesize; + uint32_t lv_badblock; + uint32_t lv_allocation; + uint32_t lv_io_timeout; + uint32_t lv_read_ahead; + /* delta to version 1 starts here */ + u32 lv_snapshot_org; + u32 lv_snapshot_prev; + u32 lv_snapshot_next; + u32 lv_block_exception; + uint32_t lv_remap_ptr; + uint32_t lv_remap_end; + uint32_t lv_chunk_size; + uint32_t lv_snapshot_minor; + char dummy[200]; +} lv32_t; + +typedef struct { + u32 hash[2]; + u32 rsector_org; + kdev_t rdev_org; + u32 rsector_new; + kdev_t rdev_new; +} lv_block_exception32_t; + +static void put_lv_t(lv_t *l) +{ + if (l->lv_current_pe) vfree(l->lv_current_pe); + if (l->lv_block_exception) vfree(l->lv_block_exception); + kfree(l); +} + +static lv_t *get_lv_t(u32 p, int *errp) +{ + int err, i; + u32 ptr1, ptr2; + size_t size; + lv_block_exception32_t *lbe32; + lv_block_exception_t *lbe; + lv32_t *ul = (lv32_t *)A(p); + lv_t *l = (lv_t *)kmalloc(sizeof(lv_t), GFP_KERNEL); + if (!l) { + *errp = -ENOMEM; + return NULL; + } + memset(l, 0, sizeof(lv_t)); + err = copy_from_user(l, ul, (long)&((lv32_t *)0)->lv_current_pe); + err |= __copy_from_user(&l->lv_current_le, &ul->lv_current_le, + ((long)&ul->lv_snapshot_org) - ((long)&ul->lv_current_le)); + err |= __copy_from_user(&l->lv_remap_ptr, &ul->lv_remap_ptr, + ((long)&ul->dummy[0]) - ((long)&ul->lv_remap_ptr)); + err |= __get_user(ptr1, &ul->lv_current_pe); + err |= __get_user(ptr2, &ul->lv_block_exception); + if (err) { + kfree(l); + *errp = -EFAULT; + return NULL; + } + if (ptr1) { + size = l->lv_allocated_le * sizeof(pe_t); + l->lv_current_pe = vmalloc(size); + if (l->lv_current_pe) + err = copy_from_user(l->lv_current_pe, (void *)A(ptr1), size); + } + if (!err && ptr2) { + size = l->lv_remap_end * sizeof(lv_block_exception_t); + l->lv_block_exception = lbe = vmalloc(size); + if (l->lv_block_exception) { + lbe32 = (lv_block_exception32_t *)A(ptr2); + memset(lbe, 0, size); + for (i = 0; i < l->lv_remap_end; i++, lbe++, lbe32++) { + err |= get_user(lbe->rsector_org, &lbe32->rsector_org); + err |= __get_user(lbe->rdev_org, &lbe32->rdev_org); + err |= __get_user(lbe->rsector_new, &lbe32->rsector_new); + err |= __get_user(lbe->rdev_new, &lbe32->rdev_new); + + } + } + } + if (err || (ptr1 && !l->lv_current_pe) || (ptr2 && !l->lv_block_exception)) { + if (!err) + *errp = -ENOMEM; + else + *errp = -EFAULT; + put_lv_t(l); + return NULL; + } + return l; +} + +static int copy_lv_t(u32 ptr, lv_t *l) +{ + int err; + lv32_t *ul = (lv32_t *)A(ptr); + u32 ptr1; + size_t size; + + err = get_user(ptr1, &ul->lv_current_pe); + if (err) + return -EFAULT; + err = copy_to_user(ul, l, (long)&((lv32_t *)0)->lv_current_pe); + err |= __copy_to_user(&ul->lv_current_le, &l->lv_current_le, + ((long)&ul->lv_snapshot_org) - ((long)&ul->lv_current_le)); + err |= __copy_to_user(&ul->lv_remap_ptr, &l->lv_remap_ptr, + ((long)&ul->dummy[0]) - ((long)&ul->lv_remap_ptr)); + size = l->lv_allocated_le * sizeof(pe_t); + if (ptr1) + err |= __copy_to_user((void *)A(ptr1), l->lv_current_pe, size); + return err ? -EFAULT : 0; +} + +static int do_lvm_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + vg_t *v; + union { + lv_req_t lv_req; + le_remap_req_t le_remap; + lv_status_byindex_req_t lv_byindex; + lv_status_bydev_req_t lv_bydev; + pv_status_req_t pv_status; + } u; + pv_t p; + int err; + u32 ptr = 0; + int i; + mm_segment_t old_fs; + void *karg = &u; + + switch (cmd) { + case VG_STATUS: + v = kmalloc(sizeof(vg_t), GFP_KERNEL); + if (!v) return -ENOMEM; + karg = v; + break; + case VG_CREATE: + v = kmalloc(sizeof(vg_t), GFP_KERNEL); + if (!v) return -ENOMEM; + if (copy_from_user(v, (void *)arg, (long)&((vg32_t *)0)->proc) || + __get_user(v->proc, &((vg32_t *)arg)->proc)) { + kfree(v); + return -EFAULT; + } + if (copy_from_user(v->vg_uuid, ((vg32_t *)arg)->vg_uuid, UUID_LEN+1)) { + kfree(v); + return -EFAULT; + } + + karg = v; + memset(v->pv, 0, sizeof(v->pv) + sizeof(v->lv)); + if (v->pv_max > ABS_MAX_PV || v->lv_max > ABS_MAX_LV) + return -EPERM; + for (i = 0; i < v->pv_max; i++) { + err = __get_user(ptr, &((vg32_t *)arg)->pv[i]); + if (err) break; + if (ptr) { + v->pv[i] = kmalloc(sizeof(pv_t), GFP_KERNEL); + if (!v->pv[i]) { + err = -ENOMEM; + break; + } + err = copy_from_user(v->pv[i], (void *)A(ptr), sizeof(pv32_t) - 8 - UUID_LEN+1); + if (err) { + err = -EFAULT; + break; + } + err = copy_from_user(v->pv[i]->pv_uuid, ((pv32_t *)A(ptr))->pv_uuid, UUID_LEN+1); + if (err) { + err = -EFAULT; + break; + } + + + v->pv[i]->pe = NULL; v->pv[i]->inode = NULL; + } + } + if (!err) { + for (i = 0; i < v->lv_max; i++) { + err = __get_user(ptr, &((vg32_t *)arg)->lv[i]); + if (err) break; + if (ptr) { + v->lv[i] = get_lv_t(ptr, &err); + if (err) break; + } + } + } + break; + case LV_CREATE: + case LV_EXTEND: + case LV_REDUCE: + case LV_REMOVE: + case LV_RENAME: + case LV_STATUS_BYNAME: + err = copy_from_user(&u.pv_status, arg, sizeof(u.pv_status.pv_name)); + if (err) return -EFAULT; + if (cmd != LV_REMOVE) { + err = __get_user(ptr, &((lv_req32_t *)arg)->lv); + if (err) return err; + u.lv_req.lv = get_lv_t(ptr, &err); + } else + u.lv_req.lv = NULL; + break; + + + case LV_STATUS_BYINDEX: + err = get_user(u.lv_byindex.lv_index, &((lv_status_byindex_req32_t *)arg)->lv_index); + err |= __get_user(ptr, &((lv_status_byindex_req32_t *)arg)->lv); + if (err) return err; + u.lv_byindex.lv = get_lv_t(ptr, &err); + break; + case LV_STATUS_BYDEV: + err = get_user(u.lv_bydev.dev, &((lv_status_bydev_req32_t *)arg)->dev); + u.lv_bydev.lv = get_lv_t(ptr, &err); + if (err) return err; + u.lv_bydev.lv = &p; + p.pe = NULL; p.inode = NULL; + break; + case VG_EXTEND: + err = copy_from_user(&p, (void *)arg, sizeof(pv32_t) - 8 - UUID_LEN+1); + if (err) return -EFAULT; + err = copy_from_user(p.pv_uuid, ((pv32_t *)arg)->pv_uuid, UUID_LEN+1); + if (err) return -EFAULT; + p.pe = NULL; p.inode = NULL; + karg = &p; + break; + case PV_CHANGE: + case PV_STATUS: + err = copy_from_user(&u.pv_status, arg, sizeof(u.lv_req.lv_name)); + if (err) return -EFAULT; + err = __get_user(ptr, &((pv_status_req32_t *)arg)->pv); + if (err) return err; + u.pv_status.pv = &p; + if (cmd == PV_CHANGE) { + err = copy_from_user(&p, (void *)A(ptr), sizeof(pv32_t) - 8 - UUID_LEN+1); + if (err) return -EFAULT; + p.pe = NULL; p.inode = NULL; + } + break; + } + old_fs = get_fs(); set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)karg); + set_fs (old_fs); + switch (cmd) { + case VG_STATUS: + if (!err) { + if (copy_to_user((void *)arg, v, (long)&((vg32_t *)0)->proc) || + clear_user(&((vg32_t *)arg)->proc, sizeof(vg32_t) - (long)&((vg32_t *)0)->proc)) + err = -EFAULT; + } + if (copy_to_user(((vg32_t *)arg)->vg_uuid, v->vg_uuid, UUID_LEN+1)) { + err = -EFAULT; + } + kfree(v); + break; + case VG_CREATE: + for (i = 0; i < v->pv_max; i++) + if (v->pv[i]) kfree(v->pv[i]); + for (i = 0; i < v->lv_max; i++) + if (v->lv[i]) put_lv_t(v->lv[i]); + kfree(v); + break; + case LV_STATUS_BYNAME: + if (!err && u.lv_req.lv) err = copy_lv_t(ptr, u.lv_req.lv); + /* Fall through */ + case LV_CREATE: + case LV_EXTEND: + case LV_REDUCE: + if (u.lv_req.lv) put_lv_t(u.lv_req.lv); + break; + case LV_STATUS_BYINDEX: + if (u.lv_byindex.lv) { + if (!err) err = copy_lv_t(ptr, u.lv_byindex.lv); + put_lv_t(u.lv_byindex.lv); + } + break; + case PV_STATUS: + if (!err) { + err = copy_to_user((void *)A(ptr), &p, sizeof(pv32_t) - 8 - UUID_LEN+1); + if (err) return -EFAULT; + err = copy_to_user(((pv_t *)A(ptr))->pv_uuid, p.pv_uuid, UUID_LEN + 1); + if (err) return -EFAULT; + } + break; + case LV_STATUS_BYDEV: + if (!err) { + if (!err) err = copy_lv_t(ptr, u.lv_bydev.lv); + put_lv_t(u.lv_byindex.lv); + } + break; + } + return err; +} +#endif + +#if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE) +/* This really belongs in include/linux/drm.h -DaveM */ +#include "../../../drivers/char/drm/drm.h" + +typedef struct drm32_version { + int version_major; /* Major version */ + int version_minor; /* Minor version */ + int version_patchlevel;/* Patch level */ + int name_len; /* Length of name buffer */ + u32 name; /* Name of driver */ + int date_len; /* Length of date buffer */ + u32 date; /* User-space buffer to hold date */ + int desc_len; /* Length of desc buffer */ + u32 desc; /* User-space buffer to hold desc */ +} drm32_version_t; +#define DRM32_IOCTL_VERSION DRM_IOWR(0x00, drm32_version_t) + +static int drm32_version(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_version_t *uversion = (drm32_version_t *)arg; + char *name_ptr, *date_ptr, *desc_ptr; + u32 tmp1, tmp2, tmp3; + drm_version_t kversion; + mm_segment_t old_fs; + int ret; + + memset(&kversion, 0, sizeof(kversion)); + if (get_user(kversion.name_len, &uversion->name_len) || + get_user(kversion.date_len, &uversion->date_len) || + get_user(kversion.desc_len, &uversion->desc_len) || + get_user(tmp1, &uversion->name) || + get_user(tmp2, &uversion->date) || + get_user(tmp3, &uversion->desc)) + return -EFAULT; + + name_ptr = (char *) A(tmp1); + date_ptr = (char *) A(tmp2); + desc_ptr = (char *) A(tmp3); + + ret = -ENOMEM; + if (kversion.name_len && name_ptr) { + kversion.name = kmalloc(kversion.name_len, GFP_KERNEL); + if (!kversion.name) + goto out; + } + if (kversion.date_len && date_ptr) { + kversion.date = kmalloc(kversion.date_len, GFP_KERNEL); + if (!kversion.date) + goto out; + } + if (kversion.desc_len && desc_ptr) { + kversion.desc = kmalloc(kversion.desc_len, GFP_KERNEL); + if (!kversion.desc) + goto out; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl (fd, DRM_IOCTL_VERSION, (unsigned long)&kversion); + set_fs(old_fs); + + if (!ret) { + if ((kversion.name && + copy_to_user(name_ptr, kversion.name, kversion.name_len)) || + (kversion.date && + copy_to_user(date_ptr, kversion.date, kversion.date_len)) || + (kversion.desc && + copy_to_user(desc_ptr, kversion.desc, kversion.desc_len))) + ret = -EFAULT; + if (put_user(kversion.version_major, &uversion->version_major) || + put_user(kversion.version_minor, &uversion->version_minor) || + put_user(kversion.version_patchlevel, &uversion->version_patchlevel) || + put_user(kversion.name_len, &uversion->name_len) || + put_user(kversion.date_len, &uversion->date_len) || + put_user(kversion.desc_len, &uversion->desc_len)) + ret = -EFAULT; + } + +out: + if (kversion.name) + kfree(kversion.name); + if (kversion.date) + kfree(kversion.date); + if (kversion.desc) + kfree(kversion.desc); + return ret; +} + +typedef struct drm32_unique { + int unique_len; /* Length of unique */ + u32 unique; /* Unique name for driver instantiation */ +} drm32_unique_t; +#define DRM32_IOCTL_GET_UNIQUE DRM_IOWR(0x01, drm32_unique_t) +#define DRM32_IOCTL_SET_UNIQUE DRM_IOW( 0x10, drm32_unique_t) + +static int drm32_getsetunique(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_unique_t *uarg = (drm32_unique_t *)arg; + drm_unique_t karg; + mm_segment_t old_fs; + char *uptr; + u32 tmp; + int ret; + + if (get_user(karg.unique_len, &uarg->unique_len)) + return -EFAULT; + karg.unique = NULL; + + if (get_user(tmp, &uarg->unique)) + return -EFAULT; + + uptr = (char *) A(tmp); + + if (uptr) { + karg.unique = kmalloc(karg.unique_len, GFP_KERNEL); + if (!karg.unique) + return -ENOMEM; + if (cmd == DRM32_IOCTL_SET_UNIQUE && + copy_from_user(karg.unique, uptr, karg.unique_len)) { + kfree(karg.unique); + return -EFAULT; + } + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + if (cmd == DRM32_IOCTL_GET_UNIQUE) + ret = sys_ioctl (fd, DRM_IOCTL_GET_UNIQUE, (unsigned long)&karg); + else + ret = sys_ioctl (fd, DRM_IOCTL_SET_UNIQUE, (unsigned long)&karg); + set_fs(old_fs); + + if (!ret) { + if (cmd == DRM32_IOCTL_GET_UNIQUE && + uptr != NULL && + copy_to_user(uptr, karg.unique, karg.unique_len)) + ret = -EFAULT; + if (put_user(karg.unique_len, &uarg->unique_len)) + ret = -EFAULT; + } + + if (karg.unique != NULL) + kfree(karg.unique); + + return ret; +} + +typedef struct drm32_map { + u32 offset; /* Requested physical address (0 for SAREA)*/ + u32 size; /* Requested physical size (bytes) */ + drm_map_type_t type; /* Type of memory to map */ + drm_map_flags_t flags; /* Flags */ + u32 handle; /* User-space: "Handle" to pass to mmap */ + /* Kernel-space: kernel-virtual address */ + int mtrr; /* MTRR slot used */ + /* Private data */ +} drm32_map_t; +#define DRM32_IOCTL_ADD_MAP DRM_IOWR(0x15, drm32_map_t) + +static int drm32_addmap(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_map_t *uarg = (drm32_map_t *) arg; + drm_map_t karg; + mm_segment_t old_fs; + u32 tmp; + int ret; + + ret = get_user(karg.offset, &uarg->offset); + ret |= get_user(karg.size, &uarg->size); + ret |= get_user(karg.type, &uarg->type); + ret |= get_user(karg.flags, &uarg->flags); + ret |= get_user(tmp, &uarg->handle); + ret |= get_user(karg.mtrr, &uarg->mtrr); + if (ret) + return -EFAULT; + + karg.handle = (void *) A(tmp); + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_ADD_MAP, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + ret = put_user(karg.offset, &uarg->offset); + ret |= put_user(karg.size, &uarg->size); + ret |= put_user(karg.type, &uarg->type); + ret |= put_user(karg.flags, &uarg->flags); + tmp = (u32) (long)karg.handle; + ret |= put_user(tmp, &uarg->handle); + ret |= put_user(karg.mtrr, &uarg->mtrr); + if (ret) + ret = -EFAULT; + } + + return ret; +} + +typedef struct drm32_buf_info { + int count; /* Entries in list */ + u32 list; /* (drm_buf_desc_t *) */ +} drm32_buf_info_t; +#define DRM32_IOCTL_INFO_BUFS DRM_IOWR(0x18, drm32_buf_info_t) + +static int drm32_info_bufs(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_buf_info_t *uarg = (drm32_buf_info_t *)arg; + drm_buf_desc_t *ulist; + drm_buf_info_t karg; + mm_segment_t old_fs; + int orig_count, ret; + u32 tmp; + + if (get_user(karg.count, &uarg->count) || + get_user(tmp, &uarg->list)) + return -EFAULT; + + ulist = (drm_buf_desc_t *) A(tmp); + + orig_count = karg.count; + + karg.list = kmalloc(karg.count * sizeof(drm_buf_desc_t), GFP_KERNEL); + if (!karg.list) + return -EFAULT; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_INFO_BUFS, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + if (karg.count <= orig_count && + (copy_to_user(ulist, karg.list, + karg.count * sizeof(drm_buf_desc_t)))) + ret = -EFAULT; + if (put_user(karg.count, &uarg->count)) + ret = -EFAULT; + } + + kfree(karg.list); + + return ret; +} + +typedef struct drm32_buf_free { + int count; + u32 list; /* (int *) */ +} drm32_buf_free_t; +#define DRM32_IOCTL_FREE_BUFS DRM_IOW( 0x1a, drm32_buf_free_t) + +static int drm32_free_bufs(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_buf_free_t *uarg = (drm32_buf_free_t *)arg; + drm_buf_free_t karg; + mm_segment_t old_fs; + int *ulist; + int ret; + u32 tmp; + + if (get_user(karg.count, &uarg->count) || + get_user(tmp, &uarg->list)) + return -EFAULT; + + ulist = (int *) A(tmp); + + karg.list = kmalloc(karg.count * sizeof(int), GFP_KERNEL); + if (!karg.list) + return -ENOMEM; + + ret = -EFAULT; + if (copy_from_user(karg.list, ulist, (karg.count * sizeof(int)))) + goto out; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_FREE_BUFS, (unsigned long) &karg); + set_fs(old_fs); + +out: + kfree(karg.list); + + return ret; +} + +typedef struct drm32_buf_pub { + int idx; /* Index into master buflist */ + int total; /* Buffer size */ + int used; /* Amount of buffer in use (for DMA) */ + u32 address; /* Address of buffer (void *) */ +} drm32_buf_pub_t; + +typedef struct drm32_buf_map { + int count; /* Length of buflist */ + u32 virtual; /* Mmaped area in user-virtual (void *) */ + u32 list; /* Buffer information (drm_buf_pub_t *) */ +} drm32_buf_map_t; +#define DRM32_IOCTL_MAP_BUFS DRM_IOWR(0x19, drm32_buf_map_t) + +static int drm32_map_bufs(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_buf_map_t *uarg = (drm32_buf_map_t *)arg; + drm32_buf_pub_t *ulist; + drm_buf_map_t karg; + mm_segment_t old_fs; + int orig_count, ret, i; + u32 tmp1, tmp2; + + if (get_user(karg.count, &uarg->count) || + get_user(tmp1, &uarg->virtual) || + get_user(tmp2, &uarg->list)) + return -EFAULT; + + karg.virtual = (void *) A(tmp1); + ulist = (drm32_buf_pub_t *) A(tmp2); + + orig_count = karg.count; + + karg.list = kmalloc(karg.count * sizeof(drm_buf_pub_t), GFP_KERNEL); + if (!karg.list) + return -ENOMEM; + + ret = -EFAULT; + for (i = 0; i < karg.count; i++) { + if (get_user(karg.list[i].idx, &ulist[i].idx) || + get_user(karg.list[i].total, &ulist[i].total) || + get_user(karg.list[i].used, &ulist[i].used) || + get_user(tmp1, &ulist[i].address)) + goto out; + + karg.list[i].address = (void *) A(tmp1); + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_MAP_BUFS, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + for (i = 0; i < orig_count; i++) { + tmp1 = (u32) (long) karg.list[i].address; + if (put_user(karg.list[i].idx, &ulist[i].idx) || + put_user(karg.list[i].total, &ulist[i].total) || + put_user(karg.list[i].used, &ulist[i].used) || + put_user(tmp1, &ulist[i].address)) { + ret = -EFAULT; + goto out; + } + } + if (put_user(karg.count, &uarg->count)) + ret = -EFAULT; + } + +out: + kfree(karg.list); + return ret; +} + +typedef struct drm32_dma { + /* Indices here refer to the offset into + buflist in drm_buf_get_t. */ + int context; /* Context handle */ + int send_count; /* Number of buffers to send */ + u32 send_indices; /* List of handles to buffers (int *) */ + u32 send_sizes; /* Lengths of data to send (int *) */ + drm_dma_flags_t flags; /* Flags */ + int request_count; /* Number of buffers requested */ + int request_size; /* Desired size for buffers */ + u32 request_indices; /* Buffer information (int *) */ + u32 request_sizes; /* (int *) */ + int granted_count; /* Number of buffers granted */ +} drm32_dma_t; +#define DRM32_IOCTL_DMA DRM_IOWR(0x29, drm32_dma_t) + +/* RED PEN The DRM layer blindly dereferences the send/request + * indice/size arrays even though they are userland + * pointers. -DaveM + */ +static int drm32_dma(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_dma_t *uarg = (drm32_dma_t *) arg; + int *u_si, *u_ss, *u_ri, *u_rs; + drm_dma_t karg; + mm_segment_t old_fs; + int ret; + u32 tmp1, tmp2, tmp3, tmp4; + + karg.send_indices = karg.send_sizes = NULL; + karg.request_indices = karg.request_sizes = NULL; + + if (get_user(karg.context, &uarg->context) || + get_user(karg.send_count, &uarg->send_count) || + get_user(tmp1, &uarg->send_indices) || + get_user(tmp2, &uarg->send_sizes) || + get_user(karg.flags, &uarg->flags) || + get_user(karg.request_count, &uarg->request_count) || + get_user(karg.request_size, &uarg->request_size) || + get_user(tmp3, &uarg->request_indices) || + get_user(tmp4, &uarg->request_sizes) || + get_user(karg.granted_count, &uarg->granted_count)) + return -EFAULT; + + u_si = (int *) A(tmp1); + u_ss = (int *) A(tmp2); + u_ri = (int *) A(tmp3); + u_rs = (int *) A(tmp4); + + if (karg.send_count) { + karg.send_indices = kmalloc(karg.send_count * sizeof(int), GFP_KERNEL); + karg.send_sizes = kmalloc(karg.send_count * sizeof(int), GFP_KERNEL); + + ret = -ENOMEM; + if (!karg.send_indices || !karg.send_sizes) + goto out; + + ret = -EFAULT; + if (copy_from_user(karg.send_indices, u_si, + (karg.send_count * sizeof(int))) || + copy_from_user(karg.send_sizes, u_ss, + (karg.send_count * sizeof(int)))) + goto out; + } + + if (karg.request_count) { + karg.request_indices = kmalloc(karg.request_count * sizeof(int), GFP_KERNEL); + karg.request_sizes = kmalloc(karg.request_count * sizeof(int), GFP_KERNEL); + + ret = -ENOMEM; + if (!karg.request_indices || !karg.request_sizes) + goto out; + + ret = -EFAULT; + if (copy_from_user(karg.request_indices, u_ri, + (karg.request_count * sizeof(int))) || + copy_from_user(karg.request_sizes, u_rs, + (karg.request_count * sizeof(int)))) + goto out; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_DMA, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + if (put_user(karg.context, &uarg->context) || + put_user(karg.send_count, &uarg->send_count) || + put_user(karg.flags, &uarg->flags) || + put_user(karg.request_count, &uarg->request_count) || + put_user(karg.request_size, &uarg->request_size) || + put_user(karg.granted_count, &uarg->granted_count)) + ret = -EFAULT; + + if (karg.send_count) { + if (copy_to_user(u_si, karg.send_indices, + (karg.send_count * sizeof(int))) || + copy_to_user(u_ss, karg.send_sizes, + (karg.send_count * sizeof(int)))) + ret = -EFAULT; + } + if (karg.request_count) { + if (copy_to_user(u_ri, karg.request_indices, + (karg.request_count * sizeof(int))) || + copy_to_user(u_rs, karg.request_sizes, + (karg.request_count * sizeof(int)))) + ret = -EFAULT; + } + } + +out: + if (karg.send_indices) + kfree(karg.send_indices); + if (karg.send_sizes) + kfree(karg.send_sizes); + if (karg.request_indices) + kfree(karg.request_indices); + if (karg.request_sizes) + kfree(karg.request_sizes); + + return ret; +} + +typedef struct drm32_ctx_res { + int count; + u32 contexts; /* (drm_ctx_t *) */ +} drm32_ctx_res_t; +#define DRM32_IOCTL_RES_CTX DRM_IOWR(0x26, drm32_ctx_res_t) + +static int drm32_res_ctx(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + drm32_ctx_res_t *uarg = (drm32_ctx_res_t *) arg; + drm_ctx_t *ulist; + drm_ctx_res_t karg; + mm_segment_t old_fs; + int orig_count, ret; + u32 tmp; + + karg.contexts = NULL; + if (get_user(karg.count, &uarg->count) || + get_user(tmp, &uarg->contexts)) + return -EFAULT; + + ulist = (drm_ctx_t *) A(tmp); + + orig_count = karg.count; + if (karg.count && ulist) { + karg.contexts = kmalloc((karg.count * sizeof(drm_ctx_t)), GFP_KERNEL); + if (!karg.contexts) + return -ENOMEM; + if (copy_from_user(karg.contexts, ulist, + (karg.count * sizeof(drm_ctx_t)))) { + kfree(karg.contexts); + return -EFAULT; + } + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_ioctl(fd, DRM_IOCTL_RES_CTX, (unsigned long) &karg); + set_fs(old_fs); + + if (!ret) { + if (orig_count) { + if (copy_to_user(ulist, karg.contexts, + (orig_count * sizeof(drm_ctx_t)))) + ret = -EFAULT; + } + if (put_user(karg.count, &uarg->count)) + ret = -EFAULT; + } + + if (karg.contexts) + kfree(karg.contexts); + + return ret; +} + +#endif + +static int ret_einval(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + return -EINVAL; +} + +static int broken_blkgetsize(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + /* The mkswap binary hard codes it to Intel value :-((( */ + return w_long(fd, BLKGETSIZE, arg); +} + +struct blkpg_ioctl_arg32 { + int op; + int flags; + int datalen; + u32 data; +}; + +static int blkpg_ioctl_trans(unsigned int fd, unsigned int cmd, struct blkpg_ioctl_arg32 *arg) +{ + struct blkpg_ioctl_arg a; + struct blkpg_partition p; + int err; + mm_segment_t old_fs = get_fs(); + + err = get_user(a.op, &arg->op); + err |= __get_user(a.flags, &arg->flags); + err |= __get_user(a.datalen, &arg->datalen); + err |= __get_user((long)a.data, &arg->data); + if (err) return err; + switch (a.op) { + case BLKPG_ADD_PARTITION: + case BLKPG_DEL_PARTITION: + if (a.datalen < sizeof(struct blkpg_partition)) + return -EINVAL; + if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition))) + return -EFAULT; + a.data = &p; + set_fs (KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&a); + set_fs (old_fs); + default: + return -EINVAL; + } + return err; +} + +static int ioc_settimeout(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + return rw_long(fd, AUTOFS_IOC_SETTIMEOUT, arg); +} + +struct ioctl_trans { + unsigned long cmd; + unsigned long handler; + unsigned long next; +}; + +#define COMPATIBLE_IOCTL(cmd) { cmd, (unsigned long)sys_ioctl, 0 } + +#define HANDLE_IOCTL(cmd,handler) { cmd, (unsigned long)handler, 0 } + +#define AUTOFS_IOC_SETTIMEOUT32 _IOWR(0x93,0x64,unsigned int) +#define SMB_IOC_GETMOUNTUID_32 _IOR('u', 1, __kernel_uid_t32) + +static struct ioctl_trans ioctl_translations[] = { + /* List here explicitly which ioctl's need translation, + * all others default to calling sys_ioctl(). + */ +/* Big T */ +COMPATIBLE_IOCTL(TCGETA), +COMPATIBLE_IOCTL(TCSETA), +COMPATIBLE_IOCTL(TCSETAW), +COMPATIBLE_IOCTL(TCSETAF), +COMPATIBLE_IOCTL(TCSBRK), +COMPATIBLE_IOCTL(TCXONC), +COMPATIBLE_IOCTL(TCFLSH), +COMPATIBLE_IOCTL(TCGETS), +COMPATIBLE_IOCTL(TCSETS), +COMPATIBLE_IOCTL(TCSETSW), +COMPATIBLE_IOCTL(TCSETSF), +COMPATIBLE_IOCTL(TIOCLINUX), +COMPATIBLE_IOCTL(TIOCSTART), +/* Little t */ +COMPATIBLE_IOCTL(TIOCGETD), +COMPATIBLE_IOCTL(TIOCSETD), +COMPATIBLE_IOCTL(TIOCEXCL), +COMPATIBLE_IOCTL(TIOCNXCL), +COMPATIBLE_IOCTL(TIOCCONS), +COMPATIBLE_IOCTL(TIOCGSOFTCAR), +COMPATIBLE_IOCTL(TIOCSSOFTCAR), +COMPATIBLE_IOCTL(TIOCSWINSZ), +COMPATIBLE_IOCTL(TIOCGWINSZ), +COMPATIBLE_IOCTL(TIOCMGET), +COMPATIBLE_IOCTL(TIOCMBIC), +COMPATIBLE_IOCTL(TIOCMBIS), +COMPATIBLE_IOCTL(TIOCMSET), +COMPATIBLE_IOCTL(TIOCPKT), +COMPATIBLE_IOCTL(TIOCNOTTY), +COMPATIBLE_IOCTL(TIOCSTI), +COMPATIBLE_IOCTL(TIOCOUTQ), +COMPATIBLE_IOCTL(TIOCSPGRP), +COMPATIBLE_IOCTL(TIOCGPGRP), +COMPATIBLE_IOCTL(TIOCSCTTY), +COMPATIBLE_IOCTL(TIOCGPTN), +COMPATIBLE_IOCTL(TIOCSPTLCK), +COMPATIBLE_IOCTL(TIOCGSERIAL), +COMPATIBLE_IOCTL(TIOCSSERIAL), +COMPATIBLE_IOCTL(TIOCSERGETLSR), +/* Big F */ +COMPATIBLE_IOCTL(FBIOGET_VSCREENINFO), +COMPATIBLE_IOCTL(FBIOPUT_VSCREENINFO), +COMPATIBLE_IOCTL(FBIOPAN_DISPLAY), +COMPATIBLE_IOCTL(FBIOGET_FCURSORINFO), +COMPATIBLE_IOCTL(FBIOGET_VCURSORINFO), +COMPATIBLE_IOCTL(FBIOPUT_VCURSORINFO), +COMPATIBLE_IOCTL(FBIOGET_CURSORSTATE), +COMPATIBLE_IOCTL(FBIOPUT_CURSORSTATE), +COMPATIBLE_IOCTL(FBIOGET_CON2FBMAP), +COMPATIBLE_IOCTL(FBIOPUT_CON2FBMAP), +#if 0 +COMPATIBLE_IOCTL(FBIOBLANK), +#endif +/* Little f */ +COMPATIBLE_IOCTL(FIOCLEX), +COMPATIBLE_IOCTL(FIONCLEX), +COMPATIBLE_IOCTL(FIOASYNC), +COMPATIBLE_IOCTL(FIONBIO), +COMPATIBLE_IOCTL(FIONREAD), /* This is also TIOCINQ */ +/* 0x00 */ +COMPATIBLE_IOCTL(FIBMAP), +COMPATIBLE_IOCTL(FIGETBSZ), +/* 0x03 -- HD/IDE ioctl's used by hdparm and friends. + * Some need translations, these do not. + */ +COMPATIBLE_IOCTL(HDIO_GET_IDENTITY), +COMPATIBLE_IOCTL(HDIO_SET_DMA), +COMPATIBLE_IOCTL(HDIO_SET_KEEPSETTINGS), +COMPATIBLE_IOCTL(HDIO_SET_UNMASKINTR), +COMPATIBLE_IOCTL(HDIO_SET_NOWERR), +COMPATIBLE_IOCTL(HDIO_SET_32BIT), +COMPATIBLE_IOCTL(HDIO_SET_MULTCOUNT), +COMPATIBLE_IOCTL(HDIO_DRIVE_CMD), +COMPATIBLE_IOCTL(HDIO_SET_PIO_MODE), +COMPATIBLE_IOCTL(HDIO_SCAN_HWIF), +COMPATIBLE_IOCTL(HDIO_SET_NICE), +/* 0x02 -- Floppy ioctls */ +COMPATIBLE_IOCTL(FDMSGON), +COMPATIBLE_IOCTL(FDMSGOFF), +COMPATIBLE_IOCTL(FDSETEMSGTRESH), +COMPATIBLE_IOCTL(FDFLUSH), +COMPATIBLE_IOCTL(FDWERRORCLR), +COMPATIBLE_IOCTL(FDSETMAXERRS), +COMPATIBLE_IOCTL(FDGETMAXERRS), +COMPATIBLE_IOCTL(FDGETDRVTYP), +COMPATIBLE_IOCTL(FDEJECT), +COMPATIBLE_IOCTL(FDCLRPRM), +COMPATIBLE_IOCTL(FDFMTBEG), +COMPATIBLE_IOCTL(FDFMTEND), +COMPATIBLE_IOCTL(FDRESET), +COMPATIBLE_IOCTL(FDTWADDLE), +COMPATIBLE_IOCTL(FDFMTTRK), +COMPATIBLE_IOCTL(FDRAWCMD), +/* 0x12 */ +COMPATIBLE_IOCTL(BLKROSET), +COMPATIBLE_IOCTL(BLKROGET), +COMPATIBLE_IOCTL(BLKRRPART), +COMPATIBLE_IOCTL(BLKFLSBUF), +COMPATIBLE_IOCTL(BLKRASET), +COMPATIBLE_IOCTL(BLKFRASET), +COMPATIBLE_IOCTL(BLKSECTSET), +COMPATIBLE_IOCTL(BLKSSZGET), + +/* RAID */ +COMPATIBLE_IOCTL(RAID_VERSION), +COMPATIBLE_IOCTL(GET_ARRAY_INFO), +COMPATIBLE_IOCTL(GET_DISK_INFO), +COMPATIBLE_IOCTL(PRINT_RAID_DEBUG), +COMPATIBLE_IOCTL(CLEAR_ARRAY), +COMPATIBLE_IOCTL(ADD_NEW_DISK), +COMPATIBLE_IOCTL(HOT_REMOVE_DISK), +COMPATIBLE_IOCTL(SET_ARRAY_INFO), +COMPATIBLE_IOCTL(SET_DISK_INFO), +COMPATIBLE_IOCTL(WRITE_RAID_INFO), +COMPATIBLE_IOCTL(UNPROTECT_ARRAY), +COMPATIBLE_IOCTL(PROTECT_ARRAY), +COMPATIBLE_IOCTL(HOT_ADD_DISK), +COMPATIBLE_IOCTL(SET_DISK_FAULTY), +COMPATIBLE_IOCTL(RUN_ARRAY), +COMPATIBLE_IOCTL(START_ARRAY), +COMPATIBLE_IOCTL(STOP_ARRAY), +COMPATIBLE_IOCTL(STOP_ARRAY_RO), +COMPATIBLE_IOCTL(RESTART_ARRAY_RW), +/* Big K */ +COMPATIBLE_IOCTL(PIO_FONT), +COMPATIBLE_IOCTL(GIO_FONT), +COMPATIBLE_IOCTL(KDSIGACCEPT), +COMPATIBLE_IOCTL(KDGETKEYCODE), +COMPATIBLE_IOCTL(KDSETKEYCODE), +COMPATIBLE_IOCTL(KIOCSOUND), +COMPATIBLE_IOCTL(KDMKTONE), +COMPATIBLE_IOCTL(KDGKBTYPE), +COMPATIBLE_IOCTL(KDSETMODE), +COMPATIBLE_IOCTL(KDGETMODE), +COMPATIBLE_IOCTL(KDSKBMODE), +COMPATIBLE_IOCTL(KDGKBMODE), +COMPATIBLE_IOCTL(KDSKBMETA), +COMPATIBLE_IOCTL(KDGKBMETA), +COMPATIBLE_IOCTL(KDGKBENT), +COMPATIBLE_IOCTL(KDSKBENT), +COMPATIBLE_IOCTL(KDGKBSENT), +COMPATIBLE_IOCTL(KDSKBSENT), +COMPATIBLE_IOCTL(KDGKBDIACR), +COMPATIBLE_IOCTL(KDSKBDIACR), +COMPATIBLE_IOCTL(KDGKBLED), +COMPATIBLE_IOCTL(KDSKBLED), +COMPATIBLE_IOCTL(KDGETLED), +COMPATIBLE_IOCTL(KDSETLED), +COMPATIBLE_IOCTL(GIO_SCRNMAP), +COMPATIBLE_IOCTL(PIO_SCRNMAP), +COMPATIBLE_IOCTL(GIO_UNISCRNMAP), +COMPATIBLE_IOCTL(PIO_UNISCRNMAP), +COMPATIBLE_IOCTL(PIO_FONTRESET), +COMPATIBLE_IOCTL(PIO_UNIMAPCLR), +/* Big S */ +COMPATIBLE_IOCTL(SCSI_IOCTL_GET_IDLUN), +COMPATIBLE_IOCTL(SCSI_IOCTL_DOORLOCK), +COMPATIBLE_IOCTL(SCSI_IOCTL_DOORUNLOCK), +COMPATIBLE_IOCTL(SCSI_IOCTL_TEST_UNIT_READY), +COMPATIBLE_IOCTL(SCSI_IOCTL_TAGGED_ENABLE), +COMPATIBLE_IOCTL(SCSI_IOCTL_TAGGED_DISABLE), +COMPATIBLE_IOCTL(SCSI_IOCTL_GET_BUS_NUMBER), +COMPATIBLE_IOCTL(SCSI_IOCTL_SEND_COMMAND), +/* Big V */ +COMPATIBLE_IOCTL(VT_SETMODE), +COMPATIBLE_IOCTL(VT_GETMODE), +COMPATIBLE_IOCTL(VT_GETSTATE), +COMPATIBLE_IOCTL(VT_OPENQRY), +COMPATIBLE_IOCTL(VT_ACTIVATE), +COMPATIBLE_IOCTL(VT_WAITACTIVE), +COMPATIBLE_IOCTL(VT_RELDISP), +COMPATIBLE_IOCTL(VT_DISALLOCATE), +COMPATIBLE_IOCTL(VT_RESIZE), +COMPATIBLE_IOCTL(VT_RESIZEX), +COMPATIBLE_IOCTL(VT_LOCKSWITCH), +COMPATIBLE_IOCTL(VT_UNLOCKSWITCH), +/* Little v, the video4linux ioctls */ +COMPATIBLE_IOCTL(VIDIOCGCAP), +COMPATIBLE_IOCTL(VIDIOCGCHAN), +COMPATIBLE_IOCTL(VIDIOCSCHAN), +COMPATIBLE_IOCTL(VIDIOCGPICT), +COMPATIBLE_IOCTL(VIDIOCSPICT), +COMPATIBLE_IOCTL(VIDIOCCAPTURE), +COMPATIBLE_IOCTL(VIDIOCKEY), +COMPATIBLE_IOCTL(VIDIOCGAUDIO), +COMPATIBLE_IOCTL(VIDIOCSAUDIO), +COMPATIBLE_IOCTL(VIDIOCSYNC), +COMPATIBLE_IOCTL(VIDIOCMCAPTURE), +COMPATIBLE_IOCTL(VIDIOCGMBUF), +COMPATIBLE_IOCTL(VIDIOCGUNIT), +COMPATIBLE_IOCTL(VIDIOCGCAPTURE), +COMPATIBLE_IOCTL(VIDIOCSCAPTURE), +/* BTTV specific... */ +COMPATIBLE_IOCTL(_IOW('v', BASE_VIDIOCPRIVATE+0, char [256])), +COMPATIBLE_IOCTL(_IOR('v', BASE_VIDIOCPRIVATE+1, char [256])), +COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+2, unsigned int)), +COMPATIBLE_IOCTL(_IOW('v' , BASE_VIDIOCPRIVATE+3, char [16])), /* struct bttv_pll_info */ +COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+4, int)), +COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+5, int)), +COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+6, int)), +COMPATIBLE_IOCTL(_IOR('v' , BASE_VIDIOCPRIVATE+7, int)), +/* Little p (/dev/rtc, /dev/envctrl, etc.) */ +COMPATIBLE_IOCTL(_IOR('p', 20, int[7])), /* RTCGET */ +COMPATIBLE_IOCTL(_IOW('p', 21, int[7])), /* RTCSET */ +COMPATIBLE_IOCTL(RTC_AIE_ON), +COMPATIBLE_IOCTL(RTC_AIE_OFF), +COMPATIBLE_IOCTL(RTC_UIE_ON), +COMPATIBLE_IOCTL(RTC_UIE_OFF), +COMPATIBLE_IOCTL(RTC_PIE_ON), +COMPATIBLE_IOCTL(RTC_PIE_OFF), +COMPATIBLE_IOCTL(RTC_WIE_ON), +COMPATIBLE_IOCTL(RTC_WIE_OFF), +COMPATIBLE_IOCTL(RTC_ALM_SET), +COMPATIBLE_IOCTL(RTC_ALM_READ), +COMPATIBLE_IOCTL(RTC_RD_TIME), +COMPATIBLE_IOCTL(RTC_SET_TIME), +COMPATIBLE_IOCTL(RTC_WKALM_SET), +COMPATIBLE_IOCTL(RTC_WKALM_RD), +/* Little m */ +COMPATIBLE_IOCTL(MTIOCTOP), +/* Socket level stuff */ +COMPATIBLE_IOCTL(FIOSETOWN), +COMPATIBLE_IOCTL(SIOCSPGRP), +COMPATIBLE_IOCTL(FIOGETOWN), +COMPATIBLE_IOCTL(SIOCGPGRP), +COMPATIBLE_IOCTL(SIOCATMARK), +COMPATIBLE_IOCTL(SIOCSIFLINK), +COMPATIBLE_IOCTL(SIOCSIFENCAP), +COMPATIBLE_IOCTL(SIOCGIFENCAP), +COMPATIBLE_IOCTL(SIOCSIFBR), +COMPATIBLE_IOCTL(SIOCGIFBR), +COMPATIBLE_IOCTL(SIOCSARP), +COMPATIBLE_IOCTL(SIOCGARP), +COMPATIBLE_IOCTL(SIOCDARP), +COMPATIBLE_IOCTL(SIOCSRARP), +COMPATIBLE_IOCTL(SIOCGRARP), +COMPATIBLE_IOCTL(SIOCDRARP), +COMPATIBLE_IOCTL(SIOCADDDLCI), +COMPATIBLE_IOCTL(SIOCDELDLCI), +/* SG stuff */ +COMPATIBLE_IOCTL(SG_SET_TIMEOUT), +COMPATIBLE_IOCTL(SG_GET_TIMEOUT), +COMPATIBLE_IOCTL(SG_EMULATED_HOST), +COMPATIBLE_IOCTL(SG_SET_TRANSFORM), +COMPATIBLE_IOCTL(SG_GET_TRANSFORM), +COMPATIBLE_IOCTL(SG_SET_RESERVED_SIZE), +COMPATIBLE_IOCTL(SG_GET_RESERVED_SIZE), +COMPATIBLE_IOCTL(SG_GET_SCSI_ID), +COMPATIBLE_IOCTL(SG_SET_FORCE_LOW_DMA), +COMPATIBLE_IOCTL(SG_GET_LOW_DMA), +COMPATIBLE_IOCTL(SG_SET_FORCE_PACK_ID), +COMPATIBLE_IOCTL(SG_GET_PACK_ID), +COMPATIBLE_IOCTL(SG_GET_NUM_WAITING), +COMPATIBLE_IOCTL(SG_SET_DEBUG), +COMPATIBLE_IOCTL(SG_GET_SG_TABLESIZE), +COMPATIBLE_IOCTL(SG_GET_COMMAND_Q), +COMPATIBLE_IOCTL(SG_SET_COMMAND_Q), +COMPATIBLE_IOCTL(SG_GET_VERSION_NUM), +COMPATIBLE_IOCTL(SG_NEXT_CMD_LEN), +COMPATIBLE_IOCTL(SG_SCSI_RESET), +COMPATIBLE_IOCTL(SG_IO), +COMPATIBLE_IOCTL(SG_GET_REQUEST_TABLE), +COMPATIBLE_IOCTL(SG_SET_KEEP_ORPHAN), +COMPATIBLE_IOCTL(SG_GET_KEEP_ORPHAN), +/* PPP stuff */ +COMPATIBLE_IOCTL(PPPIOCGFLAGS), +COMPATIBLE_IOCTL(PPPIOCSFLAGS), +COMPATIBLE_IOCTL(PPPIOCGASYNCMAP), +COMPATIBLE_IOCTL(PPPIOCSASYNCMAP), +COMPATIBLE_IOCTL(PPPIOCGUNIT), +COMPATIBLE_IOCTL(PPPIOCGRASYNCMAP), +COMPATIBLE_IOCTL(PPPIOCSRASYNCMAP), +COMPATIBLE_IOCTL(PPPIOCGMRU), +COMPATIBLE_IOCTL(PPPIOCSMRU), +COMPATIBLE_IOCTL(PPPIOCSMAXCID), +COMPATIBLE_IOCTL(PPPIOCGXASYNCMAP), +COMPATIBLE_IOCTL(PPPIOCSXASYNCMAP), +COMPATIBLE_IOCTL(PPPIOCXFERUNIT), +COMPATIBLE_IOCTL(PPPIOCGNPMODE), +COMPATIBLE_IOCTL(PPPIOCSNPMODE), +COMPATIBLE_IOCTL(PPPIOCGDEBUG), +COMPATIBLE_IOCTL(PPPIOCSDEBUG), +COMPATIBLE_IOCTL(PPPIOCNEWUNIT), +COMPATIBLE_IOCTL(PPPIOCATTACH), +COMPATIBLE_IOCTL(PPPIOCDETACH), +COMPATIBLE_IOCTL(PPPIOCSMRRU), +COMPATIBLE_IOCTL(PPPIOCCONNECT), +COMPATIBLE_IOCTL(PPPIOCDISCONN), +COMPATIBLE_IOCTL(PPPIOCATTCHAN), +COMPATIBLE_IOCTL(PPPIOCGCHAN), +/* PPPOX */ +COMPATIBLE_IOCTL(PPPOEIOCSFWD), +COMPATIBLE_IOCTL(PPPOEIOCDFWD), +/* CDROM stuff */ +COMPATIBLE_IOCTL(CDROMPAUSE), +COMPATIBLE_IOCTL(CDROMRESUME), +COMPATIBLE_IOCTL(CDROMPLAYMSF), +COMPATIBLE_IOCTL(CDROMPLAYTRKIND), +COMPATIBLE_IOCTL(CDROMREADTOCHDR), +COMPATIBLE_IOCTL(CDROMREADTOCENTRY), +COMPATIBLE_IOCTL(CDROMSTOP), +COMPATIBLE_IOCTL(CDROMSTART), +COMPATIBLE_IOCTL(CDROMEJECT), +COMPATIBLE_IOCTL(CDROMVOLCTRL), +COMPATIBLE_IOCTL(CDROMSUBCHNL), +COMPATIBLE_IOCTL(CDROMEJECT_SW), +COMPATIBLE_IOCTL(CDROMMULTISESSION), +COMPATIBLE_IOCTL(CDROM_GET_MCN), +COMPATIBLE_IOCTL(CDROMRESET), +COMPATIBLE_IOCTL(CDROMVOLREAD), +COMPATIBLE_IOCTL(CDROMSEEK), +COMPATIBLE_IOCTL(CDROMPLAYBLK), +COMPATIBLE_IOCTL(CDROMCLOSETRAY), +COMPATIBLE_IOCTL(CDROM_SET_OPTIONS), +COMPATIBLE_IOCTL(CDROM_CLEAR_OPTIONS), +COMPATIBLE_IOCTL(CDROM_SELECT_SPEED), +COMPATIBLE_IOCTL(CDROM_SELECT_DISC), +COMPATIBLE_IOCTL(CDROM_MEDIA_CHANGED), +COMPATIBLE_IOCTL(CDROM_DRIVE_STATUS), +COMPATIBLE_IOCTL(CDROM_DISC_STATUS), +COMPATIBLE_IOCTL(CDROM_CHANGER_NSLOTS), +COMPATIBLE_IOCTL(CDROM_LOCKDOOR), +COMPATIBLE_IOCTL(CDROM_DEBUG), +COMPATIBLE_IOCTL(CDROM_GET_CAPABILITY), +/* Big L */ +COMPATIBLE_IOCTL(LOOP_SET_FD), +COMPATIBLE_IOCTL(LOOP_CLR_FD), +/* Big Q for sound/OSS */ +COMPATIBLE_IOCTL(SNDCTL_SEQ_RESET), +COMPATIBLE_IOCTL(SNDCTL_SEQ_SYNC), +COMPATIBLE_IOCTL(SNDCTL_SYNTH_INFO), +COMPATIBLE_IOCTL(SNDCTL_SEQ_CTRLRATE), +COMPATIBLE_IOCTL(SNDCTL_SEQ_GETOUTCOUNT), +COMPATIBLE_IOCTL(SNDCTL_SEQ_GETINCOUNT), +COMPATIBLE_IOCTL(SNDCTL_SEQ_PERCMODE), +COMPATIBLE_IOCTL(SNDCTL_FM_LOAD_INSTR), +COMPATIBLE_IOCTL(SNDCTL_SEQ_TESTMIDI), +COMPATIBLE_IOCTL(SNDCTL_SEQ_RESETSAMPLES), +COMPATIBLE_IOCTL(SNDCTL_SEQ_NRSYNTHS), +COMPATIBLE_IOCTL(SNDCTL_SEQ_NRMIDIS), +COMPATIBLE_IOCTL(SNDCTL_MIDI_INFO), +COMPATIBLE_IOCTL(SNDCTL_SEQ_THRESHOLD), +COMPATIBLE_IOCTL(SNDCTL_SYNTH_MEMAVL), +COMPATIBLE_IOCTL(SNDCTL_FM_4OP_ENABLE), +COMPATIBLE_IOCTL(SNDCTL_SEQ_PANIC), +COMPATIBLE_IOCTL(SNDCTL_SEQ_OUTOFBAND), +COMPATIBLE_IOCTL(SNDCTL_SEQ_GETTIME), +COMPATIBLE_IOCTL(SNDCTL_SYNTH_ID), +COMPATIBLE_IOCTL(SNDCTL_SYNTH_CONTROL), +COMPATIBLE_IOCTL(SNDCTL_SYNTH_REMOVESAMPLE), +/* Big T for sound/OSS */ +COMPATIBLE_IOCTL(SNDCTL_TMR_TIMEBASE), +COMPATIBLE_IOCTL(SNDCTL_TMR_START), +COMPATIBLE_IOCTL(SNDCTL_TMR_STOP), +COMPATIBLE_IOCTL(SNDCTL_TMR_CONTINUE), +COMPATIBLE_IOCTL(SNDCTL_TMR_TEMPO), +COMPATIBLE_IOCTL(SNDCTL_TMR_SOURCE), +COMPATIBLE_IOCTL(SNDCTL_TMR_METRONOME), +COMPATIBLE_IOCTL(SNDCTL_TMR_SELECT), +/* Little m for sound/OSS */ +COMPATIBLE_IOCTL(SNDCTL_MIDI_PRETIME), +COMPATIBLE_IOCTL(SNDCTL_MIDI_MPUMODE), +COMPATIBLE_IOCTL(SNDCTL_MIDI_MPUCMD), +/* Big P for sound/OSS */ +COMPATIBLE_IOCTL(SNDCTL_DSP_RESET), +COMPATIBLE_IOCTL(SNDCTL_DSP_SYNC), +COMPATIBLE_IOCTL(SNDCTL_DSP_SPEED), +COMPATIBLE_IOCTL(SNDCTL_DSP_STEREO), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETBLKSIZE), +COMPATIBLE_IOCTL(SNDCTL_DSP_CHANNELS), +COMPATIBLE_IOCTL(SOUND_PCM_WRITE_FILTER), +COMPATIBLE_IOCTL(SNDCTL_DSP_POST), +COMPATIBLE_IOCTL(SNDCTL_DSP_SUBDIVIDE), +COMPATIBLE_IOCTL(SNDCTL_DSP_SETFRAGMENT), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETFMTS), +COMPATIBLE_IOCTL(SNDCTL_DSP_SETFMT), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETOSPACE), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETISPACE), +COMPATIBLE_IOCTL(SNDCTL_DSP_NONBLOCK), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETCAPS), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETTRIGGER), +COMPATIBLE_IOCTL(SNDCTL_DSP_SETTRIGGER), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETIPTR), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETOPTR), +/* SNDCTL_DSP_MAPINBUF, XXX needs translation */ +/* SNDCTL_DSP_MAPOUTBUF, XXX needs translation */ +COMPATIBLE_IOCTL(SNDCTL_DSP_SETSYNCRO), +COMPATIBLE_IOCTL(SNDCTL_DSP_SETDUPLEX), +COMPATIBLE_IOCTL(SNDCTL_DSP_GETODELAY), +COMPATIBLE_IOCTL(SNDCTL_DSP_PROFILE), +COMPATIBLE_IOCTL(SOUND_PCM_READ_RATE), +COMPATIBLE_IOCTL(SOUND_PCM_READ_CHANNELS), +COMPATIBLE_IOCTL(SOUND_PCM_READ_BITS), +COMPATIBLE_IOCTL(SOUND_PCM_READ_FILTER), +/* Big C for sound/OSS */ +COMPATIBLE_IOCTL(SNDCTL_COPR_RESET), +COMPATIBLE_IOCTL(SNDCTL_COPR_LOAD), +COMPATIBLE_IOCTL(SNDCTL_COPR_RDATA), +COMPATIBLE_IOCTL(SNDCTL_COPR_RCODE), +COMPATIBLE_IOCTL(SNDCTL_COPR_WDATA), +COMPATIBLE_IOCTL(SNDCTL_COPR_WCODE), +COMPATIBLE_IOCTL(SNDCTL_COPR_RUN), +COMPATIBLE_IOCTL(SNDCTL_COPR_HALT), +COMPATIBLE_IOCTL(SNDCTL_COPR_SENDMSG), +COMPATIBLE_IOCTL(SNDCTL_COPR_RCVMSG), +/* Big M for sound/OSS */ +COMPATIBLE_IOCTL(SOUND_MIXER_READ_VOLUME), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_BASS), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_TREBLE), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_SYNTH), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_PCM), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_SPEAKER), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_LINE), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_MIC), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_CD), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_IMIX), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_ALTPCM), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_RECLEV), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_IGAIN), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_OGAIN), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_LINE1), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_LINE2), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_LINE3), +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_DIGITAL1)), +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_DIGITAL2)), +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_DIGITAL3)), +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_PHONEIN)), +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_PHONEOUT)), +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_VIDEO)), +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_RADIO)), +COMPATIBLE_IOCTL(MIXER_READ(SOUND_MIXER_MONITOR)), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_MUTE), +/* SOUND_MIXER_READ_ENHANCE, same value as READ_MUTE */ +/* SOUND_MIXER_READ_LOUD, same value as READ_MUTE */ +COMPATIBLE_IOCTL(SOUND_MIXER_READ_RECSRC), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_DEVMASK), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_RECMASK), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_STEREODEVS), +COMPATIBLE_IOCTL(SOUND_MIXER_READ_CAPS), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_VOLUME), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_BASS), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_TREBLE), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_SYNTH), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_PCM), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_SPEAKER), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_LINE), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_MIC), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_CD), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_IMIX), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_ALTPCM), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_RECLEV), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_IGAIN), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_OGAIN), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_LINE1), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_LINE2), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_LINE3), +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_DIGITAL1)), +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_DIGITAL2)), +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_DIGITAL3)), +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_PHONEIN)), +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_PHONEOUT)), +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_VIDEO)), +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_RADIO)), +COMPATIBLE_IOCTL(MIXER_WRITE(SOUND_MIXER_MONITOR)), +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_MUTE), +/* SOUND_MIXER_WRITE_ENHANCE, same value as WRITE_MUTE */ +/* SOUND_MIXER_WRITE_LOUD, same value as WRITE_MUTE */ +COMPATIBLE_IOCTL(SOUND_MIXER_WRITE_RECSRC), +COMPATIBLE_IOCTL(SOUND_MIXER_INFO), +COMPATIBLE_IOCTL(SOUND_OLD_MIXER_INFO), +COMPATIBLE_IOCTL(SOUND_MIXER_ACCESS), +COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE1), +COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE2), +COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE3), +COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE4), +COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE5), +COMPATIBLE_IOCTL(SOUND_MIXER_GETLEVELS), +COMPATIBLE_IOCTL(SOUND_MIXER_SETLEVELS), +COMPATIBLE_IOCTL(OSS_GETVERSION), +/* AUTOFS */ +COMPATIBLE_IOCTL(AUTOFS_IOC_READY), +COMPATIBLE_IOCTL(AUTOFS_IOC_FAIL), +COMPATIBLE_IOCTL(AUTOFS_IOC_CATATONIC), +COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER), +COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE), +/* DEVFS */ +COMPATIBLE_IOCTL(DEVFSDIOC_GET_PROTO_REV), +COMPATIBLE_IOCTL(DEVFSDIOC_SET_EVENT_MASK), +COMPATIBLE_IOCTL(DEVFSDIOC_RELEASE_EVENT_QUEUE), +COMPATIBLE_IOCTL(DEVFSDIOC_SET_DEBUG_MASK), +/* Raw devices */ +COMPATIBLE_IOCTL(RAW_SETBIND), +COMPATIBLE_IOCTL(RAW_GETBIND), +/* SMB ioctls which do not need any translations */ +COMPATIBLE_IOCTL(SMB_IOC_NEWCONN), +/* Little a */ +COMPATIBLE_IOCTL(ATMSIGD_CTRL), +COMPATIBLE_IOCTL(ATMARPD_CTRL), +COMPATIBLE_IOCTL(ATMLEC_CTRL), +COMPATIBLE_IOCTL(ATMLEC_MCAST), +COMPATIBLE_IOCTL(ATMLEC_DATA), +COMPATIBLE_IOCTL(ATM_SETSC), +COMPATIBLE_IOCTL(SIOCSIFATMTCP), +COMPATIBLE_IOCTL(SIOCMKCLIP), +COMPATIBLE_IOCTL(ATMARP_MKIP), +COMPATIBLE_IOCTL(ATMARP_SETENTRY), +COMPATIBLE_IOCTL(ATMARP_ENCAP), +COMPATIBLE_IOCTL(ATMTCP_CREATE), +COMPATIBLE_IOCTL(ATMTCP_REMOVE), +COMPATIBLE_IOCTL(ATMMPC_CTRL), +COMPATIBLE_IOCTL(ATMMPC_DATA), +#if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE) +/* 0xfe - lvm */ +COMPATIBLE_IOCTL(VG_SET_EXTENDABLE), +COMPATIBLE_IOCTL(VG_STATUS_GET_COUNT), +COMPATIBLE_IOCTL(VG_STATUS_GET_NAMELIST), +COMPATIBLE_IOCTL(VG_REMOVE), +COMPATIBLE_IOCTL(VG_RENAME), +COMPATIBLE_IOCTL(VG_REDUCE), +COMPATIBLE_IOCTL(PE_LOCK_UNLOCK), +COMPATIBLE_IOCTL(PV_FLUSH), +COMPATIBLE_IOCTL(LVM_LOCK_LVM), +COMPATIBLE_IOCTL(LVM_GET_IOP_VERSION), +#ifdef LVM_TOTAL_RESET +COMPATIBLE_IOCTL(LVM_RESET), +#endif +COMPATIBLE_IOCTL(LV_SET_ACCESS), +COMPATIBLE_IOCTL(LV_SET_STATUS), +COMPATIBLE_IOCTL(LV_SET_ALLOCATION), +COMPATIBLE_IOCTL(LE_REMAP), +COMPATIBLE_IOCTL(LV_BMAP), +COMPATIBLE_IOCTL(LV_SNAPSHOT_USE_RATE), +#endif /* LVM */ +#if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE) +COMPATIBLE_IOCTL(DRM_IOCTL_GET_MAGIC), +COMPATIBLE_IOCTL(DRM_IOCTL_IRQ_BUSID), +COMPATIBLE_IOCTL(DRM_IOCTL_AUTH_MAGIC), +COMPATIBLE_IOCTL(DRM_IOCTL_BLOCK), +COMPATIBLE_IOCTL(DRM_IOCTL_UNBLOCK), +COMPATIBLE_IOCTL(DRM_IOCTL_CONTROL), +COMPATIBLE_IOCTL(DRM_IOCTL_ADD_BUFS), +COMPATIBLE_IOCTL(DRM_IOCTL_MARK_BUFS), +COMPATIBLE_IOCTL(DRM_IOCTL_ADD_CTX), +COMPATIBLE_IOCTL(DRM_IOCTL_RM_CTX), +COMPATIBLE_IOCTL(DRM_IOCTL_MOD_CTX), +COMPATIBLE_IOCTL(DRM_IOCTL_GET_CTX), +COMPATIBLE_IOCTL(DRM_IOCTL_SWITCH_CTX), +COMPATIBLE_IOCTL(DRM_IOCTL_NEW_CTX), +COMPATIBLE_IOCTL(DRM_IOCTL_ADD_DRAW), +COMPATIBLE_IOCTL(DRM_IOCTL_RM_DRAW), +COMPATIBLE_IOCTL(DRM_IOCTL_LOCK), +OMPATIBLE_IOCTL(DRM_IOCTL_UNLOCK), +COMPATIBLE_IOCTL(DRM_IOCTL_FINISH), +#endif /* DRM */ +/* elevator */ +COMPATIBLE_IOCTL(BLKELVGET), +COMPATIBLE_IOCTL(BLKELVSET), +/* Big W */ +/* WIOC_GETSUPPORT not yet implemented -E */ +COMPATIBLE_IOCTL(WDIOC_GETSTATUS), +COMPATIBLE_IOCTL(WDIOC_GETBOOTSTATUS), +COMPATIBLE_IOCTL(WDIOC_GETTEMP), +COMPATIBLE_IOCTL(WDIOC_SETOPTIONS), +COMPATIBLE_IOCTL(WDIOC_KEEPALIVE), +COMPATIBLE_IOCTL(PCIIOC_CONTROLLER), +COMPATIBLE_IOCTL(PCIIOC_MMAP_IS_IO), +COMPATIBLE_IOCTL(PCIIOC_MMAP_IS_MEM), +COMPATIBLE_IOCTL(PCIIOC_WRITE_COMBINE), + + HANDLE_IOCTL(SIOCGIFNAME, dev_ifname32), + HANDLE_IOCTL(SIOCGIFCONF, dev_ifconf), + HANDLE_IOCTL(SIOCGIFFLAGS, dev_ifsioc), + HANDLE_IOCTL(SIOCSIFFLAGS, dev_ifsioc), + HANDLE_IOCTL(SIOCGIFMETRIC, dev_ifsioc), + HANDLE_IOCTL(SIOCSIFMETRIC, dev_ifsioc), + HANDLE_IOCTL(SIOCGIFMTU, dev_ifsioc), + HANDLE_IOCTL(SIOCSIFMTU, dev_ifsioc), + HANDLE_IOCTL(SIOCGIFMEM, dev_ifsioc), + HANDLE_IOCTL(SIOCSIFMEM, dev_ifsioc), + HANDLE_IOCTL(SIOCGIFHWADDR, dev_ifsioc), + HANDLE_IOCTL(SIOCSIFHWADDR, dev_ifsioc), + HANDLE_IOCTL(SIOCADDMULTI, dev_ifsioc), + HANDLE_IOCTL(SIOCDELMULTI, dev_ifsioc), + HANDLE_IOCTL(SIOCGIFINDEX, dev_ifsioc), + HANDLE_IOCTL(SIOCGIFMAP, dev_ifsioc), + HANDLE_IOCTL(SIOCSIFMAP, dev_ifsioc), + HANDLE_IOCTL(SIOCGIFADDR, dev_ifsioc), + HANDLE_IOCTL(SIOCSIFADDR, dev_ifsioc), + HANDLE_IOCTL(SIOCGIFBRDADDR, dev_ifsioc), + HANDLE_IOCTL(SIOCSIFBRDADDR, dev_ifsioc), + HANDLE_IOCTL(SIOCGIFDSTADDR, dev_ifsioc), + HANDLE_IOCTL(SIOCSIFDSTADDR, dev_ifsioc), + HANDLE_IOCTL(SIOCGIFNETMASK, dev_ifsioc), + HANDLE_IOCTL(SIOCSIFNETMASK, dev_ifsioc), + HANDLE_IOCTL(SIOCSIFPFLAGS, dev_ifsioc), + HANDLE_IOCTL(SIOCGIFPFLAGS, dev_ifsioc), + HANDLE_IOCTL(SIOCGPPPSTATS, dev_ifsioc), + HANDLE_IOCTL(SIOCGPPPCSTATS, dev_ifsioc), + HANDLE_IOCTL(SIOCGPPPVER, dev_ifsioc), + HANDLE_IOCTL(SIOCGIFTXQLEN, dev_ifsioc), + HANDLE_IOCTL(SIOCSIFTXQLEN, dev_ifsioc), + HANDLE_IOCTL(SIOCADDRT, routing_ioctl), + HANDLE_IOCTL(SIOCDELRT, routing_ioctl), + /* Note SIOCRTMSG is no longer, so this is safe and + * the user would have seen just an -EINVAL anyways. */ + HANDLE_IOCTL(SIOCRTMSG, ret_einval), + HANDLE_IOCTL(SIOCGSTAMP, do_siocgstamp), + HANDLE_IOCTL(HDIO_GETGEO, hdio_getgeo), + HANDLE_IOCTL(BLKRAGET, w_long), + HANDLE_IOCTL(BLKGETSIZE, w_long), + HANDLE_IOCTL(0x1260, broken_blkgetsize), + HANDLE_IOCTL(BLKFRAGET, w_long), + HANDLE_IOCTL(BLKSECTGET, w_long), + HANDLE_IOCTL(BLKPG, blkpg_ioctl_trans), + HANDLE_IOCTL(HDIO_GET_KEEPSETTINGS, hdio_ioctl_trans), + HANDLE_IOCTL(HDIO_GET_UNMASKINTR, hdio_ioctl_trans), + HANDLE_IOCTL(HDIO_GET_DMA, hdio_ioctl_trans), + HANDLE_IOCTL(HDIO_GET_32BIT, hdio_ioctl_trans), + HANDLE_IOCTL(HDIO_GET_MULTCOUNT, hdio_ioctl_trans), + HANDLE_IOCTL(HDIO_GET_NOWERR, hdio_ioctl_trans), + HANDLE_IOCTL(HDIO_GET_NICE, hdio_ioctl_trans), + HANDLE_IOCTL(FDSETPRM32, fd_ioctl_trans), + HANDLE_IOCTL(FDDEFPRM32, fd_ioctl_trans), + HANDLE_IOCTL(FDGETPRM32, fd_ioctl_trans), + HANDLE_IOCTL(FDSETDRVPRM32, fd_ioctl_trans), + HANDLE_IOCTL(FDGETDRVPRM32, fd_ioctl_trans), + HANDLE_IOCTL(FDGETDRVSTAT32, fd_ioctl_trans), + HANDLE_IOCTL(FDPOLLDRVSTAT32, fd_ioctl_trans), + HANDLE_IOCTL(FDGETFDCSTAT32, fd_ioctl_trans), + HANDLE_IOCTL(FDWERRORGET32, fd_ioctl_trans), + HANDLE_IOCTL(PPPIOCGIDLE32, ppp_ioctl_trans), + HANDLE_IOCTL(PPPIOCSCOMPRESS32, ppp_ioctl_trans), + HANDLE_IOCTL(MTIOCGET32, mt_ioctl_trans), + HANDLE_IOCTL(MTIOCPOS32, mt_ioctl_trans), + HANDLE_IOCTL(MTIOCGETCONFIG32, mt_ioctl_trans), + HANDLE_IOCTL(MTIOCSETCONFIG32, mt_ioctl_trans), + HANDLE_IOCTL(CDROMREADMODE2, cdrom_ioctl_trans), + HANDLE_IOCTL(CDROMREADMODE1, cdrom_ioctl_trans), + HANDLE_IOCTL(CDROMREADRAW, cdrom_ioctl_trans), + HANDLE_IOCTL(CDROMREADCOOKED, cdrom_ioctl_trans), + HANDLE_IOCTL(CDROMREADAUDIO, cdrom_ioctl_trans), + HANDLE_IOCTL(CDROMREADALL, cdrom_ioctl_trans), + HANDLE_IOCTL(CDROM_SEND_PACKET, cdrom_ioctl_trans), + HANDLE_IOCTL(LOOP_SET_STATUS, loop_status), + HANDLE_IOCTL(LOOP_GET_STATUS, loop_status), + HANDLE_IOCTL(AUTOFS_IOC_SETTIMEOUT32, ioc_settimeout), +#ifdef CONFIG_VT + HANDLE_IOCTL(PIO_FONTX, do_fontx_ioctl), + HANDLE_IOCTL(GIO_FONTX, do_fontx_ioctl), + HANDLE_IOCTL(PIO_UNIMAP, do_unimap_ioctl), + HANDLE_IOCTL(GIO_UNIMAP, do_unimap_ioctl), + HANDLE_IOCTL(KDFONTOP, do_kdfontop_ioctl), + HANDLE_IOCTL(FBIOGET_FSCREENINFO, do_fbioget_fscreeninfo_ioctl), + HANDLE_IOCTL(FBIOGETCMAP, do_fbiogetcmap_ioctl), + HANDLE_IOCTL(FBIOPUTCMAP, do_fbioputcmap_ioctl), +#endif + HANDLE_IOCTL(EXT2_IOC32_GETFLAGS, do_ext2_ioctl), + HANDLE_IOCTL(EXT2_IOC32_SETFLAGS, do_ext2_ioctl), + HANDLE_IOCTL(EXT2_IOC32_GETVERSION, do_ext2_ioctl), + HANDLE_IOCTL(EXT2_IOC32_SETVERSION, do_ext2_ioctl), + HANDLE_IOCTL(VIDIOCGTUNER32, do_video_ioctl), + HANDLE_IOCTL(VIDIOCSTUNER32, do_video_ioctl), + HANDLE_IOCTL(VIDIOCGWIN32, do_video_ioctl), + HANDLE_IOCTL(VIDIOCSWIN32, do_video_ioctl), + HANDLE_IOCTL(VIDIOCGFBUF32, do_video_ioctl), + HANDLE_IOCTL(VIDIOCSFBUF32, do_video_ioctl), + HANDLE_IOCTL(VIDIOCGFREQ32, do_video_ioctl), + HANDLE_IOCTL(VIDIOCSFREQ32, do_video_ioctl), + /* One SMB ioctl needs translations. */ + HANDLE_IOCTL(SMB_IOC_GETMOUNTUID_32, do_smb_getmountuid), + HANDLE_IOCTL(ATM_GETLINKRATE32, do_atm_ioctl), + HANDLE_IOCTL(ATM_GETNAMES32, do_atm_ioctl), + HANDLE_IOCTL(ATM_GETTYPE32, do_atm_ioctl), + HANDLE_IOCTL(ATM_GETESI32, do_atm_ioctl), + HANDLE_IOCTL(ATM_GETADDR32, do_atm_ioctl), + HANDLE_IOCTL(ATM_RSTADDR32, do_atm_ioctl), + HANDLE_IOCTL(ATM_ADDADDR32, do_atm_ioctl), + HANDLE_IOCTL(ATM_DELADDR32, do_atm_ioctl), + HANDLE_IOCTL(ATM_GETCIRANGE32, do_atm_ioctl), + HANDLE_IOCTL(ATM_SETCIRANGE32, do_atm_ioctl), + HANDLE_IOCTL(ATM_SETESI32, do_atm_ioctl), + HANDLE_IOCTL(ATM_SETESIF32, do_atm_ioctl), + HANDLE_IOCTL(ATM_GETSTAT32, do_atm_ioctl), + HANDLE_IOCTL(ATM_GETSTATZ32, do_atm_ioctl), + HANDLE_IOCTL(ATM_GETLOOP32, do_atm_ioctl), + HANDLE_IOCTL(ATM_SETLOOP32, do_atm_ioctl), + HANDLE_IOCTL(ATM_QUERYLOOP32, do_atm_ioctl), + HANDLE_IOCTL(SONET_GETSTAT, do_atm_ioctl), + HANDLE_IOCTL(SONET_GETSTATZ, do_atm_ioctl), + HANDLE_IOCTL(SONET_GETDIAG, do_atm_ioctl), + HANDLE_IOCTL(SONET_SETDIAG, do_atm_ioctl), + HANDLE_IOCTL(SONET_CLRDIAG, do_atm_ioctl), + HANDLE_IOCTL(SONET_SETFRAMING, do_atm_ioctl), + HANDLE_IOCTL(SONET_GETFRAMING, do_atm_ioctl), + HANDLE_IOCTL(SONET_GETFRSENSE, do_atm_ioctl) +#if defined(CONFIG_BLK_DEV_LVM) || defined(CONFIG_BLK_DEV_LVM_MODULE) + ,HANDLE_IOCTL(VG_STATUS, do_lvm_ioctl), + HANDLE_IOCTL(VG_CREATE, do_lvm_ioctl), + HANDLE_IOCTL(VG_EXTEND, do_lvm_ioctl), + HANDLE_IOCTL(LV_CREATE, do_lvm_ioctl), + HANDLE_IOCTL(LV_REMOVE, do_lvm_ioctl), + HANDLE_IOCTL(LV_EXTEND, do_lvm_ioctl), + HANDLE_IOCTL(LV_REDUCE, do_lvm_ioctl), + HANDLE_IOCTL(LV_RENAME, do_lvm_ioctl), + HANDLE_IOCTL(LV_STATUS_BYNAME, do_lvm_ioctl), + HANDLE_IOCTL(LV_STATUS_BYINDEX, do_lvm_ioctl), + HANDLE_IOCTL(PV_CHANGE, do_lvm_ioctl), + HANDLE_IOCTL(PV_STATUS, do_lvm_ioctl) +#endif /* LVM */ +#if defined(CONFIG_DRM) || defined(CONFIG_DRM_MODULE) + ,HANDLE_IOCTL(DRM32_IOCTL_VERSION, drm32_version), + HANDLE_IOCTL(DRM32_IOCTL_GET_UNIQUE, drm32_getsetunique), + HANDLE_IOCTL(DRM32_IOCTL_SET_UNIQUE, drm32_getsetunique), + HANDLE_IOCTL(DRM32_IOCTL_ADD_MAP, drm32_addmap), + HANDLE_IOCTL(DRM32_IOCTL_INFO_BUFS, drm32_info_bufs), + HANDLE_IOCTL(DRM32_IOCTL_FREE_BUFS, drm32_free_bufs), + HANDLE_IOCTL(DRM32_IOCTL_MAP_BUFS, drm32_map_bufs), + HANDLE_IOCTL(DRM32_IOCTL_DMA, drm32_dma), + HANDLE_IOCTL(DRM32_IOCTL_RES_CTX, drm32_res_ctx) +#endif /* DRM */ +}; + +unsigned long ioctl32_hash_table[1024]; + +static inline unsigned long ioctl32_hash(unsigned long cmd) +{ + return ((cmd >> 6) ^ (cmd >> 4) ^ cmd) & 0x3ff; +} + +static void ioctl32_insert_translation(struct ioctl_trans *trans) +{ + unsigned long hash; + struct ioctl_trans *t; + + hash = ioctl32_hash (trans->cmd); + if (!ioctl32_hash_table[hash]) + ioctl32_hash_table[hash] = (long)trans; + else { + t = (struct ioctl_trans *)ioctl32_hash_table[hash]; + while (t->next) + t = (struct ioctl_trans *)(long)t->next; + trans->next = 0; + t->next = (long)trans; + } +} + +static int __init init_sys32_ioctl(void) +{ + int i, size = sizeof(ioctl_translations) / sizeof(struct ioctl_trans); + for (i=0; i < size ;i++) + ioctl32_insert_translation(&ioctl_translations[i]); + return 0; +} + +__initcall(init_sys32_ioctl); + +static struct ioctl_trans *additional_ioctls; + +/* Always call these with kernel lock held! */ + +int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *)) +{ + int i; + if (!additional_ioctls) { + additional_ioctls = module_map(PAGE_SIZE); + if (!additional_ioctls) + return -ENOMEM; + memset(additional_ioctls, 0, PAGE_SIZE); + } + for (i = 0; i < PAGE_SIZE/sizeof(struct ioctl_trans); i++) + if (!additional_ioctls[i].cmd) + break; + if (i == PAGE_SIZE/sizeof(struct ioctl_trans)) + return -ENOMEM; + additional_ioctls[i].cmd = cmd; + if (!handler) + additional_ioctls[i].handler = (long)sys_ioctl; + else + additional_ioctls[i].handler = (long)handler; + ioctl32_insert_translation(&additional_ioctls[i]); + return 0; +} + +int unregister_ioctl32_conversion(unsigned int cmd) +{ + unsigned long hash = ioctl32_hash(cmd); + struct ioctl_trans *t, *t1; + + t = (struct ioctl_trans *)ioctl32_hash_table[hash]; + if (!t) return -EINVAL; + if (t->cmd == cmd && t >= additional_ioctls && + (unsigned long)t < ((unsigned long)additional_ioctls) + PAGE_SIZE) { + ioctl32_hash_table[hash] = t->next; + t->cmd = 0; + return 0; + } else while (t->next) { + t1 = (struct ioctl_trans *)t->next; + if (t1->cmd == cmd && t1 >= additional_ioctls && + (unsigned long)t1 < ((unsigned long)additional_ioctls) + PAGE_SIZE) { + t1->cmd = 0; + t->next = t1->next; + return 0; + } + t = t1; + } + return -EINVAL; +} + +asmlinkage int sys32_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct file * filp; + int error = -EBADF; + int (*handler)(unsigned int, unsigned int, unsigned long, struct file * filp); + struct ioctl_trans *t; + + filp = fget(fd); + if(!filp) + goto out2; + + if (!filp->f_op || !filp->f_op->ioctl) { + error = sys_ioctl (fd, cmd, arg); + goto out; + } + + t = (struct ioctl_trans *)ioctl32_hash_table [ioctl32_hash (cmd)]; + + while (t && t->cmd != cmd) + t = (struct ioctl_trans *)t->next; + if (t) { + handler = (void *)t->handler; + error = handler(fd, cmd, arg, filp); + } else { + static int count = 0; + if (++count <= 20) + printk("sys32_ioctl(%s:%d): Unknown cmd fd(%d) " + "cmd(%08x) arg(%08x)\n", + current->comm, current->pid, + (int)fd, (unsigned int)cmd, (unsigned int)arg); + error = -EINVAL; + } +out: + fput(filp); +out2: + return error; +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/irq.c linuxppc64_2_4/arch/ppc64/kernel/irq.c --- linux-2.4.5-ac18/arch/ppc64/kernel/irq.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/irq.c Wed Aug 1 15:45:28 2001 @@ -0,0 +1,926 @@ +/* + * + * + * arch/ppc/kernel/irq.c + * + * Derived from arch/i386/kernel/irq.c + * Copyright (C) 1992 Linus Torvalds + * Adapted from arch/i386 by Gary Thomas + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * Updated and modified by Cort Dougan (cort@cs.nmt.edu) + * Copyright (C) 1996 Cort Dougan + * Adapted for Power Macintosh by Paul Mackerras + * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au) + * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). + * + * 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. + * + * 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. + * + * The MPC8xx has an interrupt mask in the SIU. If a bit is set, the + * interrupt is _enabled_. As expected, IRQ0 is bit 0 in the 32-bit + * mask register (of which only 16 are defined), hence the weird shifting + * and compliment of the cached_irq_mask. I want to be able to stuff + * this right into the SIU SMASK register. + * Many of the prep/chrp functions are conditional compiled on CONFIG_8xx + * to reduce code space and undefined function references. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "local_irq.h" + +atomic_t ipi_recv; +atomic_t ipi_sent; +void enable_irq(unsigned int irq_nr); +void disable_irq(unsigned int irq_nr); + +#ifdef CONFIG_SMP +extern void iSeries_smp_message_recv( struct pt_regs * ); +#endif + +volatile unsigned char *chrp_int_ack_special; +static void register_irq_proc (unsigned int irq); + +irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = + { [0 ... NR_IRQS-1] = { 0, NULL, NULL, 0, SPIN_LOCK_UNLOCKED}}; + +int ppc_spurious_interrupts = 0; +struct irqaction *ppc_irq_action[NR_IRQS]; +unsigned long ppc_cached_irq_mask[NR_MASK_WORDS]; +unsigned long ppc_lost_interrupts[NR_MASK_WORDS]; +atomic_t ppc_n_lost_interrupts; +unsigned long lpEvent_count = 0; +#ifdef CONFIG_XMON +extern void xmon(struct pt_regs *regs); +extern int xmon_bpt(struct pt_regs *regs); +extern int xmon_sstep(struct pt_regs *regs); +extern int xmon_iabr_match(struct pt_regs *regs); +extern int xmon_dabr_match(struct pt_regs *regs); +extern void (*xmon_fault_handler)(struct pt_regs *regs); +#endif +#ifdef CONFIG_XMON +extern void (*debugger)(struct pt_regs *regs); +extern int (*debugger_bpt)(struct pt_regs *regs); +extern int (*debugger_sstep)(struct pt_regs *regs); +extern int (*debugger_iabr_match)(struct pt_regs *regs); +extern int (*debugger_dabr_match)(struct pt_regs *regs); +extern void (*debugger_fault_handler)(struct pt_regs *regs); +#endif + +/* nasty hack for shared irq's since we need to do kmalloc calls but + * can't very early in the boot when we need to do a request irq. + * this needs to be removed. + * -- Cort + */ +#define IRQ_KMALLOC_ENTRIES 8 +static int cache_bitmask = 0; +static struct irqaction malloc_cache[IRQ_KMALLOC_ENTRIES]; +extern int mem_init_done; + +void *irq_kmalloc(size_t size, int pri) +{ + unsigned int i; + if ( mem_init_done ) + return kmalloc(size,pri); + for ( i = 0; i < IRQ_KMALLOC_ENTRIES ; i++ ) + if ( ! ( cache_bitmask & (1<flags & SA_SAMPLE_RANDOM) { + /* + * This function might sleep, we want to call it first, + * outside of the atomic block. + * Yes, this might clear the entropy pool if the wrong + * driver is attempted to be loaded, without actually + * installing a new handler, but is this really a problem, + * only the sysadmin is able to do this. + */ + rand_initialize_irq(irq); + } + + /* + * The following block of code has to be executed atomically + */ + spin_lock_irqsave(&desc->lock,flags); + p = &desc->action; + if ((old = *p) != NULL) { + /* Can't share interrupts unless both agree to */ + if (!(old->flags & new->flags & SA_SHIRQ)) { + spin_unlock_irqrestore(&desc->lock,flags); + return -EBUSY; + } + + /* add new interrupt at end of irq queue */ + do { + p = &old->next; + old = *p; + } while (old); + shared = 1; + } + + *p = new; + + if (!shared) { + desc->depth = 0; + desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING); + unmask_irq(irq); + } + spin_unlock_irqrestore(&desc->lock,flags); + + register_irq_proc(irq); + return 0; +} + +/* This could be promoted to a real free_irq() ... */ +static int +do_free_irq(int irq, void* dev_id) +{ + irq_desc_t *desc; + struct irqaction **p; + unsigned long flags; + + desc = irq_desc + irq; + spin_lock_irqsave(&desc->lock,flags); + p = &desc->action; + for (;;) { + struct irqaction * action = *p; + if (action) { + struct irqaction **pp = p; + p = &action->next; + if (action->dev_id != dev_id) + continue; + + /* Found it - now remove it from the list of entries */ + *pp = action->next; + if (!desc->action) { + desc->status |= IRQ_DISABLED; + mask_irq(irq); + } + spin_unlock_irqrestore(&desc->lock,flags); + +#ifdef CONFIG_SMP + /* Wait to make sure it's not being used on another CPU */ + while (desc->status & IRQ_INPROGRESS) + barrier(); +#endif + irq_kfree(action); + return 0; + } + printk("Trying to free free IRQ%d\n",irq); + spin_unlock_irqrestore(&desc->lock,flags); + break; + } + return -ENOENT; +} + +int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, const char * devname, void *dev_id) +{ + struct irqaction *action; + int retval; + + if (irq >= NR_IRQS) + return -EINVAL; + if (!handler) + /* We could implement really free_irq() instead of that... */ + return do_free_irq(irq, dev_id); + + action = (struct irqaction *) + irq_kmalloc(sizeof(struct irqaction), GFP_KERNEL); + if (!action) { + printk(KERN_ERR "irq_kmalloc() failed for irq %d !\n", irq); + return -ENOMEM; + } + + action->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + action->dev_id = dev_id; + action->next = NULL; + + retval = setup_irq(irq, action); + if (retval) + kfree(action); + + return 0; +} + +void free_irq(unsigned int irq, void *dev_id) +{ + request_irq(irq, NULL, 0, NULL, dev_id); +} + +/* + * Generic enable/disable code: this just calls + * down into the PIC-specific version for the actual + * hardware disable after having gotten the irq + * controller lock. + */ + +/** + * disable_irq_nosync - disable an irq without waiting + * @irq: Interrupt to disable + * + * Disable the selected interrupt line. Disables of an interrupt + * stack. Unlike disable_irq(), this function does not ensure existing + * instances of the IRQ handler have completed before returning. + * + * This function may be called from IRQ context. + */ + + void disable_irq_nosync(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + if (!desc->depth++) { + if (!(desc->status & IRQ_PER_CPU)) + desc->status |= IRQ_DISABLED; + mask_irq(irq); + } + spin_unlock_irqrestore(&desc->lock, flags); +} + +/** + * disable_irq - disable an irq and wait for completion + * @irq: Interrupt to disable + * + * Disable the selected interrupt line. Disables of an interrupt + * stack. That is for two disables you need two enables. This + * function waits for any pending IRQ handlers for this interrupt + * to complete before returning. If you use this function while + * holding a resource the IRQ handler may need you will deadlock. + * + * This function may be called - with care - from IRQ context. + */ + +void disable_irq(unsigned int irq) +{ + disable_irq_nosync(irq); + + if (!local_irq_count(smp_processor_id())) { + do { + barrier(); + } while (irq_desc[irq].status & IRQ_INPROGRESS); + } +} + +/** + * enable_irq - enable interrupt handling on an irq + * @irq: Interrupt to enable + * + * Re-enables the processing of interrupts on this IRQ line + * providing no disable_irq calls are now in effect. + * + * This function may be called from IRQ context. + */ + +void enable_irq(unsigned int irq) +{ + irq_desc_t *desc = irq_desc + irq; + unsigned long flags; + + spin_lock_irqsave(&desc->lock, flags); + switch (desc->depth) { + case 1: { + unsigned int status = desc->status & ~IRQ_DISABLED; + desc->status = status; + if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) { + desc->status = status | IRQ_REPLAY; + hw_resend_irq(desc->handler,irq); + } + unmask_irq(irq); + /* fall-through */ + } + default: + desc->depth--; + break; + case 0: + printk("enable_irq(%u) unbalanced\n", irq); + } + spin_unlock_irqrestore(&desc->lock, flags); +} + +int get_irq_list(char *buf) +{ + int i, len = 0, j; + struct irqaction * action; + + len += sprintf(buf+len, " "); + for (j=0; jhandler ) + continue; + len += sprintf(buf+len, "%3d: ", i); +#ifdef CONFIG_SMP + for (j = 0; j < smp_num_cpus; j++) + len += sprintf(buf+len, "%10u ", + kstat.irqs[cpu_logical_map(j)][i]); +#else + len += sprintf(buf+len, "%10u ", kstat_irqs(i)); +#endif /* CONFIG_SMP */ + if ( irq_desc[i].handler ) + len += sprintf(buf+len, " %s ", irq_desc[i].handler->typename ); + else + len += sprintf(buf+len, " None "); + len += sprintf(buf+len, "%s", (irq_desc[i].status & IRQ_LEVEL) ? "Level " : "Edge "); + len += sprintf(buf+len, " %s",action->name); + for (action=action->next; action; action = action->next) { + len += sprintf(buf+len, ", %s", action->name); + } + len += sprintf(buf+len, "\n"); + } +#ifdef CONFIG_SMP + /* should this be per processor send/receive? */ + len += sprintf(buf+len, "IPI (recv/sent): %10u/%u\n", + atomic_read(&ipi_recv), atomic_read(&ipi_sent)); +#endif + len += sprintf(buf+len, "BAD: %10u\n", ppc_spurious_interrupts); + return len; +} + +static inline void +handle_irq_event(int irq, struct pt_regs *regs, struct irqaction *action) +{ + int status = 0; + + if (!(action->flags & (SA_INTERRUPT | SA_SHIRQ))) + __sti(); + + do { + status |= action->flags; + action->handler(irq, action->dev_id, regs); + action = action->next; + } while (action); + if (status & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); + __cli(); +} + +/* + * Eventually, this should take an array of interrupts and an array size + * so it can dispatch multiple interrupts. + */ +void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq) +{ + int status; + struct irqaction *action; + int cpu = smp_processor_id(); + irq_desc_t *desc = irq_desc + irq; + + kstat.irqs[cpu][irq]++; + spin_lock(&desc->lock); + ack_irq(irq); + /* + REPLAY is when Linux resends an IRQ that was dropped earlier + WAITING is used by probe to mark irqs that are being tested + */ + status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); + if (!(status & IRQ_PER_CPU)) + status |= IRQ_PENDING; /* we _want_ to handle it */ + + /* + * If the IRQ is disabled for whatever reason, we cannot + * use the action we have. + */ + action = NULL; + if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { + action = desc->action; + if (!action || !action->handler) { + ppc_spurious_interrupts++; + printk(KERN_DEBUG "Unhandled interrupt %x, disabled\n", irq); + /* We can't call disable_irq here, it would deadlock */ + if (!desc->depth) + desc->depth = 1; + desc->status |= IRQ_DISABLED; + /* This is not a real spurrious interrupt, we + * have to eoi it, so we jump to out + */ + mask_irq(irq); + goto out; + } + status &= ~IRQ_PENDING; /* we commit to handling */ + if (!(status & IRQ_PER_CPU)) + status |= IRQ_INPROGRESS; /* we are handling it */ + } + desc->status = status; + + /* + * If there is no IRQ handler or it was disabled, exit early. + Since we set PENDING, if another processor is handling + a different instance of this same irq, the other processor + will take care of it. + */ + if (!action) + goto out; + + + /* + * Edge triggered interrupts need to remember + * pending events. + * This applies to any hw interrupts that allow a second + * instance of the same irq to arrive while we are in do_IRQ + * or in the handler. But the code here only handles the _second_ + * instance of the irq, not the third or fourth. So it is mostly + * useful for irq hardware that does not mask cleanly in an + * SMP environment. + */ + for (;;) { + spin_unlock(&desc->lock); + handle_irq_event(irq, regs, action); + spin_lock(&desc->lock); + + if (!(desc->status & IRQ_PENDING)) + break; + desc->status &= ~IRQ_PENDING; + } + desc->status &= ~IRQ_INPROGRESS; +out: + /* + * The ->end() handler has to deal with interrupts which got + * disabled while the handler was running. + */ + if (irq_desc[irq].handler) { + if (irq_desc[irq].handler->end) + irq_desc[irq].handler->end(irq); + else if (irq_desc[irq].handler->enable) + irq_desc[irq].handler->enable(irq); + } + spin_unlock(&desc->lock); +} + +int do_IRQ(struct pt_regs *regs, int isfake) +{ + int cpu = smp_processor_id(); + int irq; + unsigned long flags; + + /* if(cpu) udbg_printf("Entering do_IRQ\n"); */ + + irq_enter(cpu); + + if ( _machine != _MACH_iSeries ) { + + /* every arch is required to have a get_irq -- Cort */ + irq = ppc_md.get_irq( regs ); + + if ( irq < 0 ) + { + /* -2 means ignore, already handled */ + if (irq != -2) + { + printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n", + irq, regs->nip); + ppc_spurious_interrupts++; + } + goto out; + } + ppc_irq_dispatch_handler( regs, irq ); + if (ppc_md.post_irq) + ppc_md.post_irq( regs, irq ); + + out: + } + /* if on iSeries partition */ + else { +#ifdef CONFIG_SMP + if ( xPaca[cpu].xLpPacaPtr->xIpiCnt ) { + xPaca[cpu].xLpPacaPtr->xIpiCnt = 0; + iSeries_smp_message_recv( regs ); + } +#endif /* CONFIG_SMP */ + __no_use_save_flags( &flags ); + __no_use_cli(); + lpEvent_count += ItLpQueue_process( &xItLpQueue, regs ); + __no_lpq_restore_flags( flags ); + } + + irq_exit(cpu); + + if ( _machine == _MACH_iSeries ) { + if ( xPaca[cpu].xLpPacaPtr->xDecrInt ) { + xPaca[cpu].xLpPacaPtr->xDecrInt = 0; + /* Signal a fake decrementer interrupt */ + timer_interrupt( regs ); + } + } + + return 1; /* lets ret_from_int know we can do checks */ +} + +unsigned long probe_irq_on (void) +{ + return 0; +} + +int probe_irq_off (unsigned long irqs) +{ + return 0; +} + +unsigned int probe_irq_mask(unsigned long irqs) +{ + return 0; +} + +void __init init_IRQ(void) +{ + static int once = 0; + + if ( once ) + return; + else + once++; + + ppc_md.init_IRQ(); +} + +#ifdef CONFIG_SMP +unsigned char global_irq_holder = NO_PROC_ID; + +static void show(char * str) +{ + int cpu = smp_processor_id(); + int i; + + printk("\n%s, CPU %d:\n", str, cpu); + printk("irq: %d [ ", irqs_running()); + for (i = 0; i < smp_num_cpus; i++) + printk("%u ", __brlock_array[i][BR_GLOBALIRQ_LOCK]); + printk("]\nbh: %d [ ", + (spin_is_locked(&global_bh_lock) ? 1 : 0)); + for (i = 0; i < smp_num_cpus; i++) + printk("%u ", local_bh_count(i)); + printk("]\n"); +} + +#define MAXCOUNT 10000000 + +void synchronize_irq(void) +{ + if (irqs_running()) { + cli(); + sti(); + } +} + +static inline void get_irqlock(int cpu) +{ + int count; + + if ((unsigned char)cpu == global_irq_holder) + return; + + count = MAXCOUNT; +again: + br_write_lock(BR_GLOBALIRQ_LOCK); + for (;;) { + spinlock_t *lock; + + if (!irqs_running() && + (local_bh_count(smp_processor_id()) || !spin_is_locked(&global_bh_lock))) + break; + + br_write_unlock(BR_GLOBALIRQ_LOCK); + lock = &__br_write_locks[BR_GLOBALIRQ_LOCK].lock; + while (irqs_running() || + spin_is_locked(lock) || + (!local_bh_count(smp_processor_id()) && spin_is_locked(&global_bh_lock))) { + if (!--count) { + show("get_irqlock"); + count = (~0 >> 1); + } + __sti(); + barrier(); + __cli(); + } + goto again; + } + + global_irq_holder = cpu; +} + +/* + * A global "cli()" while in an interrupt context + * turns into just a local cli(). Interrupts + * should use spinlocks for the (very unlikely) + * case that they ever want to protect against + * each other. + * + * If we already have local interrupts disabled, + * this will not turn a local disable into a + * global one (problems with spinlocks: this makes + * save_flags+cli+sti usable inside a spinlock). + */ +void __global_cli(void) +{ + unsigned long flags; + + __save_flags(flags); + if (flags & (1UL << 15)) { + int cpu = smp_processor_id(); + __cli(); + if (!local_irq_count(cpu)) + get_irqlock(cpu); + } +} + +void __global_sti(void) +{ + int cpu = smp_processor_id(); + + if (!local_irq_count(cpu)) + release_irqlock(cpu); + __sti(); +} + +/* + * SMP flags value to restore to: + * 0 - global cli + * 1 - global sti + * 2 - local cli + * 3 - local sti + */ +unsigned long __global_save_flags(void) +{ + int retval; + int local_enabled; + unsigned long flags; + + __save_flags(flags); + local_enabled = (flags >> 15) & 1; + /* default to local */ + retval = 2 + local_enabled; + + /* check for global flags if we're not in an interrupt */ + if (!local_irq_count(smp_processor_id())) { + if (local_enabled) + retval = 1; + if (global_irq_holder == (unsigned char) smp_processor_id()) + retval = 0; + } + return retval; +} + +void __global_restore_flags(unsigned long flags) +{ + switch (flags) { + case 0: + __global_cli(); + break; + case 1: + __global_sti(); + break; + case 2: + __cli(); + break; + case 3: + __sti(); + break; + default: + printk("global_restore_flags: %016lx caller %016lx\n", + flags, __builtin_return_address(0)); + } +} + +#endif /* CONFIG_SMP */ + +static struct proc_dir_entry * root_irq_dir; +static struct proc_dir_entry * irq_dir [NR_IRQS]; +static struct proc_dir_entry * smp_affinity_entry [NR_IRQS]; + +#ifdef CONFIG_IRQ_ALL_CPUS +unsigned int irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = 0xffffffff}; +#else /* CONFIG_IRQ_ALL_CPUS */ +unsigned int irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = 0x00000000}; +#endif /* CONFIG_IRQ_ALL_CPUS */ + +#define HEX_DIGITS 8 + +static int irq_affinity_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + if (count < HEX_DIGITS+1) + return -EINVAL; + return sprintf (page, "%08x\n", irq_affinity[(int)data]); +} + +static unsigned int parse_hex_value (const char *buffer, + unsigned long count, unsigned long *ret) +{ + unsigned char hexnum [HEX_DIGITS]; + unsigned long value; + int i; + + if (!count) + return -EINVAL; + if (count > HEX_DIGITS) + count = HEX_DIGITS; + if (copy_from_user(hexnum, buffer, count)) + return -EFAULT; + + /* + * Parse the first 8 characters as a hex string, any non-hex char + * is end-of-string. '00e1', 'e1', '00E1', 'E1' are all the same. + */ + value = 0; + + for (i = 0; i < count; i++) { + unsigned int c = hexnum[i]; + + switch (c) { + case '0' ... '9': c -= '0'; break; + case 'a' ... 'f': c -= 'a'-10; break; + case 'A' ... 'F': c -= 'A'-10; break; + default: + goto out; + } + value = (value << 4) | c; + } +out: + *ret = value; + return 0; +} + +static int irq_affinity_write_proc (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + int irq = (int) data, full_count = count, err; + unsigned long new_value; + + if (!irq_desc[irq].handler->set_affinity) + return -EIO; + + err = parse_hex_value(buffer, count, &new_value); + +/* Why is this disabled ? --BenH */ +#if 0/*CONFIG_SMP*/ + /* + * Do not allow disabling IRQs completely - it's a too easy + * way to make the system unusable accidentally :-) At least + * one online CPU still has to be targeted. + */ + if (!(new_value & cpu_online_map)) + return -EINVAL; +#endif + + irq_affinity[irq] = new_value; + irq_desc[irq].handler->set_affinity(irq, new_value); + + return full_count; +} + +static int prof_cpu_mask_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + unsigned long *mask = (unsigned long *) data; + if (count < HEX_DIGITS+1) + return -EINVAL; + return sprintf (page, "%08lx\n", *mask); +} + +static int prof_cpu_mask_write_proc (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + unsigned long *mask = (unsigned long *) data, full_count = count, err; + unsigned long new_value; + + err = parse_hex_value(buffer, count, &new_value); + if (err) + return err; + + *mask = new_value; + return full_count; +} + +#define MAX_NAMELEN 10 + +static void register_irq_proc (unsigned int irq) +{ + struct proc_dir_entry *entry; + char name [MAX_NAMELEN]; + + if (!root_irq_dir || (irq_desc[irq].handler == NULL)) + return; + + memset(name, 0, MAX_NAMELEN); + sprintf(name, "%d", irq); + + /* create /proc/irq/1234 */ + irq_dir[irq] = proc_mkdir(name, root_irq_dir); + + /* create /proc/irq/1234/smp_affinity */ + entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]); + + entry->nlink = 1; + entry->data = (void *)irq; + entry->read_proc = irq_affinity_read_proc; + entry->write_proc = irq_affinity_write_proc; + + smp_affinity_entry[irq] = entry; +} + +unsigned long prof_cpu_mask = -1; + +void init_irq_proc (void) +{ + struct proc_dir_entry *entry; + int i; + + /* create /proc/irq */ + root_irq_dir = proc_mkdir("irq", 0); + + /* create /proc/irq/prof_cpu_mask */ + entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); + + entry->nlink = 1; + entry->data = (void *)&prof_cpu_mask; + entry->read_proc = prof_cpu_mask_read_proc; + entry->write_proc = prof_cpu_mask_write_proc; + + /* + * Create entries for all existing IRQs. + */ + for (i = 0; i < NR_IRQS; i++) { + if (irq_desc[i].handler == NULL) + continue; + register_irq_proc(i); + } +} + +void no_action(int irq, void *dev, struct pt_regs *regs) +{ +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/local_irq.h linuxppc64_2_4/arch/ppc64/kernel/local_irq.h --- linux-2.4.5-ac18/arch/ppc64/kernel/local_irq.h Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/local_irq.h Mon May 7 12:47:01 2001 @@ -0,0 +1,29 @@ +/* + * c 2001 PowerPC 64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef _PPC_KERNEL_LOCAL_IRQ_H +#define _PPC_KERNEL_LOCAL_IRQ_H + +#include +#include +#include +#include +#include + +void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq); + +#define NR_MASK_WORDS ((NR_IRQS + 63) / 64) + +extern int ppc_spurious_interrupts; +extern int ppc_second_irq; +extern struct irqaction *ppc_irq_action[NR_IRQS]; +extern unsigned long ppc_cached_irq_mask[NR_MASK_WORDS]; +extern unsigned long ppc_lost_interrupts[NR_MASK_WORDS]; +extern atomic_t ppc_n_lost_interrupts; + +#endif /* _PPC_KERNEL_LOCAL_IRQ_H */ diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/mf.c linuxppc64_2_4/arch/ppc64/kernel/mf.c --- linux-2.4.5-ac18/arch/ppc64/kernel/mf.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/mf.c Mon May 7 12:47:01 2001 @@ -0,0 +1,1237 @@ +/* + * mf.c + * Copyright (C) 2001 Troy D. Armstrong IBM Corporation + * + * This modules exists as an interface between a Linux secondary partition + * running on an iSeries and the primary partition's Virtual Service + * Processor (VSP) object. The VSP has final authority over powering on/off + * all partitions in the iSeries. It also provides miscellaneous low-level + * machine facility type operations. + * + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * This is the structure layout for the Machine Facilites LPAR event + * flows. + */ +struct VspCmdData; +struct CeMsgData; +union SafeCast +{ + u64 ptrAsU64; + void *ptr; +}; + + +typedef void (*CeMsgCompleteHandler)( void *token, struct CeMsgData *vspCmdRsp ); + +struct CeMsgCompleteData +{ + CeMsgCompleteHandler xHdlr; + void *xToken; +}; + +struct VspRspData +{ + struct semaphore *xSemaphore; + struct VspCmdData *xResponse; +}; + +struct IoMFLpEvent +{ + struct HvLpEvent xHvLpEvent; + + u16 xSubtypeRc; + u16 xRsvd1; + u32 xRsvd2; + + union + { + + struct AllocData + { + u16 xSize; + u16 xType; + u32 xCount; + u16 xRsvd3; + u8 xRsvd4; + HvLpIndex xTargetLp; + } xAllocData; + + struct CeMsgData + { + u8 xCEMsg[12]; + char xReserved[4]; + struct CeMsgCompleteData *xToken; + } xCEMsgData; + + struct VspCmdData + { + union SafeCast xTokenUnion; + u16 xCmd; + HvLpIndex xLpIndex; + u8 xRc; + u32 xReserved1; + + union VspCmdSubData + { + struct + { + u64 xState; + } xGetStateOut; + + struct + { + u64 xIplType; + } xGetIplTypeOut, xFunction02SelectIplTypeIn; + + struct + { + u64 xIplMode; + } xGetIplModeOut, xFunction02SelectIplModeIn; + + struct + { + u64 xPage[4]; + } xGetSrcHistoryIn; + + struct + { + u64 xFlag; + } xGetAutoIplWhenPrimaryIplsOut, xSetAutoIplWhenPrimaryIplsIn, xWhiteButtonPowerOffIn, xFunction08FastPowerOffIn, xIsSpcnRackPowerIncompleteOut; + + struct + { + u64 xToken; + u64 xAddressType; + u64 xSide; + u32 xTransferLength; + u32 xOffset; + } xSetKernelImageIn, xGetKernelImageIn, xSetKernelCmdLineIn, xGetKernelCmdLineIn; + + struct + { + u32 xTransferLength; + } xGetKernelImageOut,xGetKernelCmdLineOut; + + + u8 xReserved2[80]; + + } xSubData; + } xVspCmd; + } xUnion; +}; + + +/* + * All outgoing event traffic is kept on a FIFO queue. The first + * pointer points to the one that is outstanding, and all new + * requests get stuck on the end. Also, we keep a certain number of + * preallocated stack elements so that we can operate very early in + * the boot up sequence (before kmalloc is ready). + */ +struct StackElement +{ + struct StackElement * next; + struct IoMFLpEvent event; + MFCompleteHandler hdlr; + char dmaData[72]; + unsigned dmaDataLength; + unsigned remoteAddress; +}; +static spinlock_t spinlock; +static struct StackElement * head = NULL; +static struct StackElement * tail = NULL; +static struct StackElement * avail = NULL; +static struct StackElement prealloc[16]; + +/* + * Put a stack element onto the available queue, so it can get reused. + * Attention! You must have the spinlock before calling! + */ +void free( struct StackElement * element ) +{ + if( element != NULL ) + { + element->next = avail; + avail = element; + } +} + +/* + * Enqueue the outbound event onto the stack. If the queue was + * empty to begin with, we must also issue it via the Hypervisor + * interface. There is a section of code below that will touch + * the first stack pointer without the protection of the spinlock. + * This is OK, because we know that nobody else will be modifying + * the first pointer when we do this. + */ +static int signalEvent( struct StackElement * newElement ) +{ + int rc = 0; + unsigned long flags; + int go = 1; + struct StackElement * element; + HvLpEvent_Rc hvRc; + + /* enqueue the event */ + if( newElement != NULL ) + { + spin_lock_irqsave( &spinlock, flags ); + if( head == NULL ) + head = newElement; + else + { + go = 0; + tail->next = newElement; + } + newElement->next = NULL; + tail = newElement; + spin_unlock_irqrestore( &spinlock, flags ); + } + + /* send the event */ + while( go ) + { + go = 0; + + /* any DMA data to send beforehand? */ + if( head->dmaDataLength > 0 ) + HvCallEvent_dmaToSp( head->dmaData, head->remoteAddress, head->dmaDataLength, HvLpDma_Direction_LocalToRemote ); + + hvRc = HvCallEvent_signalLpEvent(&head->event.xHvLpEvent); + if( hvRc != HvLpEvent_Rc_Good ) + { + printk( KERN_ERR "mf.c: HvCallEvent_signalLpEvent() failed with %d\n", (int)hvRc ); + + spin_lock_irqsave( &spinlock, flags ); + element = head; + head = head->next; + if( head != NULL ) + go = 1; + spin_unlock_irqrestore( &spinlock, flags ); + + if( element == newElement ) + rc = -EIO; + else + { + if( element->hdlr != NULL ) + { + union SafeCast mySafeCast; + mySafeCast.ptrAsU64 = element->event.xHvLpEvent.xCorrelationToken; + (*element->hdlr)( mySafeCast.ptr, -EIO ); + } + } + + spin_lock_irqsave( &spinlock, flags ); + free( element ); + spin_unlock_irqrestore( &spinlock, flags ); + } + } + + return rc; +} + +/* + * Allocate a new StackElement structure, and initialize it. + */ +static struct StackElement * newStackElement( void ) +{ + struct StackElement * newElement = NULL; + HvLpIndex primaryLp = HvLpConfig_getPrimaryLpIndex(); + unsigned long flags; + + if( newElement == NULL ) + { + spin_lock_irqsave( &spinlock, flags ); + if( avail != NULL ) + { + newElement = avail; + avail = avail->next; + } + spin_unlock_irqrestore( &spinlock, flags ); + } + + if( newElement == NULL ) + newElement = kmalloc(sizeof(struct StackElement),GFP_ATOMIC); + + if( newElement == NULL ) + { + printk( KERN_ERR "mf.c: unable to kmalloc %d bytes\n", sizeof(struct StackElement) ); + return NULL; + } + + memset( newElement, 0, sizeof(struct StackElement) ); + newElement->event.xHvLpEvent.xFlags.xValid = 1; + newElement->event.xHvLpEvent.xFlags.xAckType = HvLpEvent_AckType_ImmediateAck; + newElement->event.xHvLpEvent.xFlags.xAckInd = HvLpEvent_AckInd_DoAck; + newElement->event.xHvLpEvent.xFlags.xFunction = HvLpEvent_Function_Int; + newElement->event.xHvLpEvent.xType = HvLpEvent_Type_MachineFac; + newElement->event.xHvLpEvent.xSourceLp = HvLpConfig_getLpIndex(); + newElement->event.xHvLpEvent.xTargetLp = primaryLp; + newElement->event.xHvLpEvent.xSizeMinus1 = sizeof(newElement->event)-1; + newElement->event.xHvLpEvent.xRc = HvLpEvent_Rc_Good; + newElement->event.xHvLpEvent.xSourceInstanceId = HvCallEvent_getSourceLpInstanceId(primaryLp,HvLpEvent_Type_MachineFac); + newElement->event.xHvLpEvent.xTargetInstanceId = HvCallEvent_getTargetLpInstanceId(primaryLp,HvLpEvent_Type_MachineFac); + + return newElement; +} + +static int signalVspInstruction( struct VspCmdData *vspCmd ) +{ + struct StackElement * newElement = newStackElement(); + int rc = 0; + struct VspRspData response; + DECLARE_MUTEX_LOCKED(Semaphore); + response.xSemaphore = &Semaphore; + response.xResponse = vspCmd; + + if( newElement == NULL ) + rc = -ENOMEM; + else + { + newElement->event.xHvLpEvent.xSubtype = 6; + newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('V'<<8)+('I'<<0); + newElement->event.xUnion.xVspCmd.xTokenUnion.ptr = &response; + newElement->event.xUnion.xVspCmd.xCmd = vspCmd->xCmd; + newElement->event.xUnion.xVspCmd.xLpIndex = HvLpConfig_getLpIndex(); + newElement->event.xUnion.xVspCmd.xRc = 0xFF; + newElement->event.xUnion.xVspCmd.xReserved1 = 0; + memcpy(&(newElement->event.xUnion.xVspCmd.xSubData),&(vspCmd->xSubData), sizeof(vspCmd->xSubData)); + mb(); + + rc = signalEvent(newElement); + } + + if (rc == 0) + { + down(&Semaphore); + } + + return rc; +} + + +/* + * Send a 12-byte CE message to the primary partition VSP object + */ +static int signalCEMsg( char * ceMsg, void * token ) +{ + struct StackElement * newElement = newStackElement(); + int rc = 0; + + if( newElement == NULL ) + rc = -ENOMEM; + else + { + newElement->event.xHvLpEvent.xSubtype = 0; + newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('C'<<8)+('E'<<0); + memcpy( newElement->event.xUnion.xCEMsgData.xCEMsg, ceMsg, 12 ); + newElement->event.xUnion.xCEMsgData.xToken = token; + rc = signalEvent(newElement); + } + + return rc; +} + +/* + * Send a 12-byte CE message and DMA data to the primary partition VSP object + */ +static int dmaAndSignalCEMsg( char * ceMsg, void * token, void * dmaData, unsigned dmaDataLength, unsigned remoteAddress ) +{ + struct StackElement * newElement = newStackElement(); + int rc = 0; + + if( newElement == NULL ) + rc = -ENOMEM; + else + { + newElement->event.xHvLpEvent.xSubtype = 0; + newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('C'<<8)+('E'<<0); + memcpy( newElement->event.xUnion.xCEMsgData.xCEMsg, ceMsg, 12 ); + newElement->event.xUnion.xCEMsgData.xToken = token; + memcpy( newElement->dmaData, dmaData, dmaDataLength ); + newElement->dmaDataLength = dmaDataLength; + newElement->remoteAddress = remoteAddress; + rc = signalEvent(newElement); + } + + return rc; +} + +/* + * Initiate a nice (hopefully) shutdown of Linux. We simply are + * going to try and send the init process a SIGINT signal. If + * this fails (why?), we'll simply force it off in a not-so-nice + * manner. + */ +static int shutdown( void ) +{ + int rc = kill_proc(1,SIGINT,1); + + if( rc ) + { + printk( KERN_ALERT "mf.c: SIGINT to init failed (%d), hard shutdown commencing\n", rc ); + mf_powerOff(); + } + else + printk( KERN_ALERT "mf.c: init has been successfully notified to proceed with shutdown\n" ); + + return rc; +} + +/* + * The primary partition VSP object is sending us a new + * event flow. Handle it... + */ +static void intReceived( struct IoMFLpEvent * event ) +{ + int freeIt = 0; + struct StackElement * two = NULL; + /* ack the interrupt */ + event->xHvLpEvent.xRc = HvLpEvent_Rc_Good; + HvCallEvent_ackLpEvent( &event->xHvLpEvent ); + + /* process interrupt */ + switch( event->xHvLpEvent.xSubtype ) + { + case 0: /* CE message */ + switch( event->xUnion.xCEMsgData.xCEMsg[3] ) + { + case 0x5B: /* power control notification */ + if( (event->xUnion.xCEMsgData.xCEMsg[5]&0x20) != 0 ) + { + printk( KERN_ALERT "mf.c: Commencing partition shutdown\n" ); + if( shutdown() == 0 ) + signalCEMsg( "\x00\x00\x00\xDB\x00\x00\x00\x00\x00\x00\x00\x00", NULL ); + } + break; + case 0xC0: /* get time */ + { + if ( (head != NULL) && ( head->event.xUnion.xCEMsgData.xCEMsg[3] == 0x40 ) ) + { + freeIt = 1; + if ( head->event.xUnion.xCEMsgData.xToken != 0 ) + { + CeMsgCompleteHandler xHdlr = head->event.xUnion.xCEMsgData.xToken->xHdlr; + void * token = head->event.xUnion.xCEMsgData.xToken->xToken; + + if (xHdlr != NULL) + (*xHdlr)( token, &(event->xUnion.xCEMsgData) ); + } + } + } + break; + } + + /* remove from queue */ + if ( freeIt == 1 ) + { + unsigned long flags; + spin_lock_irqsave( &spinlock, flags ); + if( head != NULL ) + { + struct StackElement *oldHead = head; + head = head->next; + two = head; + free( oldHead ); + } + spin_unlock_irqrestore( &spinlock, flags ); + } + + /* send next waiting event */ + if( two != NULL ) + signalEvent( NULL ); + break; + case 1: /* IT sys shutdown */ + printk( KERN_ALERT "mf.c: Commencing system shutdown\n" ); + shutdown(); + break; + } +} + +/* + * The primary partition VSP object is acknowledging the receipt + * of a flow we sent to them. If there are other flows queued + * up, we must send another one now... + */ +static void ackReceived( struct IoMFLpEvent * event ) +{ + unsigned long flags; + struct StackElement * two = NULL; + unsigned long freeIt = 0; + + /* handle current event */ + if( head != NULL ) + { + switch( event->xHvLpEvent.xSubtype ) + { + case 0: /* CE msg */ + if( event->xUnion.xCEMsgData.xCEMsg[3] == 0x40 ) + { + if ( event->xUnion.xCEMsgData.xCEMsg[2] != 0 ) + { + freeIt = 1; + if ( head->event.xUnion.xCEMsgData.xToken != 0 ) + { + CeMsgCompleteHandler xHdlr = head->event.xUnion.xCEMsgData.xToken->xHdlr; + void * token = head->event.xUnion.xCEMsgData.xToken->xToken; + + if (xHdlr != NULL) + (*xHdlr)( token, &(event->xUnion.xCEMsgData) ); + } + } + } + else + { + freeIt = 1; + } + break; + case 4: /* allocate */ + case 5: /* deallocate */ + if( head->hdlr != NULL ) + { + union SafeCast mySafeCast; + mySafeCast.ptrAsU64 = event->xHvLpEvent.xCorrelationToken; + (*head->hdlr)( mySafeCast.ptr, event->xUnion.xAllocData.xCount ); + } + freeIt = 1; + break; + case 6: + { + struct VspRspData *rsp = (struct VspRspData *)event->xUnion.xVspCmd.xTokenUnion.ptr; + + if (rsp != NULL) + { + if (rsp->xResponse != NULL) + memcpy(rsp->xResponse, &(event->xUnion.xVspCmd), sizeof(event->xUnion.xVspCmd)); + if (rsp->xSemaphore != NULL) + up(rsp->xSemaphore); + } + else + { + printk( KERN_ERR "mf.c: no rsp\n"); + } + freeIt = 1; + } + break; + } + } + else + printk( KERN_ERR "mf.c: stack empty for receiving ack\n" ); + + /* remove from queue */ + spin_lock_irqsave( &spinlock, flags ); + if(( head != NULL ) && ( freeIt == 1 )) + { + struct StackElement *oldHead = head; + head = head->next; + two = head; + free( oldHead ); + } + spin_unlock_irqrestore( &spinlock, flags ); + + /* send next waiting event */ + if( two != NULL ) + signalEvent( NULL ); +} + +/* + * This is the generic event handler we are registering with + * the Hypervisor. Ensure the flows are for us, and then + * parse it enough to know if it is an interrupt or an + * acknowledge. + */ +static void hvHandler( struct HvLpEvent * event, struct pt_regs * regs ) +{ + if( (event != NULL) && (event->xType == HvLpEvent_Type_MachineFac) ) + { + switch( event->xFlags.xFunction ) + { + case HvLpEvent_Function_Ack: + ackReceived( (struct IoMFLpEvent *)event ); + break; + case HvLpEvent_Function_Int: + intReceived( (struct IoMFLpEvent *)event ); + break; + default: + printk( KERN_ERR "mf.c: non ack/int event received\n" ); + break; + } + } + else + printk( KERN_ERR "mf.c: alien event received\n" ); +} + +/* + * Global kernel interface to allocate and seed events into the + * Hypervisor. + */ +void mf_allocateLpEvents( HvLpIndex targetLp, + HvLpEvent_Type type, + unsigned size, + unsigned count, + MFCompleteHandler hdlr, + void * userToken ) +{ + struct StackElement * newElement = newStackElement(); + int rc = 0; + + if( newElement == NULL ) + rc = -ENOMEM; + else + { + union SafeCast mine; + mine.ptr = userToken; + newElement->event.xHvLpEvent.xSubtype = 4; + newElement->event.xHvLpEvent.xCorrelationToken = mine.ptrAsU64; + newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('M'<<8)+('A'<<0); + newElement->event.xUnion.xAllocData.xTargetLp = targetLp; + newElement->event.xUnion.xAllocData.xType = type; + newElement->event.xUnion.xAllocData.xSize = size; + newElement->event.xUnion.xAllocData.xCount = count; + newElement->hdlr = hdlr; + rc = signalEvent(newElement); + } + + if( (rc != 0) && (hdlr != NULL) ) + (*hdlr)( userToken, rc ); +} + +/* + * Global kernel interface to unseed and deallocate events already in + * Hypervisor. + */ +void mf_deallocateLpEvents( HvLpIndex targetLp, + HvLpEvent_Type type, + unsigned count, + MFCompleteHandler hdlr, + void * userToken ) +{ + struct StackElement * newElement = newStackElement(); + int rc = 0; + + if( newElement == NULL ) + rc = -ENOMEM; + else + { + union SafeCast mine; + mine.ptr = userToken; + newElement->event.xHvLpEvent.xSubtype = 5; + newElement->event.xHvLpEvent.xCorrelationToken = mine.ptrAsU64; + newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('M'<<8)+('D'<<0); + newElement->event.xUnion.xAllocData.xTargetLp = targetLp; + newElement->event.xUnion.xAllocData.xType = type; + newElement->event.xUnion.xAllocData.xCount = count; + newElement->hdlr = hdlr; + rc = signalEvent(newElement); + } + + if( (rc != 0) && (hdlr != NULL) ) + (*hdlr)( userToken, rc ); +} + +/* + * Global kernel interface to tell the VSP object in the primary + * partition to power this partition off. + */ +void mf_powerOff( void ) +{ + printk( KERN_ALERT "mf.c: Down it goes...\n" ); + signalCEMsg( "\x00\x00\x00\x4D\x00\x00\x00\x00\x00\x00\x00\x00", NULL ); + for(;;); +} + +/* + * Global kernel interface to tell the VSP object in the primary + * partition to reboot this partition. + */ +void mf_reboot( void ) +{ + printk( KERN_ALERT "mf.c: Preparing to bounce...\n" ); + signalCEMsg( "\x00\x00\x00\x4E\x00\x00\x00\x00\x00\x00\x00\x00", NULL ); + for(;;); +} + +/* + * Display a single word SRC onto the VSP control panel. + */ +void mf_displaySrc( u32 word ) +{ + u8 ce[12]; + + memcpy( ce, "\x00\x00\x00\x4A\x00\x00\x00\x01\x00\x00\x00\x00", 12 ); + ce[8] = word>>24; + ce[9] = word>>16; + ce[10] = word>>8; + ce[11] = word; + signalCEMsg( ce, NULL ); +} + +/* + * Display a single word SRC of the form "PROGXXXX" on the VSP control panel. + */ +void mf_displayProgress( u16 value ) +{ + u8 ce[12]; + u8 src[72]; + + memcpy( ce, "\x00\x00\x04\x4A\x00\x00\x00\x48\x00\x00\x00\x00", 12 ); + memcpy( src, + "\x01\x00\x00\x01" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "\x00\x00\x00\x00" + "PROGxxxx" + " ", + 72 ); + src[6] = value>>8; + src[7] = value&255; + src[44] = "0123456789ABCDEF"[(value>>12)&15]; + src[45] = "0123456789ABCDEF"[(value>>8)&15]; + src[46] = "0123456789ABCDEF"[(value>>4)&15]; + src[47] = "0123456789ABCDEF"[value&15]; + dmaAndSignalCEMsg( ce, NULL, src, sizeof(src), 9*64*1024 ); +} + +/* + * Clear the VSP control panel. Used to "erase" an SRC that was + * previously displayed. + */ +void mf_clearSrc( void ) +{ + signalCEMsg( "\x00\x00\x00\x4B\x00\x00\x00\x00\x00\x00\x00\x00", NULL ); +} + +/* + * Initialization code here. + */ +void mf_init( void ) +{ + int i; + + /* initialize */ + spin_lock_init( &spinlock ); + for( i = 0; i < sizeof(prealloc)/sizeof(*prealloc); ++i ) + free( &prealloc[i] ); + HvLpEvent_registerHandler( HvLpEvent_Type_MachineFac, &hvHandler ); + + /* virtual continue ack */ + signalCEMsg( "\x00\x00\x00\x57\x00\x00\x00\x00\x00\x00\x00\x00", NULL ); + + /* initialization complete */ + printk( KERN_NOTICE "mf.c: iSeries Linux LPAR Machine Facilities initialized\n" ); + + iSeries_proc_callback(&mf_proc_init); +} + +void mf_setSide(char side) +{ + int rc = 0; + u64 newSide = 0; + struct VspCmdData myVspCmd; + + memset(&myVspCmd, 0, sizeof(myVspCmd)); + if (side == 'A') + newSide = 0; + else if (side == 'B') + newSide = 1; + else if (side == 'C') + newSide = 2; + else + newSide = 3; + + myVspCmd.xSubData.xFunction02SelectIplTypeIn.xIplType = newSide; + myVspCmd.xCmd = 10; + + rc = signalVspInstruction(&myVspCmd); +} + +char mf_getSide(void) +{ + char returnValue = ' '; + int rc = 0; + struct VspCmdData myVspCmd; + + memset(&myVspCmd, 0, sizeof(myVspCmd)); + myVspCmd.xCmd = 2; + myVspCmd.xSubData.xFunction02SelectIplTypeIn.xIplType = 0; + mb(); + rc = signalVspInstruction(&myVspCmd); + + if(rc != 0) + { + return returnValue; + } + else + { + if (myVspCmd.xRc == 0) + { + if (myVspCmd.xSubData.xGetIplTypeOut.xIplType == 0) + returnValue = 'A'; + else if (myVspCmd.xSubData.xGetIplTypeOut.xIplType == 1) + returnValue = 'B'; + else if (myVspCmd.xSubData.xGetIplTypeOut.xIplType == 2) + returnValue = 'C'; + else + returnValue = 'D'; + } + } + + return returnValue; +} + +void mf_getSrcHistory(char *buffer, int size) +{ + /* struct IplTypeReturnStuff returnStuff; + struct StackElement * newElement = newStackElement(); + int rc = 0; + char *pages[4]; + + pages[0] = kmalloc(4096, GFP_ATOMIC); + pages[1] = kmalloc(4096, GFP_ATOMIC); + pages[2] = kmalloc(4096, GFP_ATOMIC); + pages[3] = kmalloc(4096, GFP_ATOMIC); + if(( newElement == NULL ) || (pages[0] == NULL) || (pages[1] == NULL) || (pages[2] == NULL) || (pages[3] == NULL)) + rc = -ENOMEM; + else + { + returnStuff.xType = 0; + returnStuff.xRc = 0; + returnStuff.xDone = 0; + newElement->event.xHvLpEvent.xSubtype = 6; + newElement->event.xHvLpEvent.x.xSubtypeData = ('M'<<24)+('F'<<16)+('V'<<8)+('I'<<0); + newElement->event.xUnion.xVspCmd.xEvent = &returnStuff; + newElement->event.xUnion.xVspCmd.xCmd = 4; + newElement->event.xUnion.xVspCmd.xLpIndex = HvLpConfig_getLpIndex(); + newElement->event.xUnion.xVspCmd.xRc = 0xFF; + newElement->event.xUnion.xVspCmd.xReserved1 = 0; + newElement->event.xUnion.xVspCmd.xSubData.xGetSrcHistoryIn.xPage[0] = (0x8000000000000000ULL | virt_to_absolute((unsigned long)pages[0])); + newElement->event.xUnion.xVspCmd.xSubData.xGetSrcHistoryIn.xPage[1] = (0x8000000000000000ULL | virt_to_absolute((unsigned long)pages[1])); + newElement->event.xUnion.xVspCmd.xSubData.xGetSrcHistoryIn.xPage[2] = (0x8000000000000000ULL | virt_to_absolute((unsigned long)pages[2])); + newElement->event.xUnion.xVspCmd.xSubData.xGetSrcHistoryIn.xPage[3] = (0x8000000000000000ULL | virt_to_absolute((unsigned long)pages[3])); + mb(); + rc = signalEvent(newElement); + } + + if(rc != 0) + { + return; + } + else + { + while (returnStuff.xDone != 1) + { + udelay(10); + } + + if (returnStuff.xRc == 0) + { + memcpy(buffer, pages[0], size); + } + } + + kfree(pages[0]); + kfree(pages[1]); + kfree(pages[2]); + kfree(pages[3]);*/ +} + +void mf_setCmdLine(const char *cmdline, int size, u64 side) +{ + struct VspCmdData myVspCmd; + int rc = 0; + dma_addr_t dma_addr = 0; + char *page = pci_alloc_consistent(NULL, size, &dma_addr); + + if (page == NULL) { + printk(KERN_ERR "mf.c: couldn't allocate memory to set command line\n"); + return; + } + + copy_from_user(page, cmdline, size); + + memset(&myVspCmd, 0, sizeof(myVspCmd)); + myVspCmd.xCmd = 31; + myVspCmd.xSubData.xSetKernelCmdLineIn.xToken = dma_addr; + myVspCmd.xSubData.xSetKernelCmdLineIn.xAddressType = HvLpDma_AddressType_TceIndex; + myVspCmd.xSubData.xSetKernelCmdLineIn.xSide = side; + myVspCmd.xSubData.xSetKernelCmdLineIn.xTransferLength = size; + mb(); + rc = signalVspInstruction(&myVspCmd); + + pci_free_consistent(NULL, size, page, dma_addr); +} + +int mf_getCmdLine(char *cmdline, int *size, u64 side) +{ + struct VspCmdData myVspCmd; + int rc = 0; + int len = *size; + dma_addr_t dma_addr = pci_map_single(NULL, cmdline, *size, PCI_DMA_FROMDEVICE); + + memset(cmdline, 0, *size); + memset(&myVspCmd, 0, sizeof(myVspCmd)); + myVspCmd.xCmd = 33; + myVspCmd.xSubData.xGetKernelCmdLineIn.xToken = dma_addr; + myVspCmd.xSubData.xGetKernelCmdLineIn.xAddressType = HvLpDma_AddressType_TceIndex; + myVspCmd.xSubData.xGetKernelCmdLineIn.xSide = side; + myVspCmd.xSubData.xGetKernelCmdLineIn.xTransferLength = *size; + mb(); + rc = signalVspInstruction(&myVspCmd); + + if ( ! rc ) { + + if (myVspCmd.xRc == 0) + { + len = myVspCmd.xSubData.xGetKernelCmdLineOut.xTransferLength; + } + /* else + { + memcpy(cmdline, "Bad cmdline", 11); + } + */ + } + + pci_unmap_single(NULL, dma_addr, *size, PCI_DMA_FROMDEVICE); + + return len; +} + + +int mf_setVmlinuxChunk(const char *buffer, int size, int offset, u64 side) +{ + struct VspCmdData myVspCmd; + int rc = 0; + + dma_addr_t dma_addr = 0; + + char *page = pci_alloc_consistent(NULL, size, &dma_addr); + + if (page == NULL) { + printk(KERN_ERR "mf.c: couldn't allocate memory to set vmlinux chunk\n"); + return -ENOMEM; + } + + copy_from_user(page, buffer, size); + memset(&myVspCmd, 0, sizeof(myVspCmd)); + + myVspCmd.xCmd = 30; + myVspCmd.xSubData.xGetKernelImageIn.xToken = dma_addr; + myVspCmd.xSubData.xGetKernelImageIn.xAddressType = HvLpDma_AddressType_TceIndex; + myVspCmd.xSubData.xGetKernelImageIn.xSide = side; + myVspCmd.xSubData.xGetKernelImageIn.xOffset = offset; + myVspCmd.xSubData.xGetKernelImageIn.xTransferLength = size; + mb(); + rc = signalVspInstruction(&myVspCmd); + + if(rc == 0) + { + if (myVspCmd.xRc == 0) + { + rc = 0; + } + else + { + rc = -ENOMEM; + } + } + + pci_free_consistent(NULL, size, page, dma_addr); + + return rc; +} + +int mf_getVmlinuxChunk(char *buffer, int *size, int offset, u64 side) +{ + struct VspCmdData myVspCmd; + int rc = 0; + int len = *size; + + dma_addr_t dma_addr = pci_map_single(NULL, buffer, *size, PCI_DMA_FROMDEVICE); + + memset(buffer, 0, len); + + memset(&myVspCmd, 0, sizeof(myVspCmd)); + myVspCmd.xCmd = 32; + myVspCmd.xSubData.xGetKernelImageIn.xToken = dma_addr; + myVspCmd.xSubData.xGetKernelImageIn.xAddressType = HvLpDma_AddressType_TceIndex; + myVspCmd.xSubData.xGetKernelImageIn.xSide = side; + myVspCmd.xSubData.xGetKernelImageIn.xOffset = offset; + myVspCmd.xSubData.xGetKernelImageIn.xTransferLength = len; + mb(); + rc = signalVspInstruction(&myVspCmd); + + if(rc == 0) + { + if (myVspCmd.xRc == 0) + { + *size = myVspCmd.xSubData.xGetKernelImageOut.xTransferLength; + } + else + { + rc = -ENOMEM; + } + } + + pci_unmap_single(NULL, dma_addr, *size, PCI_DMA_FROMDEVICE); + + return rc; +} + +int mf_setRtcTime(unsigned long time) +{ +/* + char ceTime[12] = "\x00\x00\x00\x41\x00\x00\x00\x00\x00\x00\x00\x00"; + int rc = 0; + struct rtc_time tm; + u16 year; + u8 day, mon, hour, min, sec, y1, y2; + + to_tm(time, &tm); + + year = tm.tm_year; + y1 = (year >> 8) & 0x00FF; + y2 = year & 0x00FF; + + 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(y1); + BIN_TO_BCD(y2); + + year = y1 * 100 + y2; + day = tm.tm_mday; + mon = tm.tm_mon; + hour = tm.tm_hour; + min = tm.tm_min; + sec = tm.tm_sec; + + memcpy(ceTime + 4, &year, 2); + memcpy(ceTime + 6, &sec, 1); + memcpy(ceTime + 7, &min, 1); + memcpy(ceTime + 8, &hour, 1); + memcpy(ceTime + 10, &day, 1); + memcpy(ceTime + 11, &mon, 1); + + rc = signalCEMsg( ceTime, NULL ); + + return rc; +*/ + struct rtc_time tm; + + to_tm(time, &tm); + + return mf_setRtc( &tm ); +} + +struct RtcTimeData +{ + volatile int xDone; + struct CeMsgData xCeMsg; + int xRc; +}; + +void getRtcTimeComplete(void * token, struct CeMsgData *ceMsg) +{ + struct RtcTimeData *rtc = (struct RtcTimeData *)token; + + memcpy(&(rtc->xCeMsg), ceMsg, sizeof(rtc->xCeMsg)); + + rtc->xRc = 0; + rtc->xDone = 1; +} + +static unsigned long lastsec = 1; + +int mf_getRtcTime(unsigned long *time) +{ +/* unsigned long usec, tsec; */ + + u32 dataWord1 = *((u32 *)(&xSpCommArea.xBcdTimeAtIplStart)); + u32 dataWord2 = *(((u32 *)&(xSpCommArea.xBcdTimeAtIplStart)) + 1); + int year = 1970; + int year1 = ( dataWord1 >> 24 ) & 0x000000FF; + int year2 = ( dataWord1 >> 16 ) & 0x000000FF; + int sec = ( dataWord1 >> 8 ) & 0x000000FF; + int min = dataWord1 & 0x000000FF; + int hour = ( dataWord2 >> 24 ) & 0x000000FF; + int day = ( dataWord2 >> 8 ) & 0x000000FF; + int mon = dataWord2 & 0x000000FF; + + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year1); + BCD_TO_BIN(year2); + year = year1 * 100 + year2; + + *time = mktime(year, mon, day, hour, min, sec); + + *time += ( jiffies / HZ ); + + /* Now THIS is a nasty hack! + * It ensures that the first two calls to mf_getRtcTime get different + * answers. That way the loop in init_time (time.c) will not think + * the clock is stuck. + */ + if ( lastsec ) { + *time -= lastsec; + --lastsec; + } + + return 0; + +} + +int mf_getRtc( struct rtc_time * tm ) +{ + + + struct CeMsgCompleteData ceComplete; + struct RtcTimeData rtcData; + int rc = 0; + + memset(&ceComplete, 0, sizeof(ceComplete)); + memset(&rtcData, 0, sizeof(rtcData)); + + ceComplete.xHdlr = &getRtcTimeComplete; + ceComplete.xToken = (void *)&rtcData; + + rc = signalCEMsg( "\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00", &ceComplete ); + + if ( rc == 0 ) + { + while( rtcData.xDone != 1 ) + { + udelay(10); + } + + if ( rtcData.xRc == 0) + { + u32 dataWord1 = *((u32 *)(rtcData.xCeMsg.xCEMsg+4)); + u32 dataWord2 = *((u32 *)(rtcData.xCeMsg.xCEMsg+8)); + u8 year = (dataWord1 >> 16 ) & 0x000000FF; + u8 sec = ( dataWord1 >> 8 ) & 0x000000FF; + u8 min = dataWord1 & 0x000000FF; + u8 hour = ( dataWord2 >> 24 ) & 0x000000FF; + u8 day = ( dataWord2 >> 8 ) & 0x000000FF; + u8 mon = dataWord2 & 0x000000FF; + + 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 <= 69 ) + year += 100; + + tm->tm_sec = sec; + tm->tm_min = min; + tm->tm_hour = hour; + tm->tm_mday = day; + tm->tm_mon = mon; + tm->tm_year = year; + + } + else + { + rc = rtcData.xRc; + tm->tm_sec = 0; + tm->tm_min = 0; + tm->tm_hour = 0; + tm->tm_mday = 15; + tm->tm_mon = 5; + tm->tm_year = 52; + + } + } + + return rc; + +} + +int mf_setRtc(struct rtc_time * tm) +{ + char ceTime[12] = "\x00\x00\x00\x41\x00\x00\x00\x00\x00\x00\x00\x00"; + int rc = 0; + u8 day, mon, hour, min, sec, y1, y2; + unsigned year; + + year = 1900 + tm->tm_year; + y1 = year / 100; + y2 = year % 100; + + sec = tm->tm_sec; + min = tm->tm_min; + hour = tm->tm_hour; + day = tm->tm_mday; + mon = tm->tm_mon + 1; + + BIN_TO_BCD(sec); + BIN_TO_BCD(min); + BIN_TO_BCD(hour); + BIN_TO_BCD(mon); + BIN_TO_BCD(day); + BIN_TO_BCD(y1); + BIN_TO_BCD(y2); + + ceTime[4] = y1; + ceTime[5] = y2; + ceTime[6] = sec; + ceTime[7] = min; + ceTime[8] = hour; + ceTime[10] = day; + ceTime[11] = mon; + + rc = signalCEMsg( ceTime, NULL ); + + return rc; +} + + + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/mf_proc.c linuxppc64_2_4/arch/ppc64/kernel/mf_proc.c --- linux-2.4.5-ac18/arch/ppc64/kernel/mf_proc.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/mf_proc.c Fri May 4 17:13:59 2001 @@ -0,0 +1,309 @@ +/* + * mf_proc.c + * Copyright (C) 2001 Kyle A. Lucke IBM Corporation + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* Change Activity: */ +/* End Change Activity */ + +#ifndef _MF_PROC_H +#include +#endif +#ifndef MF_H_INCLUDED +#include +#endif +#include + +static struct proc_dir_entry *mf_proc_root = NULL; + +int proc_mf_dump_cmdline +(char *page, char **start, off_t off, int count, int *eof, void *data); + +int proc_mf_dump_vmlinux +(char *page, char **start, off_t off, int count, int *eof, void *data); + +int proc_mf_dump_side +(char *page, char **start, off_t off, int count, int *eof, void *data); + +int proc_mf_change_side +(struct file *file, const char *buffer, unsigned long count, void *data); + +int proc_mf_dump_src +(char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_mf_change_src (struct file *file, const char *buffer, unsigned long count, void *data); +int proc_mf_change_cmdline(struct file *file, const char *buffer, unsigned long count, void *data); +int proc_mf_change_vmlinux(struct file *file, const char *buffer, unsigned long count, void *data); + + +void mf_proc_init(struct proc_dir_entry *iSeries_proc) +{ + struct proc_dir_entry *ent = NULL; + struct proc_dir_entry *mf_a = NULL; + struct proc_dir_entry *mf_b = NULL; + struct proc_dir_entry *mf_c = NULL; + struct proc_dir_entry *mf_d = NULL; + + mf_proc_root = proc_mkdir("mf", iSeries_proc); + if (!mf_proc_root) return; + + mf_a = proc_mkdir("A", mf_proc_root); + if (!mf_a) return; + + ent = create_proc_entry("cmdline", S_IFREG|S_IRUSR|S_IWUSR, mf_a); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_mf_dump_cmdline; + ent->write_proc = proc_mf_change_cmdline; + + ent = create_proc_entry("vmlinux", S_IFREG|S_IRUSR|S_IWUSR, mf_a); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_mf_dump_vmlinux; + ent->write_proc = proc_mf_change_vmlinux; + + mf_b = proc_mkdir("B", mf_proc_root); + if (!mf_b) return; + + ent = create_proc_entry("cmdline", S_IFREG|S_IRUSR|S_IWUSR, mf_b); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)1; + ent->read_proc = proc_mf_dump_cmdline; + ent->write_proc = proc_mf_change_cmdline; + + ent = create_proc_entry("vmlinux", S_IFREG|S_IRUSR|S_IWUSR, mf_b); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)1; + ent->read_proc = proc_mf_dump_vmlinux; + ent->write_proc = proc_mf_change_vmlinux; + + mf_c = proc_mkdir("C", mf_proc_root); + if (!mf_c) return; + + ent = create_proc_entry("cmdline", S_IFREG|S_IRUSR|S_IWUSR, mf_c); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)2; + ent->read_proc = proc_mf_dump_cmdline; + ent->write_proc = proc_mf_change_cmdline; + + ent = create_proc_entry("vmlinux", S_IFREG|S_IRUSR|S_IWUSR, mf_c); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)2; + ent->read_proc = proc_mf_dump_vmlinux; + ent->write_proc = proc_mf_change_vmlinux; + + mf_d = proc_mkdir("D", mf_proc_root); + if (!mf_d) return; + + + ent = create_proc_entry("cmdline", S_IFREG|S_IRUSR|S_IWUSR, mf_d); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)3; + ent->read_proc = proc_mf_dump_cmdline; + ent->write_proc = proc_mf_change_cmdline; + + ent = create_proc_entry("vmlinux", S_IFREG|S_IRUSR, mf_d); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)3; + ent->read_proc = proc_mf_dump_vmlinux; + ent->write_proc = NULL; + + ent = create_proc_entry("side", S_IFREG|S_IRUSR|S_IWUSR, mf_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_mf_dump_side; + ent->write_proc = proc_mf_change_side; + + ent = create_proc_entry("src", S_IFREG|S_IRUSR|S_IWUSR, mf_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_mf_dump_src; + ent->write_proc = proc_mf_change_src; + + +} + +int proc_mf_dump_cmdline +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = count; + char *p; + + len = mf_getCmdLine(page, &len, (u64)data); + + p = page + len - 1; + while ( p > page ) { + if ( (*p == 0) || (*p == ' ') ) + --p; + else + break; + } + if ( *p != '\n' ) { + ++p; + *p = '\n'; + } + ++p; + *p = 0; + len = p - page; + + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) + return 0; + } else + len = count; + *start = page + off; + return len; +} + +int proc_mf_dump_vmlinux +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int sizeToGet = count; + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if (mf_getVmlinuxChunk(page, &sizeToGet, off, (u64)data) == 0) + { + if (sizeToGet != 0) + { + *start = page + off; + printk("mf_proc.c: got count %d off %d\n", sizeToGet, (int)off); + return sizeToGet; + } + else + { + printk("mf_proc.c: eof\n"); + *eof = 1; + return 0; + } + } + else + { + printk("mf_proc.c: eof\n"); + *eof = 1; + return 0; + } +} + + +int proc_mf_dump_side +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + + char mf_current_side = mf_getSide(); + len = sprintf(page, "%c\n", mf_current_side); + + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +int proc_mf_change_side(struct file *file, const char *buffer, unsigned long count, void *data) +{ + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if ((*buffer != 'A') && + (*buffer != 'B') && + (*buffer != 'C') && + (*buffer != 'D')) + { + printk(KERN_ERR "mf_proc.c: proc_mf_change_side: invalid side\n"); + return -EINVAL; + } + + mf_setSide(*buffer); + + return count; +} + +int proc_mf_dump_src +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + mf_getSrcHistory(page, count); + len = count; + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) + return 0; + } else + len = count; + *start = page + off; + return len; +} + +int proc_mf_change_src(struct file *file, const char *buffer, unsigned long count, void *data) +{ + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if ((count < 4) && (count != 1)) + { + printk(KERN_ERR "mf_proc: invalid src\n"); + return -EINVAL; + } + + if ((count == 1) && ((*buffer) == '\0')) + { + mf_clearSrc(); + } + else + { + mf_displaySrc(*(u32 *)buffer); + } + + return count; +} + +int proc_mf_change_cmdline(struct file *file, const char *buffer, unsigned long count, void *data) +{ + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + mf_setCmdLine(buffer, count, (u64)data); + + return count; +} + +int proc_mf_change_vmlinux(struct file *file, const char *buffer, unsigned long count, void *data) +{ + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + mf_setVmlinuxChunk(buffer, count, file->f_pos, (u64)data); + file->f_pos += count; + + return count; +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/misc.S linuxppc64_2_4/arch/ppc64/kernel/misc.S --- linux-2.4.5-ac18/arch/ppc64/kernel/misc.S Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/misc.S Tue Jul 10 16:11:03 2001 @@ -0,0 +1,1228 @@ +/* + * arch/ppc/kernel/misc.S + * + * + * + * This file contains miscellaneous low-level functions. + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Largely rewritten by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras. + * Adapted for iSeries by Mike Corrigan (mikejc@us.ibm.com) + * PPC64 updates by Dave Engebretsen (engebret@us.ibm.com) + * + * 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 +#include +#include +#include +#include +#include +#include +#include "ppc_asm.h" + + .text + +_GLOBAL(__delay) + cmpdi 0,r3,0 + mtctr r3 + beqlr +1: bdnz 1b + blr + +/* + * Returns (address we're running at) - (address we were linked at) + * for use before the text and data are mapped to KERNELBASE. + */ + +_GLOBAL(reloc_offset) + mflr r0 + bl 1f +1: mflr r3 + LOADADDR(r4,1b) + sub r3,r4,r3 + mtlr r0 + blr + +_GLOBAL(get_msr) + mfmsr r3 + blr + +_GLOBAL(get_dar) + mfdar r3 + blr + +_GLOBAL(get_srr0) + mfsrr0 r3 + blr + +_GLOBAL(get_srr1) + mfsrr1 r3 + blr + +_GLOBAL(get_sp) + mr r3,r1 + blr + +_GLOBAL(get_paca) + mfspr r3,SPRG3; + blr + +/* void __no_use_save_flags(unsigned long *flags) */ +_GLOBAL(__no_use_save_flags) + mfmsr r4 + std r4,0(r3) + blr + +/* void __no_use_restore_flags(unsigned long flags) */ +_GLOBAL(__no_use_restore_flags) +/* + * Just set/clear the MSR_EE bit through restore/flags but do not + * change anything else. This is needed by the RT system and makes + * sense anyway. + * -- Cort + */ + mfmsr r5 + mr r4,r3 + mr r3,r5 + rlwimi r3,r4,0,16,16 + /* Copy all except the MSR_EE bit from r4 (current MSR value) + to r3. This is the sort of thing the rlwimi instruction is + designed for. -- paulus. */ + /* rlwimi r3,r4,0,17,15 */ + /* Check if things are setup the way we want _already_. */ + cmpw 0,r3,r5 + beqlr + /* are we enabling interrupts? */ + rlwinm. r0,r3,0,16,16 + beq 1f + /* if so, check if there are any lost interrupts */ + LOADBASE(r7,ppc_n_lost_interrupts) + lwz r7,ppc_n_lost_interrupts@l(r7) + cmpi 0,r7,0 /* lost interrupts to process first? */ + bne- .do_lost_interrupts + /* Check for lost decrementer interrupts. + * (If decrementer popped while we were in the hypervisor) + * (calls timer_interrupt if so) + */ + CHECKDECR(r4,r5) + bne- .do_fake_decrementer + /* Check for pending I/O Events. If no I/O events pending, + * then CR0 = "eq" and r4 == 0 + * (kills registers r5 and r6) + */ + CHECKLPQUEUE(r4,r5,r6) + bne- .do_lost_interrupts +1: sync + mtmsrd r3 + isync + blr +/* void __no_lpq_restore_flags(unsigned long flags) */ +_GLOBAL(__no_lpq_restore_flags) + mfmsr r4 + /* Copy all except the MSR_EE bit from r4 (current MSR value) + to r3. This is the sort of thing the rlwimi instruction is + designed for. -- paulus. */ + rlwimi r3,r4,0,17,15 + /* Check if things are setup the way we want _already_. */ + cmpw 0,r3,r4 + beqlr + sync + mtmsrd r3 + isync + blr + +_GLOBAL(__no_use_cli) + mfmsr r0 /* Get current interrupt state */ + rlwinm r3,r0,16+1,32-1,31 /* Extract old value of 'EE' */ + rldicl r0,r0,48,1 + rldicl r0,r0,16,0 /* clear MSR_EE in r0 */ + mtmsrd r0 /* Update machine state */ + blr /* Done */ + +_GLOBAL(__no_use_sti) + mfmsr r3 /* Get current state */ + ori r3,r3,MSR_EE /* Turn on 'EE' bit */ + LOADBASE(r4,ppc_n_lost_interrupts) + lwz r4,ppc_n_lost_interrupts@l(r4) + cmpi 0,r4,0 /* lost interrupts to process first? */ + bne- .do_lost_interrupts + + /* Check for lost decrementer interrupts. + * (If decrementer popped while we were in the hypervisor) + * (calls timer_interrupt if so) + */ + CHECKDECR(r4,r5) + bne- .do_fake_decrementer + /* Check for pending I/O events. If no I/O events pending, + * then CR0 = "eq" and R4 = 0 + * (kills registers r5 and r6) + */ + CHECKLPQUEUE(r4,r5,r6) + bne- .do_lost_interrupts + sync /* Some chip revs have problems here... */ + mtmsrd r3 /* Update machine state */ + blr + +/* + * We were about to enable interrupts but we have to simulate + * some interrupts that were lost by enable_irq first. + */ +_GLOBAL(do_lost_interrupts) + mflr r0 + std r0,16(r1) + std r3,-8(r1) + stdu r1,-(STACK_FRAME_OVERHEAD+16)(r1) +1: bl .fake_interrupt + LOADBASE(r4,ppc_n_lost_interrupts) + lwz r4,ppc_n_lost_interrupts@l(r4) + cmpi 0,r4,0 + bne- 1b + /* Check for pending I/O events. If no I/O events pending, + * then CR0 = "eq" and R4 = 0 + * (kills registers r5 and r6) + */ + CHECKLPQUEUE(r4,r5,r6) + bne- 1b + addi r1,r1,STACK_FRAME_OVERHEAD+16 + ld r3,-8(r1) + ld r0,16(r1) + mtlr r0 + mtmsrd r3 + blr + +_GLOBAL(do_fake_decrementer) + mflr r0 + std r0,16(r1) + std r3,-8(r1) + stdu r1,-(STACK_FRAME_OVERHEAD+16)(r1) + bl .fake_decrementer + addi r1,r1,STACK_FRAME_OVERHEAD+16 + ld r3,-8(r1) + ld r0,16(r1) + mtlr r0 + CHECKLPQUEUE(r4,r5,r6) + bne- .do_lost_interrupts + mtmsrd r3 + blr + +/* + * complement mask on the msr then "or" some values on. + * _nmask_and_or_msr(nmask, value_to_or) + */ + _GLOBAL(_nmask_and_or_msr) + mfmsr r0 /* Get current msr */ + andc r0,r0,r3 /* And off the bits set in r3 (first parm) */ + or r0,r0,r4 /* Or on the bits in r4 (second parm) */ + mtmsrd r0 /* Update machine state */ + SYNC + blr /* Done */ + +/* + * Flush instruction cache. + * This is a no-op on the 601. + */ +_GLOBAL(flush_instruction_cache) + +/* + * This is called by kgdb code + * and should probably go away + * to be replaced by invalidating + * the cache lines that are actually + * modified + */ + mfspr r3,PVR + rlwinm r3,r3,16,16,31 + cmpi 0,r3,1 + beqlr /* for 601, do nothing */ + /* 603/604 processor - use invalidate-all bit in HID0 */ + mfspr r3,HID0 + ori r3,r3,HID0_ICFI + mtspr HID0,r3 + SYNC + blr + +/* + * Write any modified data cache blocks out to memory + * and invalidate the corresponding instruction cache blocks. + * This is a no-op on the 601. + * + * flush_icache_range(unsigned long start, unsigned long stop) + * + * flush all bytes from start through stop-1 inclusive + */ + +_GLOBAL(flush_icache_range) + +/* + * Flush the data cache to memory + * + * Different systems have different cache line sizes + * and in some cases i-cache and d-cache line sizes differ from + * each other. + */ + LOADADDR(r10,naca) /* Get Naca address */ + ld r10,0(r10) + lhz r7,DCACHEL1LINESIZE(r10) /* Get cache line size */ + addi r5,r7,-1 + andc r6,r3,r5 /* round low to line bdy */ + subf r8,r6,r4 /* compute length */ + add r8,r8,r5 /* ensure we get enough */ + lhz r9,DCACHEL1LOGLINESIZE(r10) /* Get log-2 of cache line size */ + srw. r8,r8,r9 /* compute line count */ + beqlr /* nothing to do? */ + mtctr r8 +1: dcbst 0,r6 + add r6,r6,r7 + bdnz 1b + sync + +/* Now invalidate the instruction cache */ + + lhz r7,ICACHEL1LINESIZE(r10) /* Get Icache line size */ + addi r5,r7,-1 + andc r6,r3,r5 /* round low to line bdy */ + subf r8,r6,r4 /* compute length */ + add r8,r8,r5 + lhz r9,ICACHEL1LOGLINESIZE(r10) /* Get log-2 of Icache line size */ + srw. r8,r8,r9 /* compute line count */ + beqlr /* nothing to do? */ + mtctr r8 +2: icbi 0,r6 + add r6,r6,r7 + bdnz 2b + sync + isync + blr + +/* + * Like above, but only do the D-cache. + * + * flush_dcache_range(unsigned long start, unsigned long stop) + * + * flush all bytes from start to stop-1 inclusive + */ +_GLOBAL(flush_dcache_range) + +/* + * Flush the data cache to memory + * + * Different systems have different cache line sizes + */ + LOADADDR(r10,naca) /* Get Naca address */ + ld r10,0(r10) + lhz r7,DCACHEL1LINESIZE(r10) /* Get dcache line size */ + addi r5,r7,-1 + andc r6,r3,r5 /* round low to line bdy */ + subf r8,r6,r4 /* compute length */ + add r8,r8,r5 /* ensure we get enough */ + lhz r9,DCACHEL1LOGLINESIZE(r10) /* Get log-2 of dcache line size */ + srw. r8,r8,r9 /* compute line count */ + beqlr /* nothing to do? */ + mtctr r8 +0: dcbst 0,r6 + add r6,r6,r7 + bdnz 0b + sync + blr + +/* + * Flush a particular page from the data cache to RAM. + * Note: this is necessary because the instruction cache does *not* + * snoop from the data cache. + * This is a no-op on the 601 which has a unified cache. + * + * void __flush_page_to_ram(void *page) + */ +_GLOBAL(__flush_page_to_ram) +/* + * Flush the data cache to memory + * + * Different systems have different cache line sizes + */ + +/* Flush the dcache */ + LOADADDR(r7,naca) + ld r7,0(r7) + clrrdi r3,r3,12 /* Page align */ + lhz r4,DCACHEL1LINESPERPAGE(r7) /* Get # dcache lines per page */ + lhz r5,DCACHEL1LINESIZE(r7) /* Get dcache line size */ + mr r6,r3 + mtctr r4 +0: dcbst 0,r6 + add r6,r6,r5 + bdnz 0b + sync + +/* Now invalidate the icache */ + + lhz r4,ICACHEL1LINESPERPAGE(r7) /* Get # icache lines per page */ + lhz r5,ICACHEL1LINESIZE(r7) /* Get icache line size */ + mtctr r4 +1: icbi 0,r3 + add r3,r3,r5 + bdnz 1b + sync + isync + blr + + +/* + * Flush a particular page from the instruction cache. + * Note: this is necessary because the instruction cache does *not* + * snoop from the data cache. + * + * void __flush_icache_page(void *page) + */ +_GLOBAL(__flush_icache_page) +/* + * Different systems have different cache line sizes + */ + +/* Invalidate the icache */ + + LOADADDR(r6,naca) + ld r6,0(r6) + clrrdi r3,r3,12 /* Page align */ + lhz r4,ICACHEL1LINESPERPAGE(r6) /* Get # icache lines per page */ + lhz r5,ICACHEL1LINESIZE(r6) /* Get icache line size */ + mtctr r4 +1: icbi 0,r3 + add r3,r3,r5 + bdnz 1b + sync + isync + blr + +/* + * Clear a page using the dcbz instruction, which doesn't cause any + * memory traffic (except to write out any cache lines which get + * displaced). This only works on cacheable memory. + */ +_GLOBAL(clear_page) + LOADADDR(r6,naca) + ld r6,0(r6) + clrrdi r3,r3,12 /* Page align */ + lhz r4,DCACHEL1LINESPERPAGE(r6) /* Get # dcache lines per page */ + lhz r5,DCACHEL1LINESIZE(r6) /* Get dcache line size */ + mtctr r4 +0: dcbz 0,r3 + add r3,r3,r5 + bdnz 0b + blr + +/* + * Copy a whole page. We use the dcbz instruction on the destination + * to reduce memory traffic (it eliminates the unnecessary reads of + * the destination into cache). This requires that the destination + * is cacheable. + */ +#define COPY_32_BYTES \ + ld r6,8(r4); \ + ld r7,16(r4); \ + ld r8,24(r4); \ + ldu r9,32(r4); \ + std r6,8(r3); \ + std r7,16(r3); \ + std r8,24(r3); \ + stdu r9,32(r3) + +_GLOBAL(copy_page) + + LOADADDR(r9,naca) + ld r9,0(r9) + clrrdi r3,r3,12 /* Page align */ + clrrdi r4,r4,12 /* Page align */ + lhz r5,DCACHEL1LINESPERPAGE(r9) /* Get # dcache lines per page */ + lhz r0,DCACHEL1LINESIZE(r9) /* Get dcache line size */ + mtctr r5 + addi r3,r3,-8 + addi r4,r4,-8 + li r10,8 + + cmpi 0,r0,32 + beq do_32_byte_line + cmpi 0,r0,64 + beq do_64_byte_line + cmpi 0,r0,128 + beq do_128_byte_line + + /* We don't have code specifically for this cache line size */ + /* Assume that the cache line size is at least 32 (and of */ + /* course a multiple of 32) */ + /* This code will work for all power-of-2 cache line sizes */ + /* from 32 to 4096 */ + +1: mr r5,r0 + dcbz r10,r3 +0: COPY_32_BYTES + addi r5,r5,-32 + or. r5,r5,r5 + bne 0b + bdnz 1b + blr + + .globl do_32_byte_line +do_32_byte_line: + dcbz r10,r3 + COPY_32_BYTES + bdnz do_32_byte_line + blr + + .globl do_64_byte_line +do_64_byte_line: + dcbz r10,r3 + COPY_32_BYTES + COPY_32_BYTES + bdnz do_64_byte_line + blr + + .globl do_128_byte_line +do_128_byte_line: + dcbz r10,r3 + COPY_32_BYTES + COPY_32_BYTES + COPY_32_BYTES + COPY_32_BYTES + bdnz do_128_byte_line + blr + +/* + * I/O string operations + * + * insb(port, buf, len) + * outsb(port, buf, len) + * insw(port, buf, len) + * outsw(port, buf, len) + * insl(port, buf, len) + * outsl(port, buf, len) + * insw_ns(port, buf, len) + * outsw_ns(port, buf, len) + * insl_ns(port, buf, len) + * outsl_ns(port, buf, len) + * + * The *_ns versions don't do byte-swapping. + */ +_GLOBAL(_insb) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,1 + blelr- +00: lbz r5,0(r3) + eieio + stbu r5,1(r4) + bdnz 00b + blr + +_GLOBAL(_outsb) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,1 + blelr- +00: lbzu r5,1(r4) + stb r5,0(r3) + eieio + bdnz 00b + blr + +_GLOBAL(_insw) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,2 + blelr- +00: lhbrx r5,0,r3 + eieio + sthu r5,2(r4) + bdnz 00b + blr + +_GLOBAL(_outsw) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,2 + blelr- +00: lhzu r5,2(r4) + eieio + sthbrx r5,0,r3 + bdnz 00b + blr + +_GLOBAL(_insl) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,4 + blelr- +00: lwbrx r5,0,r3 + eieio + stwu r5,4(r4) + bdnz 00b + blr + +_GLOBAL(_outsl) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,4 + blelr- +00: lwzu r5,4(r4) + stwbrx r5,0,r3 + eieio + bdnz 00b + blr + +_GLOBAL(ide_insw) +_GLOBAL(_insw_ns) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,2 + blelr- +00: lhz r5,0(r3) + eieio + sthu r5,2(r4) + bdnz 00b + blr + +_GLOBAL(ide_outsw) +_GLOBAL(_outsw_ns) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,2 + blelr- +00: lhzu r5,2(r4) + sth r5,0(r3) + eieio + bdnz 00b + blr + +_GLOBAL(_insl_ns) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,4 + blelr- +00: lwz r5,0(r3) + eieio + stwu r5,4(r4) + bdnz 00b + blr + +_GLOBAL(_outsl_ns) + cmpwi 0,r5,0 + mtctr r5 + subi r4,r4,4 + blelr- +00: lwzu r5,4(r4) + stw r5,0(r3) + eieio + bdnz 00b + blr + +/* + * Extended precision shifts + * + * R3/R4 has 64 bit value + * R5 has shift count + * result in R3/R4 + * + * ashrdi3: XXXYYY/ZZZAAA -> SSSXXX/YYYZZZ + * ashldi3: XXXYYY/ZZZAAA -> YYYZZZ/AAA000 + * lshrdi3: XXXYYY/ZZZAAA -> 000XXX/YYYZZZ + */ +/* MIKEC: These may no longer be needed...what does gcc expect ? */ + +_GLOBAL(__ashrdi3) + li r6,32 + sub r6,r6,r5 + slw r7,r3,r6 /* isolate YYY */ + srw r4,r4,r5 /* isolate ZZZ */ + or r4,r4,r7 /* YYYZZZ */ + sraw r3,r3,r5 /* SSSXXX */ + blr + +_GLOBAL(__ashldi3) + li r6,32 + sub r6,r6,r5 + srw r7,r4,r6 /* isolate ZZZ */ + slw r4,r4,r5 /* AAA000 */ + slw r3,r3,r5 /* YYY--- */ + or r3,r3,r7 /* YYYZZZ */ + blr + +_GLOBAL(__lshrdi3) + li r6,32 + sub r6,r6,r5 + slw r7,r3,r6 /* isolate YYY */ + srw r4,r4,r5 /* isolate ZZZ */ + or r4,r4,r7 /* YYYZZZ */ + srw r3,r3,r5 /* 000XXX */ + blr + +_GLOBAL(abs) + cmpi 0,r3,0 + bge 10f + neg r3,r3 +10: blr + +_GLOBAL(_get_SP) + mr r3,r1 /* Close enough */ + blr + +_GLOBAL(_get_THRM1) + mfspr r3,THRM1 + blr + +_GLOBAL(_get_THRM2) + mfspr r3,THRM2 + blr + +_GLOBAL(_get_THRM3) + mfspr r3,THRM3 + blr + +_GLOBAL(_set_THRM1) + mtspr THRM1,r3 + blr + +_GLOBAL(_set_THRM2) + mtspr THRM2,r3 + blr + +_GLOBAL(_set_THRM3) + mtspr THRM3,r3 + blr + +_GLOBAL(_get_PVR) + mfspr r3,PVR + blr + +_GLOBAL(_get_HID0) + mfspr r3,HID0 + blr + +_GLOBAL(_get_ICTC) + mfspr r3,ICTC + blr + +_GLOBAL(_set_ICTC) + mtspr ICTC,r3 + blr + + + +_GLOBAL(cvt_fd) + lfd 0,-4(r5) /* load up fpscr value */ + mtfsf 0xff,0 + lfs 0,0(r3) + stfd 0,0(r4) + mffs 0 /* save new fpscr value */ + stfd 0,-4(r5) + blr + +_GLOBAL(cvt_df) + lfd 0,-4(r5) /* load up fpscr value */ + mtfsf 0xff,0 + lfd 0,0(r3) + stfs 0,0(r4) + mffs 0 /* save new fpscr value */ + stfd 0,-4(r5) + blr + +_GLOBAL(__clear_msr_me) + mfmsr r0 /* Get current interrupt state */ + lis r3,0 + ori r3,r3,MSR_ME + andc r0,r0,r3 /* Clears bit in (r4) */ + sync /* Some chip revs have problems here */ + mtmsrd r0 /* Update machine state */ + blr + +/* + * Create a kernel thread + * kernel_thread(fn, arg, flags) + */ +_GLOBAL(kernel_thread) + mr r6,r3 /* function */ + ori r3,r5,CLONE_VM /* flags */ + li r0,__NR_clone + sc + cmpi 0,r3,0 /* parent or child? */ + bnelr /* return if parent */ + + mfspr r5,SPRG3 /* Get Paca */ + ld r5,PACACURRENT(r5) /* Get 'current' */ + li r0,0 /* clear out p->thread.regs */ + std r0,THREAD+PT_REGS(r5) /* since we don't have user ctx */ + + ld r6,0(r6) + mtlr r6 /* fn addr in lr */ + mr r3,r4 /* load arg and call fn */ + blrl + li r0,__NR_exit /* exit after child exits */ + li r3,0 + sc + +/* + * This routine is just here to keep GCC happy - sigh... + */ +_GLOBAL(__main) + blr + +#define SYSCALL(name) \ +_GLOBAL(name) \ + li r0,__NR_##name; \ + sc; \ + bnslr; \ + /* MIKEC: Is errno a 32-bit int or a 64-bit long ? */\ + LOADBASE(r4,errno); \ + stw r3,errno@l(r4); \ + li r3,-1; \ + blr + +#define __NR__exit __NR_exit + +SYSCALL(sync) +SYSCALL(setsid) +SYSCALL(write) +SYSCALL(dup) +SYSCALL(execve) +SYSCALL(open) +SYSCALL(close) +SYSCALL(waitpid) +SYSCALL(fork) +SYSCALL(delete_module) +SYSCALL(_exit) +SYSCALL(lseek) +SYSCALL(read) +#ifdef CONFIG_BINFMT_ELF32 +/* Why isn't this a) automatic, b) written in 'C'? */ + .data + .align 8 +_GLOBAL(sys_call_table32) + .llong .sys_ni_syscall /* 0 - old "setup()" system call */ + .llong .sys32_exit + .llong .sys32_fork + .llong .sys_read + .llong .sys_write + .llong .sys32_open /* 5 */ + .llong .sys_close + .llong .sys32_waitpid + .llong .sys32_creat + .llong .sys_link + .llong .sys_unlink /* 10 */ + .llong .sys32_execve + .llong .sys_chdir + .llong .sys_time + .llong .sys32_mknod + .llong .sys32_chmod /* 15 */ + .llong .sys_lchown + .llong .sys_ni_syscall /* old break syscall holder */ + .llong .sys32_stat + .llong .sys32_lseek + .llong .sys_getpid /* 20 */ + .llong .sys32_mount + .llong .sys_oldumount + .llong .sys_setuid + .llong .sys_getuid + .llong .sys_stime /* 25 */ + .llong .sys32_ptrace + .llong .sys_alarm + .llong .sys32_fstat + .llong .sys32_pause + .llong .sys32_utime /* 30 */ + .llong .sys_ni_syscall /* old stty syscall holder */ + .llong .sys_ni_syscall /* old gtty syscall holder */ + .llong .sys32_access + .llong .sys32_nice + .llong .sys_ni_syscall /* 35 */ /* old ftime syscall holder */ + .llong .sys_sync + .llong .sys32_kill + .llong .sys_rename + .llong .sys32_mkdir + .llong .sys_rmdir /* 40 */ + .llong .sys_dup + .llong .sys_pipe + .llong .sys32_times + .llong .sys_ni_syscall /* old prof syscall holder */ + .llong .sys_brk /* 45 */ + .llong .sys_setgid + .llong .sys_getgid + .llong .sys_signal + .llong .sys_geteuid + .llong .sys_getegid /* 50 */ + .llong .sys_acct + .llong .sys32_umount /* recycled never used phys() */ + .llong .sys_ni_syscall /* old lock syscall holder */ + .llong .sys32_ioctl + .llong .sys32_fcntl /* 55 */ + .llong .sys_ni_syscall /* old mpx syscall holder */ + .llong .sys32_setpgid + .llong .sys_ni_syscall /* old ulimit syscall holder */ + .llong .sys_olduname + .llong .sys32_umask /* 60 */ + .llong .sys_chroot + .llong .sys_ustat + .llong .sys_dup2 + .llong .sys_getppid + .llong .sys_getpgrp /* 65 */ + .llong .sys_setsid + .llong .sys32_sigaction + .llong .sys_sgetmask + .llong .sys32_ssetmask + .llong .sys_setreuid /* 70 */ + .llong .sys_setregid + .llong .sys_sigsuspend + .llong .sys32_sigpending + .llong .sys32_sethostname + .llong .sys32_setrlimit /* 75 */ + .llong .sys32_old_getrlimit + .llong .sys32_getrusage + .llong .sys32_gettimeofday + .llong .sys32_settimeofday + .llong .sys32_getgroups /* 80 */ + .llong .sys32_setgroups + .llong .ppc32_select + .llong .sys_symlink + .llong .sys32_lstat + .llong .sys32_readlink /* 85 */ + .llong .sys_uselib + .llong .sys32_swapon + .llong .sys32_reboot + .llong .old32_readdir + .llong .sys32_mmap /* 90 */ + .llong .sys_munmap + .llong .sys_truncate + .llong .sys_ftruncate + .llong .sys_fchmod + .llong .sys_fchown /* 95 */ + .llong .sys32_getpriority + .llong .sys32_setpriority + .llong .sys_ni_syscall /* old profil syscall holder */ + .llong .sys32_statfs + .llong .sys32_fstatfs /* 100 */ + .llong .sys32_ioperm + .llong .sys32_socketcall + .llong .sys32_syslog + .llong .sys32_setitimer + .llong .sys32_getitimer /* 105 */ + .llong .sys32_newstat + .llong .sys32_newlstat + .llong .sys32_newfstat + .llong .sys_uname + .llong .sys32_iopl /* 110 */ + .llong .sys_vhangup + .llong .sys_ni_syscall /* old 'idle' syscall */ + .llong .sys32_vm86 + .llong .sys32_wait4 + .llong .sys_swapoff /* 115 */ + .llong .sys32_sysinfo + .llong .sys32_ipc + .llong .sys_fsync + .llong .sys32_sigreturn + .llong .sys32_clone /* 120 */ + .llong .sys32_setdomainname + .llong .ppc64_newuname + .llong .sys32_modify_ldt + .llong .sys32_adjtimex + .llong .sys_mprotect /* 125 */ + .llong .sys32_sigprocmask + .llong .sys32_create_module + .llong .sys32_init_module + .llong .sys32_delete_module + .llong .sys32_get_kernel_syms /* 130 */ + .llong .sys32_quotactl + .llong .sys32_getpgid + .llong .sys_fchdir + .llong .sys32_bdflush + .llong .sys32_sysfs /* 135 */ + .llong .sys32_personality + .llong .sys_ni_syscall /* for afs_syscall */ + .llong .sys_setfsuid + .llong .sys_setfsgid + .llong .sys_llseek /* 140 */ + .llong .sys32_getdents + .llong .ppc32_select + .llong .sys_flock + .llong .sys32_msync + .llong .sys32_readv /* 145 */ + .llong .sys32_writev + .llong .sys32_getsid + .llong .sys_fdatasync + .llong .sys_sysctl + .llong .sys_mlock /* 150 */ + .llong .sys_munlock + .llong .sys32_mlockall + .llong .sys_munlockall + .llong .sys32_sched_setparam + .llong .sys32_sched_getparam /* 155 */ + .llong .sys32_sched_setscheduler + .llong .sys32_sched_getscheduler + .llong .sys_sched_yield + .llong .sys32_sched_get_priority_max + .llong .sys32_sched_get_priority_min /* 160 */ + .llong .sys32_sched_rr_get_interval + .llong .sys32_nanosleep + .llong .sys32_mremap + .llong .sys_setresuid + .llong .sys_getresuid /* 165 */ + .llong .sys32_query_module + .llong .sys_poll + .llong .sys32_nfsservctl + .llong .sys_setresgid + .llong .sys_getresgid /* 170 */ + .llong .sys32_prctl + .llong .sys32_rt_sigreturn + .llong .sys32_rt_sigaction + .llong .sys32_rt_sigprocmask + .llong .sys32_rt_sigpending /* 175 */ + .llong .sys32_rt_sigtimedwait + .llong .sys32_rt_sigqueueinfo + .llong .sys32_rt_sigsuspend + .llong .sys32_pread + .llong .sys32_pwrite /* 180 */ + .llong .sys_chown + .llong .sys_getcwd + .llong .sys_capget + .llong .sys_capset + .llong .sys32_sigaltstack /* 185 */ + .llong .sys32_sendfile + .llong .sys_ni_syscall /* streams1 */ + .llong .sys_ni_syscall /* streams2 */ + .llong .sys32_vfork + .llong .sys32_getrlimit /* 190 */ + .llong .sys_ni_syscall /* 191 */ /* Unused */ + .llong .sys_ni_syscall /* 192 - reserved - mmap2 */ + .llong .sys32_truncate64 /* 193 - truncate64 */ + .llong .sys32_ftruncate64 /* 194 - ftruncate64 */ + .llong .sys_stat64 /* 195 - stat64 */ + .llong .sys_lstat64 /* 196 - lstat64 */ + .llong .sys_fstat64 /* 197 - fstat64 */ + .llong .sys32_pciconfig_read /* 198 */ + .llong .sys32_pciconfig_write /* 199 */ + .llong .sys_pciconfig_iobase /* 200 */ + .llong .sys_ni_syscall /* 201 - reserved - MacOnLinux - new */ + .llong .sys_getdents64 /* 202 */ + .llong .sys_pivot_root /* 203 */ + .llong .sys32_fcntl64 /* 204 */ + .llong .sys_madvise /* 205 */ + .llong .sys_mincore /* 206 */ + .rept NR_syscalls-206 + .llong .sys_ni_syscall + .endr +#endif + .data + .align 8 +_GLOBAL(sys_call_table) + .llong .sys_ni_syscall /* 0 - old "setup()" system call */ + .llong .sys_exit + .llong .sys_fork + .llong .sys_read + .llong .sys_write + .llong .sys_open /* 5 */ + .llong .sys_close + .llong .sys_waitpid + .llong .sys_creat + .llong .sys_link + .llong .sys_unlink /* 10 */ + .llong .sys_execve + .llong .sys_chdir + .llong .sys_time + .llong .sys_mknod + .llong .sys_chmod /* 15 */ + .llong .sys_lchown + .llong .sys_ni_syscall /* old break syscall holder */ + .llong .sys_stat + .llong .sys_lseek + .llong .sys_getpid /* 20 */ + .llong .sys_mount + .llong .sys_oldumount + .llong .sys_setuid + .llong .sys_getuid + .llong .sys_stime /* 25 */ + .llong .sys_ptrace + .llong .sys_alarm + .llong .sys_fstat + .llong .sys_pause + .llong .sys_utime /* 30 */ + .llong .sys_ni_syscall /* old stty syscall holder */ + .llong .sys_ni_syscall /* old gtty syscall holder */ + .llong .sys_access + .llong .sys_nice + .llong .sys_ni_syscall /* 35 */ /* old ftime syscall holder */ + .llong .sys_sync + .llong .sys_kill + .llong .sys_rename + .llong .sys_mkdir + .llong .sys_rmdir /* 40 */ + .llong .sys_dup + .llong .sys_pipe + .llong .sys_times + .llong .sys_ni_syscall /* old prof syscall holder */ + .llong .sys_brk /* 45 */ + .llong .sys_setgid + .llong .sys_getgid + .llong .sys_signal + .llong .sys_geteuid + .llong .sys_getegid /* 50 */ + .llong .sys_acct + .llong .sys_umount /* recycled never used phys() */ + .llong .sys_ni_syscall /* old lock syscall holder */ + .llong .sys_ioctl + .llong .sys_fcntl /* 55 */ + .llong .sys_ni_syscall /* old mpx syscall holder */ + .llong .sys_setpgid + .llong .sys_ni_syscall /* old ulimit syscall holder */ + .llong .sys_olduname + .llong .sys_umask /* 60 */ + .llong .sys_chroot + .llong .sys_ustat + .llong .sys_dup2 + .llong .sys_getppid + .llong .sys_getpgrp /* 65 */ + .llong .sys_setsid + .llong .sys_sigaction + .llong .sys_sgetmask + .llong .sys_ssetmask + .llong .sys_setreuid /* 70 */ + .llong .sys_setregid + .llong .sys_sigsuspend + .llong .sys_sigpending + .llong .sys_sethostname + .llong .sys_setrlimit /* 75 */ + .llong .sys_old_getrlimit + .llong .sys_getrusage + .llong .sys_gettimeofday + .llong .sys_settimeofday + .llong .sys_getgroups /* 80 */ + .llong .sys_setgroups + .llong .sys_select + .llong .sys_symlink + .llong .sys_lstat + .llong .sys_readlink /* 85 */ + .llong .sys_uselib + .llong .sys_swapon + .llong .sys_reboot + .llong .old_readdir + .llong .sys_mmap /* 90 */ + .llong .sys_munmap + .llong .sys_truncate + .llong .sys_ftruncate + .llong .sys_fchmod + .llong .sys_fchown /* 95 */ + .llong .sys_getpriority + .llong .sys_setpriority + .llong .sys_ni_syscall /* old profil syscall holder */ + .llong .sys_statfs + .llong .sys_fstatfs /* 100 */ + .llong .sys_ioperm + .llong .sys_socketcall + .llong .sys_syslog + .llong .sys_setitimer + .llong .sys_getitimer /* 105 */ + .llong .sys_newstat + .llong .sys_newlstat + .llong .sys_newfstat + .llong .sys_uname + .llong .sys_iopl /* 110 */ + .llong .sys_vhangup + .llong .sys_ni_syscall /* old 'idle' syscall */ + .llong .sys_vm86 + .llong .sys_wait4 + .llong .sys_swapoff /* 115 */ + .llong .sys_sysinfo + .llong .sys_ipc + .llong .sys_fsync + .llong .sys_sigreturn + .llong .sys_clone /* 120 */ + .llong .sys_setdomainname + .llong .ppc64_newuname + .llong .sys_modify_ldt + .llong .sys_adjtimex + .llong .sys_mprotect /* 125 */ + .llong .sys_sigprocmask + .llong .sys_create_module + .llong .sys_init_module + .llong .sys_delete_module + .llong .sys_get_kernel_syms /* 130 */ + .llong .sys_quotactl + .llong .sys_getpgid + .llong .sys_fchdir + .llong .sys_bdflush + .llong .sys_sysfs /* 135 */ + .llong .sys_personality + .llong .sys_ni_syscall /* for afs_syscall */ + .llong .sys_setfsuid + .llong .sys_setfsgid + .llong .sys_llseek /* 140 */ + .llong .sys_getdents + .llong .sys_select + .llong .sys_flock + .llong .sys_msync + .llong .sys_readv /* 145 */ + .llong .sys_writev + .llong .sys_getsid + .llong .sys_fdatasync + .llong .sys_sysctl + .llong .sys_mlock /* 150 */ + .llong .sys_munlock + .llong .sys_mlockall + .llong .sys_munlockall + .llong .sys_sched_setparam + .llong .sys_sched_getparam /* 155 */ + .llong .sys_sched_setscheduler + .llong .sys_sched_getscheduler + .llong .sys_sched_yield + .llong .sys_sched_get_priority_max + .llong .sys_sched_get_priority_min /* 160 */ + .llong .sys_sched_rr_get_interval + .llong .sys_nanosleep + .llong .sys_mremap + .llong .sys_setresuid + .llong .sys_getresuid /* 165 */ + .llong .sys_query_module + .llong .sys_poll + .llong .sys_nfsservctl + .llong .sys_setresgid + .llong .sys_getresgid /* 170 */ + .llong .sys_prctl + .llong .sys_rt_sigreturn + .llong .sys_rt_sigaction + .llong .sys_rt_sigprocmask + .llong .sys_rt_sigpending /* 175 */ + .llong .sys_rt_sigtimedwait + .llong .sys_rt_sigqueueinfo + .llong .sys_rt_sigsuspend + .llong .sys_pread + .llong .sys_pwrite /* 180 */ + .llong .sys_chown + .llong .sys_getcwd + .llong .sys_capget + .llong .sys_capset + .llong .sys_sigaltstack /* 185 */ + .llong .sys_sendfile + .llong .sys_ni_syscall /* streams1 */ + .llong .sys_ni_syscall /* streams2 */ + .llong .sys_vfork + .llong .sys_getrlimit /* 190 */ + .llong .sys_ni_syscall /* 191 */ /* Unused */ + .llong .sys_ni_syscall /* 192 - reserved - mmap2 */ + .llong .sys_ni_syscall /* 193 - reserved - truncate64 */ + .llong .sys_ni_syscall /* 194 - reserved - ftruncate64 */ + .llong .sys_ni_syscall /* 195 - reserved - stat64 */ + .llong .sys_ni_syscall /* 196 - reserved - lstat64 */ + .llong .sys_ni_syscall /* 197 - reserved - fstat64 */ + .llong .sys_pciconfig_read /* 198 */ + .llong .sys_pciconfig_write /* 199 */ + .llong .sys_pciconfig_iobase /* 200 */ + .llong .sys_ni_syscall /* 201 - reserved - MacOnLinux - new */ + .llong .sys_getdents64 /* 202 */ + .llong .sys_pivot_root /* 203 */ + .llong .sys_ni_syscall /* 204 */ + .llong .sys_madvise /* 205 */ + .llong .sys_mincore /* 206 */ + .rept NR_syscalls-206 + .llong .sys_ni_syscall + .endr diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/mk_defs.c linuxppc64_2_4/arch/ppc64/kernel/mk_defs.c --- linux-2.4.5-ac18/arch/ppc64/kernel/mk_defs.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/mk_defs.c Sun Jun 24 22:00:18 2001 @@ -0,0 +1,197 @@ +/* + * This program is used to generate definitions needed by + * assembly language modules. + * + * We use the technique used in the OSF Mach kernel code: + * generate asm statements containing #defines, + * compile this file to assembler, and then extract the + * #defines from the assembly-language output. + * + * We go an extra step and generate C printf's which are executed + * on the host machine. These are included by mk_defs_tpl which + * does the real work of producing the #define output. This lets + * us perform simple expression evaluation that cannot otherwise + * be performed by the assembler. Just make sure the expressions + * are architecture neutral.... + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define DEFINE(sym, val) \ + asm volatile("\n\tprintf(\"#define " #sym " %%d\\n\", %0);" : : "i" (val)); + +#define DEFINEFUNCED(sym, func, val) \ + asm volatile("\n\tprintf(\"#define " #sym " %%d\\n\", " #func "(%0));" : : "i" (val)); + +int +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)); + DEFINE(PROCESSOR, offsetof(struct task_struct, processor)); + DEFINE(SIGPENDING, offsetof(struct task_struct, sigpending)); + DEFINE(THREAD, offsetof(struct task_struct, thread)); + DEFINE(MM, offsetof(struct task_struct, mm)); + DEFINE(ACTIVE_MM, offsetof(struct task_struct, active_mm)); + DEFINE(TASK_STRUCT_SIZE, sizeof(struct task_struct)); + DEFINE(KSP, offsetof(struct thread_struct, ksp)); + + DEFINE(PACA, offsetof(struct Naca, paca)); + DEFINE(PACA_SIZE, sizeof(struct Paca)); + DEFINE(RTAS_ARGS_SIZE, sizeof(struct rtas_args)); + DEFINE(ITLPPACA_SIZE, sizeof(struct ItLpPaca)); + DEFINE(ITLPREGSAVE_SIZE, sizeof(struct ItLpRegSave)); + + DEFINE(DCACHEL1LINESIZE, offsetof(struct Naca, dCacheL1LineSize)); + DEFINE(DCACHEL1LOGLINESIZE, offsetof(struct Naca, dCacheL1LogLineSize)); + DEFINE(DCACHEL1LINESPERPAGE, offsetof(struct Naca, dCacheL1LinesPerPage)); + + DEFINE(ICACHEL1LINESIZE, offsetof(struct Naca, iCacheL1LineSize)); + DEFINE(ICACHEL1LOGLINESIZE, offsetof(struct Naca, iCacheL1LogLineSize)); + DEFINE(ICACHEL1LINESPERPAGE, offsetof(struct Naca, iCacheL1LinesPerPage)); + DEFINE(PHYSICALMEMORYSIZE, offsetof(struct Naca, physicalMemorySize)); + + DEFINE(PACAPACAINDEX, offsetof(struct Paca, xPacaIndex)); + DEFINE(PACAPROCSTART, offsetof(struct Paca, xProcStart)); + DEFINE(PACAKSAVE, offsetof(struct Paca, xKsave)); + DEFINE(PACATHREAD, offsetof(struct Paca, xThread)); + DEFINE(PACACURRENT, offsetof(struct Paca, xCurrent)); + DEFINE(PACASAVEDMSR, offsetof(struct Paca, xSavedMsr)); + DEFINE(PACASAVEDLR, offsetof(struct Paca, xSavedLr)); + DEFINE(PACASTABREAL, offsetof(struct Paca, xStab_data.real)); + DEFINE(PACASTABVIRT, offsetof(struct Paca, xStab_data.virt)); + DEFINE(PACAR1, offsetof(struct Paca, xR1)); + DEFINE(PACAR21, offsetof(struct Paca, xR21)); + DEFINE(PACAR22, offsetof(struct Paca, xR22)); + DEFINE(PACALPQUEUE, offsetof(struct Paca, lpQueuePtr)); + DEFINE(PACALPPACA, offsetof(struct Paca, xLpPaca)); + DEFINE(PACATOC, offsetof(struct Paca, xTOC)); + DEFINE(LPPACA, offsetof(struct Paca, xLpPaca)); + DEFINE(LPREGSAV, offsetof(struct Paca, xRegSav)); + DEFINE(LPPACASRR0, offsetof(struct ItLpPaca, xSavedSrr0)); + DEFINE(LPPACASRR1, offsetof(struct ItLpPaca, xSavedSrr1)); + DEFINE(LPPACADECRINT, offsetof(struct ItLpPaca, xDecrInt)); + DEFINE(LPPACAIPIINT, offsetof(struct ItLpPaca, xIpiCnt)); + DEFINE(LPQCUREVENTPTR, offsetof(struct ItLpQueue, xSlicCurEventPtr)); + DEFINE(LPQOVERFLOW, offsetof(struct ItLpQueue, xPlicOverflowIntPending)); + DEFINE(LPEVENTFLAGS, offsetof(struct HvLpEvent, xFlags)); + + DEFINE(RTASBASE, offsetof(struct rtas_t, base)); + DEFINE(RTASENTRY, offsetof(struct rtas_t, entry)); + DEFINE(RTASSIZE, offsetof(struct rtas_t, size)); + + DEFINE(PGDIR, offsetof(struct thread_struct, pgdir)); + DEFINE(LAST_SYSCALL, offsetof(struct thread_struct, last_syscall)); + DEFINE(PT_REGS, offsetof(struct thread_struct, regs)); + DEFINE(PT_TRACESYS, PT_TRACESYS); + DEFINE(TASK_FLAGS, offsetof(struct task_struct, flags)); + DEFINE(TASK_PTRACE, offsetof(struct task_struct, ptrace)); + DEFINE(TASK_PERSONALITY, offsetof(struct task_struct, personality)); + DEFINE(NEED_RESCHED, offsetof(struct task_struct, need_resched)); + DEFINE(THREAD_FPR0, offsetof(struct thread_struct, fpr[0])); + DEFINE(THREAD_FPSCR, offsetof(struct thread_struct, fpscr)); + DEFINE(THREAD_FLAGS, offsetof(struct thread_struct, flags)); + DEFINE(PPC_FLAG_32BIT, PPC_FLAG_32BIT); +#ifdef CONFIG_ALTIVEC + DEFINE(THREAD_VR0, offsetof(struct thread_struct, vr[0])); + DEFINE(THREAD_VRSAVE, offsetof(struct thread_struct, vrsave)); + DEFINE(THREAD_VSCR, offsetof(struct thread_struct, vscr)); +#endif /* CONFIG_ALTIVEC */ + /* Interrupt register frame */ + DEFINE(TASK_UNION_SIZE, sizeof(union task_union)); + DEFINE(STACK_FRAME_OVERHEAD, STACK_FRAME_OVERHEAD); + /* 288 = # of volatile regs, int & fp, for leaf routines */ + /* which do not stack a frame. See the PPC64 ABI. */ + DEFINE(INT_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct pt_regs) + 288); + /* 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])); + DEFINE(GPR3, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[3])); + DEFINE(GPR4, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[4])); + DEFINE(GPR5, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[5])); + DEFINE(GPR6, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[6])); + DEFINE(GPR7, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[7])); + DEFINE(GPR8, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[8])); + DEFINE(GPR9, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[9])); + DEFINE(GPR10, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[10])); + DEFINE(GPR11, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[11])); + DEFINE(GPR12, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[12])); + DEFINE(GPR13, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[13])); + DEFINE(GPR14, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[14])); + DEFINE(GPR15, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[15])); + DEFINE(GPR16, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[16])); + DEFINE(GPR17, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[17])); + DEFINE(GPR18, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[18])); + DEFINE(GPR19, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[19])); + DEFINE(GPR20, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[20])); + DEFINE(GPR21, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[21])); + DEFINE(GPR22, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[22])); + DEFINE(GPR23, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[23])); + DEFINE(GPR24, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[24])); + DEFINE(GPR25, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[25])); + DEFINE(GPR26, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[26])); + DEFINE(GPR27, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[27])); + DEFINE(GPR28, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[28])); + DEFINE(GPR29, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[29])); + DEFINE(GPR30, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[30])); + DEFINE(GPR31, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[31])); + /* Note: these symbols include _ because they overlap with special + * register names + */ + DEFINE(_NIP, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, nip)); + DEFINE(_MSR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, msr)); + DEFINE(_CTR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, ctr)); + DEFINE(_LINK, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, link)); + DEFINE(_CCR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, ccr)); + DEFINE(_MQ, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, mq)); + DEFINE(_XER, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, xer)); + DEFINE(_DAR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dar)); + DEFINE(_DSISR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dsisr)); + /* The PowerPC 400-class processors have neither the DAR nor the DSISR + * SPRs. Hence, we overload them to hold the similar DEAR and ESR SPRs + * for such processors. + */ + DEFINE(_DEAR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dar)); + DEFINE(_ESR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dsisr)); + DEFINE(ORIG_GPR3, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, orig_gpr3)); + DEFINE(RESULT, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, result)); + DEFINE(TRAP, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, trap)); + DEFINE(CLONE_VM, CLONE_VM); + + DEFINEFUNCED(IRQ_CPUSTAT_SHIFT, __ilog2, sizeof(irq_cpustat_t)); + DEFINE(SOFTIRQ_ACTIVE, offsetof(irq_cpustat_t, __softirq_active)); + DEFINE(SOFTIRQ_MASK, offsetof(irq_cpustat_t, __softirq_mask)); + + return 0; +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/mk_defs_tpl.c linuxppc64_2_4/arch/ppc64/kernel/mk_defs_tpl.c --- linux-2.4.5-ac18/arch/ppc64/kernel/mk_defs_tpl.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/mk_defs_tpl.c Mon May 21 16:53:32 2001 @@ -0,0 +1,42 @@ +/* + * 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 +#define __KERNEL__ + +static __inline__ int __ilog2(unsigned long x) +{ + int lz; +#if (defined(__powerpc__) && defined(_ARCH_PPC64)) + asm ("cntlzd %0,%1" : "=r" (lz) : "r" (x)); + return 63-lz; +#endif +/* these worthless pieces of code are here to support x-compilation of the + * kernel because __ilog2 is used in the mk_defs program which run natively + * on whatever kind of box you are building on .. groovy eh? -- tgall + */ +#if (defined(__powerpc__) && !defined(_ARCH_PPC64)) + asm ("cntlzw %0,%1" : "=r" (lz) : "r" (x)); + return 31-lz; +#else + lz=0; + while(x > 1) + { + x=x/2; + lz++; + } + return (lz); +#endif +} + + +int main (int argc, char *argv[]) +{ + printf("/* The source for this file is automatically generated. Do not edit! */\n"); + /* include the file generated by grep printf mk_defs.s */ +# include "mk_defs_out.c" + return 0; +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/open_pic.c linuxppc64_2_4/arch/ppc64/kernel/open_pic.c --- linux-2.4.5-ac18/arch/ppc64/kernel/open_pic.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/open_pic.c Tue Jun 26 16:40:48 2001 @@ -0,0 +1,841 @@ +/* + * arch/ppc/kernel/open_pic.c -- OpenPIC Interrupt Handling + * + * Copyright (C) 1997 Geert Uytterhoeven + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "local_irq.h" +#include "open_pic.h" +#include "open_pic_defs.h" +#include "i8259.h" +#include + +void* OpenPIC_Addr; +static volatile struct OpenPIC *OpenPIC = NULL; +u_int OpenPIC_NumInitSenses __initdata = 0; +u_char *OpenPIC_InitSenses __initdata = NULL; +extern int use_of_interrupt_tree; + +void find_ISUs(void); + +static u_int NumProcessors; +static u_int NumSources; +static int NumISUs; +static int open_pic_irq_offset; +static volatile unsigned char* chrp_int_ack_special; +static int broken_ipi_registers; + +OpenPIC_SourcePtr ISU[OPENPIC_MAX_ISU]; + +static void openpic_end_irq(unsigned int irq_nr); +static void openpic_ack_irq(unsigned int irq_nr); +static void openpic_set_affinity(unsigned int irq_nr, unsigned long cpumask); + +struct hw_interrupt_type open_pic = { + " OpenPIC ", + NULL, + NULL, + openpic_enable_irq, + openpic_disable_irq, + openpic_ack_irq, + openpic_end_irq, + openpic_set_affinity +}; + +#ifdef CONFIG_SMP +static void openpic_end_ipi(unsigned int irq_nr); +static void openpic_ack_ipi(unsigned int irq_nr); +static void openpic_enable_ipi(unsigned int irq_nr); +static void openpic_disable_ipi(unsigned int irq_nr); + +struct hw_interrupt_type open_pic_ipi = { + " OpenPIC ", + NULL, + NULL, + openpic_enable_ipi, + openpic_disable_ipi, + openpic_ack_ipi, + openpic_end_ipi, + 0 +}; +#endif /* CONFIG_SMP */ + +unsigned int openpic_vec_ipi; +unsigned int openpic_vec_timer; +unsigned int openpic_vec_spurious; + +/* + * Accesses to the current processor's openpic registers + */ +#ifdef CONFIG_SMP +#define THIS_CPU Processor[cpu] +#define DECL_THIS_CPU int cpu = smp_hw_index[smp_processor_id()] +#define CHECK_THIS_CPU check_arg_cpu(cpu) +#else +#define THIS_CPU Processor[0] +#define DECL_THIS_CPU +#define CHECK_THIS_CPU +#endif /* CONFIG_SMP */ + +#if 1 +#define check_arg_ipi(ipi) \ + if (ipi < 0 || ipi >= OPENPIC_NUM_IPI) \ + printk(KERN_ERR "open_pic.c:%d: illegal ipi %d\n", __LINE__, ipi); +#define check_arg_timer(timer) \ + if (timer < 0 || timer >= OPENPIC_NUM_TIMERS) \ + printk(KERN_ERR "open_pic.c:%d: illegal timer %d\n", __LINE__, timer); +#define check_arg_vec(vec) \ + if (vec < 0 || vec >= OPENPIC_NUM_VECTORS) \ + printk(KERN_ERR "open_pic.c:%d: illegal vector %d\n", __LINE__, vec); +#define check_arg_pri(pri) \ + if (pri < 0 || pri >= OPENPIC_NUM_PRI) \ + printk(KERN_ERR "open_pic.c:%d: illegal priority %d\n", __LINE__, pri); +/* + * Print out a backtrace if it's out of range, since if it's larger than NR_IRQ's + * data has probably been corrupted and we're going to panic or deadlock later + * anyway --Troy + */ +extern unsigned long* _get_SP(void); +#define check_arg_irq(irq) \ + if (irq < open_pic_irq_offset || irq >= (NumSources+open_pic_irq_offset)){ \ + printk(KERN_ERR "open_pic.c:%d: illegal irq %d\n", __LINE__, irq); \ + print_backtrace(_get_SP()); } +#define check_arg_cpu(cpu) \ + if (cpu < 0 || cpu >= OPENPIC_MAX_PROCESSORS){ \ + printk(KERN_ERR "open_pic.c:%d: illegal cpu %d\n", __LINE__, cpu); \ + print_backtrace(_get_SP()); } +#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 + +#define GET_ISU(source) ISU[(source) >> 4][(source) & 0xf] + +void __init openpic_init_IRQ(void) +{ + struct device_node *np; + int i; + unsigned int *addrp; + unsigned char* chrp_int_ack_special = 0; + unsigned char init_senses[NR_IRQS - NUM_8259_INTERRUPTS]; + int nmi_irq = -1; +#if defined(CONFIG_VT) && defined(CONFIG_ADB_KEYBOARD) && defined(XMON) + struct device_node *kbd; +#endif + + if (!(np = find_devices("pci")) + || !(addrp = (unsigned int *) + get_property(np, "8259-interrupt-acknowledge", NULL))) + printk(KERN_ERR "Cannot find pci to get ack address\n"); + else + chrp_int_ack_special = (unsigned char *) + ioremap(addrp[prom_n_addr_cells(np)-1], 1); + /* hydra still sets OpenPIC_InitSenses to a static set of values */ + if (OpenPIC_InitSenses == NULL) { + prom_get_irq_senses(init_senses, NUM_8259_INTERRUPTS, NR_IRQS); + OpenPIC_InitSenses = init_senses; + OpenPIC_NumInitSenses = NR_IRQS - NUM_8259_INTERRUPTS; + } + openpic_init(1, NUM_8259_INTERRUPTS, chrp_int_ack_special, nmi_irq); + for ( i = 0 ; i < NUM_8259_INTERRUPTS ; i++ ) + irq_desc[i].handler = &i8259_pic; + i8259_init(); +} + +u_int openpic_read(volatile u_int *addr) +{ + u_int val; + + val = in_le32(addr); + return val; +} + +static inline void openpic_write(volatile u_int *addr, u_int val) +{ + out_le32(addr, val); +} + +static inline u_int openpic_readfield(volatile u_int *addr, u_int mask) +{ + u_int val = openpic_read(addr); + return val & mask; +} + +inline void openpic_writefield(volatile u_int *addr, u_int mask, + u_int field) +{ + u_int val = openpic_read(addr); + openpic_write(addr, (val & ~mask) | (field & mask)); +} + +static inline void openpic_clearfield(volatile u_int *addr, u_int mask) +{ + openpic_writefield(addr, mask, 0); +} + +static inline void openpic_setfield(volatile u_int *addr, u_int mask) +{ + openpic_writefield(addr, mask, mask); +} + +static void openpic_safe_writefield(volatile u_int *addr, u_int mask, + u_int field) +{ + unsigned int loops = 100000; + + openpic_setfield(addr, OPENPIC_MASK); + while (openpic_read(addr) & OPENPIC_ACTIVITY) { + if (!loops--) { + printk(KERN_ERR "openpic_safe_writefield timeout\n"); + break; + } + } + openpic_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK); +} + +#ifdef CONFIG_SMP +u_int openpic_read_IPI(volatile u_int* addr) +{ + u_int val = 0; + + if (broken_ipi_registers) + /* yes this is right ... bug, feature, you decide! -- tgall */ + val = in_be32(addr); + else + val = in_le32(addr); + + return val; +} + +static void openpic_test_broken_IPI(void) +{ + u_int t; + + openpic_write(&OpenPIC->Global.IPI_Vector_Priority(0), OPENPIC_MASK); + t = openpic_read(&OpenPIC->Global.IPI_Vector_Priority(0)); + if (t == le32_to_cpu(OPENPIC_MASK)) { + printk(KERN_INFO "OpenPIC reversed IPI registers detected\n"); + broken_ipi_registers = 1; + } +} + +/* because of the power3 be / le above, this is needed */ +inline void openpic_writefield_IPI(volatile u_int* addr, u_int mask, u_int field) +{ + u_int val = openpic_read_IPI(addr); + openpic_write(addr, (val & ~mask) | (field & mask)); +} + +static inline void openpic_clearfield_IPI(volatile u_int *addr, u_int mask) +{ + openpic_writefield_IPI(addr, mask, 0); +} + +static inline void openpic_setfield_IPI(volatile u_int *addr, u_int mask) +{ + openpic_writefield_IPI(addr, mask, mask); +} + +static void openpic_safe_writefield_IPI(volatile u_int *addr, u_int mask, u_int field) +{ + unsigned int loops = 100000; + + openpic_setfield_IPI(addr, OPENPIC_MASK); + + /* wait until it's not in use */ + /* BenH: Is this code really enough ? I would rather check the result + * and eventually retry ... + */ + while(openpic_read_IPI(addr) & OPENPIC_ACTIVITY) { + if (!loops--) { + printk(KERN_ERR "openpic_safe_writefield timeout\n"); + break; + } + } + + openpic_writefield_IPI(addr, mask, field | OPENPIC_MASK); +} +#endif /* CONFIG_SMP */ + +void __init openpic_init(int main_pic, int offset, unsigned char* chrp_ack, + int programmer_switch_irq) +{ + u_int t, i; + u_int timerfreq; + const char *version; + + if (!OpenPIC_Addr) { + printk(KERN_INFO "No OpenPIC found !\n"); + return; + } + OpenPIC = (volatile struct OpenPIC *)OpenPIC_Addr; + + ppc_md.progress("openpic enter",0x122); + + t = openpic_read(&OpenPIC->Global.Feature_Reporting0); + switch (t & OPENPIC_FEATURE_VERSION_MASK) { + case 1: + version = "1.0"; + break; + case 2: + version = "1.2"; + break; + case 3: + version = "1.3"; + break; + default: + version = "?"; + break; + } + NumProcessors = ((t & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >> + OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1; + NumSources = ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >> + OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1; + printk(KERN_INFO "OpenPIC Version %s (%d CPUs and %d IRQ sources) at %p\n", + version, NumProcessors, NumSources, OpenPIC); + timerfreq = openpic_read(&OpenPIC->Global.Timer_Frequency); + if (timerfreq) + printk(KERN_INFO "OpenPIC timer frequency is %d.%06d MHz\n", + timerfreq / 1000000, timerfreq % 1000000); + + if (!main_pic) + return; + + open_pic_irq_offset = offset; + chrp_int_ack_special = (volatile unsigned char*)chrp_ack; + + find_ISUs(); + + /* Initialize timer interrupts */ + ppc_md.progress("openpic timer",0x3ba); + for (i = 0; i < OPENPIC_NUM_TIMERS; i++) { + /* Disabled, Priority 0 */ + openpic_inittimer(i, 0, openpic_vec_timer+i); + /* No processor */ + openpic_maptimer(i, 0); + } + +#ifdef CONFIG_SMP + /* Initialize IPI interrupts */ + ppc_md.progress("openpic ipi",0x3bb); + openpic_test_broken_IPI(); + for (i = 0; i < OPENPIC_NUM_IPI; i++) { + /* Disabled, Priority 10..13 */ + openpic_initipi(i, 10+i, openpic_vec_ipi+i); + /* IPIs are per-CPU */ + irq_desc[openpic_vec_ipi+i].status |= IRQ_PER_CPU; + irq_desc[openpic_vec_ipi+i].handler = &open_pic_ipi; + } +#endif + + /* Initialize external interrupts */ + ppc_md.progress("openpic ext",0x3bc); + + openpic_set_priority(0xf); + + /* SIOint (8259 cascade) is special */ + if (offset) { + openpic_initirq(0, 8, offset, 1, 1); + openpic_mapirq(0, 1<<0); + } + + /* Init all external sources */ + for (i = 1; i < NumSources; i++) { + int pri, sense; + + /* the bootloader may have left it enabled (bad !) */ + openpic_disable_irq(i+offset); + + pri = (i == programmer_switch_irq)? 9: 8; + sense = (i < OpenPIC_NumInitSenses)? OpenPIC_InitSenses[i]: 1; + if (sense) + irq_desc[i+offset].status = IRQ_LEVEL; + + /* Enabled, Priority 8 or 9 */ + openpic_initirq(i, pri, i+offset, !sense, sense); + /* Processor 0 */ + openpic_mapirq(i, 1<<0); + } + + /* Init descriptors */ + for (i = offset; i < NumSources + offset; i++) + irq_desc[i].handler = &open_pic; + + /* Initialize the spurious interrupt */ + ppc_md.progress("openpic spurious",0x3bd); + openpic_set_spurious(openpic_vec_spurious); + + /* Initialize the cascade */ + if (offset) { + if (request_irq(offset, no_action, SA_INTERRUPT, + "82c59 cascade", NULL)) + printk(KERN_ERR "Unable to get OpenPIC IRQ 0 for cascade\n"); + } + openpic_set_priority(0); + openpic_disable_8259_pass_through(); + + ppc_md.progress("openpic exit",0x222); +} + +void openpic_setup_ISU(int isu_num, unsigned long addr) +{ + if (isu_num >= OPENPIC_MAX_ISU) + return; + ISU[isu_num] = (OpenPIC_SourcePtr) ioremap(addr, 0x400); + if (isu_num >= NumISUs) + NumISUs = isu_num + 1; +} + +void find_ISUs(void) +{ + /* Use /interrupt-controller/reg and + * /interrupt-controller/interrupt-ranges from OF device tree + * the ISU array is setup in chrp_pci.c in ibm_add_bridges + * as a result + * -- tgall + */ + + /* basically each ISU is a bus, and this assumes that + * open_pic_isu_count interrupts per bus are possible + * ISU == Interrupt Source + */ + NumSources = NumISUs * 0x10; + openpic_vec_ipi = NumSources + open_pic_irq_offset; + openpic_vec_timer = openpic_vec_ipi + OPENPIC_NUM_IPI; + openpic_vec_spurious = openpic_vec_timer + OPENPIC_NUM_TIMERS; +} + +static inline void openpic_reset(void) +{ + openpic_setfield(&OpenPIC->Global.Global_Configuration0, + OPENPIC_CONFIG_RESET); +} + +static inline void openpic_enable_8259_pass_through(void) +{ + openpic_clearfield(&OpenPIC->Global.Global_Configuration0, + OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); +} + +static void openpic_disable_8259_pass_through(void) +{ + openpic_setfield(&OpenPIC->Global.Global_Configuration0, + OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); +} + +/* + * Find out the current interrupt + */ +static u_int openpic_irq(void) +{ + u_int vec; + DECL_THIS_CPU; + + CHECK_THIS_CPU; + vec = openpic_readfield(&OpenPIC->THIS_CPU.Interrupt_Acknowledge, + OPENPIC_VECTOR_MASK); + return vec; +} + +static void openpic_eoi(void) +{ + DECL_THIS_CPU; + + CHECK_THIS_CPU; + openpic_write(&OpenPIC->THIS_CPU.EOI, 0); + /* Handle PCI write posting */ + (void)openpic_read(&OpenPIC->THIS_CPU.EOI); +} + + +static inline u_int openpic_get_priority(void) +{ + DECL_THIS_CPU; + + CHECK_THIS_CPU; + return openpic_readfield(&OpenPIC->THIS_CPU.Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK); +} + +static void openpic_set_priority(u_int pri) +{ + DECL_THIS_CPU; + + 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 + */ +static inline u_int openpic_get_spurious(void) +{ + return openpic_readfield(&OpenPIC->Global.Spurious_Vector, + OPENPIC_VECTOR_MASK); +} + +static void openpic_set_spurious(u_int vec) +{ + check_arg_vec(vec); + openpic_writefield(&OpenPIC->Global.Spurious_Vector, OPENPIC_VECTOR_MASK, + vec); +} + +#ifdef CONFIG_SMP +/* + * Convert a cpu mask from logical to physical cpu numbers. + */ +static inline u32 physmask(u32 cpumask) +{ + int i; + u32 mask = 0; + + for (i = 0; i < smp_num_cpus; ++i, cpumask >>= 1) + mask |= (cpumask & 1) << smp_hw_index[i]; + return mask; +} +#else +#define physmask(cpumask) (cpumask) +#endif + +void openpic_init_processor(u_int cpumask) +{ + openpic_write(&OpenPIC->Global.Processor_Initialization, + physmask(cpumask)); +} + +#ifdef CONFIG_SMP +/* + * Initialize an interprocessor interrupt (and disable it) + * + * ipi: OpenPIC interprocessor interrupt number + * pri: interrupt source priority + * vec: the vector it will produce + */ +static void __init openpic_initipi(u_int ipi, u_int pri, u_int vec) +{ + check_arg_ipi(ipi); + check_arg_pri(pri); + check_arg_vec(vec); + openpic_safe_writefield_IPI(&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 + * + * Externally called, however, it takes an IPI number (0...OPENPIC_NUM_IPI) + * and not a system-wide interrupt number + */ +void openpic_cause_IPI(u_int ipi, u_int cpumask) +{ + DECL_THIS_CPU; + + CHECK_THIS_CPU; + check_arg_ipi(ipi); + openpic_write(&OpenPIC->THIS_CPU.IPI_Dispatch(ipi), + physmask(cpumask)); +} + +void openpic_request_IPIs(void) +{ + int i; + + /* + * Make sure this matches what is defined in smp.c for + * smp_message_{pass|recv}() or what shows up in + * /proc/interrupts will be wrong!!! --Troy */ + + if (OpenPIC == NULL) + return; + + request_irq(openpic_vec_ipi, + openpic_ipi_action, 0, "IPI0 (call function)", 0); + request_irq(openpic_vec_ipi+1, + openpic_ipi_action, 0, "IPI1 (reschedule)", 0); + request_irq(openpic_vec_ipi+2, + openpic_ipi_action, 0, "IPI2 (invalidate tlb)", 0); + request_irq(openpic_vec_ipi+3, + openpic_ipi_action, 0, "IPI3 (xmon break)", 0); + + for ( i = 0; i < OPENPIC_NUM_IPI ; i++ ) + openpic_enable_ipi(openpic_vec_ipi+i); +} + +/* + * Do per-cpu setup for SMP systems. + * + * Get IPI's working and start taking interrupts. + * -- Cort + */ +static spinlock_t openpic_setup_lock __initdata = SPIN_LOCK_UNLOCKED; + +void __init do_openpic_setup_cpu(void) +{ +#ifdef CONFIG_IRQ_ALL_CPUS + int i; + u32 msk = 1 << smp_hw_index[smp_processor_id()]; +#endif + + spin_lock(&openpic_setup_lock); + +#ifdef CONFIG_IRQ_ALL_CPUS + /* let the openpic know we want intrs. default affinity + * is 0xffffffff until changed via /proc + * That's how it's done on x86. If we want it differently, then + * we should make sure we also change the default values of irq_affinity + * in irq.c. + */ + for (i = 0; i < NumSources ; i++) + openpic_mapirq(i, openpic_read(&GET_ISU(i).Destination) | msk); +#endif /* CONFIG_IRQ_ALL_CPUS */ + openpic_set_priority(0); + + spin_unlock(&openpic_setup_lock); +} +#endif /* CONFIG_SMP */ + +/* + * Initialize a timer interrupt (and disable it) + * + * timer: OpenPIC timer number + * pri: interrupt source priority + * vec: the vector it will produce + */ +static void __init 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 + */ +static void __init openpic_maptimer(u_int timer, u_int cpumask) +{ + check_arg_timer(timer); + openpic_write(&OpenPIC->Global.Timer[timer].Destination, + physmask(cpumask)); +} + + +/* + * + * All functions below take an offset'ed irq argument + * + */ + + +/* + * Enable/disable an external interrupt source + * + * Externally called, irq is an offseted system-wide interrupt number + */ +static void openpic_enable_irq(u_int irq) +{ + unsigned int loops = 100000; + check_arg_irq(irq); + + openpic_clearfield(&GET_ISU(irq - open_pic_irq_offset).Vector_Priority, OPENPIC_MASK); + /* make sure mask gets to controller before we return to user */ + do { + if (!loops--) { + printk(KERN_ERR "openpic_enable_irq timeout\n"); + break; + } + + mb(); /* sync is probably useless here */ + } while(openpic_readfield(&GET_ISU(irq - open_pic_irq_offset).Vector_Priority, + OPENPIC_MASK)); +} + +static void openpic_disable_irq(u_int irq) +{ + u32 vp; + unsigned int loops = 100000; + + check_arg_irq(irq); + + openpic_setfield(&GET_ISU(irq - open_pic_irq_offset).Vector_Priority, OPENPIC_MASK); + /* make sure mask gets to controller before we return to user */ + do { + if (!loops--) { + printk(KERN_ERR "openpic_disable_irq timeout\n"); + break; + } + + mb(); /* sync is probably useless here */ + vp = openpic_readfield(&GET_ISU(irq - open_pic_irq_offset).Vector_Priority, + OPENPIC_MASK | OPENPIC_ACTIVITY); + } while((vp & OPENPIC_ACTIVITY) && !(vp & OPENPIC_MASK)); +} + +#ifdef CONFIG_SMP +/* + * Enable/disable an IPI interrupt source + * + * Externally called, irq is an offseted system-wide interrupt number + */ +void openpic_enable_ipi(u_int irq) +{ + irq -= openpic_vec_ipi; + check_arg_ipi(irq); + openpic_clearfield_IPI(&OpenPIC->Global.IPI_Vector_Priority(irq), OPENPIC_MASK); + +} +void openpic_disable_ipi(u_int irq) +{ + /* NEVER disable an IPI... that's just plain wrong! */ +} + +#endif + +/* + * 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 + */ +static void openpic_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense) +{ + openpic_safe_writefield(&GET_ISU(irq).Vector_Priority, + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK | + OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK, + (pri << OPENPIC_PRIORITY_SHIFT) | vec | + (pol ? OPENPIC_POLARITY_POSITIVE : + OPENPIC_POLARITY_NEGATIVE) | + (sense ? OPENPIC_SENSE_LEVEL : OPENPIC_SENSE_EDGE)); +} + +/* + * Map an interrupt source to one or more CPUs + */ +static void openpic_mapirq(u_int irq, u_int physmask) +{ + openpic_write(&GET_ISU(irq).Destination, physmask); +} + +/* + * Set the sense for an interrupt source (and disable it!) + * + * sense: 1 for level, 0 for edge + */ +static inline void openpic_set_sense(u_int irq, int sense) +{ + openpic_safe_writefield(&GET_ISU(irq).Vector_Priority, + OPENPIC_SENSE_LEVEL, + (sense ? OPENPIC_SENSE_LEVEL : 0)); +} + +/* No spinlocks, should not be necessary with the OpenPIC + * (1 register = 1 interrupt and we have the desc lock). + */ +static void openpic_ack_irq(unsigned int irq_nr) +{ +#if 1 /* masking should be unnecessary, but I still get spurrious */ + openpic_disable_irq(irq_nr); +#endif + if ((irq_desc[irq_nr].status & IRQ_LEVEL) == 0) + openpic_eoi(); +} + +static void openpic_end_irq(unsigned int irq_nr) +{ + if ((irq_desc[irq_nr].status & IRQ_LEVEL) != 0) + openpic_eoi(); + +#if 1 /* masking should be unnecessary, but I still get spurrious */ + if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + openpic_enable_irq(irq_nr); +#endif +} + +static void openpic_set_affinity(unsigned int irq_nr, unsigned long cpumask) +{ + openpic_mapirq(irq_nr - open_pic_irq_offset, physmask(cpumask)); +} + +#ifdef CONFIG_SMP +static void openpic_ack_ipi(unsigned int irq_nr) +{ +} + +static void openpic_end_ipi(unsigned int irq_nr) +{ + /* IPIs are marked IRQ_PER_CPU. This has the side effect of + * preventing the IRQ_PENDING/IRQ_INPROGRESS logic from + * applying to them. We EOI them late to avoid re-entering. + * however, I'm wondering if we could simply let them have the + * SA_INTERRUPT flag and let them execute with all interrupts OFF. + * This would have the side effect of either running cross-CPU + * functions with interrupts off, or we can re-enable them explicitely + * with a __sti() in smp_call_function_interrupt(), since + * smp_call_function() is protected by a spinlock. + * Or maybe we shouldn't set the IRQ_PER_CPU flag on cross-CPU + * function calls IPI at all but that would make a special case. + */ + openpic_eoi(); +} + +static void openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs) +{ + smp_message_recv(cpl-openpic_vec_ipi, regs); +} + +#endif /* CONFIG_SMP */ + +int openpic_get_irq(struct pt_regs *regs) +{ + extern int i8259_irq(int cpu); + + int irq = openpic_irq(); + + /* Management of the cascade should be moved out of here */ + if (open_pic_irq_offset && irq == open_pic_irq_offset) + { + /* + * This magic address generates a PCI IACK cycle. + */ + if ( chrp_int_ack_special ) + irq = *chrp_int_ack_special; + else + irq = i8259_irq( smp_processor_id() ); + openpic_eoi(); + } + if (irq == openpic_vec_spurious) + irq = -1; + return irq; +} + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/open_pic.h linuxppc64_2_4/arch/ppc64/kernel/open_pic.h --- linux-2.4.5-ac18/arch/ppc64/kernel/open_pic.h Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/open_pic.h Fri May 4 17:13:58 2001 @@ -0,0 +1,43 @@ +/* + * arch/ppc/kernel/open_pic.h -- 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. + * + */ + +#ifndef _PPC64_KERNEL_OPEN_PIC_H +#define _PPC64_KERNEL_OPEN_PIC_H + +#define OPENPIC_SIZE 0x40000 + +/* OpenPIC IRQ controller structure */ +extern struct hw_interrupt_type open_pic; + +/* OpenPIC IPI controller structure */ +#ifdef CONFIG_SMP +extern struct hw_interrupt_type open_pic_ipi; +#endif /* CONFIG_SMP */ + +extern u_int OpenPIC_NumInitSenses; +extern u_char *OpenPIC_InitSenses; +extern void* OpenPIC_Addr; + +/* Exported functions */ +extern void openpic_init(int, int, unsigned char *, int); +extern void openpic_request_IPIs(void); +extern void do_openpic_setup_cpu(void); +extern int openpic_get_irq(struct pt_regs *regs); +extern void openpic_init_processor(u_int cpumask); +extern void openpic_setup_ISU(int isu_num, unsigned long addr); +extern void openpic_cause_IPI(u_int ipi, u_int cpumask); + +extern inline int openpic_to_irq(int irq) +{ + return irq += NUM_8259_INTERRUPTS; +} +/*extern int open_pic_irq_offset;*/ +#endif /* _PPC64_KERNEL_OPEN_PIC_H */ diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/open_pic_defs.h linuxppc64_2_4/arch/ppc64/kernel/open_pic_defs.h --- linux-2.4.5-ac18/arch/ppc64/kernel/open_pic_defs.h Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/open_pic_defs.h Fri Jun 1 00:22:20 2001 @@ -0,0 +1,318 @@ +/* + * linux/openpic.h -- OpenPIC definitions + * + * Copyright (C) 1997 Geert Uytterhoeven + * + * This file is based on the following documentation: + * + * The Open Programmable Interrupt Controller (PIC) + * Register Interface Specification Revision 1.2 + * + * Issue Date: October 1995 + * + * Issued jointly by Advanced Micro Devices and Cyrix Corporation + * + * AMD is a registered trademark of Advanced Micro Devices, Inc. + * Copyright (C) 1995, Advanced Micro Devices, Inc. and Cyrix, Inc. + * All Rights Reserved. + * + * To receive a copy of this documentation, send an email to openpic@amd.com. + * + * 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. + */ + +#ifndef _LINUX_OPENPIC_H +#define _LINUX_OPENPIC_H + +#ifdef __KERNEL__ + +#include + + /* + * OpenPIC supports up to 2048 interrupt sources and up to 32 processors + */ + +#define OPENPIC_MAX_SOURCES 2048 +#define OPENPIC_MAX_PROCESSORS 32 +#define OPENPIC_MAX_ISU 32 + +#define OPENPIC_NUM_TIMERS 4 +#define OPENPIC_NUM_IPI 4 +#define OPENPIC_NUM_PRI 16 +#define OPENPIC_NUM_VECTORS OPENPIC_MAX_SOURCES + + /* + * OpenPIC Registers are 32 bits and aligned on 128 bit boundaries + */ + +typedef struct _OpenPIC_Reg { + u_int Reg; /* Little endian! */ + char Pad[0xc]; +} OpenPIC_Reg; + + + /* + * Per Processor Registers + */ + +typedef struct _OpenPIC_Processor { + /* + * Private Shadow Registers (for SLiC backwards compatibility) + */ + u_int IPI0_Dispatch_Shadow; /* Write Only */ + char Pad1[0x4]; + u_int IPI0_Vector_Priority_Shadow; /* Read/Write */ + char Pad2[0x34]; + /* + * Interprocessor Interrupt Command Ports + */ + OpenPIC_Reg _IPI_Dispatch[OPENPIC_NUM_IPI]; /* Write Only */ + /* + * Current Task Priority Register + */ + OpenPIC_Reg _Current_Task_Priority; /* Read/Write */ + char Pad3[0x10]; + /* + * Interrupt Acknowledge Register + */ + OpenPIC_Reg _Interrupt_Acknowledge; /* Read Only */ + /* + * End of Interrupt (EOI) Register + */ + OpenPIC_Reg _EOI; /* Read/Write */ + char Pad5[0xf40]; +} OpenPIC_Processor; + + + /* + * Timer Registers + */ + +typedef struct _OpenPIC_Timer { + OpenPIC_Reg _Current_Count; /* Read Only */ + OpenPIC_Reg _Base_Count; /* Read/Write */ + OpenPIC_Reg _Vector_Priority; /* Read/Write */ + OpenPIC_Reg _Destination; /* Read/Write */ +} OpenPIC_Timer; + + + /* + * Global Registers + */ + +typedef struct _OpenPIC_Global { + /* + * Feature Reporting Registers + */ + OpenPIC_Reg _Feature_Reporting0; /* Read Only */ + OpenPIC_Reg _Feature_Reporting1; /* Future Expansion */ + /* + * Global Configuration Registers + */ + OpenPIC_Reg _Global_Configuration0; /* Read/Write */ + OpenPIC_Reg _Global_Configuration1; /* Future Expansion */ + /* + * Vendor Specific Registers + */ + OpenPIC_Reg _Vendor_Specific[4]; + /* + * Vendor Identification Register + */ + OpenPIC_Reg _Vendor_Identification; /* Read Only */ + /* + * Processor Initialization Register + */ + OpenPIC_Reg _Processor_Initialization; /* Read/Write */ + /* + * IPI Vector/Priority Registers + */ + OpenPIC_Reg _IPI_Vector_Priority[OPENPIC_NUM_IPI]; /* Read/Write */ + /* + * Spurious Vector Register + */ + OpenPIC_Reg _Spurious_Vector; /* Read/Write */ + /* + * Global Timer Registers + */ + OpenPIC_Reg _Timer_Frequency; /* Read/Write */ + OpenPIC_Timer Timer[OPENPIC_NUM_TIMERS]; + char Pad1[0xee00]; +} OpenPIC_Global; + + + /* + * Interrupt Source Registers + */ + +typedef struct _OpenPIC_Source { + OpenPIC_Reg _Vector_Priority; /* Read/Write */ + OpenPIC_Reg _Destination; /* Read/Write */ +} OpenPIC_Source, *OpenPIC_SourcePtr; + + + /* + * OpenPIC Register Map + */ + +struct OpenPIC { + char Pad1[0x1000]; + /* + * Global Registers + */ + OpenPIC_Global Global; + /* + * Interrupt Source Configuration Registers + */ + OpenPIC_Source Source[OPENPIC_MAX_SOURCES]; + /* + * Per Processor Registers + */ + OpenPIC_Processor Processor[OPENPIC_MAX_PROCESSORS]; +}; + +extern volatile struct OpenPIC *OpenPIC; + + + /* + * Current Task Priority Register + */ + +#define OPENPIC_CURRENT_TASK_PRIORITY_MASK 0x0000000f + + /* + * Who Am I Register + */ + +#define OPENPIC_WHO_AM_I_ID_MASK 0x0000001f + + /* + * Feature Reporting Register 0 + */ + +#define OPENPIC_FEATURE_LAST_SOURCE_MASK 0x07ff0000 +#define OPENPIC_FEATURE_LAST_SOURCE_SHIFT 16 +#define OPENPIC_FEATURE_LAST_PROCESSOR_MASK 0x00001f00 +#define OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT 8 +#define OPENPIC_FEATURE_VERSION_MASK 0x000000ff + + /* + * Global Configuration Register 0 + */ + +#define OPENPIC_CONFIG_RESET 0x80000000 +#define OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE 0x20000000 +#define OPENPIC_CONFIG_BASE_MASK 0x000fffff + + /* + * Vendor Identification Register + */ + +#define OPENPIC_VENDOR_ID_STEPPING_MASK 0x00ff0000 +#define OPENPIC_VENDOR_ID_STEPPING_SHIFT 16 +#define OPENPIC_VENDOR_ID_DEVICE_ID_MASK 0x0000ff00 +#define OPENPIC_VENDOR_ID_DEVICE_ID_SHIFT 8 +#define OPENPIC_VENDOR_ID_VENDOR_ID_MASK 0x000000ff + + /* + * Vector/Priority Registers + */ + +#define OPENPIC_MASK 0x80000000 +#define OPENPIC_ACTIVITY 0x40000000 /* Read Only */ +#define OPENPIC_PRIORITY_MASK 0x000f0000 +#define OPENPIC_PRIORITY_SHIFT 16 +#define OPENPIC_VECTOR_MASK 0x000007ff + + + /* + * Interrupt Source Registers + */ + +#define OPENPIC_POLARITY_POSITIVE 0x00800000 +#define OPENPIC_POLARITY_NEGATIVE 0x00000000 +#define OPENPIC_POLARITY_MASK 0x00800000 +#define OPENPIC_SENSE_LEVEL 0x00400000 +#define OPENPIC_SENSE_EDGE 0x00000000 +#define OPENPIC_SENSE_MASK 0x00400000 + + + /* + * Timer Registers + */ + +#define OPENPIC_COUNT_MASK 0x7fffffff +#define OPENPIC_TIMER_TOGGLE 0x80000000 +#define OPENPIC_TIMER_COUNT_INHIBIT 0x80000000 + + + /* + * Aliases to make life simpler + */ + +/* Per Processor Registers */ +#define IPI_Dispatch(i) _IPI_Dispatch[i].Reg +#define Current_Task_Priority _Current_Task_Priority.Reg +#define Interrupt_Acknowledge _Interrupt_Acknowledge.Reg +#define EOI _EOI.Reg + +/* Global Registers */ +#define Feature_Reporting0 _Feature_Reporting0.Reg +#define Feature_Reporting1 _Feature_Reporting1.Reg +#define Global_Configuration0 _Global_Configuration0.Reg +#define Global_Configuration1 _Global_Configuration1.Reg +#define Vendor_Specific(i) _Vendor_Specific[i].Reg +#define Vendor_Identification _Vendor_Identification.Reg +#define Processor_Initialization _Processor_Initialization.Reg +#define IPI_Vector_Priority(i) _IPI_Vector_Priority[i].Reg +#define Spurious_Vector _Spurious_Vector.Reg +#define Timer_Frequency _Timer_Frequency.Reg + +/* Timer Registers */ +#define Current_Count _Current_Count.Reg +#define Base_Count _Base_Count.Reg +#define Vector_Priority _Vector_Priority.Reg +#define Destination _Destination.Reg + +/* Interrupt Source Registers */ +#define Vector_Priority _Vector_Priority.Reg +#define Destination _Destination.Reg + + /* + * Local (static) OpenPIC Operations + */ + + +/* Global Operations */ +static void openpic_reset(void); +static void openpic_enable_8259_pass_through(void); +static void openpic_disable_8259_pass_through(void); +static u_int openpic_irq(void); +static void openpic_eoi(void); +static u_int openpic_get_priority(void); +static void openpic_set_priority(u_int pri); +static u_int openpic_get_spurious(void); +static void openpic_set_spurious(u_int vector); + +#ifdef CONFIG_SMP +/* Interprocessor Interrupts */ +static void openpic_initipi(u_int ipi, u_int pri, u_int vector); +static void openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs); +#endif + +/* Timer Interrupts */ +static void openpic_inittimer(u_int timer, u_int pri, u_int vector); +static void openpic_maptimer(u_int timer, u_int cpumask); + +/* Interrupt Sources */ +static void openpic_enable_irq(u_int irq); +static void openpic_disable_irq(u_int irq); +static void openpic_initirq(u_int irq, u_int pri, u_int vector, int polarity, + int is_level); +static void openpic_mapirq(u_int irq, u_int cpumask); +static void openpic_set_sense(u_int irq, int sense); + +#endif /* __KERNEL__ */ + +#endif /* _LINUX_OPENPIC_H */ diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/pSeries_pci.c linuxppc64_2_4/arch/ppc64/kernel/pSeries_pci.c --- linux-2.4.5-ac18/arch/ppc64/kernel/pSeries_pci.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/pSeries_pci.c Wed Aug 1 15:45:42 2001 @@ -0,0 +1,922 @@ +/* + * pSeries_pci.c + * + * pSeries_pcibios_init(void)opyright (C) 2001 Dave Engebretsen, IBM Corporation + * + * pSeries specific routines for PCI. + * + * Based on code from pci.c and chrp_pci.c + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xics.h" +#include "open_pic.h" +#include "pci.h" + +/* Supporting macros *********************************************/ +#define TESTBIT(value,bits) ((value&bits) == bits) +#define TRUE 1 +#define FALSE 0 + +extern struct pci_controller* hose_head; +extern struct pci_controller** hose_tail; +extern struct Naca *naca; +extern struct pci_ops rtas_pci_ops; +extern struct pci_ops ibm_phb_pci_ops; +extern struct device_node *allnodes; +extern unsigned long phb_tce_table_init(struct pci_controller *phb); + +/******************************************************************* + * Forward declares of prototypes. + *******************************************************************/ +unsigned long find_and_init_phbs(void); +struct pci_controller* alloc_phb(struct device_node *dev, char *model, unsigned int addr_size_words) ; +void pSeries_pcibios_fixup(void); +void pci_build_bar_resources(struct pci_dev* Pci_Dev, int BarCount); +void pci_build_rom_resources(struct pci_dev* Pci_Dev); +void pci_set_BARS(struct pci_dev* Pci_Dev); + +/********************************************************************************** + * + * pSeries I/O Operations to access the PCI configuration space. + * + **********************************************************************************/ +#define RTAS_PCI_READ_OP(size, type, nbytes) \ +int __chrp \ +rtas_read_config_##size(struct pci_dev *dev, int offset, type val) { \ + unsigned long ReturnValue, ConfigAddr; \ + int ReturnCode; \ + ConfigAddr = ((dev->bus->number&0x0000FF) << 16) | (dev->devfn << 8) | (offset & 0xff); \ + ReturnCode = call_rtas("read-pci-config", 2, 2, &ReturnValue, ConfigAddr, nbytes);\ + *val = ReturnValue; \ + return ReturnCode; \ +} +#define RTAS_PCI_WRITE_OP(size, type, nbytes) \ +int __chrp \ +rtas_write_config_##size(struct pci_dev *dev, int offset, type val) { \ + unsigned long ConfigAddr; \ + int ReturnCode; \ + ConfigAddr = ((dev->bus->number&0x0000FF) << 16) | (dev->devfn << 8) | (offset & 0xff); \ + ReturnCode = call_rtas("write-pci-config", 3, 1, NULL, ConfigAddr, nbytes, (ulong)val); \ + return ReturnCode; \ +} + +RTAS_PCI_READ_OP(byte, u8 *, 1) +RTAS_PCI_READ_OP(word, u16 *, 2) +RTAS_PCI_READ_OP(dword, u32 *, 4) +RTAS_PCI_WRITE_OP(byte, u8, 1) +RTAS_PCI_WRITE_OP(word, u16, 2) +RTAS_PCI_WRITE_OP(dword, u32, 4) + +struct pci_ops rtas_pci_ops = { + rtas_read_config_byte, + rtas_read_config_word, + rtas_read_config_dword, + rtas_write_config_byte, + rtas_write_config_word, + rtas_write_config_dword, + pci_read_bar_registers, + pci_read_irq_line +}; + +/********************************************************************************** + * + * pSeries I/O Operations to access the PCI configuration space. + * This is the support for Large Systems(>256 buses). + * + **********************************************************************************/ +#define RTAS64_PCI_READ_OP(size, type, nbytes) \ +int __chrp \ +rtas64_read_config_##size(struct pci_dev *dev, int offset, type val) \ +{ \ + struct pci_controller *hose = dev->sysdata; \ + unsigned long addr = (offset & 0xff) | ((dev->devfn & 0xff) << 8) \ + | (((dev->bus->number) & 0xff) << 16); \ + unsigned long ret = ~0UL; \ + int rval; \ + int buidhi = hose->buid >> 32; \ + int buidlo = hose->buid & 0xffffffff; \ + \ + rval = call_rtas("ibm,read-pci-config", 4, 2, &ret, \ + addr, buidhi, buidlo, nbytes); \ + *val = ret; \ + /* udbg_printf("%08x %08x SYM Read Config %d\n",addr, ret,rval); */ \ + return rval? PCIBIOS_DEVICE_NOT_FOUND: PCIBIOS_SUCCESSFUL; \ +} + +#define RTAS64_PCI_WRITE_OP(size, type, nbytes) \ +int __chrp \ +rtas64_write_config_##size(struct pci_dev *dev, int offset, type val) \ +{ \ + struct pci_controller *hose = dev->sysdata; \ + unsigned long addr = (offset & 0xff) | ((dev->devfn & 0xff) << 8) \ + | (((dev->bus->number) & 0xff) << 16); \ + int rval; \ + int buidhi = hose->buid >> 32; \ + int buidlo = hose->buid & 0xffffffff; \ + \ + rval = call_rtas("ibm,write-pci-config", 5, 1, NULL, \ + addr, buidhi, buidlo, nbytes, (ulong)val); \ + /* udbg_printf("%08x %08x SYM Write Config %d\n",addr, val,rval); */ \ + return rval? PCIBIOS_DEVICE_NOT_FOUND: PCIBIOS_SUCCESSFUL; \ +} + +RTAS64_PCI_READ_OP(byte, u8 *, 1) +RTAS64_PCI_READ_OP(word, u16 *, 2) +RTAS64_PCI_READ_OP(dword, u32 *, 4) +RTAS64_PCI_WRITE_OP(byte, u8, 1) +RTAS64_PCI_WRITE_OP(word, u16, 2) +RTAS64_PCI_WRITE_OP(dword, u32, 4) + +struct pci_ops rtas64_pci_ops = { + rtas64_read_config_byte, + rtas64_read_config_word, + rtas64_read_config_dword, + rtas64_write_config_byte, + rtas64_write_config_word, + rtas64_write_config_dword +}; + + +/****************************************************************** + * pci_read_irq_line + * + * Reads the Interrupt Pin to determine if interrupt is use by card. + * If the interrupt is used, then gets the interrupt line from the + * openfirmware and sets it in the pci_dev and pci_config line. + * + ******************************************************************/ +int +pci_read_irq_line(struct pci_dev* Pci_Dev) { + u8 InterruptPin; + struct device_node* Node; + + pci_read_config_byte(Pci_Dev, PCI_INTERRUPT_PIN, &InterruptPin); + if(InterruptPin == 0) { + PPCDBG(PPCDBG_BUSWALK,"\tDevice: %s No Interrupt used by device.\n",Pci_Dev->slot_name); + return 0; + } + Node = pci_device_to_OF_node(Pci_Dev); + if( Node == NULL) { + PPCDBG(PPCDBG_BUSWALK,"\tDevice: %s Device Node not found.\n",Pci_Dev->slot_name); + return -1; + } + if(Node->n_intrs == 0) { + PPCDBG(PPCDBG_BUSWALK,"\tDevice: %s No Device OF interrupts defined.\n",Pci_Dev->slot_name); + return -1; + } + Pci_Dev->irq = Node->intrs[0].line; + pci_write_config_byte(Pci_Dev, PCI_INTERRUPT_LINE, Pci_Dev->irq); + + PPCDBG(PPCDBG_BUSWALK,"\tDevice: %s pci_dev->irq = 0x%02X\n",Pci_Dev->slot_name,Pci_Dev->irq); + return 0; +} +/****************************************************************** + * pci_set_BARS + * + * Sets the IOA BAR registers from the values from the register + * values found in the OpenFirmware node for the device. Or in + * the future, assigns space in phb and sets the values. + ******************************************************************/ +void +pci_set_BARS(struct pci_dev* Pci_Dev) { + int AddrIndex; + struct device_node *Device_Node = pci_device_to_OF_node(Pci_Dev); + + if(Device_Node == 0 || Device_Node->n_addrs == 0 || Device_Node->addrs == NULL) { + PPCDBG(PPCDBG_BUSWALK, "\tDevice Node was not found or no defined bars\n"); + return; + } + PPCDBG(PPCDBG_BUSWALK, "\tSet Bar Regs(%d)\n",Device_Node->n_addrs); + for(AddrIndex = 0; AddrIndex < Device_Node->n_addrs; ++AddrIndex) { + int PciReg = (Device_Node->addrs[AddrIndex].space & 0x000000FF); + u32 PciBar = Device_Node->addrs[AddrIndex].address; + int BarSze = Device_Node->addrs[AddrIndex].size; + + PPCDBG(PPCDBG_BUSWALK, "\tPciBar 0x%02X, 0x%08X, 0x%08X\n",PciReg,PciBar,BarSze); + pci_write_config_dword(Pci_Dev, PciReg,PciBar); + } +} +/****************************************************************** + * + * pci_build_bar_registers + * + * This function will build the resources based on the information + * that is extracted from the BAR register. The original BAR value + * is saved and restored. + * + * Note: This is pSeries brand specific code. + * In the future, this code will be setting the initial BAR + * value and handing out the memory space in the PHB. + ******************************************************************/ +void +pci_build_bar_resources(struct pci_dev* Pci_Dev, int Count) { + int BarRegister = PCI_BASE_ADDRESS_0; + int EndingBar = BarRegister + (Count*4); + int ResourceIndex = 0; + PPCDBG(PPCDBG_BUSWALK, "\npci_read_bar_registers %s\n",Pci_Dev->slot_name); + + /************************************************************** + * Read Bars until the ending bar has been read. + **************************************************************/ + for(BarRegister = PCI_BASE_ADDRESS_0;BarRegister <= EndingBar; BarRegister += 4) { + struct resource* Resource = &Pci_Dev->resource[ResourceIndex]; + u32 BarSaveArea, BarSizeBits, BarSize; + u64 BarStart; + /********************************************************** + * Save the original bar value and check for success. + **********************************************************/ + if((pci_read_config_dword( Pci_Dev, BarRegister, &BarSaveArea) != 0) || + (BarSaveArea == 0xFFFFFFFF )) { + BarRegister += 4; + continue; + } + /********************************************************** + * Write all ones to register to get the size of area. + **********************************************************/ + pci_write_config_dword(Pci_Dev, BarRegister, 0xFFFFFFFF); + pci_read_config_dword( Pci_Dev, BarRegister, &BarSizeBits); + pci_write_config_dword(Pci_Dev, BarRegister, BarSaveArea); + + /********************************************************** + * Error reading bar(all ones back) or unimplemented BAR(zero) + **********************************************************/ + if(( BarSizeBits == 0xFFFFFFFF ) || BarSizeBits == 0 ) { + BarRegister += 4; + continue; + } + Resource->name = Pci_Dev->name; + /********************************************************** + * Test and read Io Space, It is always 32 bits. + **********************************************************/ + if(TESTBIT(BarSizeBits,PCI_BASE_ADDRESS_SPACE_IO) == TRUE) { + BarSize = (~(BarSizeBits&PCI_BASE_ADDRESS_IO_MASK)) +1; + Resource->flags = IORESOURCE_IO | PCI_BASE_ADDRESS_SPACE_IO; + Resource->start = BarSaveArea & PCI_BASE_ADDRESS_IO_MASK; + Resource->end = Resource->start + BarSize - 1; + ++ResourceIndex; + } + /********************************************************** + * Memory Space, could be 32 bits or 64 bits + **********************************************************/ + else { + Resource->flags = IORESOURCE_MEM; + if( TESTBIT(BarSizeBits,PCI_BASE_ADDRESS_MEM_PREFETCH) == TRUE) { + Resource->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH; + } + BarStart = BarSaveArea &PCI_BASE_ADDRESS_MEM_MASK; + BarSize = ~((BarSizeBits)&PCI_BASE_ADDRESS_MEM_MASK)+1; + /********************************************************** + * 64 bit register, read next register to get the high 32 + * bits. PCI Spec says to write both and THEN read. + **********************************************************/ + if( TESTBIT(BarSizeBits,PCI_BASE_ADDRESS_MEM_TYPE_64) == TRUE) { + u64 High64Bits; + u32 BarHigh64Save; + Resource->flags |= PCI_BASE_ADDRESS_MEM_TYPE_64; + BarRegister += 4; /* Index to next Bar */ + pci_read_config_dword( Pci_Dev, BarRegister, &BarHigh64Save); + High64Bits = BarHigh64Save; + BarStart += (High64Bits<< 32); + pci_write_config_dword(Pci_Dev, BarRegister-4,0xFFFFFFFF); + pci_write_config_dword(Pci_Dev, BarRegister, 0xFFFFFFFF); + pci_read_config_dword( Pci_Dev, BarRegister-4,&BarSizeBits); + BarSize = ~((BarSizeBits)&PCI_BASE_ADDRESS_MEM_MASK)+1; + pci_read_config_dword( Pci_Dev, BarRegister, &BarSizeBits); + High64Bits = ~(BarSizeBits); + BarSize += (High64Bits << 32); + pci_write_config_dword(Pci_Dev, BarRegister-4,BarSaveArea); + pci_write_config_dword(Pci_Dev, BarRegister, BarHigh64Save); + ++ResourceIndex; /* Skip next resource */ + } + /********************************************************** + * Set resource fields with values. + **********************************************************/ + Resource->start = BarStart; + Resource->end = BarStart + BarSize - 1; + ++ResourceIndex; + } + } + PPCDBGCALL(PPCDBG_BUSWALK, dumpPci_Dev(Pci_Dev) ); +} +/****************************************************************** + * + * Read the rom register + * + ******************************************************************/ +void +pci_build_rom_resources(struct pci_dev* Pci_Dev) { + u32 RomSaveArea, RomSizeBits; + u64 RomSize = 0; + struct resource* Resource = &Pci_Dev->resource[PCI_ROM_RESOURCE]; + PPCDBG(PPCDBG_BUSWALK, "\tpci_read_bar_Rom \n"); + + pci_read_config_dword( Pci_Dev, PCI_ROM_ADDRESS, &RomSaveArea); + pci_write_config_dword(Pci_Dev, PCI_ROM_ADDRESS, 0XFFFFFFFF); + pci_read_config_dword( Pci_Dev, PCI_ROM_ADDRESS, &RomSizeBits); + pci_write_config_dword(Pci_Dev, PCI_ROM_ADDRESS, RomSaveArea); + + if( (RomSizeBits&PCI_ROM_ADDRESS_ENABLE) == PCI_ROM_ADDRESS_ENABLE) { + RomSize = ~(RomSizeBits) & PCI_ROM_ADDRESS_MASK; + if(RomSize > 0) { + Resource->name = Pci_Dev->name; + Resource->flags = IORESOURCE_MEM | IORESOURCE_READONLY | IORESOURCE_PREFETCH; + Resource->start = RomSaveArea; + Resource->end = Resource->start + RomSize - 1; + } + } +} +/****************************************************************** + * + * pci_read_bar_registers + * + * This function is the hook from the independant code to the arch + * dependant code to set and read the BAR registers. + * + * Note: This is pSeries brand specific code. + * In the future, this code will be setting the initial BAR + * value and handing out the PBH memory space. + ******************************************************************/ +int +pci_read_bar_registers(struct pci_dev* Pci_Dev, int Count, int RomFlag) { + u16 Pci_Command_Register_Save = 0; + u16 Pci_Mask_Reg; + /************************************************************** + * If Io or Memory is enabled, disable while the BARs are being + * changed to avoid any side affects. + **************************************************************/ + if(( pci_read_config_word( Pci_Dev, PCI_COMMAND, &Pci_Command_Register_Save) != 0 ) || + (Pci_Command_Register_Save == (u16)0xFFFF) ) { + printk("PCI: Device %s Read Command Failed.\n",Pci_Dev->slot_name); + PPCDBG(PPCDBG_BUSWALK,"\tDevice %s Read Command Failed.\n",Pci_Dev->slot_name); + return -1; + } + Pci_Mask_Reg = Pci_Command_Register_Save &(PCI_COMMAND_IO|PCI_COMMAND_MEMORY); + if( Pci_Mask_Reg != 0) { + u16 ResetCmdReg = Pci_Command_Register_Save & (~Pci_Mask_Reg); + pci_write_config_word( Pci_Dev, PCI_COMMAND, ResetCmdReg); + } + else { + Pci_Command_Register_Save = 0; + } + /************************************************************** + * Do the base BARS. + **************************************************************/ + pci_build_bar_resources(Pci_Dev, Count); + + /************************************************************** + * Rom Flag on, read ROM BARS. + **************************************************************/ + if(RomFlag != 0) { + pci_build_rom_resources(Pci_Dev); + } + + /************************************************************** + * If the Command Register was modifed, restore it. + **************************************************************/ + if(Pci_Command_Register_Save != 0) { + pci_write_config_word( Pci_Dev, PCI_COMMAND, Pci_Command_Register_Save); + PPCDBG(PPCDBG_BUSWALK,"\tPCI_COMMAND Register Restored 0x%04X\n", + Pci_Command_Register_Save); + } + return 0; +} + +/****************************************************************** + * Find all PHBs in the system and initialize a set of data + * structures to represent them. + ******************************************************************/ +unsigned long __init +find_and_init_phbs(void) +{ + struct device_node *Pci_Node; + struct pci_controller *phb; + unsigned int root_addr_size_words = 0, this_addr_size_words = 0; + unsigned int this_addr_count = 0, range_stride; + unsigned int *ui_ptr = NULL, *ranges; + char *model; + struct pci_range64 range; + struct resource *res; + unsigned int memno, rlen, i, index; + unsigned int *opprop; + + PPCDBG(PPCDBG_PHBINIT, "find_and_init_phbs\n"); + + if(naca->interrupt_controller == IC_OPEN_PIC) { + opprop = (unsigned int *) + get_property(find_path_device("/"),"platform-open-pic", NULL); + } + + /* Get the root address word size. */ + ui_ptr = (unsigned int *) get_property(find_path_device("/"), + "#size-cells", NULL); + if(ui_ptr) root_addr_size_words = *ui_ptr; + else { + PPCDBG(PPCDBG_PHBINIT, "\tget #size-cells failed.\n"); + return(-1); + } + + index = 0; + + /****************************************************************** + * Find all PHB devices and create an object for them. + ******************************************************************/ + for( Pci_Node = find_devices("pci");Pci_Node != NULL;Pci_Node = Pci_Node->next) { + model = (char *) get_property(Pci_Node, "model", NULL); + if(model != NULL) { + phb = alloc_phb(Pci_Node, model, root_addr_size_words); + if(phb == NULL) return(-1); + } + else { + continue; + } + + /* Get this node's address word size. */ + ui_ptr = (unsigned int *) get_property(Pci_Node, "#size-cells", NULL); + if(ui_ptr) this_addr_size_words = *ui_ptr; + else this_addr_size_words = 1; + /* Get this node's address word count. */ + ui_ptr = (unsigned int *) get_property(Pci_Node, "#address-cells", NULL); + if(ui_ptr) this_addr_count = *ui_ptr; + else this_addr_count = 3; + + range_stride = this_addr_count + root_addr_size_words + this_addr_size_words; + + memno = 0; + phb->io_base_phys = 0; + + ranges = (unsigned int *) get_property(Pci_Node, "ranges", &rlen); + PPCDBG(PPCDBG_PHBINIT, "\trange_stride = 0x%lx, rlen = 0x%x\n", range_stride, rlen); + + for(i = 0; i < (rlen/sizeof(*ranges)); i+=range_stride) { + /* Put the PCI addr part of the current element into a + * '64' struct. + */ + range = *((struct pci_range64 *)(ranges + i)); + + /* If this is a '32' element, map into a 64 struct. */ + if((range_stride * sizeof(int)) == + sizeof(struct pci_range32)) { + range.parent_addr = + (unsigned long)(*(ranges + i + 3)); + range.size = + (((unsigned long)(*(ranges + i + 4)))<<32) | + (*(ranges + i + 5)); + } else { + range.parent_addr = + (((unsigned long)(*(ranges + i + 3)))<<32) | + (*(ranges + i + 4)); + range.size = + (((unsigned long)(*(ranges + i + 5)))<<32) | + (*(ranges + i + 6)); + } + + PPCDBG(PPCDBG_PHBINIT, "\trange.parent_addr = 0x%lx\n", + range.parent_addr); + PPCDBG(PPCDBG_PHBINIT, "\trange.child_addr.hi = 0x%lx\n", + range.child_addr.a_hi); + PPCDBG(PPCDBG_PHBINIT, "\trange.child_addr.mid = 0x%lx\n", + range.child_addr.a_mid); + PPCDBG(PPCDBG_PHBINIT, "\trange.child_addr.lo = 0x%lx\n", + range.child_addr.a_lo); + PPCDBG(PPCDBG_PHBINIT, "\trange.size = 0x%lx\n", + range.size); + + res = NULL; + switch ((range.child_addr.a_hi >> 24) & 0x3) { + case 1: /* I/O space */ + phb->io_base_phys = range.parent_addr; + res = &phb->io_resource; + res->flags = IORESOURCE_IO; + if (isa_io_base == 0) { + isa_io_base = (unsigned long) + ioremap(phb->io_base_phys, range.size); + PPCDBG(PPCDBG_PHBINIT, "\tisa_io_base = 0x%lx\n", isa_io_base); + } + phb->pci_io_offset = range.parent_addr - + ((((unsigned long) + range.child_addr.a_mid) << 32) | + (range.child_addr.a_lo)); + PPCDBG(PPCDBG_PHBINIT, "\tpci_io_offset = 0x%lx\n", + phb->pci_io_offset); + + break; + case 2: /* mem space */ + PPCDBG(PPCDBG_PHBINIT, "\tMem Space\n"); + phb->pci_mem_offset = range.parent_addr - + ((((unsigned long) + range.child_addr.a_mid) << 32) | + (range.child_addr.a_lo)); + PPCDBG(PPCDBG_PHBINIT, "\tpci_mem_offset = 0x%lx\n", + phb->pci_mem_offset); + res = &(phb->mem_resources[memno]); + res->flags = IORESOURCE_MEM; + ++memno; + break; + } + if (res) { + res->name = Pci_Node->full_name; + res->start = range.parent_addr; + res->end = range.parent_addr + range.size - 1; + res->parent = NULL; + res->sibling = NULL; + res->child = NULL; + } + } + PPCDBG(PPCDBG_PHBINIT, "\tphb->io_base_phys = 0x%lx\n", + phb->io_base_phys); + PPCDBG(PPCDBG_PHBINIT, "\tphb->pci_mem_offset = 0x%lx\n", + phb->pci_mem_offset); + + if(naca->interrupt_controller == IC_OPEN_PIC) { + openpic_setup_ISU(index, opprop[index+1]); + } + index++; + } + return 0; /*Success */ +} + +/****************************************************************** + * + * Allocate and partially initialize a structure to represent a PHB. + * + ******************************************************************/ +struct pci_controller * +alloc_phb(struct device_node *dev, char *model, unsigned int addr_size_words) +{ + struct pci_controller *phb; + unsigned int *ui_ptr = NULL, len; + struct reg_property64 reg_struct; + int *bus_range; + + PPCDBG(PPCDBG_PHBINIT, "alloc_phb: %s\n", dev->full_name); + PPCDBG(PPCDBG_PHBINIT, "\tdev = 0x%lx\n", dev); + PPCDBG(PPCDBG_PHBINIT, "\tmodel = 0x%lx\n", model); + PPCDBG(PPCDBG_PHBINIT, "\taddr_size_words = 0x%lx\n", addr_size_words); + + /* Found a PHB, now figure out where his registers are mapped. */ + ui_ptr = (unsigned int *) get_property(dev, "reg", &len); + if(ui_ptr == NULL) { + PPCDBG(PPCDBG_PHBINIT, "\tget reg failed.\n"); + return(NULL); + } + + if(addr_size_words == 1) { + reg_struct.address = ((struct reg_property32 *)ui_ptr)->address; + reg_struct.size = ((struct reg_property32 *)ui_ptr)->size; + } else { + reg_struct = *((struct reg_property64 *)ui_ptr); + } + + PPCDBG(PPCDBG_PHBINIT, "\treg_struct.address = 0x%lx\n", reg_struct.address); + PPCDBG(PPCDBG_PHBINIT, "\treg_struct.size = 0x%lx\n", reg_struct.size); + + /*************************************************************** + * Set chip specific data in the phb, including types & + * register pointers. + ***************************************************************/ + + /**************************************************************** + * Python + ***************************************************************/ + if(strstr(model, "Python")) { + PPCDBG(PPCDBG_PHBINIT, "\tCreate python\n"); + phb = pci_alloc_pci_controller("PHB PY",phb_type_python); + if(phb == NULL) return NULL; + + phb->cfg_addr = (volatile unsigned long *) + ioremap(reg_struct.address + 0xf8000, PAGE_SIZE); + PPCDBG(PPCDBG_PHBINIT, "\tcfg_addr_r = 0x%lx\n", + reg_struct.address + 0xf8000); + PPCDBG(PPCDBG_PHBINIT, "\tcfg_addr_v = 0x%lx\n", + phb->cfg_addr); + phb->cfg_data = (char*)(phb->cfg_addr + 0x02); + phb->phb_regs = (volatile unsigned long *) + ioremap(reg_struct.address + 0xf7000, PAGE_SIZE); + /* Python's register file is 1 MB in size. */ + phb->chip_regs = ioremap(reg_struct.address & ~(0xfffffUL), + 0x100000); + /*************************************************************** + * Speedwagon + ***************************************************************/ + } else if(strstr(model, "Speedwagon")) { + PPCDBG(PPCDBG_PHBINIT, "\tCreate speedwagon\n"); + phb = pci_alloc_pci_controller("PHB SW",phb_type_speedwagon); + if(phb == NULL) return NULL; + + phb->cfg_addr = (volatile unsigned long *) + ioremap(reg_struct.address + 0x140, PAGE_SIZE); + phb->cfg_data = (char*)(phb->cfg_addr - 0x02); /* minus is correct */ + phb->phb_regs = (volatile unsigned long *) + ioremap(reg_struct.address, PAGE_SIZE); + + phb->local_number = ((reg_struct.address >> 12) & 0xf) - 0x8; + + /* Speedwagon's register file is 1 MB in size. */ + phb->chip_regs = ioremap(reg_struct.address & ~(0xfffffUL), + 0x100000); + PPCDBG(PPCDBG_PHBINIT, "\tmapping chip_regs from 0x%lx -> 0x%lx\n", + reg_struct.address & 0xfffff, phb->chip_regs); + /*************************************************************** + * Trying to build a known just gets the code in trouble. + ***************************************************************/ + } else { + PPCDBG(PPCDBG_PHBINIT, "\tUnknown PHB Type!\n"); + printk("PCI: Unknown Phb Type!\n"); + return NULL; + } + + bus_range = (int *) get_property(dev, "bus-range", &len); + if (bus_range == NULL || len < 2 * sizeof(int)) { + PPCDBG(PPCDBG_PHBINIT, "Can't get bus-range for %s\n", dev->full_name); + kfree(phb); + return(NULL); + } + + /*************************************************************** + * Finished with the initialization + ***************************************************************/ + phb->first_busno = bus_range[0]; + phb->last_busno = bus_range[1]; + //phb->first_busno = (phb->global_number <<8) + bus_range[0]; + //phb->last_busno = (phb->global_number <<8) + bus_range[1]; + + phb->arch_data = dev; + if( Pci_Large_Bus_System == 0 ) phb->ops = &rtas_pci_ops; + else phb->ops = &rtas64_pci_ops; + + /*************************************************************** + * Build tce table for phb + ***************************************************************/ + phb_tce_table_init(phb); + + /* Dump PHB information for Debug */ + PPCDBGCALL(PPCDBG_PHBINIT,dumpPci_Controller(phb) ); + + return phb; +} + +void +fixup_resources(struct pci_dev *dev) { + int i; + unsigned long size; + struct pci_controller* phb = (struct pci_controller *)dev->sysdata; + + PPCDBG(PPCDBG_PHBINIT, "fixup_resources:\n"); + PPCDBG(PPCDBG_PHBINIT, "\tphb = 0x%016LX\n", phb); + PPCDBG(PPCDBG_PHBINIT, "\tphb->pci_io_offset = 0x%016LX\n", phb->pci_io_offset); + PPCDBG(PPCDBG_PHBINIT, "\tphb->pci_mem_offset = 0x%016LX\n", phb->pci_mem_offset); + + PPCDBG(PPCDBG_PHBINIT, "\tdev->name = %s\n", dev->name); + PPCDBG(PPCDBG_PHBINIT, "\tdev->vendor:device = 0x%04X : 0x%04X\n", dev->vendor, dev->device); + + if(phb == NULL) return; + + for (i = 0; i < 6; ++i) { + PPCDBG(PPCDBG_PHBINIT, "\tdevice %x.%x[%d] (flags %x) [%lx..%lx]\n", + dev->bus->number, dev->devfn, i, + dev->resource[i].flags, + dev->resource[i].start, + dev->resource[i].end); + + if ((dev->resource[i].start == 0) && (dev->resource[i].end == 0)) { + continue; + } + + if (dev->resource[i].flags & IORESOURCE_IO) { + dev->resource[i].start += phb->pci_io_offset; + dev->resource[i].end += phb->pci_io_offset; + size = dev->resource[i].end - dev->resource[i].start; + dev->resource[i].start = + ((unsigned long) ioremap(dev->resource[i].start, + size)) - isa_io_base; + dev->resource[i].end = dev->resource[i].start + size; + + PPCDBG(PPCDBG_PHBINIT, "\t\t-> now [%lx (%lx) .. %lx (%lx)]\n", + dev->resource[i].start, __pa(dev->resource[i].start + isa_io_base), + dev->resource[i].end, __pa(dev->resource[i].end + isa_io_base)); + } else if (dev->resource[i].flags & IORESOURCE_MEM) { + dev->resource[i].start += phb->pci_mem_offset; + dev->resource[i].end += phb->pci_mem_offset; + PPCDBG(PPCDBG_PHBINIT, "\t\t-> now [%lx..%lx]\n", + dev->resource[i].start, dev->resource[i].end); + + } else { + continue; + } + + /* zap the 2nd function of the winbond chip */ + if (dev->resource[i].flags & IORESOURCE_IO + && dev->bus->number == 0 && dev->devfn == 0x81) + dev->resource[i].flags &= ~IORESOURCE_IO; + } +} + +void __init +pSeries_pcibios_fixup(void) { + struct pci_dev *dev; + + PPCDBG(PPCDBG_PHBINIT, "pSeries_pcibios_fixup: start\n"); + pci_assign_all_busses = 0; + + pci_for_each_dev(dev) { + PPCDBGCALL(PPCDBG_PHBINIT, dumpPci_Dev(dev) ); + } + + if(naca->interrupt_controller == IC_PPC_XIC) { + xics_isa_init(); + } +} + +/*********************************************************************** + * pci_find_hose_for_OF_device + * + * This function finds the PHB that matching device_node in the + * OpenFirmware by scanning all the pci_controllers. + * + ***********************************************************************/ +struct pci_controller* +pci_find_hose_for_OF_device(struct device_node* node) { + while(node) { + struct pci_controller* hose; + for (hose=hose_head;hose;hose=hose->next) + if (hose->arch_data == node) + return hose; + node=node->parent; + } + return NULL; +} + +/*********************************************************************** + * + * scan_OF_childs_for_device + * + * The function is a helper function for the pci_device_to_OF_node. It + * walks down the passed node, looking for a node entry that matches the + * requested bus and device function. NOTE: If a bridge is found in the + * scan, it will recurse this the function to to scan that bridge looking + * for a match. If none found, the return will continue down the orginal + * tree. + * + * Return: + * Node matching the bus and devfn passed or NULL. + ***********************************************************************/ +static struct device_node* +scan_OF_childs_for_device(struct device_node* node, u8 bus, u8 dev_fn) +{ + struct device_node* CurrentNode; /* Current node being scanned. */ + struct device_node* DeviceNode; /* Node of Device */ + u32* Register; /* Pointer to Register Array */ + u32* Class_Code; /* Pointer to ClassCode property */ + CurrentNode = node; + DeviceNode = NULL; + while(CurrentNode != NULL && DeviceNode == NULL) { + u32 IoaAddress; + u8 IoaBus, IoaDevFn; + + Register = (unsigned int *) get_property(CurrentNode,"reg", 0); + if(Register != NULL) { + /* 1st register entry is the Ioa Address = 00BBSS00 */ + IoaAddress = Register[0]; + IoaBus = (IoaAddress & 0x00FF0000) >> 16; + IoaDevFn = (IoaAddress & 0x0000FF00) >> 8; + if( (IoaBus == bus) && (IoaDevFn == dev_fn ) ) { + DeviceNode = CurrentNode; + return DeviceNode; + } + } + /*************************************************************** + * check for a bridge, if so scan the branch of the tree for a match. + ***************************************************************/ + Class_Code = (unsigned int*) get_property(CurrentNode,"class-code", 0); + if(Class_Code != NULL) { + u32 PciClassCode = ((*Class_Code)&0x00FFFF00)>>8; + + if(( PciClassCode == PCI_CLASS_BRIDGE_PCI) || + ( PciClassCode == PCI_CLASS_BRIDGE_CARDBUS) ) { + + PPCDBG(PPCDBG_BUSWALK,"\tScan OF behind bridge\n"); + DeviceNode = scan_OF_childs_for_device(CurrentNode->child, bus, dev_fn); + if(DeviceNode != NULL) { + return DeviceNode; + } + } + } + /*************************************************************** + * Try the next node. + ***************************************************************/ + CurrentNode = CurrentNode->sibling; + } + return NULL; +} +/*********************************************************************** + * pci_device_to_OF_node + * + * This function Finds the Open Firmware node for the passed in pci_dev. + * It starts at the Phb's node for the device and calls the + * scan_OF_childs_for_device to looking for the matching entry. + * + * Return: + * Node matching the device or NULL if it was not found. + ***********************************************************************/ +struct device_node* +pci_device_to_OF_node(struct pci_dev* Pci_Dev) +{ + struct pci_controller* Phb; + struct device_node* PhbNode; + struct device_node* DeviceNode; + Phb = PCI_GET_PHB_PTR(Pci_Dev); + PhbNode = (struct device_node *) Phb->arch_data; + DeviceNode = scan_OF_childs_for_device(PhbNode->child, PCI_GET_BUS_NUMBER(Pci_Dev), Pci_Dev->devfn); + return DeviceNode; +} +/*********************************************************************** + * find_floppy(void) + * + * Finds the default floppy device, if the system has one, and returns + * the pci_dev for the isa bridge for the floppy device. + * + * Note: This functions finds the first "fdc" device and then looks to + * the parent device which should be the isa bridge device. If there + * is more than one floppy on the system, it will find the first one + * and maybe that is okay. + ***********************************************************************/ +struct pci_dev* +find_floppy() { + struct device_node *FloppyParent, *FloppyNode; + struct pci_dev *floppy_dev = NULL; + int *Register; + + FloppyNode = find_type_devices("fdc"); + if(FloppyNode != NULL && FloppyNode->parent != NULL) { + FloppyParent = FloppyNode->parent; + Register = (unsigned int *)get_property(FloppyParent,"reg", 0); + if(Register != NULL) { + u8 IoaBus = (Register[0] & 0x00FF0000) >> 16; + u8 IoaDevFn = (Register[0] & 0x0000FF00) >> 8; + floppy_dev = pci_find_slot(IoaBus, IoaDevFn); + } + } + PPCDBG(PPCDBG_BUSWALK,"\tFloppy pci_dev\n"); + PPCDBGCALL(PPCDBG_BUSWALK, dumpPci_Dev(floppy_dev) ); + return floppy_dev; +} + +/*********************************************************************** + * ppc64_pcibios_init + * + * Chance to initialize and structures or variable before PCI Bus walk. + * + ***********************************************************************/ +void +pSeries_pcibios_init(void) { + PPCDBG(PPCDBG_PHBINIT, "\tppc64_pcibios_init Entry.\n"); + + if(get_property(find_path_device("/rtas"),"read-pc-config",NULL) != NULL) { + PPCDBG(PPCDBG_PHBINIT, "\tFound: read-pc-config\n"); + } + else PPCDBG(PPCDBG_PHBINIT, "\tNOT Found: read-pc-config\n"); + + + if(get_property(find_path_device("/rtas"),"ibm,read-pc-config",NULL) != NULL) { + PPCDBG(PPCDBG_PHBINIT, "\tFound: ibm,read-pc-config\n"); + Pci_Set_IOA_Address = 1; + } + if(get_property(find_path_device("/rtas"),"ibm,fw-phb-id",NULL) != NULL) { + PPCDBG(PPCDBG_PHBINIT, "\tFound: ibm,fw-phb-id\n"); + Pci_Large_Bus_System = 1; + } +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/pacaData.c linuxppc64_2_4/arch/ppc64/kernel/pacaData.c --- linux-2.4.5-ac18/arch/ppc64/kernel/pacaData.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/pacaData.c Wed Aug 1 14:06:21 2001 @@ -0,0 +1,120 @@ +/* + * c 2001 PPC 64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#define __KERNEL__ 1 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +/* The Paca is an array with one entry per processor. Each contains an + * ItLpPaca, which contains the information shared between the + * hypervisor and Linux. Each also contains an ItLpRegSave area which + * is used by the hypervisor to save registers. + * On systems with hardware multi-threading, there are two threads + * per processor. The Paca array must contain an entry for each thread. + * The VPD Areas will give a max logical processors = 2 * max physical + * processors. The processor VPD array needs one entry per physical + * processor (not thread). + */ +#define PACAINITDATA(number,start,lpq,asrr,asrv) \ +{ (struct ItLpPaca *)(((char *)(&xPaca[number]))+offsetof(struct Paca, xLpPaca)), \ + (struct ItLpRegSave *)(((char *)(&xPaca[number]))+offsetof(struct Paca, xRegSav)), \ + 0, /* Current */ \ + 0, /* R21 Save */ \ + 0, /* R22 Save */ \ + 0, /* Kernel stack addr save */ \ + (number), /* Paca Index */ \ + 0, /* HW Proc Number */ \ + (start), /* Processor start */ \ + {0,0,0}, /* Resv */ \ + 0, /* MSR Save */ \ + 0, /* LR Save */ \ + 0, /* Pointer to thread */ \ + {(asrr), /* Real pointer to segment table */ \ + (asrv), /* Virt pointer to segment table */ \ + REGION_COUNT-1}, /* Round robin index */ \ + {0,0,0,0,0,0,0,0},/* Segment cache */ \ + 0, /* Kernel TOC address */ \ + 0, /* R1 Save */ \ + (lpq), /* &xItLpQueue, */ \ + {0,0,0,{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, SPIN_LOCK_UNLOCKED, 0}, /*xRtas */ \ + {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, /* Resv */ \ + { /* LpPaca */ \ + xDesc: 0xd397d781, /* "LpPa" */ \ + xSize: sizeof(struct ItLpPaca), /* */ \ + xEndOfQuantum: 0xffffffffffffffff /* */ \ + }, \ + { /* LpRegSave */ \ + 0xd397d9e2, /* "LpRS" */ \ + sizeof(struct ItLpRegSave) /* */ \ + } \ +} + +struct Paca xPaca[maxPacas] = { +#ifdef CONFIG_PPC_ISERIES + PACAINITDATA( 0, 1, &xItLpQueue, 0x5000, 0xc000000000005000), +#else + PACAINITDATA( 0, 1, 0, 0x5000, 0xc000000000005000), +#endif + PACAINITDATA( 1, 0, 0, 0, 0), + PACAINITDATA( 2, 0, 0, 0, 0), + PACAINITDATA( 3, 0, 0, 0, 0), + PACAINITDATA( 4, 0, 0, 0, 0), + PACAINITDATA( 5, 0, 0, 0, 0), + PACAINITDATA( 6, 0, 0, 0, 0), + PACAINITDATA( 7, 0, 0, 0, 0), + PACAINITDATA( 8, 0, 0, 0, 0), + PACAINITDATA( 9, 0, 0, 0, 0), + PACAINITDATA(10, 0, 0, 0, 0), + PACAINITDATA(11, 0, 0, 0, 0), + PACAINITDATA(12, 0, 0, 0, 0), + PACAINITDATA(13, 0, 0, 0, 0), + PACAINITDATA(14, 0, 0, 0, 0), + PACAINITDATA(15, 0, 0, 0, 0), + PACAINITDATA(16, 0, 0, 0, 0), + PACAINITDATA(17, 0, 0, 0, 0), + PACAINITDATA(18, 0, 0, 0, 0), + PACAINITDATA(19, 0, 0, 0, 0), + PACAINITDATA(20, 0, 0, 0, 0), + PACAINITDATA(21, 0, 0, 0, 0), + PACAINITDATA(22, 0, 0, 0, 0), + PACAINITDATA(23, 0, 0, 0, 0), + PACAINITDATA(24, 0, 0, 0, 0), + PACAINITDATA(25, 0, 0, 0, 0), + PACAINITDATA(26, 0, 0, 0, 0), + PACAINITDATA(27, 0, 0, 0, 0), + PACAINITDATA(28, 0, 0, 0, 0), + PACAINITDATA(29, 0, 0, 0, 0), + PACAINITDATA(30, 0, 0, 0, 0), + PACAINITDATA(31, 0, 0, 0, 0), + PACAINITDATA(32, 0, 0, 0, 0), + PACAINITDATA(33, 0, 0, 0, 0), + PACAINITDATA(34, 0, 0, 0, 0), + PACAINITDATA(35, 0, 0, 0, 0), + PACAINITDATA(36, 0, 0, 0, 0), + PACAINITDATA(37, 0, 0, 0, 0), + PACAINITDATA(38, 0, 0, 0, 0), + PACAINITDATA(39, 0, 0, 0, 0), + PACAINITDATA(40, 0, 0, 0, 0), + PACAINITDATA(41, 0, 0, 0, 0), + PACAINITDATA(42, 0, 0, 0, 0), + PACAINITDATA(43, 0, 0, 0, 0), + PACAINITDATA(44, 0, 0, 0, 0), + PACAINITDATA(45, 0, 0, 0, 0), + PACAINITDATA(46, 0, 0, 0, 0), + PACAINITDATA(47, 0, 0, 0, 0) +}; diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/pci.c linuxppc64_2_4/arch/ppc64/kernel/pci.c --- linux-2.4.5-ac18/arch/ppc64/kernel/pci.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/pci.c Mon Jul 2 10:08:53 2001 @@ -0,0 +1,814 @@ +/* + * + * + * Port for PPC64 David Engebretsen, IBM Corp. + * Contains common pci routines for ppc64 platform, pSeries and iSeries brands. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pci.h" + +unsigned long isa_io_base = 0; +unsigned long isa_mem_base = 0; +unsigned long pci_dram_offset = 0; + +/****************************************************************** + * Forward declare of prototypes + ******************************************************************/ +void pcibios_fixup_resources(struct pci_dev* dev); +void fixup_resources(struct pci_dev* dev); /* In brand pci code */ + +struct pci_dev *find_floppy(void); + +extern struct Naca *naca; + +int pci_assign_all_busses = 0; + +struct pci_controller* hose_head; +struct pci_controller** hose_tail = &hose_head; + +/****************************************************************** + * + ******************************************************************/ +int global_phb_number = 0; /* Global phb counter */ +int Pci_Large_Bus_System = 0; +int Pci_Set_IOA_Address = 0; +int Pci_Manage_Phb_Space = 0; + +static int pci_bus_count; + +/* Floppy dev for ppc64_fd_dma_setup(). May be null if no floppy in the system. */ +struct pci_dev *ppc64_floppy_dev = NULL; + +struct pci_fixup pcibios_fixups[] = { + { PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pcibios_fixup_resources }, + { 0 } +}; + +void pcibios_fixup_pbus_ranges(struct pci_bus *pbus, + struct pbus_set_ranges_data *pranges) +{ +} + + +void +pcibios_update_resource(struct pci_dev *dev, struct resource *root, + struct resource *res, int resource) +{ + u32 new, check; + int reg; + struct pci_controller* hose = dev->sysdata; + + new = res->start; + if (hose && res->flags & IORESOURCE_MEM) + new -= hose->pci_mem_offset; + new |= (res->flags & PCI_REGION_FLAG_MASK); + if (resource < 6) { + reg = PCI_BASE_ADDRESS_0 + 4*resource; + } else if (resource == PCI_ROM_RESOURCE) { + res->flags |= PCI_ROM_ADDRESS_ENABLE; + reg = dev->rom_base_reg; + } else { + /* Somebody might have asked allocation of a non-standard resource */ + return; + } + + pci_write_config_dword(dev, reg, new); + pci_read_config_dword(dev, reg, &check); + if ((new ^ check) & ((new & PCI_BASE_ADDRESS_SPACE_IO) ? PCI_BASE_ADDRESS_IO_MASK : PCI_BASE_ADDRESS_MEM_MASK)) { + printk(KERN_ERR "PCI: Error while updating region " + "%s/%d (%08x != %08x)\n", dev->slot_name, resource, + new, check); + } +} + +static void +pcibios_fixup_resources(struct pci_dev* dev) +{ + fixup_resources(dev); +} + +/* + * We need to avoid collisions with `mirrored' VGA ports + * and other strange ISA hardware, so we always want the + * addresses to be allocated in the 0x000-0x0ff region + * modulo 0x400. + * + * Why? Because some silly external IO cards only decode + * the low 10 bits of the IO address. The 0x00-0xff region + * is reserved for motherboard devices that decode all 16 + * bits, so it's ok to allocate at, say, 0x2800-0x28ff, + * but we want to try to avoid allocating at 0x2900-0x2bff + * which might have be mirrored at 0x0100-0x03ff.. + */ +void +pcibios_align_resource(void *data, struct resource *res, unsigned long size) +{ + struct pci_dev *dev = data; + + if (res->flags & IORESOURCE_IO) { + unsigned long start = res->start; + + if (size > 0x100) { + printk(KERN_ERR "PCI: Can not align I/O Region %s %s because size %ld is too large.\n", + dev->slot_name, res->name, size); + } + + if (start & 0x300) { + start = (start + 0x3ff) & ~0x3ff; + res->start = start; + } + } +} + + +/* + * Handle resources of PCI devices. If the world were perfect, we could + * just allocate all the resource regions and do nothing more. It isn't. + * On the other hand, we cannot just re-allocate all devices, as it would + * require us to know lots of host bridge internals. So we attempt to + * keep as much of the original configuration as possible, but tweak it + * when it's found to be wrong. + * + * Known BIOS problems we have to work around: + * - I/O or memory regions not configured + * - regions configured, but not enabled in the command register + * - bogus I/O addresses above 64K used + * - expansion ROMs left enabled (this may sound harmless, but given + * the fact the PCI specs explicitly allow address decoders to be + * shared between expansion ROMs and other resource regions, it's + * at least dangerous) + * + * Our solution: + * (1) Allocate resources for all buses behind PCI-to-PCI bridges. + * This gives us fixed barriers on where we can allocate. + * (2) Allocate resources for all enabled devices. If there is + * a collision, just mark the resource as unallocated. Also + * disable expansion ROMs during this step. + * (3) Try to allocate resources for disabled devices. If the + * resources were assigned correctly, everything goes well, + * if they weren't, they won't disturb allocation of other + * resources. + * (4) Assign new addresses to resources which were either + * not configured at all or misconfigured. If explicitly + * requested by the user, configure expansion ROM address + * as well. + */ + +static void __init +pcibios_allocate_bus_resources(struct list_head *bus_list) +{ + struct list_head *ln; + struct pci_bus *bus; + struct pci_dev *dev; + int idx; + struct resource *r, *pr; + + /* Depth-First Search on bus tree */ + for (ln=bus_list->next; ln != bus_list; ln=ln->next) { + bus = pci_bus_b(ln); + if ((dev = bus->self)) { + for (idx = PCI_BRIDGE_RESOURCES; idx < PCI_NUM_RESOURCES; idx++) { + r = &dev->resource[idx]; + if (!r->start) + continue; + pr = pci_find_parent_resource(dev, r); + if (!pr || request_resource(pr, r) < 0) + printk(KERN_ERR "PCI: Cannot allocate resource region %d of bridge %s\n", idx, dev->slot_name); + } + } + pcibios_allocate_bus_resources(&bus->children); + } +} + +static void __init +pcibios_allocate_resources(int pass) +{ + struct pci_dev *dev; + int idx, disabled; + u16 command; + struct resource *r, *pr; + + pci_for_each_dev(dev) { + pci_read_config_word(dev, PCI_COMMAND, &command); + for(idx = 0; idx < 6; idx++) { + r = &dev->resource[idx]; + if (r->parent) /* Already allocated */ + continue; + if (!r->start) /* Address not assigned at all */ + continue; + + if (r->flags & IORESOURCE_IO) + disabled = !(command & PCI_COMMAND_IO); + else + disabled = !(command & PCI_COMMAND_MEMORY); + if (pass == disabled) { + PPCDBG(PPCDBG_PHBINIT, + "PCI: Resource %08lx-%08lx (f=%lx, d=%d, p=%d)\n", + r->start, r->end, r->flags, disabled, pass); + pr = pci_find_parent_resource(dev, r); + if (!pr || request_resource(pr, r) < 0) { + PPCDBG(PPCDBG_PHBINIT, + "PCI: Cannot allocate resource region %d of device %s, pr = 0x%lx\n", idx, dev->slot_name, pr); + if(pr) { + PPCDBG(PPCDBG_PHBINIT, + "PCI: Cannot allocate resource 0x%lx\n", request_resource(pr,r)); + } + /* We'll assign a new address later */ + r->end -= r->start; + r->start = 0; + } + } + } + if (!pass) { + r = &dev->resource[PCI_ROM_RESOURCE]; + if (r->flags & PCI_ROM_ADDRESS_ENABLE) { + /* Turn the ROM off, leave the resource region, but keep it unregistered. */ + u32 reg; + r->flags &= ~PCI_ROM_ADDRESS_ENABLE; + pci_read_config_dword(dev, dev->rom_base_reg, ®); + pci_write_config_dword(dev, dev->rom_base_reg, reg & ~PCI_ROM_ADDRESS_ENABLE); + } + } + } +} + +static void __init +pcibios_assign_resources(void) +{ + struct pci_dev *dev; + int idx; + struct resource *r; + + pci_for_each_dev(dev) { + int class = dev->class >> 8; + + /* Don't touch classless devices and host bridges */ + if (!class || class == PCI_CLASS_BRIDGE_HOST) + continue; + + for(idx=0; idx<6; idx++) { + r = &dev->resource[idx]; + + /* + * Don't touch IDE controllers and I/O ports of video cards! + */ + if ((class == PCI_CLASS_STORAGE_IDE && idx < 4) || + (class == PCI_CLASS_DISPLAY_VGA && (r->flags & IORESOURCE_IO))) + continue; + + /* + * We shall assign a new address to this resource, either because + * the BIOS forgot to do so or because we have decided the old + * address was unusable for some reason. + */ + if (!r->start && r->end && ppc_md.pcibios_enable_device_hook && + !ppc_md.pcibios_enable_device_hook(dev, 1)) + pci_assign_resource(dev, idx); + } + + if (0) { /* don't assign ROMs */ + r = &dev->resource[PCI_ROM_RESOURCE]; + r->end -= r->start; + r->start = 0; + if (r->end) + pci_assign_resource(dev, PCI_ROM_RESOURCE); + } + } +} + + +int +pcibios_enable_resources(struct pci_dev *dev) +{ + u16 cmd, old_cmd; + int idx; + struct resource *r; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + old_cmd = cmd; + for(idx=0; idx<6; idx++) { + r = &dev->resource[idx]; + if (!r->start && r->end) { + printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", dev->slot_name); + return -EINVAL; + } + if (r->flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (r->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; + } + if (dev->resource[PCI_ROM_RESOURCE].start) + cmd |= PCI_COMMAND_MEMORY; + if (cmd != old_cmd) { + printk("PCI: Enabling device %s (%04x -> %04x)\n", dev->slot_name, old_cmd, cmd); + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + return 0; +} + +/* + * Allocate pci_controller(phb) initialized common variables. + */ +struct pci_controller * __init +pci_alloc_pci_controller(char *model, enum phb_types controller_type) +{ + struct pci_controller *hose; + PPCDBG(PPCDBG_PHBINIT, "PCI: Allocate pci_controller for %s\n",model); + hose = (struct pci_controller *)alloc_bootmem(sizeof(struct pci_controller)); + if(hose == NULL) { + printk(KERN_ERR "PCI: Allocate pci_controller failed.\n"); + return NULL; + } + memset(hose, 0, sizeof(struct pci_controller)); + if(strlen(model) < 8) strcpy(hose->what,model); + else memcpy(hose->what,model,7); + hose->type = controller_type; + hose->global_number = global_phb_number; + global_phb_number++; + + *hose_tail = hose; + hose_tail = &hose->next; + return hose; +} + +/* + * This fixup is arch independent and probably should go somewhere else. + */ +void __init +pcibios_generic_fixup(void) +{ + struct pci_dev *dev; + + /* Fix miss-identified vendor AMD pcnet32 adapters. */ + dev = NULL; + while ((dev = pci_find_device(PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_AMD_LANCE, dev)) != NULL && + dev->class == (PCI_CLASS_NETWORK_ETHERNET << 8)) + dev->vendor = PCI_VENDOR_ID_AMD; +} + + + +/*********************************************************************** + * + * + * + ***********************************************************************/ +void __init +pcibios_init(void) +{ + struct pci_controller *hose; + struct pci_bus *bus; + int next_busno; + + pSeries_pcibios_init(); + + printk("PCI: Probing PCI hardware\n"); + PPCDBG(PPCDBG_BUSWALK,"PCI: Probing PCI hardware\n"); + + + /* Scan all of the recorded PCI controllers. */ + for (next_busno = 0, hose = hose_head; hose; hose = hose->next) { + hose->last_busno = 0xff; + bus = pci_scan_bus(hose->first_busno, hose->ops, hose); + hose->bus = bus; + hose->last_busno = bus->subordinate; + if (pci_assign_all_busses || next_busno <= hose->last_busno) + next_busno = hose->last_busno+1; + } + pci_bus_count = next_busno; + + + /* Call machine dependant fixup */ + if (ppc_md.pcibios_fixup) { + ppc_md.pcibios_fixup(); + } + + /* Generic fixups */ + pcibios_generic_fixup(); + + /* Allocate and assign resources */ + pcibios_allocate_bus_resources(&pci_root_buses); + if(naca->io_subsystem == IOS_OPEN_PIC) { + pcibios_allocate_resources(0); // DRENG Condor removed. This will have to be reviewed!!! + } + if(naca->io_subsystem == IOS_OPEN_PIC) { + pcibios_allocate_resources(1); // DRENG Condor removed. This will have to be reviewed!!! + } + pcibios_assign_resources(); + + /* + * Set up TCE tables for each PHB. + */ + for (hose = hose_head; hose; hose = hose->next) { + create_pci_bus_tce_table(hose->global_number, + (unsigned long)hose); + } + + ppc64_floppy_dev = find_floppy(); + + printk("PCI: Probing PCI hardware done\n"); + PPCDBG(PPCDBG_BUSWALK,"PCI: Probing PCI hardware done.\n"); + +} + +int __init +pcibios_assign_all_busses(void) +{ + return pci_assign_all_busses; +} + +unsigned long resource_fixup(struct pci_dev * dev, struct resource * res, + unsigned long start, unsigned long size) +{ + return start; +} + +void __init pcibios_fixup_bus(struct pci_bus *bus) +{ + pci_read_bridge_bases(bus); + + if ( ppc_md.pcibios_fixup_bus ) + ppc_md.pcibios_fixup_bus(bus); +} + +char __init *pcibios_setup(char *str) +{ + return str; +} + +int pcibios_enable_device(struct pci_dev *dev) +{ + u16 cmd, old_cmd; + int idx; + struct resource *r; + + if (ppc_md.pcibios_enable_device_hook) + if (ppc_md.pcibios_enable_device_hook(dev, 0)) + return -EINVAL; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + old_cmd = cmd; + for (idx=0; idx<6; idx++) { + r = &dev->resource[idx]; + if (!r->start && r->end) { + printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", dev->slot_name); + return -EINVAL; + } + if (r->flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (r->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; + } + if (cmd != old_cmd) { + printk("PCI: Enabling device %s (%04x -> %04x)\n", + dev->slot_name, old_cmd, cmd); + PPCDBG(PPCDBG_BUSWALK,"PCI: Enabling device %s \n",dev->slot_name); + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + return 0; +} + +struct pci_controller* +pci_bus_to_hose(int bus) +{ + struct pci_controller* hose = hose_head; + + for (; hose; hose = hose->next) + if (bus >= hose->first_busno && bus <= hose->last_busno) + return hose; + return NULL; +} + +void* +pci_bus_io_base(unsigned int bus) +{ + struct pci_controller *hose; + + hose = pci_bus_to_hose(bus); + if (!hose) + return NULL; + return hose->io_base_virt; +} + +unsigned long +pci_bus_io_base_phys(unsigned int bus) +{ + struct pci_controller *hose; + + hose = pci_bus_to_hose(bus); + if (!hose) + return 0; + return hose->io_base_phys; +} + +unsigned long +pci_bus_mem_base_phys(unsigned int bus) +{ + struct pci_controller *hose; + + hose = pci_bus_to_hose(bus); + if (!hose) + return 0; + return hose->pci_mem_offset; +} + +/* + * Return the index of the PCI controller for device pdev. + */ +int pci_controller_num(struct pci_dev *dev) +{ + struct pci_controller *hose = (struct pci_controller *) dev->sysdata; + + return hose->global_number; +} + +/* + * Platform support for /proc/bus/pci/X/Y mmap()s, + * modelled on the sparc64 implementation by Dave Miller. + * -- paulus. + */ + +/* + * Adjust vm_pgoff of VMA such that it is the physical page offset + * corresponding to the 32-bit pci bus offset for DEV requested by the user. + * + * Basically, the user finds the base address for his device which he wishes + * to mmap. They read the 32-bit value from the config space base register, + * add whatever PAGE_SIZE multiple offset they wish, and feed this into the + * offset parameter of mmap on /proc/bus/pci/XXX for that device. + * + * Returns negative error code on failure, zero on success. + */ +static __inline__ int +__pci_mmap_make_offset(struct pci_dev *dev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state) +{ + struct pci_controller *hose = (struct pci_controller *) dev->sysdata; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + unsigned long io_offset = 0; + int i, res_bit; + + if (hose == 0) + return -EINVAL; /* should never happen */ + + /* If memory, add on the PCI bridge address offset */ + if (mmap_state == pci_mmap_mem) { + offset += hose->pci_mem_offset; + res_bit = IORESOURCE_MEM; + } else { + io_offset = (unsigned long)hose->io_base_virt - isa_io_base; + offset += io_offset; + res_bit = IORESOURCE_IO; + } + + /* + * Check that the offset requested corresponds to one of the + * resources of the device. + */ + for (i = 0; i <= PCI_ROM_RESOURCE; i++) { + struct resource *rp = &dev->resource[i]; + int flags = rp->flags; + + /* treat ROM as memory (should be already) */ + if (i == PCI_ROM_RESOURCE) + flags |= IORESOURCE_MEM; + + /* Active and same type? */ + if ((flags & res_bit) == 0) + continue; + + /* In the range of this resource? */ + if (offset < (rp->start & PAGE_MASK) || offset > rp->end) + continue; + + /* found it! construct the final physical address */ + if (mmap_state == pci_mmap_io) + offset += hose->io_base_phys - io_offset; + + vma->vm_pgoff = offset >> PAGE_SHIFT; + return 0; + } + + return -EINVAL; +} + +/* + * Set vm_flags of VMA, as appropriate for this architecture, for a pci device + * mapping. + */ +static __inline__ void +__pci_mmap_set_flags(struct pci_dev *dev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state) +{ + vma->vm_flags |= VM_SHM | VM_LOCKED | VM_IO; +} + +/* + * Set vm_page_prot of VMA, as appropriate for this architecture, for a pci + * device mapping. + */ +static __inline__ void +__pci_mmap_set_pgprot(struct pci_dev *dev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, int write_combine) +{ + long prot = pgprot_val(vma->vm_page_prot); + + /* XXX would be nice to have a way to ask for write-through */ + prot |= _PAGE_NO_CACHE; + if (!write_combine) + prot |= _PAGE_GUARDED; + vma->vm_page_prot = __pgprot(prot); +} + +/* + * Perform the actual remap of the pages for a PCI device mapping, as + * appropriate for this architecture. The region in the process to map + * is described by vm_start and vm_end members of VMA, the base physical + * address is found in vm_pgoff. + * The pci device structure is provided so that architectures may make mapping + * decisions on a per-device or per-bus basis. + * + * Returns a negative error code on failure, zero on success. + */ +int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, + enum pci_mmap_state mmap_state, + int write_combine) +{ + int ret; + + ret = __pci_mmap_make_offset(dev, vma, mmap_state); + if (ret < 0) + return ret; + + __pci_mmap_set_flags(dev, vma, mmap_state); + __pci_mmap_set_pgprot(dev, vma, mmap_state, write_combine); + + ret = remap_page_range(vma->vm_start, vma->vm_pgoff << PAGE_SHIFT, + vma->vm_end - vma->vm_start, vma->vm_page_prot); + + return ret; +} + +/* Provide information on locations of various I/O regions in physical + * memory. Do this on a per-card basis so that we choose the right + * root bridge. + * Note that the returned IO or memory base is a physical address + */ + +long +sys_pciconfig_iobase(long which, unsigned long bus, unsigned long devfn) +{ + struct pci_controller* hose = pci_bus_to_hose(bus); + long result = -EOPNOTSUPP; + + if (!hose) + return -ENODEV; + + switch (which) { + case IOBASE_BRIDGE_NUMBER: + return (long)hose->first_busno; + case IOBASE_MEMORY: + return (long)hose->pci_mem_offset; + case IOBASE_IO: + return (long)hose->io_base_phys; + case IOBASE_ISA_IO: + return (long)isa_io_base; + case IOBASE_ISA_MEM: + return (long)isa_mem_base; + } + + return result; +} + +/***************************************************** + * Dump Resource information + *****************************************************/ +void dumpResources(struct resource* Resource) { + if(Resource != NULL) { + int Flags = 0x00000F00 & Resource->flags; + if(Resource->start == 0 && Resource->end == 0) return; + else if(Resource->start == Resource->end ) return; + else { + if (Flags == IORESOURCE_IO) udbg_printf("IO.:"); + else if(Flags == IORESOURCE_MEM) udbg_printf("MEM:"); + else if(Flags == IORESOURCE_IRQ) udbg_printf("IRQ:"); + else udbg_printf("0x%02X:",Resource->flags); + + } + udbg_printf("0x%016LX / 0x%016LX (0x%08X)\n", + Resource->start, Resource->end, Resource->end - Resource->start); + } +} +int resourceSize(struct resource* Resource) { + if(Resource->start == 0 && Resource->end == 0) return 0; + else if(Resource->start == Resource->end ) return 0; + else return (Resource->end-1)-Resource->start; +} + + +/***************************************************** + * Dump PHB information for Debug + *****************************************************/ +void dumpPci_Controller(struct pci_controller* phb) { + udbg_printf("\tpci_controller= 0x%016LX\n", phb); + if(phb != NULL) { + udbg_printf("\twhat & type = %s 0x%02X\n ",phb->what,phb->type); + udbg_printf("\tbus = "); + if(phb->bus != NULL) udbg_printf("0x%02X\n", phb->bus->number); + else udbg_printf("\n"); + udbg_printf("\tarch_data = 0x%016LX\n", phb->arch_data); + udbg_printf("\tfirst_busno = 0x%02X\n", phb->first_busno); + udbg_printf("\tlast_busno = 0x%02X\n", phb->last_busno); + udbg_printf("\tio_base_virt* = 0x%016LX\n", phb->io_base_virt); + udbg_printf("\tio_base_phys = 0x%016LX\n", phb->io_base_phys); + udbg_printf("\tpci_mem_offset= 0x%016LX\n", phb->pci_mem_offset); + udbg_printf("\tpci_io_offset = 0x%016LX\n", phb->pci_io_offset); + + udbg_printf("\tcfg_addr = 0x%016LX\n", phb->cfg_addr); + udbg_printf("\tcfg_data = 0x%016LX\n", phb->cfg_data); + udbg_printf("\tphb_regs = 0x%016LX\n", phb->phb_regs); + udbg_printf("\tchip_regs = 0x%016LX\n", phb->chip_regs); + + + udbg_printf("\tResources\n"); + dumpResources(&phb->io_resource); + if(phb->mem_resource_count > 0) dumpResources(&phb->mem_resources[0]); + if(phb->mem_resource_count > 1) dumpResources(&phb->mem_resources[1]); + if(phb->mem_resource_count > 2) dumpResources(&phb->mem_resources[2]); + + udbg_printf("\tglobal_num = 0x%02X\n", phb->global_number); + udbg_printf("\tlocal_num = 0x%02X\n", phb->local_number); + } +} +/***************************************************** + * Dump PHB information for Debug + *****************************************************/ +void dumpPci_Bus(struct pci_bus* Pci_Bus){ + int i; + udbg_printf("\tpci_bus = 0x%016LX \n",Pci_Bus); + if(Pci_Bus != NULL) { + + udbg_printf("\tnumber = 0x%02X \n",Pci_Bus->number); + udbg_printf("\tprimary = 0x%02X \n",Pci_Bus->primary); + udbg_printf("\tsecondary = 0x%02X \n",Pci_Bus->secondary); + udbg_printf("\tsubordinate = 0x%02X \n",Pci_Bus->subordinate); + + for(i=0;i<4;++i) { + if(Pci_Bus->resource[i] == NULL) continue; + if(Pci_Bus->resource[i]->start == 0 && Pci_Bus->resource[i]->end == 0) break; + udbg_printf("\tResources[%d]",i); + dumpResources(Pci_Bus->resource[i]); + } + } +} +/***************************************************** + * Dump Device information for Debug + *****************************************************/ +void dumpPci_Dev(struct pci_dev* Pci_Dev) { + int i; + udbg_printf("\tpci_dev = 0x%016LX \n",Pci_Dev); + if( Pci_Dev == NULL ) return; + udbg_printf("\tname = %s\n",Pci_Dev->name); + udbg_printf("\tpci_dev phb = 0x%016LX \n",PCI_GET_PHB_PTR(Pci_Dev) ); + udbg_printf("\tDevice = 0x%4X%02X:%02X.%02X 0x%04X:%04X\n", + PCI_GET_PHB_NUMBER(Pci_Dev), + PCI_GET_BUS_NUMBER(Pci_Dev), + PCI_SLOT(Pci_Dev->devfn), + PCI_FUNC(Pci_Dev->devfn), + Pci_Dev->vendor, + Pci_Dev->device); + udbg_printf("\tHdr/Irq = 0x%02X/0x%02X \n",Pci_Dev->hdr_type,Pci_Dev->irq); + for(i=0;iresource[i].start == 0 && Pci_Dev->resource[i].end == 0) continue; + udbg_printf("\tResources[%d] ",i); + dumpResources(&Pci_Dev->resource[i]); + } + dumpResources(&Pci_Dev->resource[i]); +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/pci.h linuxppc64_2_4/arch/ppc64/kernel/pci.h --- linux-2.4.5-ac18/arch/ppc64/kernel/pci.h Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/pci.h Tue Jul 3 12:41:06 2001 @@ -0,0 +1,72 @@ +/* + * c 2001 PPC 64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef __PPC_KERNEL_PCI_H__ +#define __PPC_KERNEL_PCI_H__ + +extern unsigned long isa_io_base; +extern unsigned long isa_mem_base; +extern unsigned long pci_dram_offset; + +/******************************************************************* + * Platform independant variables referenced. + ******************************************************************* + * Set pci_assign_all_busses to 1 if you want the kernel to re-assign + * all PCI bus numbers. + *******************************************************************/ +extern int pci_assign_all_busses; + +extern struct pci_controller* pci_alloc_pci_controller(char *model, enum phb_types controller_type); +extern struct pci_controller* pci_find_hose_for_OF_device(struct device_node* node); + +/******************************************************************* + * Platform functions that are brand specific implementation. + *******************************************************************/ +extern unsigned long find_and_init_phbs(void); + +extern int pci_read_irq_line(struct pci_dev* PciDev); +extern int pci_read_bar_registers(struct pci_dev* PciDev, int Count, int RomFlag); + +extern void fixup_resources(struct pci_dev *dev); +extern void ppc64_pcibios_init(void); + +extern int pci_reset_device(struct pci_dev*); +extern int pci_get_location(struct pci_dev*, char*, int); + +extern struct pci_dev* find_floppy(void); +extern struct pci_dev *ppc64_floppy_dev; +/******************************************************************* + * Platform configuration flags.. (Live in pci.c) + *******************************************************************/ +extern int Pci_Large_Bus_System; /* System has > 256 buses */ +extern int Pci_Set_IOA_Address; /* Set IOA BARs from OF */ +extern int Pci_Manage_Phb_Space; /* Manage Phb Space for IOAs*/ + +/******************************************************************* + * Helper macros for extracting data from pci structures. + * PCI_GET_PHB_PTR(struct pci_dev*) returns the Phb pointer. + * PCI_GET_PHB_NUMBER(struct pci_dev*) returns the Phb number. + * PCI_GET_BUS_NUMBER(struct pci_dev*) returns the bus number. + *******************************************************************/ +#define PCI_GET_PHB_PTR(PCIDEV) ((struct pci_controller*)##PCIDEV##->sysdata) +#define PCI_GET_PHB_NUMBER(PCIDEV) ((##PCIDEV##->bus->number&0x00FFFF00)>>8) +#define PCI_GET_BUS_NUMBER(PCIDEV) ( ##PCIDEV##->bus->number&0x0000FF) + +/******************************************************************* + * Debugging Routines. + *******************************************************************/ +extern void dumpResources(struct resource* Resource); +extern void dumpPci_Controller(struct pci_controller* phb); +extern void dumpPci_Bus(struct pci_bus* Pci_Bus); +extern void dumpPci_Dev(struct pci_dev* Pci_Dev); + +extern void dump_Phb_tree(void); +extern void dump_Bus_tree(void); +extern void dump_Dev_tree(void); + +#endif /* __PPC_KERNEL_PCI_H__ */ diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/pci_dma.c linuxppc64_2_4/arch/ppc64/kernel/pci_dma.c --- linux-2.4.5-ac18/arch/ppc64/kernel/pci_dma.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/pci_dma.c Fri Jul 13 10:05:25 2001 @@ -0,0 +1,1382 @@ +/* + * pci_dma.c + * Copyright (C) 2001 Mike Corrigan & Dave Engebretsen, IBM Corporation + * + * Dynamic DMA mapping support. + * + * Manages the TCE space assigned to this partition. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define DEBUG_TCE 1 + +/* Initialize so this guy does not end up in the BSS section. + * Only used to pass OF initialization data set in prom.c into the main + * kernel code -- data ultimately copied into tceTables[]. + */ +extern struct _of_tce_table of_tce_table[]; + +struct TceTable virtBusTceTable; /* Tce table for virtual bus */ + +struct TceTable * tceTables[256]; /* Tce tables for 256 busses + * Bus 255 is the virtual bus + * zero indicates no bus defined + */ + /* allocates a contiguous range of tces (power-of-2 size) */ +static long alloc_tce_range( struct TceTable *, + unsigned order ); + /* allocates a contiguous range of tces (power-of-2 size) + * assumes lock already held + */ +static long alloc_tce_range_nolock( struct TceTable *, + unsigned order ); + /* frees a contiguous range of tces (power-of-2 size) */ +static void free_tce_range( struct TceTable *, + long tcenum, + unsigned order ); + /* frees a contiguous rnage of tces (power-of-2 size) + * assumes lock already held + */ +static void free_tce_range_nolock( struct TceTable *, + long tcenum, + unsigned order ); + /* allocates a range of tces and sets them to the pages */ +static dma_addr_t get_tces( struct TceTable *, + unsigned order, + void *page, + unsigned numPages, + int tceType, + int direction ); +static void free_tces( struct TceTable *, + dma_addr_t tce, + unsigned order, + unsigned numPages ); +static long test_tce_range( struct TceTable *, + long tcenum, + unsigned order ); + +static unsigned fill_scatterlist_sg( struct scatterlist *sg, int nents, + dma_addr_t dma_addr, unsigned long numTces ); + +static unsigned long num_tces_sg( struct scatterlist *sg, + int nents ); + +static dma_addr_t create_tces_sg( struct TceTable *tbl, + struct scatterlist *sg, + int nents, + unsigned numTces, + int tceType, + int direction ); + +static void getTceTableParms( struct pci_controller *phb, + struct TceTable *tce_table_parms ); + + +static unsigned long setTce( unsigned long base, + unsigned long tce_num, + unsigned long tce_data); + +static long resv_tce_range_top_level( struct TceTable *tbl, + unsigned long dma_addr, + unsigned size ); + +u8 iSeries_Get_Bus( struct pci_dev * dv ) +{ + return 0; +} + +/* + * Given a pci device, return which tce table is assigned to it. + */ +unsigned long get_tce_table_index( struct pci_dev *dev) { + unsigned long index = + ((struct pci_controller *)dev->sysdata)->global_number; + PPCDBG(PPCDBG_TCE, "get_tce_table_index:\n"); + PPCDBG(PPCDBG_TCE, "\tdev = 0x%lx, index = 0x%lx\n", dev, index); + return(index); +} + +static unsigned __inline__ count_leading_zeros32( unsigned long x ) +{ + unsigned lz; + asm("cntlzw %0,%1" : "=r"(lz) : "r"(x)); + return lz; +} + +static void __inline__ build_tce( struct TceTable * tbl, long tcenum, + unsigned long uaddr, int tceType, int direction ) +{ + u64 setTceRc; + union Tce tce; + + PPCDBG(PPCDBG_TCE, "build_tce: uaddr = 0x%lx\n", uaddr); + tce.wholeTce = 0; + tce.tceBits.rpn = (virt_to_absolute(uaddr)) >> PAGE_SHIFT; + /* If for virtual bus */ + if ( tceType == TCE_VB ) { + tce.tceBits.valid = 1; + tce.tceBits.allIo = 1; + if ( direction != PCI_DMA_TODEVICE ) + tce.tceBits.readWrite = 1; + } + /* If for PCI bus */ + else { + tce.tceBits.readWrite = 1; // Read allowed + if ( direction != PCI_DMA_TODEVICE ) + tce.tceBits.pciWrite = 1; + } + +#ifdef CONFIG_PPC_ISERIES + setTceRc = HvCallXm_setTce( (u64)tbl->index, (u64)tcenum, tce.wholeTce ); +#else + setTceRc = setTce( (u64)tbl->base, (u64)tcenum, tce.wholeTce ); +#endif + + if ( setTceRc ) { + PPCDBG(PPCDBG_TCE, "build_tce: HvCallXm_setTce failed, rc=%ld, index=%ld, tcenum=%0lx, tce=%016lx\n", + (u64)tbl->index, (u64)tcenum, tce.wholeTce ); + } + +} + + + +/* + * Build a TceTable structure. This contains a multi-level bit map which + * is used to manage allocation of the tce space. + */ +struct TceTable * build_tce_table( struct TceTable * tbl ) +{ + unsigned long bits, bytes, totalBytes; + unsigned long numBits[NUM_TCE_LEVELS], numBytes[NUM_TCE_LEVELS]; + unsigned i, k, m; + unsigned char * pos, * p, b; + + spin_lock_init( &(tbl->lock) ); + + tbl->mlbm.maxLevel = 0; + + /* Compute number of bits and bytes for each level of the + * multi-level bit map + */ + totalBytes = 0; + bits = tbl->size * (PAGE_SIZE / sizeof( union Tce )); + + for ( i=0; imlbm.level[i].map = pos; + tbl->mlbm.maxLevel = i; + + if ( numBits[i] & 1 ) { + p = pos + numBytes[i] - 1; + m = (( numBits[i] % 8) - 1) & 7; + *p = 0x80 >> m; + PPCDBG(PPCDBG_TCE, "build_tce_table: level %d last bit %x\n", i, 0x80>>m ); + } + } + else + tbl->mlbm.level[i].map = 0; + pos += numBytes[i]; + tbl->mlbm.level[i].numBits = numBits[i]; + tbl->mlbm.level[i].numBytes = numBytes[i]; + + } + + /* For the highest level, turn on all the bits */ + + i = tbl->mlbm.maxLevel; + p = tbl->mlbm.level[i].map; + m = numBits[i]; + PPCDBG(PPCDBG_TCE, "build_tce_table: highest level (%d) has all bits set\n", i); + for (k=0; k= 8 ) { + /* handle full bytes */ + *p++ = 0xff; + m -= 8; + } + else { + /* handle the last partial byte */ + b = 0x80; + *p = 0; + while (m) { + *p |= b; + b >>= 1; + --m; + } + } + } + + + return tbl; + +} + +static long alloc_tce_range( struct TceTable *tbl, unsigned order ) +{ + long retval; + unsigned long flags; + + /* Lock the tce allocation bitmap */ + spin_lock_irqsave( &(tbl->lock), flags ); + + /* Do the actual work */ + retval = alloc_tce_range_nolock( tbl, order ); + + /* Unlock the tce allocation bitmap */ + spin_unlock_irqrestore( &(tbl->lock), flags ); + + return retval; +} + + + +static long alloc_tce_range_nolock( struct TceTable *tbl, unsigned order ) +{ + unsigned long numBits, numBytes; + unsigned long i, bit, block, mask; + long tcenum; + unsigned char * map; + + /* If the order (power of 2 size) requested is larger than our + * biggest, indicate failure + */ + if ( order > tbl->mlbm.maxLevel ) { + PPCDBG(PPCDBG_TCE, "alloc_tce_range_nolock: invalid order requested, order = %d\n", + order ); + return -1; + } + + numBits = tbl->mlbm.level[order].numBits; + numBytes = tbl->mlbm.level[order].numBytes; + map = tbl->mlbm.level[order].map; + + /* Initialize return value to -1 (failure) */ + tcenum = -1; + + /* Loop through the bytes of the bitmap */ + for (i=0; i> bit); + *map &= mask; + /* compute the index into our tce table for + * the first tce in the block + */ + PPCDBG(PPCDBG_TCE, "alloc_tce_range_nolock: allocating block %ld, (byte=%ld, bit=%ld) order %d\n", block, i, bit, order ); + tcenum = block << order; + break; + } + ++map; + } + +#ifdef DEBUG_TCE + if ( tcenum == -1 ) { + PPCDBG(PPCDBG_TCE, "alloc_tce_range_nolock: no available blocks of order = %d\n", order ); + if ( order < tbl->mlbm.maxLevel ) + PPCDBG(PPCDBG_TCE, "alloc_tce_range_nolock: trying next bigger size\n" ); + else + PPCDBG(PPCDBG_TCE, "alloc_tce_range_nolock: maximum size reached...failing\n"); + } +#endif + + /* If no block of the requested size was found, try the next + * size bigger. If one of those is found, return the second + * half of the block to freespace and keep the first half + */ + if ( ( tcenum == -1 ) && ( order < tbl->mlbm.maxLevel ) ) { + tcenum = alloc_tce_range_nolock( tbl, order+1 ); + if ( tcenum != -1 ) { + free_tce_range_nolock( tbl, tcenum+(1<lock), flags ); + + /* Do the actual work */ + free_tce_range_nolock( tbl, tcenum, order ); + + /* Unlock the tce allocation bitmap */ + spin_unlock_irqrestore( &(tbl->lock), flags ); + +} + +static void free_tce_range_nolock( struct TceTable *tbl, long tcenum, unsigned order ) +{ + unsigned long block; + unsigned byte, bit, mask, b; + unsigned char * map, * bytep; + + if ( order > tbl->mlbm.maxLevel ) { + PPCDBG(PPCDBG_TCE, "free_tce_range: order too large, order = %d, tcenum = %d\n", order, tcenum ); + return; + } + + block = tcenum >> order; + if ( tcenum != (block << order ) ) { + PPCDBG(PPCDBG_TCE, "free_tce_range: tcenum %lx is not on appropriate boundary for order %x\n", tcenum, order ); + return; + } + if ( block >= tbl->mlbm.level[order].numBits ) { + PPCDBG(PPCDBG_TCE, "free_tce_range: tcenum %lx is outside the range of this map (order %x, numBits %lx\n", tcenum, order, tbl->mlbm.level[order].numBits ); + return; + } +#ifdef DEBUG_TCE + if ( test_tce_range( tbl, tcenum, order ) ) { + PPCDBG(PPCDBG_TCE, "free_tce_range: freeing range not completely allocated.\n"); + PPCDBG(PPCDBG_TCE, "free_tce_range: TceTable %p, tcenum %lx, order %x\n", tbl, tcenum, order ); + } +#endif + map = tbl->mlbm.level[order].map; + byte = block / 8; + bit = block % 8; + mask = 0x80 >> bit; + bytep = map + byte; +#ifdef DEBUG_TCE + PPCDBG(PPCDBG_TCE, "free_tce_range_nolock: freeing block %ld (byte=%d, bit=%d) of order %d\n",block, byte, bit, order); + if ( *bytep & mask ) + PPCDBG(PPCDBG_TCE, "free_tce_range: already free: TceTable %p, tcenum %lx, order %x\n", tbl, tcenum, order ); +#endif + *bytep |= mask; + + /* If there is a higher level in the bit map than this we may be + * able to buddy up this block with its partner. + * If this is the highest level we can't buddy up + * If this level has an odd number of bits and + * we are freeing the last block we can't buddy up + */ + if ( ( order < tbl->mlbm.maxLevel ) && + ( ( 0 == ( tbl->mlbm.level[order].numBits & 1 ) ) || + ( block < tbl->mlbm.level[order].numBits-1 ) ) ) { + + /* See if we can buddy up the block we just freed */ + bit &= 6; /* get to the first of the buddy bits */ + mask = 0xc0 >> bit; /* build two bit mask */ + b = *bytep & mask; /* Get the two bits */ + if ( 0 == (b ^ mask) ) { /* If both bits are on */ + /* both of the buddy blocks are free we can combine them */ + *bytep ^= mask; /* turn off the two bits */ + block = ( byte * 8 ) + bit; /* block of first of buddies */ + tcenum = block << order; + /* free the buddied block */ + PPCDBG(PPCDBG_TCE, "free_tce_range: buddying up block %ld and block %ld\n", block, block+1); + free_tce_range_nolock( tbl, tcenum, order+1 ); + } + } +} + +static long test_tce_range( struct TceTable *tbl, long tcenum, unsigned order ) +{ + unsigned long block; + unsigned byte, bit, mask, b; + long retval, retLeft, retRight; + unsigned char * map; + + map = tbl->mlbm.level[order].map; + block = tcenum >> order; + byte = block / 8; /* Byte within bitmap */ + bit = block % 8; /* Bit within byte */ + mask = 0x80 >> bit; + b = (*(map+byte) & mask ); /* 0 if block is allocated, else free */ + if ( b ) + retval = 1; /* 1 == block is free */ + else + retval = 0; /* 0 == block is allocated */ + /* Test bits at all levels below this to ensure that all agree */ + + if (order) { + retLeft = test_tce_range( tbl, tcenum, order-1 ); + retRight = test_tce_range( tbl, tcenum+(1<<(order-1)), order-1 ); + if ( retLeft || retRight ) { + retval = 2; + } + } + + /* Test bits at all levels above this to ensure that all agree */ + + return retval; +} + +static dma_addr_t get_tces( struct TceTable *tbl, unsigned order, void *page, unsigned numPages, int tceType, int direction ) +{ + long tcenum; + unsigned long uaddr; + unsigned i; + dma_addr_t retTce = NO_TCE; + + uaddr = (unsigned long)page & PAGE_MASK; + + /* Allocate a range of tces */ + tcenum = alloc_tce_range( tbl, order ); + if ( tcenum != -1 ) { + /* We got the tces we wanted */ + tcenum += tbl->startOffset; /* Offset into real TCE table */ + retTce = tcenum << PAGE_SHIFT; /* Set the return dma address */ + /* Setup a tce for each page */ + for (i=0; isize * (PAGE_SIZE / sizeof(union Tce))) - 1; + + tcenum = dma_addr >> PAGE_SHIFT; + tcenum -= tbl->startOffset; + + if ( tcenum > maxTcenum ) { + PPCDBG(PPCDBG_TCE, "free_tces: tcenum > maxTcenum, tcenum = %ld, maxTcenum = %ld\n", + tcenum, maxTcenum ); + PPCDBG(PPCDBG_TCE, "free_tces: TCE Table at %16lx\n", (unsigned long)tbl ); + PPCDBG(PPCDBG_TCE, "free_tces: bus# %lu\n", (unsigned long)tbl->busNumber ); + PPCDBG(PPCDBG_TCE, "free_tces: size %lu\n", (unsigned long)tbl->size ); + PPCDBG(PPCDBG_TCE, "free_tces: startOff %lu\n", (unsigned long)tbl->startOffset ); + PPCDBG(PPCDBG_TCE, "free_tces: index %lu\n", (unsigned long)tbl->index ); + return; + } + + freeTce = tcenum; + + for (i=0; iindex, (u64)tcenum, tce.wholeTce ); +#else + setTceRc = setTce( (u64)tbl->base, (u64)tcenum, tce.wholeTce ); +#endif + + if ( setTceRc ) { + PPCDBG(PPCDBG_TCE, "free_tces: HvCallXm_setTce failed, rc=%ld, index=%ld, tcenum=%0lx, tce=%016lx\n", + (u64)tbl->index, (u64)tcenum, tce.wholeTce ); + } + + ++tcenum; + } + + free_tce_range( tbl, freeTce, order ); + +} + +void __init create_virtual_bus_tce_table(void) +{ + struct TceTable *t; + struct TceTableManagerCB virtBusTceTableParms; + u64 absParmsPtr; + + virtBusTceTableParms.busNumber = 255; /* Bus 255 is the virtual bus */ + virtBusTceTableParms.virtualBusFlag = 0xff; /* Ask for virtual bus */ + + absParmsPtr = virt_to_absolute( (u64)&virtBusTceTableParms ); + HvCallXm_getTceTableParms( absParmsPtr ); + + virtBusTceTable.size = virtBusTceTableParms.size; + virtBusTceTable.busNumber = virtBusTceTableParms.busNumber; + virtBusTceTable.startOffset = virtBusTceTableParms.startOffset; + virtBusTceTable.index = virtBusTceTableParms.index; + + t = build_tce_table( &virtBusTceTable ); + if ( t ) { + tceTables[255] = t; + PPCDBG(PPCDBG_TCE, "Virtual Bus TCE table built successfully.\n"); + PPCDBG(PPCDBG_TCE, " TCE table size = %ld entries\n", + (unsigned long)t->size*(PAGE_SIZE/sizeof(union Tce)) ); + PPCDBG(PPCDBG_TCE, " TCE table token = %d\n", + (unsigned)t->index ); + PPCDBG(PPCDBG_TCE, " TCE table start entry = 0x%lx\n", + (unsigned long)t->startOffset ); + } + else + PPCDBG(PPCDBG_TCE, "Virtual Bus TCE table failed.\n"); +} + +void create_pci_bus_tce_table( unsigned long busNumber, + unsigned long token ) +{ + struct TceTable * t; + struct TceTable * newTceTable; + struct TceTableManagerCB pciBusTceTableParms; +#ifdef CONFIG_PPC_ISERIES + u64 parmsPtr; +#endif + struct pci_controller *phb = (struct pci_controller *)token; + + PPCDBG(PPCDBG_TCE, "Entering create_pci_bus_tce_table.\n"); + PPCDBG(PPCDBG_TCE, "\tbusNumber = 0x%lx, token = 0x%lx.\n", + busNumber, token); + if ( busNumber > 254 ) { + PPCDBG(PPCDBG_TCE, "PCI Bus TCE table failed.\n"); + PPCDBG(PPCDBG_TCE, " Invalid bus number %u\n", busNumber ); + return; + } + + newTceTable = kmalloc( sizeof(struct TceTable), GFP_KERNEL ); + PPCDBG(PPCDBG_TCE, "\tnewTceTable = 0x%lx\n", newTceTable); + + pciBusTceTableParms.busNumber = busNumber; + pciBusTceTableParms.virtualBusFlag = 0; + +#ifdef CONFIG_PPC_ISERIES + parmsPtr = virt_to_absolute( (u64)&pciBusTceTableParms ); + + /* + * Call HV with the architected data structure to get TCE table info. + * Put the returned data into the Linux representation of the TCE + * table data. + */ + HvCallXm_getTceTableParms( parmsPtr ); + newTceTable->size = pciBusTceTableParms.size; + newTceTable->busNumber = pciBusTceTableParms.busNumber; + newTceTable->startOffset = pciBusTceTableParms.startOffset; + newTceTable->index = pciBusTceTableParms.index; +#else + getTceTableParms( phb, newTceTable ); +#endif + + t = build_tce_table( newTceTable ); + if ( t ) { + tceTables[busNumber] = t; + PPCDBG(PPCDBG_TCE, "PCI Bus TCE table built successfully.\n"); + PPCDBG(PPCDBG_TCE, " TCE table size = %ld entries\n", + (unsigned long)t->size*(PAGE_SIZE/sizeof(union Tce)) ); + PPCDBG(PPCDBG_TCE, " TCE table token = %d\n", + (unsigned)t->index ); + PPCDBG(PPCDBG_TCE, " TCE table start entry = 0x%lx\n", + (unsigned long)t->startOffset ); + } + else { + kfree( newTceTable ); + PPCDBG(PPCDBG_TCE, "PCI Bus TCE table failed.\n"); + return; + } + +#ifndef CONFIG_PPC_ISERIES + /* Do not allow DMA's to the 1st 16MB of PCI space in order + * to account for the I/O hole and aliases. + * + * DRENG: this only really needs to be done on PHB0 & should be + * validated against dma-ranges. This is all somewhat compilcated + * by some OF oddities: on a 260, there is no dma-ranges, but there + * is a io-hole. On an F80, there are dma-ranges, but no I/O hole. + * Just seems easier to simply map out the first 16MB in all cases. + * + * The RTAS case here ends up being redundant as it falls into + * the 16MB already mapped out. + */ + resv_tce_range_top_level(newTceTable, 0, 4096); + resv_tce_range_top_level(newTceTable, rtas_data.base, + (rtas_data.size) >> PAGE_SHIFT); + +#if 1 + /* Initialize the table to have a one-to-one mapping over RTAS */ + + /* DRENG strictly speaking, the RPA says this should be done. + * I do not see how it can really be a valid thing to require however. + */ + { + unsigned long tce_entry, *tce_entryp, i; + + tce_entryp = (unsigned long *)newTceTable->base; + tce_entryp += (rtas_data.base >> PAGE_SHIFT); + tce_entry = (rtas_data.base & PAGE_MASK) | 0x3; + + for(i = 0; i < (rtas_data.size >> PAGE_SHIFT); i++) { + *tce_entryp = tce_entry; + + tce_entryp++; + tce_entry += (1UL << PAGE_SHIFT); + } + } +#endif +#endif +} + +static void getTceTableParms( struct pci_controller *phb, + struct TceTable *newTceTable ) { +#if 0 + struct pci_dev *dev; + struct device_node *np; +#endif + phandle node; + unsigned long i; + + node = ((struct device_node *)(phb->arch_data))->node; + + PPCDBG(PPCDBG_TCE, "getTceTableParms: start\n"); + PPCDBG(PPCDBG_TCE, "\tof_tce_table = 0x%lx\n", of_tce_table); + PPCDBG(PPCDBG_TCE, "\tphb = 0x%lx\n", phb); + PPCDBG(PPCDBG_TCE, "\tnewTceTable = 0x%lx\n", newTceTable); + + i = 0; + while(of_tce_table[i].node) { + PPCDBG(PPCDBG_TCE, "\ttce_table[%d].node = 0x%lx\n", + i, of_tce_table[i].node); + PPCDBG(PPCDBG_TCE, "\ttce_table[%d].base = 0x%lx\n", + i, of_tce_table[i].base); + PPCDBG(PPCDBG_TCE, "\ttce_table[%d].size = 0x%lx\n", + i, of_tce_table[i].size >> PAGE_SHIFT); + PPCDBG(PPCDBG_TCE, "\tphb->arch_data->node = 0x%lx\n", node); + + if(of_tce_table[i].node == node) { + memset((void *)of_tce_table[i].base, + 0, of_tce_table[i].size); + newTceTable->busNumber = phb->bus->number; + newTceTable->size = + (of_tce_table[i].size) >> PAGE_SHIFT; + newTceTable->startOffset = 0; + newTceTable->base = + of_tce_table[i].base; + newTceTable->index = 0; + } + i++; + } + +#if 0 + pci_for_each_dev(dev) { + np = pci_device_to_OF_node(dev); + node = np->node; + bus_number = dev->bus->number; + create_pci_bus_tce_table( unsigned long bus_number ); + node = hose->arch_data->node; + } +#endif +} + +/* Allocates a contiguous real buffer and creates TCEs over it. + * Returns the virtual address of the buffer and sets dma_handle + * to the dma address (tce) of the first page. + */ +void *pci_alloc_consistent(struct pci_dev *hwdev, size_t size, + dma_addr_t *dma_handle) +{ + struct TceTable * tbl; + void *ret = NULL; + unsigned order, nPages, bus; + dma_addr_t tce; + int tceType; + + PPCDBG(PPCDBG_TCE, "pci_alloc_consistent:\n"); + PPCDBG(PPCDBG_TCE, "\thwdev = 0x%16.16lx\n", hwdev); + PPCDBG(PPCDBG_TCE, "\tsize = 0x%16.16lx\n", size); + PPCDBG(PPCDBG_TCE, "\tdma_handle = 0x%16.16lx\n", dma_handle); + + size = PAGE_ALIGN(size); + order = get_order(size); + nPages = 1 << order; + + /* If no pci_dev then use virtual bus */ + if (hwdev == NULL ) { + bus = 255; + tceType = TCE_VB; + } + else { +#ifdef CONFIG_PCI + /* Get the iSeries bus # to use as an index + * into the TCE table array + */ + bus = get_tce_table_index( hwdev ); + // bus = ISERIES_GET_BUS( hwdev ); + tceType = TCE_PCI; +#else + BUG(); + return NULL; +#endif /* CONFIG_PCI */ + } + + tbl = tceTables[bus]; + if ( tbl ) { + /* Alloc enough pages (and possibly more) */ + ret = (void *)__get_free_pages( GFP_ATOMIC, order ); + if ( ret ) { + /* Page allocation succeeded */ + memset(ret, 0, nPages << PAGE_SHIFT); + /* Set up tces to cover the allocated range */ + tce = get_tces( tbl, order, ret, nPages, tceType, + PCI_DMA_BIDIRECTIONAL ); + if ( tce == NO_TCE ) { + PPCDBG(PPCDBG_TCE, "pci_alloc_consistent: get_tces failed\n" ); + free_pages( (unsigned long)ret, order ); + ret = NULL; + } + else + { + *dma_handle = tce; + } + } + else + PPCDBG(PPCDBG_TCE, "pci_alloc_consistent: __get_free_pages failed for order = %d\n", order); + } + + PPCDBG(PPCDBG_TCE, "\tpci_alloc_consistent: dma_handle = 0x%16.16lx\n", *dma_handle); + PPCDBG(PPCDBG_TCE, "\tpci_alloc_consistent: return = 0x%16.16lx\n", ret); + return ret; +} + +void pci_free_consistent(struct pci_dev *hwdev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + struct TceTable * tbl; + unsigned order, nPages, bus; + + PPCDBG(PPCDBG_TCE, "pci_free_consistent:\n"); + PPCDBG(PPCDBG_TCE, "\thwdev = 0x%16.16lx, size = 0x%16.16lx, dma_handle = 0x%16.16lx, vaddr = 0x%16.16lx\n", hwdev, size, dma_handle, vaddr); + + size = PAGE_ALIGN(size); + order = get_order(size); + nPages = 1 << order; + + if ( order > 10 ) + PPCDBG(PPCDBG_TCE, "pci_free_consistent: order=%d, size=%d, nPages=%d, dma_handle=%016lx, vaddr=%016lx\n", + order, size, nPages, (unsigned long)dma_handle, (unsigned long)vaddr ); + + /* If no pci_dev then use virtual bus */ + if (hwdev == NULL ) + bus = 255; + else { +#ifdef CONFIG_PCI + /* Get the iSeries bus # to use as an index + * into the TCE table array + */ + bus = get_tce_table_index( hwdev ); + // bus = ISERIES_GET_BUS( hwdev ); +#else + BUG(); + return; +#endif /* CONFIG_PCI */ + } + + if ( bus > 255 ) { + PPCDBG(PPCDBG_TCE, "pci_free_consistent: invalid bus # %d\n", bus ); + PPCDBG(PPCDBG_TCE, "pci_free_consistent: hwdev = %08lx\n", (unsigned long)hwdev ); + } + + tbl = tceTables[bus]; + + if ( tbl ) { + free_tces( tbl, dma_handle, order, nPages ); + free_pages( (unsigned long)vaddr, order ); + } +} + +/* Creates TCEs for a user provided buffer. The user buffer must be + * contiguous real kernel storage (not vmalloc). The address of the buffer + * passed here is the kernel (virtual) address of the buffer. The buffer + * need not be page aligned, the dma_addr_t returned will point to the same + * byte within the page as vaddr. + */ +dma_addr_t pci_map_single( struct pci_dev *hwdev, void *vaddr, size_t size, int direction ) +{ + struct TceTable * tbl; + dma_addr_t dma_handle; + unsigned long uaddr; + unsigned order, nPages, bus; + int tceType; + + PPCDBG(PPCDBG_TCE, "pci_map_single:\n"); + PPCDBG(PPCDBG_TCE, "\thwdev = 0x%16.16lx, size = 0x%16.16lx, direction = 0x%16.16lx, vaddr = 0x%16.16lx\n", hwdev, size, direction, vaddr); + if ( direction == PCI_DMA_NONE ) + BUG(); + + dma_handle = NO_TCE; + + uaddr = (unsigned long)vaddr; + nPages = PAGE_ALIGN( uaddr + size ) - ( uaddr & PAGE_MASK ); + order = get_order( nPages & PAGE_MASK ); + nPages >>= PAGE_SHIFT; + + /* If no pci_dev then use virtual bus */ + if (hwdev == NULL ) { + bus = 255; + tceType = TCE_VB; + } + else { +#ifdef CONFIG_PCI + /* Get the iSeries bus # to use as an index + * into the TCE table array + */ + bus = get_tce_table_index( hwdev ); + // bus = ISERIES_GET_BUS( hwdev ); + tceType = TCE_PCI; +#else + BUG(); + return NO_TCE; +#endif /* CONFIG_PCI */ + + } + + tbl = tceTables[bus]; + + if ( tbl ) { + dma_handle = get_tces( tbl, order, vaddr, nPages, tceType, + direction ); + dma_handle |= ( uaddr & ~PAGE_MASK ); + } + + return dma_handle; +} + +void pci_unmap_single( struct pci_dev *hwdev, dma_addr_t dma_handle, size_t size, int direction ) +{ + struct TceTable * tbl; + unsigned order, nPages, bus; + + PPCDBG(PPCDBG_TCE, "pci_unmap_single:\n"); + PPCDBG(PPCDBG_TCE, "\thwdev = 0x%16.16lx, size = 0x%16.16lx, direction = 0x%16.16lx, dma_handle = 0x%16.16lx\n", hwdev, size, direction, dma_handle); + if ( direction == PCI_DMA_NONE ) + BUG(); + + nPages = PAGE_ALIGN( dma_handle + size ) - ( dma_handle & PAGE_MASK ); + order = get_order( nPages & PAGE_MASK ); + nPages >>= PAGE_SHIFT; + + if ( order > 10 ) + PPCDBG(PPCDBG_TCE, "pci_unmap_single: order=%d, size=%d, nPages=%d, dma_handle=%016lx\n", + order, size, nPages, (unsigned long)dma_handle ); + + /* If no pci_dev then use virtual bus */ + if (hwdev == NULL ) + bus = 255; + else { +#ifdef CONFIG_PCI + /* Get the iSeries bus # to use as an index + * into the TCE table array + */ + bus = get_tce_table_index( hwdev ); + // bus = ISERIES_GET_BUS( hwdev ); +#else + BUG(); + return; +#endif /* CONFIG_PCI */ + } + + if ( bus > 255 ) { + PPCDBG(PPCDBG_TCE, "pci_unmap_single: invalid bus # %d\n", bus ); + PPCDBG(PPCDBG_TCE, "pci_unmap_single: hwdev = %08lx\n", (unsigned long)hwdev ); + } + + tbl = tceTables[bus]; + + if ( tbl ) + free_tces( tbl, dma_handle, order, nPages ); + +} + +/* Figure out how many TCEs are actually going to be required + * to map this scatterlist. This code is not optimal. It + * takes into account the case where entry n ends in the same + * page in which entry n+1 starts. It does not handle the + * general case of entry n ending in the same page in which + * entry m starts. + */ +static unsigned long num_tces_sg( struct scatterlist *sg, int nents ) +{ + unsigned long nTces, numPages, startPage, endPage, prevEndPage; + unsigned i; + + prevEndPage = 0; + nTces = 0; + + for (i=0; iaddress >> PAGE_SHIFT; + endPage = ((unsigned long)sg->address + sg->length - 1) >> PAGE_SHIFT; + numPages = endPage - startPage + 1; + /* Simple optimization: if the previous entry ended + * in the same page in which this entry starts + * then we can reduce the required pages by one. + * This matches assumptions in fill_scatterlist_sg and + * create_tces_sg + */ + if ( startPage == prevEndPage ) + --numPages; + nTces += numPages; + prevEndPage = endPage; + sg++; + } + return nTces; +} + +/* Fill in the dma data in the scatterlist + * return the number of dma sg entries created + */ +static unsigned fill_scatterlist_sg( struct scatterlist *sg, int nents, + dma_addr_t dma_addr , unsigned long numTces) +{ + struct scatterlist *dma_sg; + u32 cur_start_dma; + unsigned long cur_len_dma, cur_end_virt, uaddr; + unsigned num_dma_ents; + + dma_sg = sg; + num_dma_ents = 1; + + /* Process the first sg entry */ + cur_start_dma = dma_addr + ((unsigned long)sg->address & (~PAGE_MASK)); + cur_len_dma = sg->length; + /* cur_end_virt holds the address of the byte immediately after the + * end of the current buffer. + */ + cur_end_virt = (unsigned long)sg->address + cur_len_dma; + /* Later code assumes that unused sg->dma_address and sg->dma_length + * fields will be zero. Other archs seem to assume that the user + * (device driver) guarantees that...I don't want to depend on that + */ + sg->dma_address = sg->dma_length = 0; + + /* Process the rest of the sg entries */ + while (--nents) { + ++sg; + /* Clear possibly unused fields. Note: sg >= dma_sg so + * this can't be clearing a field we've already set + */ + sg->dma_address = sg->dma_length = 0; + + /* Check if it is possible to make this next entry + * contiguous (in dma space) with the previous entry. + */ + + /* The entries can be contiguous in dma space if + * the previous entry ends immediately before the + * start of the current entry (in virtual space) + * or if the previous entry ends at a page boundary + * and the current entry starts at a page boundary. + */ + uaddr = (unsigned long)sg->address; + if ( ( uaddr != cur_end_virt ) && + ( ( ( uaddr | cur_end_virt ) & (~PAGE_MASK) ) || + ( ( uaddr & PAGE_MASK ) == ( ( cur_end_virt-1 ) & PAGE_MASK ) ) ) ) { + /* This entry can not be contiguous in dma space. + * save the previous dma entry and start a new one + */ + dma_sg->dma_address = cur_start_dma; + dma_sg->dma_length = cur_len_dma; + + ++dma_sg; + ++num_dma_ents; + + cur_start_dma += cur_len_dma-1; + /* If the previous entry ends and this entry starts + * in the same page then they share a tce. In that + * case don't bump cur_start_dma to the next page + * in dma space. This matches assumptions made in + * num_tces_sg and create_tces_sg. + */ + if ((uaddr & PAGE_MASK) == ((cur_end_virt-1) & PAGE_MASK)) + cur_start_dma &= PAGE_MASK; + else + cur_start_dma = PAGE_ALIGN(cur_start_dma+1); + cur_start_dma += ( uaddr & (~PAGE_MASK) ); + cur_len_dma = 0; + } + /* Accumulate the length of this entry for the next + * dma entry + */ + cur_len_dma += sg->length; + cur_end_virt = uaddr + sg->length; + } + /* Fill in the last dma entry */ + dma_sg->dma_address = cur_start_dma; + dma_sg->dma_length = cur_len_dma; + + if ((((cur_start_dma +cur_len_dma - 1)>> PAGE_SHIFT) - (dma_addr >> PAGE_SHIFT) + 1) != numTces) + { + PPCDBG(PPCDBG_TCE, "fill_scatterlist_sg: numTces %ld, used tces %d\n", + numTces, + (unsigned)(((cur_start_dma + cur_len_dma - 1) >> PAGE_SHIFT) - (dma_addr >> PAGE_SHIFT) + 1)); + } + + + return num_dma_ents; +} + +/* Call the hypervisor to create the TCE entries. + * return the number of TCEs created + */ +static dma_addr_t create_tces_sg( struct TceTable *tbl, struct scatterlist *sg, + int nents, unsigned numTces, int tceType, int direction ) +{ + unsigned order, i, j; + unsigned long startPage, endPage, prevEndPage, numPages, uaddr; + long tcenum, starttcenum; + dma_addr_t dmaAddr; + + dmaAddr = NO_TCE; + + order = get_order( numTces << PAGE_SHIFT ); + /* allocate a block of tces */ + tcenum = alloc_tce_range( tbl, order ); + if ( tcenum != -1 ) { + tcenum += tbl->startOffset; + starttcenum = tcenum; + dmaAddr = tcenum << PAGE_SHIFT; + prevEndPage = 0; + for (j=0; jaddress >> PAGE_SHIFT; + endPage = ((unsigned long)sg->address + sg->length - 1) >> PAGE_SHIFT; + numPages = endPage - startPage + 1; + + uaddr = (unsigned long)sg->address; + + /* If the previous entry ended in the same page that + * the current page starts then they share that + * tce and we reduce the number of tces we need + * by one. This matches assumptions made in + * num_tces_sg and fill_scatterlist_sg + */ + if ( startPage == prevEndPage ) { + --numPages; + uaddr += PAGE_SIZE; + } + + for (i=0; idma_address = pci_map_single( hwdev, sg->address, + sg->length, direction ); + sg->dma_length = sg->length; + return 1; + } + + if ( direction == PCI_DMA_NONE ) + BUG(); + + /* If no pci_dev then use virtual bus */ + if (hwdev == NULL ) { + bus = 255; + tceType = TCE_VB; + } + else { +#ifdef CONFIG_PCI + /* Get the iSeries bus # to use as an index + * into the TCE table array + */ + bus = get_tce_table_index( hwdev ); + // bus = ISERIES_GET_BUS( hwdev ); + tceType = TCE_PCI; +#else + BUG(); + return 0; +#endif /* CONFIG_PCI */ + } + + tbl = tceTables[bus]; + + if ( tbl ) { + /* Compute the number of tces required */ + numTces = num_tces_sg( sg, nents ); + /* Create the tces and get the dma address */ + dma_handle = create_tces_sg( tbl, sg, nents, numTces, + tceType, direction ); + + /* Fill in the dma scatterlist */ + num_dma = fill_scatterlist_sg( sg, nents, dma_handle, numTces ); + } + + return num_dma; +} + +void pci_unmap_sg( struct pci_dev *hwdev, struct scatterlist *sg, int nelms, int direction ) +{ + struct TceTable * tbl; + unsigned order, numTces, bus, i; + dma_addr_t dma_end_page, dma_start_page; + + PPCDBG(PPCDBG_TCE, "pci_unmap_sg:\n"); + PPCDBG(PPCDBG_TCE, "\thwdev = 0x%16.16lx, sg = 0x%16.16lx, direction = 0x%16.16lx, nelms = 0x%16.16lx\n", hwdev, sg, direction, nelms); + + if ( direction == PCI_DMA_NONE ) + BUG(); + + dma_start_page = sg->dma_address & PAGE_MASK; + for ( i=nelms; i>0; --i ) { + unsigned k = i - 1; + if ( sg[k].dma_length ) { + dma_end_page = ( sg[k].dma_address + + sg[k].dma_length - 1 ) & PAGE_MASK; + break; + } + } + + numTces = ((dma_end_page - dma_start_page ) >> PAGE_SHIFT) + 1; + order = get_order( numTces << PAGE_SHIFT ); + + if ( order > 10 ) + PPCDBG(PPCDBG_TCE, "pci_unmap_sg: order=%d, numTces=%d, nelms=%d, dma_start_page=%016lx, dma_end_page=%016lx\n", + order, numTces, nelms, (unsigned long)dma_start_page, (unsigned long)dma_end_page ); + + + /* If no pci_dev then use virtual bus */ + if (hwdev == NULL ) + bus = 255; + else { +#ifdef CONFIG_PCI + /* Get the iSeries bus # to use as an index + * into the TCE table array + */ + bus = get_tce_table_index( hwdev ); + // bus = ISERIES_GET_BUS( hwdev ); +#else + BUG(); + return; +#endif /* CONFIG_PCI */ + } + + if ( bus > 255 ) { + PPCDBG(PPCDBG_TCE, "pci_unmap_sg: invalid bus # %d\n", bus ); + PPCDBG(PPCDBG_TCE, "pci_unmap_sg: hwdev = %08lx\n", (unsigned long)hwdev ); + } + + + tbl = tceTables[bus]; + + if ( tbl ) + free_tces( tbl, dma_start_page, order, numTces ); + +} + +static unsigned long setTce( unsigned long base, + unsigned long tce_num, + unsigned long tce_data) { + union Tce *tce_addr; + + PPCDBG(PPCDBG_TCE, "setTce:\n"); + PPCDBG(PPCDBG_TCE, "\tbase = 0x%lx\n", base); + PPCDBG(PPCDBG_TCE, "\ttce_num = 0x%lx\n", tce_num); + PPCDBG(PPCDBG_TCE, "\ttce_data = 0x%lx\n", tce_data); + + tce_addr = ((union Tce *)base) + tce_num; + + PPCDBG(PPCDBG_TCE, "\ttce_addr = 0x%lx\n", tce_addr); + + *tce_addr = (union Tce)tce_data; + + /* Make sure the update is visible to hardware. */ + __asm__ __volatile__ ("sync" : : : "memory"); + + return(0); +} + +unsigned long phb_tce_table_init(struct pci_controller *phb) { + unsigned int r, cfg_rw, i; + unsigned long r64; + phandle node; + + PPCDBG(PPCDBG_TCE, "phb_tce_table_init: start.\n"); + + node = ((struct device_node *)(phb->arch_data))->node; + + PPCDBG(PPCDBG_TCEINIT, "\tphb = 0x%lx\n", phb); + PPCDBG(PPCDBG_TCEINIT, "\tphb->type = 0x%lx\n", phb->type); + PPCDBG(PPCDBG_TCEINIT, "\tphb->phb_regs = 0x%lx\n", phb->phb_regs); + PPCDBG(PPCDBG_TCEINIT, "\tphb->chip_regs = 0x%lx\n", phb->chip_regs); + PPCDBG(PPCDBG_TCEINIT, "\tphb: node = 0x%lx\n", node); + PPCDBG(PPCDBG_TCEINIT, "\tphb->arch_data = 0x%lx\n", phb->arch_data); + + i = 0; + while(of_tce_table[i].node) { + if(of_tce_table[i].node == node) { + if(phb->type == phb_type_python) { + r = *(((unsigned int *)phb->phb_regs) + (0xf10>>2)); + PPCDBG(PPCDBG_TCEINIT, "\tTAR(low) = 0x%x\n", r); + r = *(((unsigned int *)phb->phb_regs) + (0xf00>>2)); + PPCDBG(PPCDBG_TCEINIT, "\tTAR(high) = 0x%x\n", r); + r = *(((unsigned int *)phb->phb_regs) + (0xfd0>>2)); + PPCDBG(PPCDBG_TCEINIT, "\tPHB cfg(rw) = 0x%x\n", r); + break; + } else if(phb->type == phb_type_speedwagon) { + r64 = *(((unsigned long *)phb->chip_regs) + + (0x800>>3)); + PPCDBG(PPCDBG_TCEINIT, "\tNCFG = 0x%lx\n", r64); + r64 = *(((unsigned long *)phb->chip_regs) + + (0x580>>3)); + PPCDBG(PPCDBG_TCEINIT, "\tTAR0 = 0x%lx\n", r64); + r64 = *(((unsigned long *)phb->chip_regs) + + (0x588>>3)); + PPCDBG(PPCDBG_TCEINIT, "\tTAR1 = 0x%lx\n", r64); + r64 = *(((unsigned long *)phb->chip_regs) + + (0x590>>3)); + PPCDBG(PPCDBG_TCEINIT, "\tTAR2 = 0x%lx\n", r64); + r64 = *(((unsigned long *)phb->chip_regs) + + (0x598>>3)); + PPCDBG(PPCDBG_TCEINIT, "\tTAR3 = 0x%lx\n", r64); + cfg_rw = *(((unsigned int *)phb->chip_regs) + + ((0x160 + + (((phb->local_number)+8)<<12))>>2)); + PPCDBG(PPCDBG_TCEINIT, "\tcfg_rw = 0x%x\n", cfg_rw); + } + } + i++; + } + + PPCDBG(PPCDBG_TCEINIT, "phb_tce_table_init: done\n"); + // create_pci_bus_tce_table(phb->number, (unsigned long)phb); +} + +/* + * Reserve a range of TCE table entries. + * + * Inputs: + * tbl TCE Table. + * dma_addr PCI Base Address, must be aligned to 2^(NUM_TCE_LEVELS-1). + * size Size to reserve in pages. Value is rounded up to a multiple + * of (2^(NUM_TCE_LEVELS-1)); ie the min size of the allocation + * is 2^9 * 4096 = 2MB. + * + * This should only be called after the table has been initialized, but + * before it has been used. + */ +static long resv_tce_range_top_level( struct TceTable *tbl, + unsigned long dma_addr, + unsigned size ) +{ + unsigned long i, segments, block; + long tcenum, maxTcenum; + unsigned byte, bit, mask; + unsigned char *map, *bytep; + + PPCDBG(PPCDBG_TCEINIT, "resv_tce_range_top: start\n"); + PPCDBG(PPCDBG_TCEINIT, "\tdma_addr=0x%lx, size=0x%lx\n", + dma_addr, size); + maxTcenum = (tbl->size * (PAGE_SIZE / sizeof(union Tce))) - 1; + tcenum = (dma_addr >> PAGE_SHIFT) & + (~((1UL << (NUM_TCE_LEVELS - 1)) - 1)); + tcenum -= tbl->startOffset; + + PPCDBG(PPCDBG_TCEINIT, "\ttcenum=0x%lx, maxTcenum=0x%lx\n", + tcenum, maxTcenum); + + if ( tcenum > maxTcenum ) { + return(-1); + } + + segments = (size + (1UL << (NUM_TCE_LEVELS - 1)) - 1) >> + (NUM_TCE_LEVELS - 1); + map = tbl->mlbm.level[NUM_TCE_LEVELS-1].map; + block = tcenum >> (NUM_TCE_LEVELS -1); + + PPCDBG(PPCDBG_TCEINIT, "\tsegments = 0x%lx, map = 0x%lx\n", + segments, map); + + for(i = 0; i < segments; i++) { + byte = block / 8; + bit = block % 8; + mask = 0x80 >> bit; + bytep = map + byte; + + *bytep = *bytep & (~mask); + + block++; + } + + return(0); +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/pmac_nvram.c linuxppc64_2_4/arch/ppc64/kernel/pmac_nvram.c --- linux-2.4.5-ac18/arch/ppc64/kernel/pmac_nvram.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/pmac_nvram.c Mon May 7 12:47:01 2001 @@ -0,0 +1,396 @@ +/* + * c 2001 PPC 64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Miscellaneous procedures for dealing with the PowerMac hardware. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG + +/* + * Read and write the non-volatile RAM on PowerMacs and CHRP machines. + */ +static int nvram_naddrs; +static volatile unsigned char *nvram_addr; +static volatile unsigned char *nvram_data; +static int nvram_mult, is_core_99; +static char* nvram_image; +static int core99_bank = 0; +sys_ctrler_t sys_ctrler = SYS_CTRLER_UNKNOWN; + +extern int pmac_newworld; + +#define NVRAM_SIZE 0x2000 /* 8kB of non-volatile RAM */ + +#define CORE99_SIGNATURE 0x5a +#define CORE99_ADLER_START 0x14 + +/* Core99 nvram is a flash */ +#define CORE99_FLASH_STATUS_DONE 0x80 +#define CORE99_FLASH_STATUS_ERR 0x38 +#define CORE99_FLASH_CMD_ERASE_CONFIRM 0xd0 +#define CORE99_FLASH_CMD_ERASE_SETUP 0x20 +#define CORE99_FLASH_CMD_RESET 0xff +#define CORE99_FLASH_CMD_WRITE_SETUP 0x40 + +/* CHRP NVRAM header */ +struct chrp_header { + u8 signature; + u8 cksum; + u16 len; + char name[12]; + u8 data[0]; +}; + +struct core99_header { + struct chrp_header hdr; + u32 adler; + u32 generation; + u32 reserved[2]; +}; + +static int nvram_partitions[3]; + +static u8 +chrp_checksum(struct chrp_header* hdr) +{ + u8 *ptr; + u16 sum = hdr->signature; + for (ptr = (u8 *)&hdr->len; ptr < hdr->data; ptr++) + sum += *ptr; + while (sum > 0xFF) + sum = (sum & 0xFF) + (sum>>8); + return sum; +} + +static u32 +core99_calc_adler(u8 *buffer) +{ + int cnt; + u32 low, high; + + buffer += CORE99_ADLER_START; + low = 1; + high = 0; + for (cnt=0; cnt<(NVRAM_SIZE-CORE99_ADLER_START); cnt++) { + if ((cnt % 5000) == 0) { + high %= 65521UL; + high %= 65521UL; + } + low += buffer[cnt]; + high += low; + } + low %= 65521UL; + high %= 65521UL; + + return (high << 16) | low; +} + +static u32 +core99_check(u8* datas) +{ + struct core99_header* hdr99 = (struct core99_header*)datas; + + if (hdr99->hdr.signature != CORE99_SIGNATURE) { +#ifdef DEBUG + printk("Invalid signature\n"); +#endif + return 0; + } + if (hdr99->hdr.cksum != chrp_checksum(&hdr99->hdr)) { +#ifdef DEBUG + printk("Invalid checksum\n"); +#endif + return 0; + } + if (hdr99->adler != core99_calc_adler(datas)) { +#ifdef DEBUG + printk("Invalid adler\n"); +#endif + return 0; + } + return hdr99->generation; +} + +static int +core99_erase_bank(int bank) +{ + int stat, i; + + u8* base = (u8 *)nvram_data + core99_bank*NVRAM_SIZE; + + out_8(base, CORE99_FLASH_CMD_ERASE_SETUP); + out_8(base, CORE99_FLASH_CMD_ERASE_CONFIRM); + do { stat = in_8(base); } + while(!(stat & CORE99_FLASH_STATUS_DONE)); + out_8(base, CORE99_FLASH_CMD_RESET); + if (stat & CORE99_FLASH_STATUS_ERR) { + printk("nvram: flash error 0x%02x on erase !\n", stat); + return -ENXIO; + } + for (i=0; iname, "common")) + nvram_partitions[pmac_nvram_OF] = offset + 0x10; + if (!strcmp(hdr->name, "APL,MacOS75")) { + nvram_partitions[pmac_nvram_XPRAM] = offset + 0x10; + nvram_partitions[pmac_nvram_NR] = offset + 0x110; + } + offset += (hdr->len * 0x10); + } while(offset < NVRAM_SIZE); + } else { + nvram_partitions[pmac_nvram_OF] = 0x1800; + nvram_partitions[pmac_nvram_XPRAM] = 0x1300; + nvram_partitions[pmac_nvram_NR] = 0x1400; + } +#ifdef DEBUG + printk("nvram: OF partition at 0x%x\n", nvram_partitions[pmac_nvram_OF]); + printk("nvram: XP partition at 0x%x\n", nvram_partitions[pmac_nvram_XPRAM]); + printk("nvram: NR partition at 0x%x\n", nvram_partitions[pmac_nvram_NR]); +#endif +} + +__init +void pmac_nvram_init(void) +{ + struct device_node *dp; + + nvram_naddrs = 0; + + dp = find_devices("nvram"); + if (dp == NULL) { + printk(KERN_ERR "Can't find NVRAM device\n"); + return; + } + nvram_naddrs = dp->n_addrs; + is_core_99 = device_is_compatible(dp, "nvram,flash"); + if (is_core_99) { + int i; + u32 gen_bank0, gen_bank1; + + if (nvram_naddrs < 1) { + printk(KERN_ERR "nvram: no address\n"); + return; + } + nvram_image = kmalloc(NVRAM_SIZE, GFP_KERNEL); + if (!nvram_image) { + printk(KERN_ERR "nvram: can't allocate image\n"); + return; + } + nvram_data = ioremap(dp->addrs[0].address, NVRAM_SIZE*2); +#ifdef DEBUG + printk("nvram: Checking bank 0...\n"); +#endif + gen_bank0 = core99_check((u8 *)nvram_data); + gen_bank1 = core99_check((u8 *)nvram_data + NVRAM_SIZE); + core99_bank = (gen_bank0 < gen_bank1) ? 1 : 0; +#ifdef DEBUG + printk("nvram: gen0=%d, gen1=%d\n", gen_bank0, gen_bank1); + printk("nvram: Active bank is: %d\n", core99_bank); +#endif + for (i=0; iaddrs[0].address, dp->addrs[0].size); + nvram_mult = 1; + } else if (nvram_naddrs == 1) { + nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size); + nvram_mult = (dp->addrs[0].size + NVRAM_SIZE - 1) / NVRAM_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 if (nvram_naddrs == 0 && sys_ctrler == SYS_CTRLER_PMU) { + nvram_naddrs = -1; + } else { + printk(KERN_ERR "Don't know how to access NVRAM with %d addresses\n", + nvram_naddrs); + } +} + +void +pmac_nvram_update(void) +{ + struct core99_header* hdr99; + + if (!is_core_99 || !nvram_data || !nvram_image) + return; + if (!memcmp(nvram_image, (u8*)nvram_data + core99_bank*NVRAM_SIZE, + NVRAM_SIZE)) + return; +#ifdef DEBUG + printk("Updating nvram...\n"); +#endif + hdr99 = (struct core99_header*)nvram_image; + hdr99->generation++; + hdr99->hdr.signature = CORE99_SIGNATURE; + hdr99->hdr.cksum = chrp_checksum(&hdr99->hdr); + hdr99->adler = core99_calc_adler(nvram_image); + core99_bank = core99_bank ? 0 : 1; + if (core99_erase_bank(core99_bank)) { + printk("nvram: Error erasing bank %d\n", core99_bank); + return; + } + if (core99_write_bank(core99_bank, nvram_image)) + printk("nvram: Error writing bank %d\n", core99_bank); +} + +__openfirmware +unsigned char nvram_read_byte(int addr) +{ + #ifdef CONFIG_ADB_PMU // -aglitke + struct adb_request req; + #endif + + switch (nvram_naddrs) { +#ifdef CONFIG_ADB_PMU + case -1: + if (pmu_request(&req, NULL, 3, PMU_READ_NVRAM, + (addr >> 8) & 0xff, addr & 0xff)) + break; + while (!req.complete) + pmu_poll(); + return req.reply[1]; +#endif + case 1: + if (is_core_99) + return nvram_image[addr]; + return nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult]; + case 2: + *nvram_addr = addr >> 5; + eieio(); + return nvram_data[(addr & 0x1f) << 4]; + } + return 0; +} + +__openfirmware +void nvram_write_byte(unsigned char val, int addr) +{ + #ifdef CONFIG_ADB_PMU // -aglitke + struct adb_request req; + #endif + switch (nvram_naddrs) { +#ifdef CONFIG_ADB_PMU + case -1: + if (pmu_request(&req, NULL, 4, PMU_WRITE_NVRAM, + (addr >> 8) & 0xff, addr & 0xff, val)) + break; + while (!req.complete) + pmu_poll(); + break; +#endif + case 1: + if (is_core_99) { + nvram_image[addr] = val; + break; + } + nvram_data[(addr & (NVRAM_SIZE - 1)) * nvram_mult] = val; + break; + case 2: + *nvram_addr = addr >> 5; + eieio(); + nvram_data[(addr & 0x1f) << 4] = val; + break; + } + eieio(); +} + +int +pmac_get_partition(int partition) +{ + return nvram_partitions[partition]; +} + +u8 +pmac_xpram_read(int xpaddr) +{ + int offset = nvram_partitions[pmac_nvram_XPRAM]; + + if (offset < 0) + return 0; + + return nvram_read_byte(xpaddr + offset); +} + +void +pmac_xpram_write(int xpaddr, u8 data) +{ + int offset = nvram_partitions[pmac_nvram_XPRAM]; + + if (offset < 0) + return; + + nvram_write_byte(xpaddr + offset, data); +} + + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/pmc.c linuxppc64_2_4/arch/ppc64/kernel/pmc.c --- linux-2.4.5-ac18/arch/ppc64/kernel/pmc.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/pmc.c Thu Jun 21 12:58:46 2001 @@ -0,0 +1,168 @@ +/* + * pmc.c + * Copyright (C) 2001 Dave Engebretsen & Mike Corrigan IBM Corporation + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Change Activity: + * 2001/06/05 : engebret : Created. + * End Change Activity + */ + +#ifndef _PMC_PROC_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +extern struct Naca *naca; + +struct _pmc_sw pmc_sw_system = { + 0 +}; + +struct _pmc_sw pmc_sw_cpu[NR_CPUS] = { + {0 }, +}; + +/* + * Provide enough storage for either system level counters or + * one cpu's counters. + */ +struct _pmc_sw_text pmc_sw_text; +struct _pmc_hw_text pmc_hw_text; + +char * +ppc64_pmc_stab(int file) { + int n; + unsigned long stab_faults, stab_capacity_castouts, stab_invalidations; + unsigned long i; + + stab_faults = stab_capacity_castouts = stab_invalidations = n = 0; + + if(file == -1) { + for(i = 0; i < smp_num_cpus; i++) { + stab_faults += pmc_sw_cpu[i].stab_faults; + stab_capacity_castouts += pmc_sw_cpu[i].stab_capacity_castouts; + stab_invalidations += pmc_sw_cpu[i].stab_invalidations; + } + n += sprintf(pmc_sw_text.buffer + n, + "Faults 0x%lx\n", stab_faults); + n += sprintf(pmc_sw_text.buffer + n, + "Castouts 0x%lx\n", stab_capacity_castouts); + n += sprintf(pmc_sw_text.buffer + n, + "Invalidations 0x%lx\n", stab_invalidations); + } else { + n += sprintf(pmc_sw_text.buffer + n, + "Faults 0x%lx\n", + pmc_sw_cpu[file].stab_faults); + + n += sprintf(pmc_sw_text.buffer + n, + "Castouts 0x%lx\n", + pmc_sw_cpu[file].stab_capacity_castouts); + + n += sprintf(pmc_sw_text.buffer + n, + "Invalidations 0x%lx\n", + pmc_sw_cpu[file].stab_invalidations); + + for(i = 0; i < STAB_ENTRY_MAX; i++) { + if(pmc_sw_cpu[file].stab_entry_use[i]) { + n += sprintf(pmc_sw_text.buffer + n, + "Entry %02ld 0x%lx\n", i, + pmc_sw_cpu[file].stab_entry_use[i]); + } + } + + } + + return(pmc_sw_text.buffer); +} + +char * +ppc64_pmc_htab(int file) { + int n; + unsigned long htab_primary_overflows, htab_capacity_castouts; + unsigned long htab_read_to_write_faults; + unsigned long i; + + htab_primary_overflows = htab_capacity_castouts = 0; + htab_read_to_write_faults = n = 0; + + if(file == -1) { + n += sprintf(pmc_sw_text.buffer + n, + "Primary Overflows 0x%lx\n", + pmc_sw_system.htab_primary_overflows); + n += sprintf(pmc_sw_text.buffer + n, + "Castouts 0x%lx\n", + pmc_sw_system.htab_capacity_castouts); + } else { + n += sprintf(pmc_sw_text.buffer + n, + "Primary Overflows N/A\n"); + + n += sprintf(pmc_sw_text.buffer + n, + "Castouts N/A\n\n"); + + } + + return(pmc_sw_text.buffer); +} + +char * +ppc64_pmc_hw(int file) { + int n; + + n = 0; + if(file == -1) { + n += sprintf(pmc_hw_text.buffer + n, "Not Implemented\n"); + } else { + n += sprintf(pmc_hw_text.buffer + n, + "MMCR0 0x%lx\n", mfspr(MMCR0)); + n += sprintf(pmc_hw_text.buffer + n, + "MMCR1 0x%lx\n", mfspr(MMCR1)); +#if 0 + n += sprintf(pmc_hw_text.buffer + n, + "MMCRA 0x%lx\n", mfspr(MMCRA)); +#endif + + n += sprintf(pmc_hw_text.buffer + n, + "PMC1 0x%lx\n", mfspr(PMC1)); + n += sprintf(pmc_hw_text.buffer + n, + "PMC2 0x%lx\n", mfspr(PMC2)); + n += sprintf(pmc_hw_text.buffer + n, + "PMC3 0x%lx\n", mfspr(PMC3)); + n += sprintf(pmc_hw_text.buffer + n, + "PMC4 0x%lx\n", mfspr(PMC4)); + n += sprintf(pmc_hw_text.buffer + n, + "PMC5 0x%lx\n", mfspr(PMC5)); + n += sprintf(pmc_hw_text.buffer + n, + "PMC6 0x%lx\n", mfspr(PMC6)); + n += sprintf(pmc_hw_text.buffer + n, + "PMC7 0x%lx\n", mfspr(PMC7)); + n += sprintf(pmc_hw_text.buffer + n, + "PMC8 0x%lx\n", mfspr(PMC8)); + } + + return(pmc_hw_text.buffer); +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/pmc_proc.c linuxppc64_2_4/arch/ppc64/kernel/pmc_proc.c --- linux-2.4.5-ac18/arch/ppc64/kernel/pmc_proc.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/pmc_proc.c Mon May 7 12:47:01 2001 @@ -0,0 +1,650 @@ +/* + * pmc_proc.c + * Copyright (C) 2001 Mike Corrigan IBM Corporation + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* Change Activity: */ +/* End Change Activity */ + +#ifndef _PMC_PROC_H +#include +#endif + +#include +#include +#include +#include + +#define MMCR0 795 +#define MMCR1 798 +#define MMCRA 786 +#define PMC1 787 +#define PMC2 788 +#define PMC3 789 +#define PMC4 790 +#define PMC5 791 +#define PMC6 792 +#define PMC7 793 +#define PMC8 794 + +static int proc_pmc_control_mode = 0; +#define PMC_CONTROL_CPI 1 +#define PMC_CONTROL_TLB 2 + +static struct proc_dir_entry *pmc_proc_root = NULL; + +int proc_get_lpevents( char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_reset_lpevents( struct file *file, const char *buffer, unsigned long count, void *data); + +int proc_pmc_get_control( char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_pmc_get_mmcr0( char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_pmc_get_mmcr1( char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_pmc_get_mmcra( char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_pmc_get_pmc1( char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_pmc_get_pmc2( char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_pmc_get_pmc3( char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_pmc_get_pmc4( char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_pmc_get_pmc5( char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_pmc_get_pmc6( char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_pmc_get_pmc7( char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_pmc_get_pmc8( char *page, char **start, off_t off, int count, int *eof, void *data); + +int proc_pmc_set_control( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_mmcr0( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_mmcr1( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_mmcra( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc1( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc2( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc3( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc4( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc5( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc6( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc7( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc8( struct file *file, const char *buffer, unsigned long count, void *data); + +void pmc_proc_init(struct proc_dir_entry *iSeries_proc) +{ + struct proc_dir_entry *ent = NULL; + + ent = create_proc_entry("lpevents", S_IFREG|S_IRUGO, iSeries_proc); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_get_lpevents; + ent->write_proc = proc_reset_lpevents; + + pmc_proc_root = proc_mkdir("pmc", iSeries_proc); + if (!pmc_proc_root) return; + + ent = create_proc_entry("control", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_pmc_get_control; + ent->write_proc = proc_pmc_set_control; + + ent = create_proc_entry("mmcr0", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_pmc_get_mmcr0; + ent->write_proc = proc_pmc_set_mmcr0; + + ent = create_proc_entry("mmcr1", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_pmc_get_mmcr1; + ent->write_proc = proc_pmc_set_mmcr1; + + ent = create_proc_entry("mmcra", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_pmc_get_mmcra; + ent->write_proc = proc_pmc_set_mmcra; + + ent = create_proc_entry("pmc1", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_pmc_get_pmc1; + ent->write_proc = proc_pmc_set_pmc1; + + ent = create_proc_entry("pmc2", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_pmc_get_pmc2; + ent->write_proc = proc_pmc_set_pmc2; + + ent = create_proc_entry("pmc3", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_pmc_get_pmc3; + ent->write_proc = proc_pmc_set_pmc3; + + ent = create_proc_entry("pmc4", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_pmc_get_pmc4; + ent->write_proc = proc_pmc_set_pmc4; + + ent = create_proc_entry("pmc5", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_pmc_get_pmc5; + ent->write_proc = proc_pmc_set_pmc5; + + ent = create_proc_entry("pmc6", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_pmc_get_pmc6; + ent->write_proc = proc_pmc_set_pmc6; + + ent = create_proc_entry("pmc7", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_pmc_get_pmc7; + ent->write_proc = proc_pmc_set_pmc7; + + ent = create_proc_entry("pmc8", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_pmc_get_pmc8; + ent->write_proc = proc_pmc_set_pmc8; + + +} + +static int pmc_calc_metrics( char *page, char **start, off_t off, int count, int *eof, int len) +{ + if ( len <= off+count) + *eof = 1; + *start = page+off; + len -= off; + if ( len > count ) + len = count; + if ( len < 0 ) + len = 0; + return len; +} + +static char * lpEventTypes[9] = { + "Hypervisor\t\t", + "Machine Facilities\t", + "Session Manager\t", + "SPD I/O\t\t", + "Virtual Bus\t\t", + "PCI I/O\t\t", + "RIO I/O\t\t", + "Virtual Lan\t\t", + "Virtual I/O\t\t" + }; + + +int proc_get_lpevents +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + unsigned i; + int len = 0; + + len += sprintf( page+len, "LpEventQueue 0\n" ); + len += sprintf( page+len, " events processed:\t%lu\n", + (unsigned long)xItLpQueue.xLpIntCount ); + for (i=0; i<9; ++i) { + len += sprintf( page+len, " %s %10lu\n", + lpEventTypes[i], + (unsigned long)xItLpQueue.xLpIntCountByType[i] ); + } + return pmc_calc_metrics( page, start, off, count, eof, len ); + +} + +int proc_reset_lpevents( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + return count; +} + +int proc_pmc_get_control +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + + if ( proc_pmc_control_mode == PMC_CONTROL_CPI ) { + unsigned long mach_cycles = mfspr( PMC5 ); + unsigned long inst_complete = mfspr( PMC4 ); + unsigned long inst_dispatch = mfspr( PMC3 ); + unsigned long thread_active_run = mfspr( PMC1 ); + unsigned long thread_active = mfspr( PMC2 ); + unsigned long cpi = 0; + unsigned long cpithou = 0; + unsigned long remain; + + if ( inst_complete ) { + cpi = thread_active_run / inst_complete; + remain = thread_active_run % inst_complete; + if ( inst_complete > 1000000 ) + cpithou = remain / ( inst_complete / 1000 ); + else + cpithou = ( remain * 1000 ) / inst_complete; + } + len += sprintf( page+len, "PMC CPI Mode\nRaw Counts\n" ); + len += sprintf( page+len, "machine cycles : %12lu\n", mach_cycles ); + len += sprintf( page+len, "thread active cycles : %12lu\n\n", thread_active ); + + len += sprintf( page+len, "instructions completed : %12lu\n", inst_complete ); + len += sprintf( page+len, "instructions dispatched : %12lu\n", inst_dispatch ); + len += sprintf( page+len, "thread active run cycles : %12lu\n", thread_active_run ); + + len += sprintf( page+len, "thread active run cycles/instructions completed\n" ); + len += sprintf( page+len, "CPI = %lu.%03lu\n", cpi, cpithou ); + + } + else if ( proc_pmc_control_mode == PMC_CONTROL_TLB ) { + len += sprintf( page+len, "PMC TLB Mode\n" ); + len += sprintf( page+len, "I-miss count : %12lu\n", mfspr( PMC1 ) ); + len += sprintf( page+len, "I-miss latency : %12lu\n", mfspr( PMC2 ) ); + len += sprintf( page+len, "D-miss count : %12lu\n", mfspr( PMC3 ) ); + len += sprintf( page+len, "D-miss latency : %12lu\n", mfspr( PMC4 ) ); + len += sprintf( page+len, "IERAT miss count : %12lu\n", mfspr( PMC5 ) ); + len += sprintf( page+len, "D-reference count : %12lu\n", mfspr( PMC6 ) ); + len += sprintf( page+len, "miss PTEs searched : %12lu\n", mfspr( PMC7 ) ); + len += sprintf( page+len, "miss >8 PTEs searched : %12lu\n", mfspr( PMC8 ) ); + } + /* IMPLEMENT ME */ + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +int proc_pmc_get_mmcr0 +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = sprintf( page, "0x%08lx", mfspr(MMCR0) ); + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +int proc_pmc_get_mmcr1 +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = sprintf( page, "0x%08lx", mfspr(MMCR1) ); + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +int proc_pmc_get_mmcra +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = sprintf( page, "0x%08lx", mfspr(MMCRA) ); + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +int proc_pmc_get_pmc1 +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = sprintf( page, "0x%08lx", mfspr(PMC1) ); + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +int proc_pmc_get_pmc2 +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = sprintf( page, "0x%08lx", mfspr(PMC2) ); + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +int proc_pmc_get_pmc3 +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = sprintf( page, "0x%08lx", mfspr(PMC3) ); + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +int proc_pmc_get_pmc4 +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = sprintf( page, "0x%08lx", mfspr(PMC4) ); + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +int proc_pmc_get_pmc5 +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = sprintf( page, "0x%08lx", mfspr(PMC5) ); + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +int proc_pmc_get_pmc6 +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = sprintf( page, "0x%08lx", mfspr(PMC6) ); + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +int proc_pmc_get_pmc7 +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = sprintf( page, "0x%08lx", mfspr(PMC7) ); + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +int proc_pmc_get_pmc8 +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = sprintf( page, "0x%08lx", mfspr(PMC8) ); + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +unsigned long proc_pmc_conv_int( const char *buf, unsigned count ) +{ + const char * p; + char b0, b1; + unsigned v, multiplier, mult, i; + unsigned long val; + multiplier = 10; + p = buf; + if ( count >= 3 ) { + b0 = buf[0]; + b1 = buf[1]; + if ( ( b0 == '0' ) && + ( ( b1 == 'x' ) || ( b1 == 'X' ) ) ) { + p = buf + 2; + count -= 2; + multiplier = 16; + } + + } + val = 0; + for ( i=0; i= '0' ) && ( b0 <= '9' ) ) + v = b0 - '0'; + else if ( multiplier == 16 ) { + if ( ( b0 >= 'a' ) && ( b0 <= 'f' ) ) + v = b0 - 'a' + 10; + else if ( ( b0 >= 'A' ) && ( b0 <= 'F' ) ) + v = b0 - 'A' + 10; + else + mult = 1; + } + else + mult = 1; + val *= mult; + val += v; + } + + return val; + +} + +static inline void proc_pmc_stop(void) +{ + /* Freeze all counters, leave everything else alone */ + mtspr( MMCR0, mfspr( MMCR0 ) | 0x80000000 ); +} + +static inline void proc_pmc_start(void) +{ + /* Unfreeze all counters, leave everything else alone */ + mtspr( MMCR0, mfspr( MMCR0 ) & ~0x80000000 ); + +} + +static inline void proc_pmc_reset(void) +{ + /* Clear all the PMCs to zeros + * Assume a "stop" has already frozen the counters + * Clear all the PMCs + */ + mtspr( PMC1, 0 ); + mtspr( PMC2, 0 ); + mtspr( PMC3, 0 ); + mtspr( PMC4, 0 ); + mtspr( PMC5, 0 ); + mtspr( PMC6, 0 ); + mtspr( PMC7, 0 ); + mtspr( PMC8, 0 ); + +} + +static inline void proc_pmc_cpi(void) +{ + /* Configure the PMC registers to count cycles and instructions */ + /* so we can compute cpi */ + /* + * MMCRA[30] = 1 Don't count in wait state (CTRL[31]=0) + * MMCR0[6] = 1 Freeze counters when any overflow + * MMCR0[19:25] = 0x01 PMC1 counts Thread Active Run Cycles + * MMCR0[26:31] = 0x05 PMC2 counts Thread Active Cycles + * MMCR1[0:4] = 0x07 PMC3 counts Instructions Dispatched + * MMCR1[5:9] = 0x03 PMC4 counts Instructions Completed + * MMCR1[10:14] = 0x06 PMC5 counts Machine Cycles + * + */ + + proc_pmc_control_mode = PMC_CONTROL_CPI; + + // Indicate to hypervisor that we are using the PMCs + ((struct Paca *)mfspr(SPRG3))->xLpPacaPtr->xPMCRegsInUse = 1; + + // Freeze all counters + mtspr( MMCR0, 0x80000000 ); + mtspr( MMCR1, 0x00000000 ); + + // Clear all the PMCs + mtspr( PMC1, 0 ); + mtspr( PMC2, 0 ); + mtspr( PMC3, 0 ); + mtspr( PMC4, 0 ); + mtspr( PMC5, 0 ); + mtspr( PMC6, 0 ); + mtspr( PMC7, 0 ); + mtspr( PMC8, 0 ); + + /* Freeze counters in Wait State (CTRL[31]=0) */ + mtspr( MMCRA, 0x00000002 ); + + /* PMC3<-0x07, PMC4<-0x03, PMC5<-0x06 */ + mtspr( MMCR1, 0x38cc0000 ); + + mb(); + + /* PMC1<-0x01, PMC2<-0x05 + * Start all counters + */ + mtspr( MMCR0, 0x02000045 ); + +} + +static inline void proc_pmc_tlb(void) +{ + /* Configure the PMC registers to count tlb misses */ + /* + * MMCR0[6] = 1 Freeze counters when any overflow + * MMCR0[19:25] = 0x55 Group count + * PMC1 counts I misses + * PMC2 counts I miss duration (latency) + * PMC3 counts D misses + * PMC4 counts D miss duration (latency) + * PMC5 counts IERAT misses + * PMC6 counts D references (including PMC7) + * PMC7 counts miss PTEs searched + * PMC8 counts miss >8 PTEs searched + * + */ + + proc_pmc_control_mode = PMC_CONTROL_TLB; + + /* Indicate to hypervisor that we are using the PMCs */ + ((struct Paca *)mfspr(SPRG3))->xLpPacaPtr->xPMCRegsInUse = 1; + + /* Freeze all counters */ + mtspr( MMCR0, 0x80000000 ); + mtspr( MMCR1, 0x00000000 ); + + /* Clear all the PMCs */ + mtspr( PMC1, 0 ); + mtspr( PMC2, 0 ); + mtspr( PMC3, 0 ); + mtspr( PMC4, 0 ); + mtspr( PMC5, 0 ); + mtspr( PMC6, 0 ); + mtspr( PMC7, 0 ); + mtspr( PMC8, 0 ); + + mtspr( MMCRA, 0x00000000 ); + + mb(); + + /* PMC1<-0x55 + * Start all counters + */ + mtspr( MMCR0, 0x02001540 ); + +} + +int proc_pmc_set_control( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + if ( ! strncmp( buffer, "stop", 4 ) ) + proc_pmc_stop(); + else if ( ! strncmp( buffer, "start", 5 ) ) + proc_pmc_start(); + else if ( ! strncmp( buffer, "reset", 5 ) ) + proc_pmc_reset(); + else if ( ! strncmp( buffer, "cpi", 3 ) ) + proc_pmc_cpi(); + else if ( ! strncmp( buffer, "tlb", 3 ) ) + proc_pmc_tlb(); + + /* IMPLEMENT ME */ + return count; +} + +int proc_pmc_set_mmcr0( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + v = v & ~0x04000000; /* Don't allow interrupts for now */ + if ( v & ~0x80000000 ) /* Inform hypervisor we are using PMCs */ + ((struct Paca *)mfspr(SPRG3))->xLpPacaPtr->xPMCRegsInUse = 1; + else + ((struct Paca *)mfspr(SPRG3))->xLpPacaPtr->xPMCRegsInUse = 0; + mtspr( MMCR0, v ); + + return count; +} + +int proc_pmc_set_mmcr1( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( MMCR1, v ); + + return count; +} + +int proc_pmc_set_mmcra( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + v = v & ~0x00008000; /* Don't allow interrupts for now */ + mtspr( MMCRA, v ); + + return count; +} + + +int proc_pmc_set_pmc1( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC1, v ); + + return count; +} + +int proc_pmc_set_pmc2( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC2, v ); + + return count; +} + +int proc_pmc_set_pmc3( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC3, v ); + + return count; +} + +int proc_pmc_set_pmc4( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC4, v ); + + return count; +} + +int proc_pmc_set_pmc5( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC5, v ); + + return count; +} + +int proc_pmc_set_pmc6( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC6, v ); + + return count; +} + +int proc_pmc_set_pmc7( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC7, v ); + + return count; +} + +int proc_pmc_set_pmc8( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC8, v ); + + return count; +} + + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/ppc-stub.c linuxppc64_2_4/arch/ppc64/kernel/ppc-stub.c --- linux-2.4.5-ac18/arch/ppc64/kernel/ppc-stub.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/ppc-stub.c Mon Jun 18 13:47:18 2001 @@ -0,0 +1,747 @@ +/* + * ppc-stub.c: KGDB support for the Linux kernel. + * + * adapted from arch/sparc/kernel/sparc-stub.c for the PowerPC + * some stuff borrowed from Paul Mackerras' xmon + * Copyright (C) 1998 Michael AK Tesch (tesch@cs.wisc.edu) + * + * Modifications to run under Linux + * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) + * + * This file originally came from the gdb sources, and the + * copyright notices have been retained below. + */ + +/**************************************************************************** + + THIS SOFTWARE IS NOT COPYRIGHTED + + HP offers the following for use in the public domain. HP makes no + warranty with regard to the software or its performance and the + user accepts the software "AS IS" with all faults. + + HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD + TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + +****************************************************************************/ + +/**************************************************************************** + * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ + * + * Module name: remcom.c $ + * Revision: 1.34 $ + * Date: 91/03/09 12:29:49 $ + * Contributor: Lake Stevens Instrument Division$ + * + * Description: low level support for gdb debugger. $ + * + * Considerations: only works on target hardware $ + * + * Written by: Glenn Engel $ + * ModuleState: Experimental $ + * + * NOTES: See Below $ + * + * Modified for SPARC by Stu Grossman, Cygnus Support. + * + * This code has been extensively tested on the Fujitsu SPARClite demo board. + * + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing a trap #1. + * + ************* + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * qOffsets Get section offsets. Reply is Text=xxx;Data=yyy;Bss=zzz + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * bBB..BB Set baud rate to BB..BB OK or BNN, then sets + * baud rate + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $#. + * + * where + * :: + * :: > + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +void breakinst(void); + +/* + * BUFMAX defines the maximum number of characters in inbound/outbound buffers + * at least NUMREGBYTES*2 are needed for register packets + */ +#define BUFMAX 2048 +static char remcomInBuffer[BUFMAX]; +static char remcomOutBuffer[BUFMAX]; + +static int initialized = 0; +static int kgdb_active = 0; +static int kgdb_started = 0; +static u_int fault_jmp_buf[100]; +static int kdebug; + +static const char hexchars[]="0123456789abcdef"; + +/* Place where we save old trap entries for restoration - sparc*/ +/* struct tt_entry kgdb_savettable[256]; */ +/* typedef void (*trapfunc_t)(void); */ + +#if 0 +/* Install an exception handler for kgdb */ +static void exceptionHandler(int tnum, unsigned int *tfunc) +{ + /* We are dorking with a live trap table, all irqs off */ +} +#endif + +int +kgdb_setjmp(long *buf) +{ + asm ("mflr 0; stw 0,0(%0);" + "stw 1,4(%0); stw 2,8(%0);" + "mfcr 0; stw 0,12(%0);" + "stmw 13,16(%0)" + : : "r" (buf)); + /* XXX should save fp regs as well */ + return 0; +} +void +kgdb_longjmp(long *buf, int val) +{ + if (val == 0) + val = 1; + asm ("lmw 13,16(%0);" + "lwz 0,12(%0); mtcrf 0x38,0;" + "lwz 0,0(%0); lwz 1,4(%0); lwz 2,8(%0);" + "mtlr 0; mr 3,%1" + : : "r" (buf), "r" (val)); +} +/* Convert ch from a hex digit to an int */ +static int +hex(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'f') + return ch-'a'+10; + if (ch >= '0' && ch <= '9') + return ch-'0'; + if (ch >= 'A' && ch <= 'F') + return ch-'A'+10; + return -1; +} + +/* Convert the memory pointed to by mem into hex, placing result in buf. + * Return a pointer to the last char put in buf (null), in case of mem fault, + * return 0. + */ +static unsigned char * +mem2hex(char *mem, char *buf, int count) +{ + unsigned char ch; + + if (kgdb_setjmp((long*)fault_jmp_buf) == 0) { + debugger_fault_handler = kgdb_fault_handler; + while (count-- > 0) { + ch = *mem++; + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch & 0xf]; + } + } else { + /* error condition */ + } + debugger_fault_handler = 0; + *buf = 0; + return buf; +} + +/* convert the hex array pointed to by buf into binary to be placed in mem + * return a pointer to the character AFTER the last byte written. +*/ +static char * +hex2mem(char *buf, char *mem, int count) +{ + int i; + unsigned char ch; + + if (kgdb_setjmp((long*)fault_jmp_buf) == 0) { + debugger_fault_handler = kgdb_fault_handler; + for (i=0; i# */ +static void +getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + unsigned char ch; + + do { + /* wait around for the start character, ignore all other + * characters */ + while ((ch = (getDebugChar() & 0x7f)) != '$') ; + + checksum = 0; + xmitcsum = -1; + + count = 0; + + /* now, read until a # or end of buffer is found */ + while (count < BUFMAX) { + ch = getDebugChar() & 0x7f; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + + if (count >= BUFMAX) + continue; + + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex(getDebugChar() & 0x7f) << 4; + xmitcsum |= hex(getDebugChar() & 0x7f); + if (checksum != xmitcsum) + putDebugChar('-'); /* failed checksum */ + else { + putDebugChar('+'); /* successful transfer */ + /* if a sequence char is present, reply the ID */ + if (buffer[2] == ':') { + putDebugChar(buffer[0]); + putDebugChar(buffer[1]); + /* remove sequence chars from buffer */ + count = strlen(buffer); + for (i=3; i <= count; i++) + buffer[i-3] = buffer[i]; + } + } + } + } while (checksum != xmitcsum); +} + +/* send the packet in buffer. */ +static void putpacket(unsigned char *buffer) +{ + unsigned char checksum; + int count; + unsigned char ch, recv; + + /* $#. */ + do { + putDebugChar('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count])) { + putDebugChar(ch); + checksum += ch; + count += 1; + } + + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum & 0xf]); + recv = getDebugChar(); + } while ((recv & 0x7f) != '+'); +} + +static void kgdb_flush_cache_all(void) +{ + flush_instruction_cache(); +} + + +/* Set up exception handlers for tracing and breakpoints + * [could be called kgdb_init()] + */ +void set_debug_traps(void) +{ +#if 0 + unsigned char c; + + save_and_cli(flags); + + /* In case GDB is started before us, ack any packets (presumably + * "$?#xx") sitting there. + * + * I've found this code causes more problems than it solves, + * so that's why it's commented out. GDB seems to work fine + * now starting either before or after the kernel -bwb + */ + + while((c = getDebugChar()) != '$'); + while((c = getDebugChar()) != '#'); + c = getDebugChar(); /* eat first csum byte */ + c = getDebugChar(); /* eat second csum byte */ + putDebugChar('+'); /* ack it */ +#endif + debugger = kgdb; + debugger_bpt = kgdb_bpt; + debugger_sstep = kgdb_sstep; + debugger_iabr_match = kgdb_iabr_match; + debugger_dabr_match = kgdb_dabr_match; + + initialized = 1; +} + +static void kgdb_fault_handler(struct pt_regs *regs) +{ + kgdb_longjmp((long*)fault_jmp_buf, 1); +} + +int kgdb_bpt(struct pt_regs *regs) +{ + handle_exception(regs); + return 1; +} + +int kgdb_sstep(struct pt_regs *regs) +{ + handle_exception(regs); + return 1; +} + +void kgdb(struct pt_regs *regs) +{ + handle_exception(regs); +} + +int kgdb_iabr_match(struct pt_regs *regs) +{ + printk("kgdb doesn't support iabr, what?!?\n"); + handle_exception(regs); + return 1; +} + +int kgdb_dabr_match(struct pt_regs *regs) +{ + printk("kgdb doesn't support dabr, what?!?\n"); + handle_exception(regs); + return 1; +} + +/* Convert the SPARC hardware trap type code to a unix signal number. */ +/* + * This table contains the mapping between PowerPC hardware trap types, and + * signals, which are primarily what GDB understands. + */ +static struct hard_trap_info +{ + unsigned int tt; /* Trap type code for powerpc */ + unsigned char signo; /* Signal that we map this trap into */ +} hard_trap_info[] = { + { 0x200, SIGSEGV }, /* machine check */ + { 0x300, SIGSEGV }, /* address error (store) */ + { 0x400, SIGBUS }, /* instruction bus error */ + { 0x500, SIGINT }, /* interrupt */ + { 0x600, SIGBUS }, /* alingment */ + { 0x700, SIGTRAP }, /* breakpoint trap */ + { 0x800, SIGFPE }, /* fpu unavail */ + { 0x900, SIGALRM }, /* decrementer */ + { 0xa00, SIGILL }, /* reserved */ + { 0xb00, SIGILL }, /* reserved */ + { 0xc00, SIGCHLD }, /* syscall */ + { 0xd00, SIGTRAP }, /* single-step/watch */ + { 0xe00, SIGFPE }, /* fp assist */ + { 0, 0} /* Must be last */ +}; + +static int computeSignal(unsigned int tt) +{ + struct hard_trap_info *ht; + + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + if (ht->tt == tt) + return ht->signo; + + return SIGHUP; /* default for things we don't know about */ +} + +#define PC_REGNUM 64 +#define SP_REGNUM 1 + +/* + * This function does all command processing for interfacing to gdb. + */ +static void +handle_exception (struct pt_regs *regs) +{ + int sigval; + int addr; + int length; + char *ptr; + unsigned long msr; + + if (debugger_fault_handler) { + debugger_fault_handler(regs); + panic("kgdb longjump failed!\n"); + } + if (kgdb_active) { + printk("interrupt while in kgdb, returning\n"); + return; + } + kgdb_active = 1; + kgdb_started = 1; + +#ifdef KGDB_DEBUG + printk("kgdb: entering handle_exception; trap [0x%x]\n", + (unsigned int)regs->trap); +#endif + + kgdb_interruptible(0); + lock_kernel(); + msr = get_msr(); + set_msr(msr & ~MSR_EE); /* disable interrupts */ + + if (regs->nip == (unsigned long)breakinst) { + /* Skip over breakpoint trap insn */ + regs->nip += 4; + } + + /* reply to host that an exception has occurred */ + sigval = computeSignal(regs->trap); + ptr = remcomOutBuffer; + +#if 0 + *ptr++ = 'S'; + *ptr++ = hexchars[sigval >> 4]; + *ptr++ = hexchars[sigval & 0xf]; +#else + *ptr++ = 'T'; + *ptr++ = hexchars[sigval >> 4]; + *ptr++ = hexchars[sigval & 0xf]; + *ptr++ = hexchars[PC_REGNUM >> 4]; + *ptr++ = hexchars[PC_REGNUM & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((char *)®s->nip, ptr, 4); + *ptr++ = ';'; + *ptr++ = hexchars[SP_REGNUM >> 4]; + *ptr++ = hexchars[SP_REGNUM & 0xf]; + *ptr++ = ':'; + ptr = mem2hex(((char *)®s) + SP_REGNUM*4, ptr, 4); + *ptr++ = ';'; +#endif + + *ptr++ = 0; + + putpacket(remcomOutBuffer); + + /* XXX We may want to add some features dealing with poking the + * XXX page tables, ... (look at sparc-stub.c for more info) + * XXX also required hacking to the gdb sources directly... + */ + + while (1) { + remcomOutBuffer[0] = 0; + + getpacket(remcomInBuffer); + switch (remcomInBuffer[0]) { + case '?': /* report most recent signal */ + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = hexchars[sigval >> 4]; + remcomOutBuffer[2] = hexchars[sigval & 0xf]; + remcomOutBuffer[3] = 0; + break; +#if 0 + case 'q': /* this screws up gdb for some reason...*/ + { + extern long _start, sdata, __bss_start; + + ptr = &remcomInBuffer[1]; + if (strncmp(ptr, "Offsets", 7) != 0) + break; + + ptr = remcomOutBuffer; + sprintf(ptr, "Text=%8.8x;Data=%8.8x;Bss=%8.8x", + &_start, &sdata, &__bss_start); + break; + } +#endif + case 'd': + /* toggle debug flag */ + kdebug ^= 1; + break; + + case 'g': /* return the value of the CPU registers. + * some of them are non-PowerPC names :( + * they are stored in gdb like: + * struct { + * u32 gpr[32]; + * f64 fpr[32]; + * u32 pc, ps, cnd, lr; (ps=msr) + * u32 cnt, xer, mq; + * } + */ + { + int i; + ptr = remcomOutBuffer; + /* General Purpose Regs */ + ptr = mem2hex((char *)regs, ptr, 32 * 4); + /* Floating Point Regs - FIXME */ + /*ptr = mem2hex((char *), ptr, 32 * 8);*/ + for(i=0; i<(32*8*2); i++) { /* 2chars/byte */ + ptr[i] = '0'; + } + ptr += 32*8*2; + /* pc, msr, cr, lr, ctr, xer, (mq is unused) */ + ptr = mem2hex((char *)®s->nip, ptr, 4); + ptr = mem2hex((char *)®s->msr, ptr, 4); + ptr = mem2hex((char *)®s->ccr, ptr, 4); + ptr = mem2hex((char *)®s->link, ptr, 4); + ptr = mem2hex((char *)®s->ctr, ptr, 4); + ptr = mem2hex((char *)®s->xer, ptr, 4); + } + break; + + case 'G': /* set the value of the CPU registers */ + { + ptr = &remcomInBuffer[1]; + + /* + * If the stack pointer has moved, you should pray. + * (cause only god can help you). + */ + + /* General Purpose Regs */ + hex2mem(ptr, (char *)regs, 32 * 4); + + /* Floating Point Regs - FIXME?? */ + /*ptr = hex2mem(ptr, ??, 32 * 8);*/ + ptr += 32*8*2; + + /* pc, msr, cr, lr, ctr, xer, (mq is unused) */ + ptr = hex2mem(ptr, (char *)®s->nip, 4); + ptr = hex2mem(ptr, (char *)®s->msr, 4); + ptr = hex2mem(ptr, (char *)®s->ccr, 4); + ptr = hex2mem(ptr, (char *)®s->link, 4); + ptr = hex2mem(ptr, (char *)®s->ctr, 4); + ptr = hex2mem(ptr, (char *)®s->xer, 4); + + strcpy(remcomOutBuffer,"OK"); + } + break; + case 'H': + /* don't do anything, yet, just acknowledge */ + hexToInt(&ptr, &addr); + strcpy(remcomOutBuffer,"OK"); + break; + + case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + /* Try to read %x,%x. */ + + ptr = &remcomInBuffer[1]; + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length)) { + if (mem2hex((char *)addr, remcomOutBuffer,length)) + break; + strcpy (remcomOutBuffer, "E03"); + } else { + strcpy(remcomOutBuffer,"E01"); + } + break; + + case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ + /* Try to read '%x,%x:'. */ + + ptr = &remcomInBuffer[1]; + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length) + && *ptr++ == ':') { + if (hex2mem(ptr, (char *)addr, length)) { + strcpy(remcomOutBuffer, "OK"); + } else { + strcpy(remcomOutBuffer, "E03"); + } + flush_icache_range(addr, addr+length); + } else { + strcpy(remcomOutBuffer, "E02"); + } + break; + + + case 'k': /* kill the program, actually just continue */ + case 'c': /* cAA..AA Continue; address AA..AA optional */ + /* try to read optional parameter, pc unchanged if no parm */ + + ptr = &remcomInBuffer[1]; + if (hexToInt(&ptr, &addr)) { + regs->nip = addr; + } + +/* Need to flush the instruction cache here, as we may have deposited a + * breakpoint, and the icache probably has no way of knowing that a data ref to + * some location may have changed something that is in the instruction cache. + */ + kgdb_flush_cache_all(); + set_msr(msr); + kgdb_interruptible(1); + unlock_kernel(); + kgdb_active = 0; + return; + + case 's': + kgdb_flush_cache_all(); + regs->msr |= MSR_SE; +#if 0 + set_msr(msr | MSR_SE); +#endif + unlock_kernel(); + kgdb_active = 0; + return; + + case 'r': /* Reset (if user process..exit ???)*/ + panic("kgdb reset."); + break; + } /* switch */ + if (remcomOutBuffer[0] && kdebug) { + printk("remcomInBuffer: %s\n", remcomInBuffer); + printk("remcomOutBuffer: %s\n", remcomOutBuffer); + } + /* reply to the request */ + putpacket(remcomOutBuffer); + } /* while(1) */ +} + +/* This function will generate a breakpoint exception. It is used at the + beginning of a program to sync up with a debugger and can be used + otherwise as a quick means to stop program execution and "break" into + the debugger. */ + +void +breakpoint(void) +{ + if (!initialized) { + printk("breakpoint() called b4 kgdb init\n"); + return; + } + + asm(" .globl breakinst + breakinst: .long 0x7d821008 + "); +} + +/* Output string in GDB O-packet format if GDB has connected. If nothing + output, returns 0 (caller must then handle output). */ +int +kgdb_output_string (const char* s, unsigned int count) +{ + char buffer[512]; + + if (!kgdb_started) + return 0; + + count = (count <= (sizeof(buffer) / 2 - 2)) + ? count : (sizeof(buffer) / 2 - 2); + + buffer[0] = 'O'; + mem2hex (s, &buffer[1], count); + putpacket(buffer); + + return 1; + } + +/* I don't know why other platforms don't need this. The function for + * the 8xx is found in arch/ppc/8xx_io/uart.c. -- Dan + */ +void +kgdb_map_scc(void) +{ +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/ppc_asm.h linuxppc64_2_4/arch/ppc64/kernel/ppc_asm.h --- linux-2.4.5-ac18/arch/ppc64/kernel/ppc_asm.h Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/ppc_asm.h Fri May 4 17:13:59 2001 @@ -0,0 +1,141 @@ +/* + * arch/ppc/kernel/ppc_asm.h + * + * Definitions used by various bits of low-level assembly code on PowerPC. + * + * Copyright (C) 1995-1999 Gary Thomas, Paul Mackerras, Cort Dougan. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include + +#include "ppc_asm.tmpl" +#include "ppc_defs.h" + +/* + * Macros for storing registers into and loading registers from + * exception frames. + */ +#define SAVE_GPR(n, base) std n,GPR0+8*(n)(base) +#define SAVE_2GPRS(n, base) SAVE_GPR(n, base); SAVE_GPR(n+1, base) +#define SAVE_4GPRS(n, base) SAVE_2GPRS(n, base); SAVE_2GPRS(n+2, base) +#define SAVE_8GPRS(n, base) SAVE_4GPRS(n, base); SAVE_4GPRS(n+4, base) +#define SAVE_10GPRS(n, base) SAVE_8GPRS(n, base); SAVE_2GPRS(n+8, base) +#define REST_GPR(n, base) ld n,GPR0+8*(n)(base) +#define REST_2GPRS(n, base) REST_GPR(n, base); REST_GPR(n+1, base) +#define REST_4GPRS(n, base) REST_2GPRS(n, base); REST_2GPRS(n+2, base) +#define REST_8GPRS(n, base) REST_4GPRS(n, base); REST_4GPRS(n+4, base) +#define REST_10GPRS(n, base) REST_8GPRS(n, base); REST_2GPRS(n+8, base) + +#define SAVE_FPR(n, base) stfd n,THREAD_FPR0+8*(n)(base) +#define SAVE_2FPRS(n, base) SAVE_FPR(n, base); SAVE_FPR(n+1, base) +#define SAVE_4FPRS(n, base) SAVE_2FPRS(n, base); SAVE_2FPRS(n+2, base) +#define SAVE_8FPRS(n, base) SAVE_4FPRS(n, base); SAVE_4FPRS(n+4, base) +#define SAVE_16FPRS(n, base) SAVE_8FPRS(n, base); SAVE_8FPRS(n+8, base) +#define SAVE_32FPRS(n, base) SAVE_16FPRS(n, base); SAVE_16FPRS(n+16, base) +#define REST_FPR(n, base) lfd n,THREAD_FPR0+8*(n)(base) +#define REST_2FPRS(n, base) REST_FPR(n, base); REST_FPR(n+1, base) +#define REST_4FPRS(n, base) REST_2FPRS(n, base); REST_2FPRS(n+2, base) +#define REST_8FPRS(n, base) REST_4FPRS(n, base); REST_4FPRS(n+4, base) +#define REST_16FPRS(n, base) REST_8FPRS(n, base); REST_8FPRS(n+8, base) +#define REST_32FPRS(n, base) REST_16FPRS(n, base); REST_16FPRS(n+16, base) + +#define SYNC \ + sync; \ + isync + +/* iSeries LPAR uses an event queue to signal I/O events + This code checks the queue */ + +#define CHECKLPQUEUE(ra,rb,rc) \ + mfspr ra,SPRG3; /* Get PACA address */\ + ld ra,PACALPQUEUE(ra); /* Get LpQueue address */\ + cmpi 0,ra,0; /* Does LpQueue exist? */\ + beq 99f; /* No? - then skip rest */\ + ld rb,LPQCUREVENTPTR(ra); /* Get current LpEvent */\ + lbz rb,LPEVENTFLAGS(rb); /* Get valid bit */\ + lbz rc,LPQOVERFLOW(ra); /* Get LpQueue overflow */\ + andi. ra,rb,0x0080; /* Isolate valid bit */\ + or. ra,ra,rc; /* 0 == no pending events */\ +99: + +#define CHECKDECR(ra,rb) \ + mfspr rb,SPRG3; /* Get Paca address */\ + lbz ra,PACALPPACA+LPPACADECRINT(rb); /* Get DECR int flag */\ + cmpi 0,ra,0; /* DECR occurred in hypervisor ? */\ + beq 99f; /* If not, skip rest */\ + xor ra,ra,ra; \ + stb ra,PACALPPACA+LPPACADECRINT(rb); /* Clear DECR int flag */\ +99: + + +/* Macros to adjust thread priority for Iseries hardware multithreading */ +#define HMT_LOW or 1,1,1 +#define HMT_MEDIUM or 2,2,2 +#define HMT_HIGH or 3,3,3 + +/* Insert the high 32 bits of the MSR into what will be the new + MSR (via SRR1 and rfid) This preserves the MSR.SF and MSR.ISF + bits. */ + +#define FIX_SRR1(ra, rb) \ + mr rb,ra; \ + mfmsr ra; \ + rldimi ra,rb,0,32 + +#define CLR_TOP32(r) rlwinm (r),(r),0,0,31 /* clear top 32 bits */ + +/* + * LOADADDR( rn, name ) + * loads the address of 'name' into 'rn' + * + * LOADBASE( rn, name ) + * loads the address (less the low 16 bits) of 'name' into 'rn' + * suitable for base+disp addressing + */ +#define LOADADDR(rn,name) \ + lis rn,name##@highest; \ + ori rn,rn,name##@higher; \ + rldicr rn,rn,32,31; \ + oris rn,rn,name##@h; \ + ori rn,rn,name##@l + +#define LOADBASE(rn,name) \ + lis rn,name@highest; \ + ori rn,rn,name@higher; \ + rldicr rn,rn,32,31; \ + oris rn,rn,name@ha + + +#define SET_REG_TO_CONST(reg, value) \ + lis reg,(((value)>>48)&0xFFFF); \ + ori reg,reg,(((value)>>32)&0xFFFF); \ + rldicr reg,reg,32,31; \ + oris reg,reg,(((value)>>16)&0xFFFF); \ + ori reg,reg,((value)&0xFFFF); + +#define SET_REG_TO_LABEL(reg, label) \ + lis reg,(label)@highest; \ + ori reg,reg,(label)@higher; \ + rldicr reg,reg,32,31; \ + oris reg,reg,(label)@h; \ + ori reg,reg,(label)@l; + + +/* PPPBBB - DRENG If KERNELBASE is always 0xC0..., + * Then we can easily do this with one asm insn. -Peter + */ +#define tophys(rd,rs) \ + lis rd,((KERNELBASE>>48)&0xFFFF); \ + rldicr rd,rd,32,31; \ + sub rd,rs,rd + +#define tovirt(rd,rs) \ + lis rd,((KERNELBASE>>48)&0xFFFF); \ + rldicr rd,rd,32,31; \ + add rd,rs,rd + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/ppc_asm.tmpl linuxppc64_2_4/arch/ppc64/kernel/ppc_asm.tmpl --- linux-2.4.5-ac18/arch/ppc64/kernel/ppc_asm.tmpl Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/ppc_asm.tmpl Fri May 4 17:13:59 2001 @@ -0,0 +1,115 @@ +/* Condition Register Bit Fields */ + +#define cr0 0 +#define cr1 1 +#define cr2 2 +#define cr3 3 +#define cr4 4 +#define cr5 5 +#define cr6 6 +#define cr7 7 + + +/* General Purpose Registers (GPRs) */ + +#define r0 0 +#define r1 1 +#define r2 2 +#define r3 3 +#define r4 4 +#define r5 5 +#define r6 6 +#define r7 7 +#define r8 8 +#define r9 9 +#define r10 10 +#define r11 11 +#define r12 12 +#define r13 13 +#define r14 14 +#define r15 15 +#define r16 16 +#define r17 17 +#define r18 18 +#define r19 19 +#define r20 20 +#define r21 21 +#define r22 22 +#define r23 23 +#define r24 24 +#define r25 25 +#define r26 26 +#define r27 27 +#define r28 28 +#define r29 29 +#define r30 30 +#define r31 31 + + +/* Floating Point Registers (FPRs) */ + +#define fr0 0 +#define fr1 1 +#define fr2 2 +#define fr3 3 +#define fr4 4 +#define fr5 5 +#define fr6 6 +#define fr7 7 +#define fr8 8 +#define fr9 9 +#define fr10 10 +#define fr11 11 +#define fr12 12 +#define fr13 13 +#define fr14 14 +#define fr15 15 +#define fr16 16 +#define fr17 17 +#define fr18 18 +#define fr19 19 +#define fr20 20 +#define fr21 21 +#define fr22 22 +#define fr23 23 +#define fr24 24 +#define fr25 25 +#define fr26 26 +#define fr27 27 +#define fr28 28 +#define fr29 29 +#define fr30 30 +#define fr31 31 + +#define vr0 0 +#define vr1 1 +#define vr2 2 +#define vr3 3 +#define vr4 4 +#define vr5 5 +#define vr6 6 +#define vr7 7 +#define vr8 8 +#define vr9 9 +#define vr10 10 +#define vr11 11 +#define vr12 12 +#define vr13 13 +#define vr14 14 +#define vr15 15 +#define vr16 16 +#define vr17 17 +#define vr18 18 +#define vr19 19 +#define vr20 20 +#define vr21 21 +#define vr22 22 +#define vr23 23 +#define vr24 24 +#define vr25 25 +#define vr26 26 +#define vr27 27 +#define vr28 28 +#define vr29 29 +#define vr30 30 +#define vr31 31 diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/ppc_defs.head linuxppc64_2_4/arch/ppc64/kernel/ppc_defs.head --- linux-2.4.5-ac18/arch/ppc64/kernel/ppc_defs.head Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/ppc_defs.head Fri May 4 17:13:59 2001 @@ -0,0 +1,3 @@ +/* + * WARNING! This file is automatically generated - DO NOT EDIT! + */ diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/ppc_ksyms.c linuxppc64_2_4/arch/ppc64/kernel/ppc_ksyms.c --- linux-2.4.5-ac18/arch/ppc64/kernel/ppc_ksyms.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/ppc_ksyms.c Thu May 24 12:56:50 2001 @@ -0,0 +1,273 @@ +/* + * c 2001 PPC 64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_SMP +#include +#endif /* CONFIG_SMP */ +//#include + +/* Tell string.h we don't want memcpy etc. as cpp defines */ +#define EXPORT_SYMTAB_STROPS + +extern void transfer_to_handler(void); +extern void syscall_trace(void); +extern void do_IRQ(struct pt_regs *regs, int isfake); +extern void SystemResetException(struct pt_regs *regs); +extern void MachineCheckException(struct pt_regs *regs); +extern void AlignmentException(struct pt_regs *regs); +extern void ProgramCheckException(struct pt_regs *regs); +extern void SingleStepException(struct pt_regs *regs); +extern int sys_sigreturn(struct pt_regs *regs); +extern void do_lost_interrupts(unsigned long); +extern int do_signal(sigset_t *, struct pt_regs *); + +long long __ashrdi3(long long, int); +long long __ashldi3(long long, int); +long long __lshrdi3(long long, int); +int abs(int); +extern unsigned long ret_to_user_hook; + +EXPORT_SYMBOL(clear_page); +EXPORT_SYMBOL(do_signal); +EXPORT_SYMBOL(syscall_trace); +EXPORT_SYMBOL(transfer_to_handler); +EXPORT_SYMBOL(do_IRQ); +EXPORT_SYMBOL(SystemResetException); +EXPORT_SYMBOL(MachineCheckException); +EXPORT_SYMBOL(AlignmentException); +EXPORT_SYMBOL(ProgramCheckException); +EXPORT_SYMBOL(SingleStepException); +EXPORT_SYMBOL(sys_sigreturn); +EXPORT_SYMBOL(ppc_n_lost_interrupts); +EXPORT_SYMBOL(ppc_lost_interrupts); +EXPORT_SYMBOL(do_lost_interrupts); +EXPORT_SYMBOL(enable_irq); +EXPORT_SYMBOL(disable_irq); +EXPORT_SYMBOL(disable_irq_nosync); +#ifdef CONFIG_SMP +EXPORT_SYMBOL(kernel_flag); +#endif /* CONFIG_SMP */ + +EXPORT_SYMBOL(isa_io_base); +EXPORT_SYMBOL(isa_mem_base); +EXPORT_SYMBOL(pci_dram_offset); +EXPORT_SYMBOL(DMA_MODE_READ); +EXPORT_SYMBOL(DMA_MODE_WRITE); +#if defined(CONFIG_ALL_PPC) +EXPORT_SYMBOL(_prep_type); +EXPORT_SYMBOL(ucSystemType); +#endif + +#if !__INLINE_BITOPS +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); +#endif /* __INLINE_BITOPS */ + +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(strcmp); +EXPORT_SYMBOL(strncmp); + +/* 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(__strnlen_user); + +/* +EXPORT_SYMBOL(inb); +EXPORT_SYMBOL(inw); +EXPORT_SYMBOL(inl); +EXPORT_SYMBOL(outb); +EXPORT_SYMBOL(outw); +EXPORT_SYMBOL(outl); +EXPORT_SYMBOL(outsl);*/ + +EXPORT_SYMBOL(_insb); +EXPORT_SYMBOL(_outsb); +EXPORT_SYMBOL(_insw); +EXPORT_SYMBOL(_outsw); +EXPORT_SYMBOL(_insl); +EXPORT_SYMBOL(_outsl); +EXPORT_SYMBOL(_insw_ns); +EXPORT_SYMBOL(_outsw_ns); +EXPORT_SYMBOL(_insl_ns); +EXPORT_SYMBOL(_outsl_ns); +EXPORT_SYMBOL(ioremap); +EXPORT_SYMBOL(__ioremap); +EXPORT_SYMBOL(iounmap); + +EXPORT_SYMBOL(ide_insw); +EXPORT_SYMBOL(ide_outsw); +EXPORT_SYMBOL(ppc_ide_md); +#ifdef CONFIG_BLK_DEV_IDE_MODULE +EXPORT_SYMBOL(chrp_ide_irq); +EXPORT_SYMBOL(chrp_ide_ports_known); +EXPORT_SYMBOL(chrp_ide_regbase); +EXPORT_SYMBOL(chrp_ide_probe); +#endif + +#ifdef CONFIG_PCI +EXPORT_SYMBOL(pci_alloc_consistent); +EXPORT_SYMBOL(pci_free_consistent); +#endif /* CONFIG_PCI */ + +EXPORT_SYMBOL(start_thread); +EXPORT_SYMBOL(kernel_thread); + +EXPORT_SYMBOL(flush_instruction_cache); +EXPORT_SYMBOL(_get_PVR); +EXPORT_SYMBOL(giveup_fpu); +EXPORT_SYMBOL(enable_kernel_fp); +EXPORT_SYMBOL(flush_icache_range); +EXPORT_SYMBOL(xchg_u32); +#ifdef CONFIG_SMP +EXPORT_SYMBOL(__global_cli); +EXPORT_SYMBOL(__global_sti); +EXPORT_SYMBOL(__global_save_flags); +EXPORT_SYMBOL(__global_restore_flags); +EXPORT_SYMBOL(_spin_lock); +EXPORT_SYMBOL(_spin_unlock); +EXPORT_SYMBOL(spin_trylock); +EXPORT_SYMBOL(_read_lock); +EXPORT_SYMBOL(_read_unlock); +EXPORT_SYMBOL(_write_lock); +EXPORT_SYMBOL(_write_unlock); +#endif + +#ifndef CONFIG_MACH_SPECIFIC +EXPORT_SYMBOL(_machine); +#endif +EXPORT_SYMBOL(ppc_md); + +#if defined(CONFIG_ALL_PPC) +EXPORT_SYMBOL(find_devices); +EXPORT_SYMBOL(find_type_devices); +EXPORT_SYMBOL(find_compatible_devices); +EXPORT_SYMBOL(find_path_device); +EXPORT_SYMBOL(find_phandle); +EXPORT_SYMBOL(device_is_compatible); +EXPORT_SYMBOL(machine_is_compatible); +EXPORT_SYMBOL(find_pci_device_OFnode); +EXPORT_SYMBOL(find_all_nodes); +EXPORT_SYMBOL(get_property); +EXPORT_SYMBOL(pci_io_base); +EXPORT_SYMBOL(pci_device_loc); +#endif /* defined(CONFIG_ALL_PPC) */ +#if defined(CONFIG_SCSI) && defined(CONFIG_ALL_PPC) +EXPORT_SYMBOL(note_scsi_host); +#endif +EXPORT_SYMBOL(kd_mksound); +EXPORT_SYMBOL_NOVERS(sys_ctrler); /* tibit */ +#ifdef CONFIG_NVRAM +EXPORT_SYMBOL(nvram_read_byte); +EXPORT_SYMBOL(nvram_write_byte); +#endif /* CONFIG_NVRAM */ + +EXPORT_SYMBOL_NOVERS(__ashrdi3); +EXPORT_SYMBOL_NOVERS(__ashldi3); +EXPORT_SYMBOL_NOVERS(__lshrdi3); +EXPORT_SYMBOL_NOVERS(memcpy); +EXPORT_SYMBOL_NOVERS(memset); +EXPORT_SYMBOL_NOVERS(memmove); +EXPORT_SYMBOL_NOVERS(memscan); +EXPORT_SYMBOL_NOVERS(memcmp); + +EXPORT_SYMBOL(abs); + +#ifdef CONFIG_VT +EXPORT_SYMBOL(screen_info); +#endif + +EXPORT_SYMBOL(int_control); +EXPORT_SYMBOL(timer_interrupt_intercept); +EXPORT_SYMBOL(timer_interrupt); +EXPORT_SYMBOL(do_IRQ_intercept); +EXPORT_SYMBOL(irq_desc); +void ppc_irq_dispatch_handler(struct pt_regs *, int); +EXPORT_SYMBOL(ppc_irq_dispatch_handler); +EXPORT_SYMBOL(decrementer_count); +EXPORT_SYMBOL(get_wchan); +EXPORT_SYMBOL(console_drivers); +EXPORT_SYMBOL(console_lock); +#ifdef CONFIG_XMON +EXPORT_SYMBOL(xmon); +#endif +EXPORT_SYMBOL(down_read_failed); + +#if defined(CONFIG_KGDB) || defined(CONFIG_XMON) +extern void (*debugger)(struct pt_regs *regs); +extern int (*debugger_bpt)(struct pt_regs *regs); +extern int (*debugger_sstep)(struct pt_regs *regs); +extern int (*debugger_iabr_match)(struct pt_regs *regs); +extern int (*debugger_dabr_match)(struct pt_regs *regs); +extern void (*debugger_fault_handler)(struct pt_regs *regs); + +EXPORT_SYMBOL(debugger); +EXPORT_SYMBOL(debugger_bpt); +EXPORT_SYMBOL(debugger_sstep); +EXPORT_SYMBOL(debugger_iabr_match); +EXPORT_SYMBOL(debugger_dabr_match); +EXPORT_SYMBOL(debugger_fault_handler); +#endif + +EXPORT_SYMBOL(ret_to_user_hook); +EXPORT_SYMBOL(do_softirq); diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/proc_pmc.c linuxppc64_2_4/arch/ppc64/kernel/proc_pmc.c --- linux-2.4.5-ac18/arch/ppc64/kernel/proc_pmc.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/proc_pmc.c Fri Jun 15 13:33:18 2001 @@ -0,0 +1,718 @@ +/* + * proc_pmc.c + * Copyright (C) 2001 Mike Corrigan & Dave Engebretsen IBM Corporation + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* Change Activity: + * 2001 : mikec : Created + * 2001/06/05 : engebret : Software event count support. + * End Change Activity + */ + +#ifndef _PMC_PROC_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +static int proc_pmc_control_mode = 0; + +static struct proc_dir_entry *proc_ppc64_root = NULL; +static struct proc_dir_entry *proc_ppc64_pmc_root = NULL; +static struct proc_dir_entry *proc_ppc64_pmc_system_root = NULL; +static struct proc_dir_entry *proc_ppc64_pmc_cpu_root[NR_CPUS] = {NULL, }; + +static spinlock_t proc_ppc64_lock; + +extern struct Naca *naca; + +int proc_ppc64_pmc_find_file(void *data); +int proc_ppc64_pmc_read(char *page, char **start, off_t off, + int count, int *eof, char *buffer); +int proc_ppc64_pmc_stab_read(char *page, char **start, off_t off, + int count, int *eof, void *data); +int proc_ppc64_pmc_htab_read(char *page, char **start, off_t off, + int count, int *eof, void *data); +int proc_ppc64_pmc_hw_read(char *page, char **start, off_t off, + int count, int *eof, void *data); + +static struct proc_dir_entry *pmc_proc_root = NULL; + +int proc_get_lpevents( char *page, char **start, off_t off, int count, int *eof, void *data); +int proc_reset_lpevents( struct file *file, const char *buffer, unsigned long count, void *data); + +int proc_pmc_get_control( char *page, char **start, off_t off, int count, int *eof, void *data); + +int proc_pmc_set_control( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_mmcr0( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_mmcr1( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_mmcra( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc1( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc2( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc3( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc4( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc5( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc6( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc7( struct file *file, const char *buffer, unsigned long count, void *data); +int proc_pmc_set_pmc8( struct file *file, const char *buffer, unsigned long count, void *data); + + +void proc_ppc64_init(void) +{ + unsigned long i; + struct proc_dir_entry *ent = NULL; + char buf[256]; + + printk("proc_ppc64: Creating /proc/ppc64/pmc\n"); + + /* + * Create the root, system, and cpu directories as follows: + * /proc/ppc64/pmc/system + * /proc/ppc64/pmc/cpu0 + */ + spin_lock(&proc_ppc64_lock); + proc_ppc64_root = proc_mkdir("ppc64", 0); + if (!proc_ppc64_root) return; + spin_unlock(&proc_ppc64_lock); + + proc_ppc64_pmc_root = proc_mkdir("pmc", proc_ppc64_root); + + proc_ppc64_pmc_system_root = proc_mkdir("system", proc_ppc64_pmc_root); + for(i = 0; i < naca->processorCount; i++) { + sprintf(buf, "cpu%ld", i); + proc_ppc64_pmc_cpu_root[i] = proc_mkdir(buf, proc_ppc64_pmc_root); + } + + + /* Create directories for the software counters. */ + for(i = 0; i < naca->processorCount; i++) { + ent = create_proc_entry("stab", S_IRUGO | S_IWUSR, + proc_ppc64_pmc_cpu_root[i]); + if(ent) { + ent->nlink = 1; + ent->data = (void *)proc_ppc64_pmc_cpu_root[i]; + ent->read_proc = proc_ppc64_pmc_stab_read; + ent->write_proc = proc_ppc64_pmc_stab_read; + } + + ent = create_proc_entry("htab", S_IRUGO | S_IWUSR, + proc_ppc64_pmc_cpu_root[i]); + if(ent) { + ent->nlink = 1; + ent->data = (void *)proc_ppc64_pmc_cpu_root[i]; + ent->read_proc = proc_ppc64_pmc_htab_read; + ent->write_proc = proc_ppc64_pmc_htab_read; + } + } + + ent = create_proc_entry("stab", S_IRUGO | S_IWUSR, + proc_ppc64_pmc_system_root); + if(ent) { + ent->nlink = 1; + ent->data = (void *)proc_ppc64_pmc_system_root; + ent->read_proc = proc_ppc64_pmc_stab_read; + ent->write_proc = proc_ppc64_pmc_stab_read; + } + + ent = create_proc_entry("htab", S_IRUGO | S_IWUSR, + proc_ppc64_pmc_system_root); + if(ent) { + ent->nlink = 1; + ent->data = (void *)proc_ppc64_pmc_system_root; + ent->read_proc = proc_ppc64_pmc_htab_read; + ent->write_proc = proc_ppc64_pmc_htab_read; + } + + /* Create directories for the hardware counters. */ + for(i = 0; i < naca->processorCount; i++) { + ent = create_proc_entry("hardware", S_IRUGO | S_IWUSR, + proc_ppc64_pmc_cpu_root[i]); + if(ent) { + ent->nlink = 1; + ent->data = (void *)proc_ppc64_pmc_cpu_root[i]; + ent->read_proc = proc_ppc64_pmc_hw_read; + ent->write_proc = proc_ppc64_pmc_hw_read; + } + } + + ent = create_proc_entry("hardware", S_IRUGO | S_IWUSR, + proc_ppc64_pmc_system_root); + if(ent) { + ent->nlink = 1; + ent->data = (void *)proc_ppc64_pmc_system_root; + ent->read_proc = proc_ppc64_pmc_hw_read; + ent->write_proc = proc_ppc64_pmc_hw_read; + } +} + +/* + * Find the requested 'file' given a proc token. + * + * Inputs: void * data: proc token + * Output: int : (0, ..., +N) = CPU number. + * -1 = System. + */ +int proc_ppc64_pmc_find_file(void *data) { + int i; + + if((unsigned long)data == + (unsigned long) proc_ppc64_pmc_system_root) { + return(-1); + } else { + for(i = 0; i < naca->processorCount; i++) { + if((unsigned long)data == + (unsigned long)proc_ppc64_pmc_cpu_root[i]) { + return(i); + } + } + } + + /* On error, just default to a type of system. */ + printk("proc_ppc64_pmc_find_file: failed to find file token.\n"); + return(-1); +} + +int +proc_ppc64_pmc_read(char *page, char **start, off_t off, + int count, int *eof, char *buffer) { + int buffer_size, n, i; + unsigned long flags; + + if (count < 0) return 0; + + if(buffer == NULL) { + *eof = 1; + return 0; + } + + /* Check for read beyond EOF */ + buffer_size = n = strlen(buffer); + if(off >= buffer_size) { + *eof = 1; + return 0; + } + if (n > (buffer_size - off)) n = buffer_size - off; + + /* Never return more than was requested */ + if (n > count) { + n = count; + } else { + *eof = 1; + } + + memcpy(page, buffer + off, n); + + *start = page; + + return n; +} + +int +proc_ppc64_pmc_stab_read(char *page, char **start, off_t off, + int count, int *eof, void *data) { + int n, file; + char *buffer = NULL; + + if (count < 0) return 0; + spin_lock(&proc_ppc64_lock); + + /* Figure out which file is being request. */ + file = proc_ppc64_pmc_find_file(data); + + /* Update the counters and the text buffer representation. */ + buffer = ppc64_pmc_stab(file); + + /* Put the data into the requestor's buffer. */ + n = proc_ppc64_pmc_read(page, start, off, count, eof, buffer); + + spin_unlock(&proc_ppc64_lock); + return n; +} + +int +proc_ppc64_pmc_htab_read(char *page, char **start, off_t off, + int count, int *eof, void *data) { + int n, file; + char *buffer = NULL; + + if (count < 0) return 0; + spin_lock(&proc_ppc64_lock); + + /* Figure out which file is being request. */ + file = proc_ppc64_pmc_find_file(data); + + /* Update the counters and the text buffer representation. */ + buffer = ppc64_pmc_htab(file); + + /* Put the data into the requestor's buffer. */ + n = proc_ppc64_pmc_read(page, start, off, count, eof, buffer); + + spin_unlock(&proc_ppc64_lock); + return n; +} + +int +proc_ppc64_pmc_hw_read(char *page, char **start, off_t off, + int count, int *eof, void *data) { + int n, file; + char *buffer = NULL; + + if (count < 0) return 0; + spin_lock(&proc_ppc64_lock); + + /* Figure out which file is being request. */ + file = proc_ppc64_pmc_find_file(data); + + /* Update the counters and the text buffer representation. */ + buffer = ppc64_pmc_hw(file); + + /* Put the data into the requestor's buffer. */ + n = proc_ppc64_pmc_read(page, start, off, count, eof, buffer); + + spin_unlock(&proc_ppc64_lock); + return n; +} + +/* + * DRENG the remainder of these functions still need work ... + */ +void pmc_proc_init(struct proc_dir_entry *iSeries_proc) +{ + struct proc_dir_entry *ent = NULL; + + ent = create_proc_entry("lpevents", S_IFREG|S_IRUGO, iSeries_proc); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_get_lpevents; + ent->write_proc = proc_reset_lpevents; + + pmc_proc_root = proc_mkdir("pmc", iSeries_proc); + if (!pmc_proc_root) return; + + ent = create_proc_entry("control", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)0; + ent->read_proc = proc_pmc_get_control; + ent->write_proc = proc_pmc_set_control; + +} + +static int pmc_calc_metrics( char *page, char **start, off_t off, int count, int *eof, int len) +{ + if ( len <= off+count) + *eof = 1; + *start = page+off; + len -= off; + if ( len > count ) + len = count; + if ( len < 0 ) + len = 0; + return len; +} + +static char * lpEventTypes[9] = { + "Hypervisor\t\t", + "Machine Facilities\t", + "Session Manager\t", + "SPD I/O\t\t", + "Virtual Bus\t\t", + "PCI I/O\t\t", + "RIO I/O\t\t", + "Virtual Lan\t\t", + "Virtual I/O\t\t" + }; + + +int proc_get_lpevents +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + unsigned i; + int len = 0; + + len += sprintf( page+len, "LpEventQueue 0\n" ); + len += sprintf( page+len, " events processed:\t%lu\n", + (unsigned long)xItLpQueue.xLpIntCount ); + for (i=0; i<9; ++i) { + len += sprintf( page+len, " %s %10lu\n", + lpEventTypes[i], + (unsigned long)xItLpQueue.xLpIntCountByType[i] ); + } + return pmc_calc_metrics( page, start, off, count, eof, len ); + +} + +int proc_reset_lpevents( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + return count; +} + +int proc_pmc_get_control +(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + + if ( proc_pmc_control_mode == PMC_CONTROL_CPI ) { + unsigned long mach_cycles = mfspr( PMC5 ); + unsigned long inst_complete = mfspr( PMC4 ); + unsigned long inst_dispatch = mfspr( PMC3 ); + unsigned long thread_active_run = mfspr( PMC1 ); + unsigned long thread_active = mfspr( PMC2 ); + unsigned long cpi = 0; + unsigned long cpithou = 0; + unsigned long remain; + + if ( inst_complete ) { + cpi = thread_active_run / inst_complete; + remain = thread_active_run % inst_complete; + if ( inst_complete > 1000000 ) + cpithou = remain / ( inst_complete / 1000 ); + else + cpithou = ( remain * 1000 ) / inst_complete; + } + len += sprintf( page+len, "PMC CPI Mode\nRaw Counts\n" ); + len += sprintf( page+len, "machine cycles : %12lu\n", mach_cycles ); + len += sprintf( page+len, "thread active cycles : %12lu\n\n", thread_active ); + + len += sprintf( page+len, "instructions completed : %12lu\n", inst_complete ); + len += sprintf( page+len, "instructions dispatched : %12lu\n", inst_dispatch ); + len += sprintf( page+len, "thread active run cycles : %12lu\n", thread_active_run ); + + len += sprintf( page+len, "thread active run cycles/instructions completed\n" ); + len += sprintf( page+len, "CPI = %lu.%03lu\n", cpi, cpithou ); + + } + else if ( proc_pmc_control_mode == PMC_CONTROL_TLB ) { + len += sprintf( page+len, "PMC TLB Mode\n" ); + len += sprintf( page+len, "I-miss count : %12lu\n", mfspr( PMC1 ) ); + len += sprintf( page+len, "I-miss latency : %12lu\n", mfspr( PMC2 ) ); + len += sprintf( page+len, "D-miss count : %12lu\n", mfspr( PMC3 ) ); + len += sprintf( page+len, "D-miss latency : %12lu\n", mfspr( PMC4 ) ); + len += sprintf( page+len, "IERAT miss count : %12lu\n", mfspr( PMC5 ) ); + len += sprintf( page+len, "D-reference count : %12lu\n", mfspr( PMC6 ) ); + len += sprintf( page+len, "miss PTEs searched : %12lu\n", mfspr( PMC7 ) ); + len += sprintf( page+len, "miss >8 PTEs searched : %12lu\n", mfspr( PMC8 ) ); + } + /* IMPLEMENT ME */ + return pmc_calc_metrics( page, start, off, count, eof, len ); +} + +unsigned long proc_pmc_conv_int( const char *buf, unsigned count ) +{ + const char * p; + char b0, b1; + unsigned v, multiplier, mult, i; + unsigned long val; + multiplier = 10; + p = buf; + if ( count >= 3 ) { + b0 = buf[0]; + b1 = buf[1]; + if ( ( b0 == '0' ) && + ( ( b1 == 'x' ) || ( b1 == 'X' ) ) ) { + p = buf + 2; + count -= 2; + multiplier = 16; + } + + } + val = 0; + for ( i=0; i= '0' ) && ( b0 <= '9' ) ) + v = b0 - '0'; + else if ( multiplier == 16 ) { + if ( ( b0 >= 'a' ) && ( b0 <= 'f' ) ) + v = b0 - 'a' + 10; + else if ( ( b0 >= 'A' ) && ( b0 <= 'F' ) ) + v = b0 - 'A' + 10; + else + mult = 1; + } + else + mult = 1; + val *= mult; + val += v; + } + + return val; + +} + +static inline void proc_pmc_stop(void) +{ + /* Freeze all counters, leave everything else alone */ + mtspr( MMCR0, mfspr( MMCR0 ) | 0x80000000 ); +} + +static inline void proc_pmc_start(void) +{ + /* Unfreeze all counters, leave everything else alone */ + mtspr( MMCR0, mfspr( MMCR0 ) & ~0x80000000 ); + +} + +static inline void proc_pmc_reset(void) +{ + /* Clear all the PMCs to zeros + * Assume a "stop" has already frozen the counters + * Clear all the PMCs + */ + mtspr( PMC1, 0 ); + mtspr( PMC2, 0 ); + mtspr( PMC3, 0 ); + mtspr( PMC4, 0 ); + mtspr( PMC5, 0 ); + mtspr( PMC6, 0 ); + mtspr( PMC7, 0 ); + mtspr( PMC8, 0 ); + +} + +static inline void proc_pmc_cpi(void) +{ + /* Configure the PMC registers to count cycles and instructions */ + /* so we can compute cpi */ + /* + * MMCRA[30] = 1 Don't count in wait state (CTRL[31]=0) + * MMCR0[6] = 1 Freeze counters when any overflow + * MMCR0[19:25] = 0x01 PMC1 counts Thread Active Run Cycles + * MMCR0[26:31] = 0x05 PMC2 counts Thread Active Cycles + * MMCR1[0:4] = 0x07 PMC3 counts Instructions Dispatched + * MMCR1[5:9] = 0x03 PMC4 counts Instructions Completed + * MMCR1[10:14] = 0x06 PMC5 counts Machine Cycles + * + */ + + proc_pmc_control_mode = PMC_CONTROL_CPI; + + // Indicate to hypervisor that we are using the PMCs + ((struct Paca *)mfspr(SPRG3))->xLpPacaPtr->xPMCRegsInUse = 1; + + // Freeze all counters + mtspr( MMCR0, 0x80000000 ); + mtspr( MMCR1, 0x00000000 ); + + // Clear all the PMCs + mtspr( PMC1, 0 ); + mtspr( PMC2, 0 ); + mtspr( PMC3, 0 ); + mtspr( PMC4, 0 ); + mtspr( PMC5, 0 ); + mtspr( PMC6, 0 ); + mtspr( PMC7, 0 ); + mtspr( PMC8, 0 ); + + /* Freeze counters in Wait State (CTRL[31]=0) */ + mtspr( MMCRA, 0x00000002 ); + + /* PMC3<-0x07, PMC4<-0x03, PMC5<-0x06 */ + mtspr( MMCR1, 0x38cc0000 ); + + mb(); + + /* PMC1<-0x01, PMC2<-0x05 + * Start all counters + */ + mtspr( MMCR0, 0x02000045 ); + +} + +static inline void proc_pmc_tlb(void) +{ + /* Configure the PMC registers to count tlb misses */ + /* + * MMCR0[6] = 1 Freeze counters when any overflow + * MMCR0[19:25] = 0x55 Group count + * PMC1 counts I misses + * PMC2 counts I miss duration (latency) + * PMC3 counts D misses + * PMC4 counts D miss duration (latency) + * PMC5 counts IERAT misses + * PMC6 counts D references (including PMC7) + * PMC7 counts miss PTEs searched + * PMC8 counts miss >8 PTEs searched + * + */ + + proc_pmc_control_mode = PMC_CONTROL_TLB; + + /* Indicate to hypervisor that we are using the PMCs */ + ((struct Paca *)mfspr(SPRG3))->xLpPacaPtr->xPMCRegsInUse = 1; + + /* Freeze all counters */ + mtspr( MMCR0, 0x80000000 ); + mtspr( MMCR1, 0x00000000 ); + + /* Clear all the PMCs */ + mtspr( PMC1, 0 ); + mtspr( PMC2, 0 ); + mtspr( PMC3, 0 ); + mtspr( PMC4, 0 ); + mtspr( PMC5, 0 ); + mtspr( PMC6, 0 ); + mtspr( PMC7, 0 ); + mtspr( PMC8, 0 ); + + mtspr( MMCRA, 0x00000000 ); + + mb(); + + /* PMC1<-0x55 + * Start all counters + */ + mtspr( MMCR0, 0x02001540 ); + +} + +int proc_pmc_set_control( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + if ( ! strncmp( buffer, "stop", 4 ) ) + proc_pmc_stop(); + else if ( ! strncmp( buffer, "start", 5 ) ) + proc_pmc_start(); + else if ( ! strncmp( buffer, "reset", 5 ) ) + proc_pmc_reset(); + else if ( ! strncmp( buffer, "cpi", 3 ) ) + proc_pmc_cpi(); + else if ( ! strncmp( buffer, "tlb", 3 ) ) + proc_pmc_tlb(); + + /* IMPLEMENT ME */ + return count; +} + +int proc_pmc_set_mmcr0( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + v = v & ~0x04000000; /* Don't allow interrupts for now */ + if ( v & ~0x80000000 ) /* Inform hypervisor we are using PMCs */ + ((struct Paca *)mfspr(SPRG3))->xLpPacaPtr->xPMCRegsInUse = 1; + else + ((struct Paca *)mfspr(SPRG3))->xLpPacaPtr->xPMCRegsInUse = 0; + mtspr( MMCR0, v ); + + return count; +} + +int proc_pmc_set_mmcr1( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( MMCR1, v ); + + return count; +} + +int proc_pmc_set_mmcra( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + v = v & ~0x00008000; /* Don't allow interrupts for now */ + mtspr( MMCRA, v ); + + return count; +} + + +int proc_pmc_set_pmc1( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC1, v ); + + return count; +} + +int proc_pmc_set_pmc2( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC2, v ); + + return count; +} + +int proc_pmc_set_pmc3( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC3, v ); + + return count; +} + +int proc_pmc_set_pmc4( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC4, v ); + + return count; +} + +int proc_pmc_set_pmc5( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC5, v ); + + return count; +} + +int proc_pmc_set_pmc6( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC6, v ); + + return count; +} + +int proc_pmc_set_pmc7( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC7, v ); + + return count; +} + +int proc_pmc_set_pmc8( struct file *file, const char *buffer, unsigned long count, void *data ) +{ + unsigned long v; + v = proc_pmc_conv_int( buffer, count ); + mtspr( PMC8, v ); + + return count; +} + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/process.c linuxppc64_2_4/arch/ppc64/kernel/process.c --- linux-2.4.5-ac18/arch/ppc64/kernel/process.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/process.c Thu Jul 12 00:51:37 2001 @@ -0,0 +1,666 @@ +/* + * + * + * linux/arch/ppc/kernel/process.c + * + * Derived from "arch/i386/kernel/process.c" + * Copyright (C) 1995 Linus Torvalds + * + * Updated and modified by Cort Dougan (cort@cs.nmt.edu) and + * Paul Mackerras (paulus@cs.anu.edu.au) + * + * PowerPC version + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs); +extern unsigned long _get_SP(void); + +struct task_struct *last_task_used_math = NULL; +static struct vm_area_struct init_mmap = INIT_MMAP; +static struct fs_struct init_fs = INIT_FS; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS; +struct mm_struct init_mm = INIT_MM(init_mm); + + +/* **** THE FOLLOWING IS AN AWFUL HACK **** + * We define swapper_pg_dir to be ioremap_dir + * and init_mmap to be ioremap_mmap + * so that the ioremap_mm will get initialized + * correctly by the INIT_MM macro (from linux/sched.h) + + * ioremap_mm is really a dummy mm which is only used + * to contain the ioremap_dir pointer and the + * page_table_lock for the ioremap page table + * This is similar to the way init_mm is used + */ + +#define swapper_pg_dir ioremap_dir +#define init_mmap ioremap_mmap +extern struct vm_area_struct ioremap_mmap; +struct mm_struct ioremap_mm = INIT_MM(ioremap_mm); +struct vm_area_struct ioremap_mmap = IOREMAP_MMAP; +#undef swapper_pg_dir +#undef init_mmap +// **** END OF HACK **** + +/* this is 16-byte aligned because it has a stack in it */ +union task_union __attribute((aligned(16))) init_task_union = { + INIT_TASK(init_task_union.task) +}; + +#ifdef CONFIG_SMP +struct current_set_struct current_set[NR_CPUS] = {{&init_task, 0}, }; +#endif + +char *sysmap = NULL; +unsigned long sysmap_size = 0; + +extern char __toc_start; + +#undef SHOW_TASK_SWITCHES +#undef CHECK_STACK + +#if defined(CHECK_STACK) +unsigned long +kernel_stack_top(struct task_struct *tsk) +{ + return ((unsigned long)tsk) + sizeof(union task_union); +} + +unsigned long +task_top(struct task_struct *tsk) +{ + return ((unsigned long)tsk) + sizeof(struct task_struct); +} + +/* check to make sure the kernel stack is healthy */ +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; + +#if 0 + /* check thread magic */ + if ( tsk->thread.magic != THREAD_MAGIC ) + { + ret |= 1; + printk("thread.magic bad: %08x\n", tsk->thread.magic); + } +#endif + + if ( !tsk ) + printk("check_stack(): tsk bad tsk %p\n",tsk); + + /* check if stored ksp is bad */ + if ( (tsk->thread.ksp > stack_top) || (tsk->thread.ksp < tsk_top) ) + { + printk("stack out of bounds: %s/%d\n" + " tsk_top %08lx ksp %08lx stack_top %08lx\n", + tsk->comm,tsk->pid, + tsk_top, tsk->thread.ksp, stack_top); + ret |= 2; + } + + /* check if stack ptr RIGHT NOW is bad */ + if ( (tsk == current) && ((_get_SP() > stack_top ) || (_get_SP() < tsk_top)) ) + { + printk("current stack ptr out of bounds: %s/%d\n" + " tsk_top %08lx sp %08lx stack_top %08lx\n", + current->comm,current->pid, + tsk_top, _get_SP(), stack_top); + ret |= 4; + } + +#if 0 + /* check amount of free stack */ + for ( i = (unsigned long *)task_top(tsk) ; i < kernel_stack_top(tsk) ; i++ ) + { + if ( !i ) + printk("check_stack(): i = %p\n", i); + if ( *i != 0 ) + { + /* only notify if it's less than 900 bytes */ + if ( (i - (unsigned long *)task_top(tsk)) < 900 ) + printk("%d bytes free on stack\n", + i - task_top(tsk)); + break; + } + } +#endif + + if (ret) + { + panic("bad kernel stack"); + } + return(ret); +} +#endif /* defined(CHECK_STACK) */ + +void +enable_kernel_fp(void) +{ +#ifdef CONFIG_SMP + if (current->thread.regs && (current->thread.regs->msr & MSR_FP)) + giveup_fpu(current); + else + giveup_fpu(NULL); /* just enables FP for kernel */ +#else + giveup_fpu(last_task_used_math); +#endif /* CONFIG_SMP */ +} + +int +dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs) +{ + if (regs->msr & MSR_FP) + giveup_fpu(current); + memcpy(fpregs, ¤t->thread.fpr[0], sizeof(*fpregs)); + return 1; +} + +void +_switch_to(struct task_struct *prev, struct task_struct *new, + struct task_struct **last) +{ + struct thread_struct *new_thread, *old_thread; + unsigned long s; + + __save_flags(s); + __cli(); +#if CHECK_STACK + check_stack(prev); + check_stack(new); +#endif + +#ifdef SHOW_TASK_SWITCHES + printk("%s/%d -> %s/%d NIP %08lx cpu %d root %x/%x\n", + prev->comm,prev->pid, + new->comm,new->pid,new->thread.regs->nip,new->processor, + new->fs->root,prev->fs->root); +#endif +#ifdef CONFIG_SMP + /* avoid complexity of lazy save/restore of fpu + * by just saving it every time we switch out if + * this task used the fpu during the last quantum. + * + * If it tries to use the fpu again, it'll trap and + * reload its fp regs. So we don't have to do a restore + * every switch, just a save. + * -- Cort + */ + if ( prev->thread.regs && (prev->thread.regs->msr & MSR_FP) ) + giveup_fpu(prev); + + /* prev->last_processor = prev->processor; */ + current_set[smp_processor_id()].task = new; +#endif /* CONFIG_SMP */ + new_thread = &new->thread; + old_thread = ¤t->thread; + *last = _switch(old_thread, new_thread); + __restore_flags(s); +} + +void show_regs(struct pt_regs * regs) +{ + int i; + + printk("NIP: %016lX XER: %016lX LR: %016lX REGS: %p TRAP: %04lx\n", + regs->nip, regs->xer, regs->link, regs,regs->trap); + printk("MSR: %016lx 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 = %p[%d] '%s' ", + current, current->pid, current->comm); + printk("Last syscall: %ld ", current->thread.last_syscall); + printk("\nlast math %p ", last_task_used_math); + +#ifdef CONFIG_SMP + /* printk(" CPU: %d last CPU: %d", current->processor,current->last_processor); */ +#endif /* CONFIG_SMP */ + + printk("\n"); + for (i = 0; i < 32; i++) + { + long r; + if ((i % 4) == 0) + { + printk("GPR%02d: ", i); + } + + if ( __get_user(r, &(regs->gpr[i])) ) + goto out; + printk("%016lX ", r); + if ((i % 4) == 3) + { + printk("\n"); + } + } +out: +} + +void exit_thread(void) +{ + if (last_task_used_math == current) + last_task_used_math = NULL; +} + +void flush_thread(void) +{ + if (last_task_used_math == current) + last_task_used_math = NULL; +} + +void +release_thread(struct task_struct *t) +{ +} + +/* + * Copy a thread.. + */ +int +copy_thread(int nr, unsigned long clone_flags, unsigned long usp, + unsigned long unused, + struct task_struct * p, struct pt_regs * regs) +{ + unsigned long msr; + struct pt_regs * childregs, *kregs; +#ifdef CONFIG_SMP + extern void ret_from_smpfork(void); +#else + extern void ret_from_except(void); +#endif + /* Copy registers */ + childregs = ((struct pt_regs *) + ((unsigned long)p + sizeof(union task_union) + - STACK_FRAME_OVERHEAD)) - 2; + *childregs = *regs; + childregs->gpr[3] = 0; /* Result from fork() */ + p->thread.regs = childregs; + p->thread.ksp = (unsigned long) childregs - STACK_FRAME_OVERHEAD; + p->thread.ksp -= sizeof(struct pt_regs ) + STACK_FRAME_OVERHEAD; + kregs = (struct pt_regs *)(p->thread.ksp + STACK_FRAME_OVERHEAD); +#ifdef CONFIG_SMP + kregs->nip = *((unsigned long *)ret_from_smpfork); +#else + /* The PPC64 compiler makes use of a TOC to contain function + * pointers. The function (ret_from_except) is actually a pointer + * to the TOC entry. The first entry is a pointer to the actual + * function. + */ + kregs->nip = *((unsigned long *)ret_from_except); +#endif + asm volatile("mfmsr %0" : "=r" (msr):); + kregs->msr = msr; + kregs->gpr[1] = (unsigned long)childregs - STACK_FRAME_OVERHEAD; + kregs->gpr[2] = (((unsigned long)&__toc_start) + 0x8000); + + if (usp >= (unsigned long) regs) { + /* Stack is in kernel space - must adjust */ + childregs->gpr[1] = (unsigned long)(childregs + 1); + *((unsigned long *) childregs->gpr[1]) = 0; + } else { + /* Provided stack is in user space */ + childregs->gpr[1] = usp; + } + p->thread.last_syscall = -1; + + /* + * copy fpu info - assume lazy fpu switch now always + * -- Cort + */ + if (regs->msr & MSR_FP) + giveup_fpu(current); + memcpy(&p->thread.fpr, ¤t->thread.fpr, sizeof(p->thread.fpr)); + p->thread.fpscr = current->thread.fpscr; + childregs->msr &= ~MSR_FP; + + atomic_set(&p->thread.refcount, 1); + +#ifdef CONFIG_SMP + // p->last_processor = NO_PROC_ID; +#endif /* CONFIG_SMP */ + 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; + + sp += sizeof(int); + 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) +{ + /* NIP is *really* a pointer to the function descriptor for + * the elf _start routine. The first entry in the function + * descriptor is the entry address of _start and the second + * entry is the TOC value we need to use. + */ + unsigned long *entry = (unsigned long *)nip; + unsigned long *toc = entry + 1; + + PPCDBG(PPCDBG_SYS64, "start_thread - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + PPCDBG(PPCDBG_SYS64, " pt_regs=0x%lx, nip=0x%lx, sp=0x%lx\n", regs, nip, sp); + + set_fs(USER_DS); + __get_user(regs->nip, entry); + regs->gpr[1] = sp; + __get_user(regs->gpr[2], toc); + regs->msr = MSR_USER64; + shove_aux_table(sp); + if (last_task_used_math == current) + last_task_used_math = 0; + current->thread.fpscr = 0; +} + +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; + + PPCDBG(PPCDBG_SYS64, "sys_clone - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + res = do_fork(clone_flags, regs->gpr[1], regs, 0); +#ifdef CONFIG_SMP + /* When we clone the idle task we keep the same pid but + * the return value of 0 for both causes problems. + * -- Cort + */ + if ((current->pid == 0) && (current == &init_task)) + res = 1; +#endif /* CONFIG_SMP */ + + PPCDBG(PPCDBG_SYS64, "sys_clone - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return res; +} + +asmlinkage int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6, + struct pt_regs *regs) +{ + int res; + + PPCDBG(PPCDBG_SYS64, "sys_fork - entered - pid=%ld comm=%s \n", current->pid, current->comm); + + res = do_fork(SIGCHLD, regs->gpr[1], regs, 0); + + #ifdef CONFIG_SMP + /* When we clone the idle task we keep the same pid but + * the return value of 0 for both causes problems. + * -- Cort + */ + if ((current->pid == 0) && (current == &init_task)) + res = 1; + #endif /* CONFIG_SMP */ + + PPCDBG(PPCDBG_SYS64, "sys_fork - exited - pid=%ld comm=%s \n", current->pid, current->comm); + + return res; +} + +asmlinkage int sys_vfork(int p1, int p2, int p3, int p4, int p5, int p6, + struct pt_regs *regs) +{ + PPCDBG(PPCDBG_SYS64, "sys_vfork - running - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->gpr[1], regs, 0); +} + +asmlinkage int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2, + unsigned long a3, unsigned long a4, unsigned long a5, + struct pt_regs *regs) +{ + int error; + char * filename; + + PPCDBG(PPCDBG_SYS64, "sys_execve - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + filename = getname((char *) a0); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + goto out; + if (regs->msr & MSR_FP) + giveup_fpu(current); + + PPCDBG(PPCDBG_SYS64, "sys_execve - before do_execve : filename = %s\n", filename); + + error = do_execve(filename, (char **) a1, (char **) a2, regs); + + if (error == 0) + current->ptrace &= ~PT_DTRACE; + putname(filename); + + out: + PPCDBG(PPCDBG_SYS64, "sys_execve - exited - pid=%ld current=%lx comm=%s error = %lx\n", current->pid, current, current->comm, error); + + return error; +} + +void +print_backtrace(unsigned long *sp) +{ + int cnt = 0; + unsigned long i; + + printk("Call backtrace: "); + while (sp) { + if (__get_user( i, &sp[2] )) + break; + if (cnt++ % 4 == 0) + printk("\n"); + printk("%016lX ", i); + if (cnt > 32) break; + if (__get_user(sp, (unsigned long **)sp)) + break; + } + printk("\n"); +} + +#if 0 +/* + * Low level print for debugging - Cort + */ +int __init ll_printk(const char *fmt, ...) +{ + va_list args; + char buf[256]; + int i; + + va_start(args, fmt); + i=vsprintf(buf,fmt,args); + ll_puts(buf); + va_end(args); + return i; +} + +int lines = 24, cols = 80; +int orig_x = 0, orig_y = 0; + +void puthex(unsigned long val) +{ + unsigned char buf[10]; + int i; + for (i = 7; i >= 0; i--) + { + buf[i] = "0123456789ABCDEF"[val & 0x0F]; + val >>= 4; + } + buf[8] = '\0'; + prom_print(buf); +} + +void __init ll_puts(const char *s) +{ + int x,y; + char *vidmem = (char *)/*(_ISA_MEM_BASE + 0xB8000) */0xD00B8000; + char c; + extern int mem_init_done; + + if ( mem_init_done ) /* assume this means we can printk */ + { + printk(s); + return; + } + + /* + * can't ll_puts on chrp without openfirmware yet. + * vidmem just needs to be setup for it. + * -- Cort + */ + if ( _machine != _MACH_prep ) + return; + x = orig_x; + y = orig_y; + + while ( ( c = *s++ ) != '\0' ) { + if ( c == '\n' ) { + x = 0; + if ( ++y >= lines ) { + /*scroll();*/ + /*y--;*/ + y = 0; + } + } else { + vidmem [ ( x + cols * y ) * 2 ] = c; + if ( ++x >= cols ) { + x = 0; + if ( ++y >= lines ) { + /*scroll();*/ + /*y--;*/ + y = 0; + } + } + } + } + + orig_x = x; + orig_y = y; +} +#endif + +/* + * These bracket the sleeping functions.. + */ +extern void scheduling_functions_start_here(void); +extern void scheduling_functions_end_here(void); +#define first_sched (*(unsigned long *)scheduling_functions_start_here) +#define last_sched (*(unsigned long *)scheduling_functions_end_here) + +unsigned long get_wchan(struct task_struct *p) +{ + unsigned long ip, sp; + unsigned long stack_page = (unsigned long)p; + int count = 0; + if (!p || p == current || p->state == TASK_RUNNING) + return 0; + sp = p->thread.ksp; + do { + sp = *(unsigned long *)sp; + if (sp < (stack_page + sizeof(struct task_struct)) || + sp >= (stack_page + (4 * PAGE_SIZE))) + return 0; + if (count > 0) { + ip = *(unsigned long *)(sp + 16); + if (ip < first_sched || ip >= last_sched) + return (ip & 0xFFFFFFFF); + } + } while (count++ < 16); + return 0; +} + +void show_trace_task(struct task_struct *p) +{ + unsigned long ip, sp; + unsigned long stack_page = (unsigned long)p; + int count = 0; + + if (!p) + return; + + printk("Call Trace: "); + sp = p->thread.ksp; + do { + sp = *(unsigned long *)sp; + if (sp < (stack_page + sizeof(struct task_struct)) || + sp >= (stack_page + (4 * PAGE_SIZE))) + break; + if (count > 0) { + ip = *(unsigned long *)(sp + 16); + printk("[%016lx] ", ip); + } + } while (count++ < 16); + printk("\n"); +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/prom.c linuxppc64_2_4/arch/ppc64/kernel/prom.c --- linux-2.4.5-ac18/arch/ppc64/kernel/prom.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/prom.c Wed Aug 1 15:45:52 2001 @@ -0,0 +1,2384 @@ +/* + * + * + * Procedures for interfacing to Open Firmware. + * + * Paul Mackerras August 1996. + * Copyright (C) 1996 Paul Mackerras. + * + * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner. + * {engebret|bergner}@us.ibm.com + * + * 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. + */ + +#if 0 +#define DEBUG_YABOOT +#endif + +#if 0 +#define DEBUG_PROM +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG_YABOOT +#define call_yaboot(FUNC,...) \ + do { \ + RELOC(function32b_ptr) = (unsigned long)(FUNC); \ + call_function32b_ptr(__VA_ARGS__); \ + } while(0) +#else +#define call_yaboot(FUNC,...) do { ; } while(0) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "open_pic.h" +#include + +#ifdef CONFIG_FB +#include +#endif + +/* + * prom_init() is called very early on, before the kernel text + * and data have been mapped to KERNELBASE. At this point the code + * is running at whatever address it has been loaded at, so + * references to extern and static variables must be relocated + * explicitly. The procedure reloc_offset() returns the address + * we're currently running at minus the address we were linked at. + * (Note that strings count as static variables.) + * + * Because OF may have mapped I/O devices into the area starting at + * KERNELBASE, particularly on CHRP machines, we can't safely call + * OF once the kernel has been mapped to KERNELBASE. Therefore all + * OF calls should be done within prom_init(), and prom_init() + * and all routines called within it must be careful to relocate + * references as necessary. + * + * Note that the bss is cleared *after* prom_init runs, so we have + * to make sure that any static or extern variables it accesses + * are put in the data segment. + */ + + +#define PROM_BUG() do { \ + prom_print(RELOC("kernel BUG at ")); \ + prom_print(RELOC(__FILE__)); \ + prom_print(RELOC(":")); \ + prom_print_hex(__LINE__); \ + prom_print(RELOC("!\n")); \ + __asm__ __volatile__(".long " BUG_ILLEGAL_INSTR); \ +} while (0) + +/* + * Properties whose value is longer than this get excluded from our + * copy of the device tree. This way we don't waste space storing + * things like "driver,AAPL,MacOS,PowerPC" properties. + */ +/* #define MAX_PROPERTY_LENGTH 1024 --NO! interrupt-map props can get large. TAI */ + + + +struct pci_reg_property { + struct pci_address addr; + u32 size_hi; + u32 size_lo; +}; + + +struct isa_reg_property { + u32 space; + u32 address; + u32 size; +}; + +struct pci_intr_map { + struct pci_address addr; + u32 dunno; + phandle int_ctrler; + u32 intr; +}; + + +typedef unsigned long interpret_func(struct device_node *, unsigned long, + int, int); +#if 0 +static interpret_func interpret_pci_props; +#endif +static unsigned long interpret_pci_props(struct device_node *, unsigned long, + int, int); + +static interpret_func interpret_dbdma_props; +static interpret_func interpret_isa_props; +static interpret_func interpret_macio_props; +static interpret_func interpret_root_props; + +#ifndef FB_MAX /* avoid pulling in all of the fb stuff */ +#define FB_MAX 8 +#endif + + +struct prom_t prom_data = { 0, 0, 0, 0, { 0, 0, 0, {0}, NULL} +#ifdef DEBUG_YABOOT + , NULL +#endif +}; + +char *prom_display_paths[FB_MAX] __initdata = { 0, }; +unsigned int prom_num_displays = 0; +char *of_stdout_device = 0; + +extern struct rtas_t rtas_data; +extern char *klimit; +extern struct Naca *naca; + +#define MAX_PHB 16 * 3 // 16 Towers * 3 PHBs/tower +struct _of_tce_table of_tce_table[MAX_PHB + 1] = {{0, 0, 0}}; + +char *bootpath = 0; +char *bootdevice = 0; + +/* Set for a newworld machine */ +int use_of_interrupt_tree = 0; +int pmac_newworld = 0; + +struct device_node *allnodes = 0; + +static unsigned long call_prom(const char *service, int nargs, int nret, ...); +static void prom_exit(void); +static unsigned long copy_device_tree(unsigned long, unsigned long); +static unsigned long inspect_node(phandle, struct device_node *, unsigned long, + unsigned long, struct device_node ***); +static unsigned long finish_node(struct device_node *, unsigned long, + interpret_func *, int, int); +static unsigned long finish_node_interrupts(struct device_node *, unsigned long); +static unsigned long check_display(unsigned long); +static int prom_next_node(phandle *); + +extern unsigned long reloc_offset(void); + +unsigned long function32b_ptr = 0; +extern void call_function32b_ptr(); +extern void call_function64b_ptr(); + +void cacheable_memzero(void *, unsigned int); + +extern char cmd_line[512]; /* XXX */ +unsigned long dev_tree_size; + +char testString[] = "LINUX\n"; + +/* This is the one and *ONLY* place where we actually call open + * firmware from, since we need to make sure we're running in 32b + * mode when we do. We switch back to 64b mode upon return. + */ + +__init +static unsigned long +call_prom(const char *service, int nargs, int nret, ...) +{ + int i; + unsigned long offset = reloc_offset(); + struct prom_t *prom = PTRRELOC(&prom_data); + va_list list; + + prom->args.service = (u32)LONG_LSW(service); + prom->args.nargs = nargs; + prom->args.nret = nret; + prom->args.rets = (prom_arg_t *)&(prom->args.args[nargs]); + + va_start(list, nret); + for (i = 0; i < nargs; ++i) + prom->args.args[i] = (prom_arg_t)LONG_LSW(va_arg(list, unsigned long)); + va_end(list); + + for (i = 0; i < nret; ++i) + prom->args.rets[i] = 0; + + RELOC(function32b_ptr) = (unsigned long)prom->entry; + call_function32b_ptr((unsigned long)&prom->args); + + return (unsigned long)((nret > 0) ? prom->args.rets[0] : 0); +} + + +__init +static void +prom_exit() +{ + unsigned long offset = reloc_offset(); + + call_prom(RELOC("exit"), 0, 0); + + for (;;) /* should never get here */ + ; +} + +__init +void +prom_enter(void) +{ + unsigned long offset = reloc_offset(); + + call_prom(RELOC("enter"), 0, 0); +} + + +__init +void +prom_print(const char *msg) +{ + const char *p, *q; + unsigned long offset = reloc_offset(); + struct prom_t *prom = PTRRELOC(&prom_data); + + if (prom->stdout == 0) + return; + + for (p = msg; *p != 0; p = q) { + for (q = p; *q != 0 && *q != '\n'; ++q) + ; + if (q > p) + call_prom(RELOC("write"), 3, 1, prom->stdout, + p, q - p); + if (*q != 0) { + ++q; + call_prom(RELOC("write"), 3, 1, prom->stdout, + RELOC("\r\n"), 2); + } + } +} + +void +prom_print_hex(unsigned long val) +{ + int i, nibbles = sizeof(val)*2; + char buf[sizeof(val)*2+1]; + + for (i = nibbles-1; i >= 0; i--) { + buf[i] = (val & 0xf) + '0'; + if (buf[i] > '9') + buf[i] += ('a'-'0'-10); + val >>= 4; + } + buf[nibbles] = '\0'; + prom_print(buf); +} + +void +prom_print_nl(void) +{ + unsigned long offset = reloc_offset(); + prom_print(RELOC("\n")); +} + + +static void +prom_get_naca_info(struct Naca *naca) +{ + phandle node; + char type[64]; + unsigned long num_cpus = 0; + unsigned long mem_idx = 0; + unsigned long i; + struct { + u64 base; + u64 size; + } mem_mods[16]; + unsigned long offset = reloc_offset(); + ihandle root; + u32 size_cells = 1; + +#ifdef DEBUG_PROM + prom_print(RELOC("prom_get_naca_info: start...\n")); +#endif + + root = call_prom(RELOC("finddevice"), 1, 1, RELOC("/")); + if (root != (ihandle)-1) { + call_prom(RELOC("getprop"), 4, 1, + root, RELOC("#size-cells"), + &size_cells, sizeof(size_cells)); + } + + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"), + type, sizeof(type)); + +#ifdef DEBUG_PROM + prom_print(RELOC(" device_type = ")); + prom_print(type); + prom_print_nl(); +#endif + if (!strcmp(type, RELOC("cpu"))) { + num_cpus += 1; + +#ifdef DEBUG_PROM + prom_print(RELOC("prom_get_naca_info: loc 1\n")); +#endif + + // We're assuming *all* of the CPUs have the same + // d-cache and i-cache sizes... -Peter + if ( num_cpus == 1 ) { + u32 size; + +#ifdef DEBUG_PROM + prom_print(RELOC("prom_get_naca_info: loc 2\n")); +#endif + call_prom(RELOC("getprop"), 4, 1, node, RELOC("d-cache-line-size"), + &size, sizeof(size)); + +#ifdef DEBUG_PROM + prom_print(RELOC("prom_get_naca_info: loc 3\n")); +#endif + naca->dCacheL1LineSize = size; + naca->dCacheL1LogLineSize = __ilog2(size); + naca->dCacheL1LinesPerPage = PAGE_SIZE / size; +#ifdef DEBUG_PROM + prom_print(RELOC("prom_get_naca_info: loc 4\n")); +#endif + + call_prom(RELOC("getprop"), 4, 1, node, RELOC("i-cache-line-size"), + &size, sizeof(size)); + +#ifdef DEBUG_PROM + prom_print(RELOC("prom_get_naca_info: loc 5\n")); +#endif + naca->iCacheL1LineSize = size; + naca->iCacheL1LogLineSize = __ilog2(size); + naca->iCacheL1LinesPerPage = PAGE_SIZE / size; + } + } else if (!strcmp(type, RELOC("memory"))) { + union { + struct { + u32 base; + u32 size; + } addr32; + struct { + u64 base; + u64 size; + } addr64; + } memory; + +#ifdef DEBUG_PROM + prom_print(RELOC("prom_get_naca_info: loc 6\n")); +#endif + call_prom(RELOC("getprop"), 4, 1, node, RELOC("reg"), + &memory, sizeof(memory)); +#ifdef DEBUG_PROM + prom_print(RELOC("prom_get_naca_info: loc 7\n")); +#endif + + switch (size_cells) { + case 1: +#ifdef DEBUG_PROM + prom_print(RELOC("prom_get_naca_info: loc 7a\n")); +#endif + if ( (mem_idx > 0) && + ( (mem_mods[mem_idx-1].base + + mem_mods[mem_idx-1].size) == + memory.addr32.base)) { + mem_mods[mem_idx-1].size += memory.addr32.size; + } else { + mem_mods[mem_idx].base = memory.addr32.base; + mem_mods[mem_idx].size = memory.addr32.size; + mem_idx++; + } + break; + case 2: +#ifdef DEBUG_PROM + prom_print(RELOC("prom_get_naca_info: loc 7b\n")); +#endif + if ( (mem_idx > 0) && + ( (mem_mods[mem_idx-1].base + + mem_mods[mem_idx-1].size) == + memory.addr64.base)) { + mem_mods[mem_idx-1].size += memory.addr64.size; + } else { + mem_mods[mem_idx].base = memory.addr64.base; + mem_mods[mem_idx].size = memory.addr64.size; + mem_idx++; + } + break; + default: + PROM_BUG(); + } + +#ifdef DEBUG_PROM + prom_print(RELOC("prom_get_naca_info: loc 7c\n")); +#endif +#if 1 + } else if (!strcmp(type, RELOC("serial"))) { + phandle isa, pci; + struct isa_reg_property reg; + union pci_range ranges; + +#ifdef DEBUG_PROM + prom_print(RELOC("SERIAL: loc 1\n")); +#endif + type[0] = 0; + call_prom(RELOC("getprop"), 4, 1, node, + RELOC("ibm,aix-loc"), type, sizeof(type)); + +#ifdef DEBUG_PROM + prom_print(RELOC("SERIAL: str='")); + prom_print(type); + prom_print(RELOC("'\n")); +#endif + if (strcmp(type, RELOC("S1"))) + continue; + +#ifdef DEBUG_PROM + prom_print(RELOC("SERIAL: loc 3\n")); +#endif + call_prom(RELOC("getprop"), 4, 1, node, RELOC("reg"), + ®, sizeof(reg)); + + isa = call_prom(RELOC("parent"), 1, 1, node); + if (!isa) + PROM_BUG(); + pci = call_prom(RELOC("parent"), 1, 1, isa); + if (!pci) + PROM_BUG(); + + call_prom(RELOC("getprop"), 4, 1, pci, RELOC("ranges"), + &ranges, sizeof(ranges)); + +#ifdef DEBUG_PROM + prom_print(RELOC("SERIAL: loc 4\n")); +#endif + if ( size_cells == 1 ) + naca->serialPortAddr = ranges.pci32.phys+reg.address; + else { + naca->serialPortAddr = + ((((unsigned long)ranges.pci64.phys_hi) << 32) | + (ranges.pci64.phys_lo)) + + reg.address; + } +#ifdef DEBUG_PROM + prom_print(RELOC("SERIAL: loc 5\n")); +#endif +#endif + } + } + + naca->interrupt_controller = IC_INVALID; + naca->io_subsystem = IOS_INVALID; + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("name"), + type, sizeof(type)); + if (strcmp(type, RELOC("interrupt-controller"))) { + continue; + } + call_prom(RELOC("getprop"), 4, 1, node, RELOC("compatible"), + type, sizeof(type)); + if (strstr(type, RELOC("open-pic"))) { + naca->interrupt_controller = IC_OPEN_PIC; + naca->io_subsystem = IOS_OPEN_PIC; + } else if (strstr(type, RELOC("ppc-xicp"))) { + naca->interrupt_controller = IC_PPC_XIC; + naca->io_subsystem = IOS_PPC_XIC; + } else { + prom_print(RELOC("prom: failed to recognize interrupt-controller\n")); + } + break; + } + + if(naca->interrupt_controller == IC_INVALID) { + prom_print(RELOC("prom: failed to find interrupt-controller\n")); + PROM_BUG(); + } + + +#ifdef DEBUG_PROM + prom_print(RELOC("prom_get_naca_info: loc 8\n")); +#endif + // We gotta have at least 1 cpu... + if ( (naca->processorCount = num_cpus) < 1 ) + PROM_BUG(); + +#ifdef DEBUG_PROM + // Dunno what to do if mem_idx > 0 ... -Peter + prom_print(RELOC("prom_get_naca_info: mem_idx =")); + prom_print_hex(naca->physicalMemorySize); + prom_print_nl(); +#endif + + for(i=0; i <= mem_idx ;i++) { + if ( mem_mods[i].base == 0 ) { + naca->physicalMemorySize = mem_mods[i].size; + } + } + +#ifdef DEBUG_PROM + prom_print(RELOC("initial: naca->physicalMemorySize = 0x")); + prom_print_hex(naca->physicalMemorySize); + prom_print_nl(); +#endif + + // Limit of 3G for now... DRENG / PPPBBB + if( naca->physicalMemorySize > (3UL << 30) ) + naca->physicalMemorySize = (3UL << 30); + // naca->physicalMemorySize = (8UL << 30); + +#ifdef DEBUG_PROM + prom_print(RELOC("modified: naca->physicalMemorySize = 0x")); + prom_print_hex(naca->physicalMemorySize); + prom_print_nl(); + + prom_print(RELOC(" naca->serialPortAddr = 0x")); + prom_print_hex(naca->serialPortAddr); + prom_print_nl(); +#endif + + /* + * Hardcode to GP size. I am not sure where to get this info + * in general, as there does not appear to be a slb-size OF + * entry. At least in Condor and earlier. DRENG + */ + naca->slb_size = 64; + +#ifdef DEBUG_PROM + prom_print(RELOC("prom_get_naca_info: end...\n")); +#endif +} + +unsigned long prom_strtoul(const char *cp) +{ + unsigned long result = 0,value; + + while (*cp) { + value = *cp-'0'; + result = result*10 + value; + cp++; + } + + return result; +} + +unsigned long prom_initialize_tce_table(unsigned long mem) { + phandle node; + char compatible[64], path[64], type[64]; + unsigned long table = 0; + unsigned long base, align; + unsigned int minalign, minsize; + unsigned long offset = reloc_offset(); + struct _of_tce_table *prom_tce_table = RELOC(of_tce_table); + ihandle phb_node; + unsigned long tce_entry, *tce_entryp, i; + struct Naca *prom_naca = RELOC(naca); + unsigned long mem_top; + + mem_top = prom_naca->physicalMemorySize; + +#ifdef DEBUG_PROM + prom_print(RELOC("starting prom_initialize_tce_table\n")); +#endif + + /* Search all nodes looking for PHBs. */ + for (node = 0; prom_next_node(&node); ) { + compatible[0] = 0; + type[0] = 0; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("compatible"), + compatible, sizeof(compatible)); + call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"), + type, sizeof(type)); + + if((compatible[0] == 0) || + ((strstr(compatible, RELOC("python")) == NULL) && + (strstr(compatible, RELOC("Speedwagon")) == NULL))) { + continue; + } + if((type[0] == 0) || (strstr(type, RELOC("pci")) == NULL)) { + continue; + } + + if(call_prom(RELOC("getprop"), 4, 1, node, + RELOC("tce-table-minalign"), &minalign, + sizeof(minalign)) < 0) { + minalign = 0; + } + + if(call_prom(RELOC("getprop"), 4, 1, node, + RELOC("tce-table-minsize"), &minsize, + sizeof(minsize)) < 0) { + minsize = 4UL << 20; + } + + /* Even though we read what OF wants, we just set the table size to + * 4 MB. This is enough to map 2GB of PCI DMA space. By doing this, + * we avoid the pitfalls of trying to DMA to MMIO space and the DMA + * alias hole. + */ + minsize = 4UL << 20; + + /* Align to the greater of the align or size */ + if(minalign < minsize) { + align = minsize; + } else { + align = minalign; + } + + /* Carve out storage for the TCE table. */ + mem_top -= minsize; + mem_top &= (~(align - 1)); + base = mem_top; + + /* Save away the TCE table attributes for later use. */ + prom_tce_table[table].node = node; + prom_tce_table[table].base = base + KERNELBASE; + prom_tce_table[table].size = minsize; + + prom_print(RELOC("TCE table: 0x")); + prom_print_hex(table); + prom_print_nl(); + + prom_print(RELOC("\tnode = 0x")); + prom_print_hex(prom_tce_table[table].node); + prom_print_nl(); + + prom_print(RELOC("\tbase = 0x")); + prom_print_hex(prom_tce_table[table].base); + prom_print_nl(); + + prom_print(RELOC("\tsize = 0x")); + prom_print_hex(prom_tce_table[table].size); + prom_print_nl(); + + /* Initialize the table to have a one-to-one mapping over the allocated size. */ + for(tce_entryp = prom_tce_table[table].base-KERNELBASE, i = 0; + i < (prom_tce_table[table].size >> 3); + tce_entryp++, i++) { + tce_entry = (i << PAGE_SHIFT); + tce_entry |= 0x3; + *tce_entryp = tce_entry; + } + + /* Call OF to setup the TCE hardware */ + if(call_prom(RELOC("package-to-path"), 3, 1, node, + path, 255) <= 0) { + prom_print(RELOC("package-to-path failed\n")); + } else { + prom_print(RELOC("opened ")); + prom_print(path); + prom_print_nl(); + } + + if((phb_node = call_prom(RELOC("open"), 1, 1, path)) <= 0) { + prom_print(RELOC("open failed\n")); + } + call_prom(RELOC("call-method"), 6, 0, + RELOC("set-64-bit-addressing"), + phb_node, -1, (prom_tce_table[table].size), + (prom_tce_table[table].base - KERNELBASE) + & 0xffffffff, + ((prom_tce_table[table].base - KERNELBASE) >> 32) + & 0xffffffff); + call_prom(RELOC("close"), 1, 0, phb_node); + + table++; + } + + /* Flag the first invalid entry */ + prom_tce_table[table].node = 0; + + return(mem); +} + +/* + * With CHRP SMP we need to use the OF to start the other + * processors so we can't wait until smp_boot_cpus (the OF is + * trashed by then) so we have to put the processors into + * a holding pattern controlled by the kernel (not OF) before + * we destroy the OF. + * + * This uses a chunk of low memory, puts some holding pattern + * code there and sends the other processors off to there until + * smp_boot_cpus tells them to do something. The holding pattern + * checks that address until its cpu # is there, when it is that + * cpu jumps to __secondary_start(). smp_boot_cpus() takes care + * of setting those values. + * + * We also use physical address 0x4 here to tell when a cpu + * is in its holding pattern code. + * + * Fixup comment... DRENG / PPPBBB - Peter + * + * -- Cort + */ +static void +prom_hold_cpus(unsigned long mem) +{ + unsigned long i; + int cpu; + unsigned int reg; + phandle node; + unsigned long offset = reloc_offset(); + char type[64], *path; + int cpuid = -1; + extern void __secondary_hold(void); + extern unsigned long __secondary_hold_spinloop; + extern unsigned long __secondary_hold_acknowledge; + unsigned long *spinloop = virt_to_phys(&__secondary_hold_spinloop); + unsigned long *acknowledge = virt_to_phys(&__secondary_hold_acknowledge); + // unsigned int secondary_hold = virt_to_phys(*((unsigned long *)__secondary_hold)); + unsigned int secondary_hold = virt_to_phys(*PTRRELOC((unsigned long *)__secondary_hold)); + // unsigned int secondary_hold = 0x60; + struct Naca *prom_naca = RELOC(naca); + + /* Initially, we must have one active CPU. */ + prom_naca->processorCount = 1; + + // #ifdef DEBUG_PROM +#ifdef DEBUG_PROM + prom_print(RELOC("prom_hold_cpus: start...\n")); + prom_print(RELOC(" 1) spinloop = 0x")); + prom_print_hex(spinloop); + prom_print_nl(); + prom_print(RELOC(" 1) *spinloop = 0x")); + prom_print_hex(*spinloop); + prom_print_nl(); + prom_print(RELOC(" 1) acknowledge = 0x")); + prom_print_hex(acknowledge); + prom_print_nl(); + prom_print(RELOC(" 1) *acknowledge = 0x")); + prom_print_hex(*acknowledge); + prom_print_nl(); + prom_print(RELOC(" 1) secondary_hold = 0x")); + prom_print_hex(secondary_hold); + prom_print_nl(); +#endif + + /* Set the common spinloop variable, so all of the secondary cpus + * will block when they are awakened from their OF spinloop. + * This must occur for both SMP and non SMP kernels, since OF will + * be trashed when we move the kernel. + */ + *spinloop = 0; + +#ifdef DEBUG_PROM + prom_print(RELOC(" 2) spinloop = 0x")); + prom_print_hex(spinloop); + prom_print_nl(); + prom_print(RELOC(" 2) *spinloop = 0x")); + prom_print_hex(*spinloop); + prom_print_nl(); + prom_print(RELOC(" 2) acknowledge = 0x")); + prom_print_hex(acknowledge); + prom_print_nl(); + prom_print(RELOC(" 2) *acknowledge = 0x")); + prom_print_hex(*acknowledge); + prom_print_nl(); + prom_print(RELOC(" 2) secondary_hold = 0x")); + prom_print_hex(secondary_hold); + prom_print_nl(); +#endif + /* look for cpus */ + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"), + type, sizeof(type)); + if (strcmp(type, RELOC("cpu")) != 0) + continue; + + // Skip non-configured cpus. + call_prom(RELOC("getprop"), 4, 1, node, RELOC("status"), + type, sizeof(type)); + if (strcmp(type, RELOC("okay")) != 0) + continue; + + path = (char *) mem; + memset(path, 0, 256); + if ((long) call_prom(RELOC("package-to-path"), 3, 1, + node, path, 255) < 0) + continue; + reg = -1; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("reg"), + ®, sizeof(reg)); + + cpuid++; + cpu = cpuid; +#ifdef DEBUG_PROM + prom_print_nl(); + prom_print(RELOC("cpuid = 0x")); + prom_print_hex(cpuid); + prom_print_nl(); + prom_print(RELOC("cpu = 0x")); + prom_print_hex(cpu); + prom_print_nl(); + prom_print(RELOC("reg = 0x")); + prom_print_hex(reg); + prom_print_nl(); +#endif +#ifdef CONFIG_SMP + RELOC(smp_hw_index)[cpuid] = reg; +#endif + /* XXX: hack - don't start cpu 0, this cpu -- Cort */ + if (cpu == 0) + continue; + + prom_print(RELOC("starting cpu ")); + prom_print(path); + + /* Init the acknowledge var which will be reset by + * the secondary cpu when it awakens from its OF + * spinloop. + */ + *acknowledge = (unsigned long)-1; + +#ifdef DEBUG_PROM + prom_print_nl(); + prom_print(RELOC("cpuid = 0x")); + prom_print_hex(cpuid); + prom_print_nl(); + prom_print(RELOC("reg = 0x")); + prom_print_hex(reg); + prom_print_nl(); +#endif + // #ifdef DEBUG_PROM +#ifdef DEBUG_PROM + prom_print(RELOC(" 3) spinloop = 0x")); + prom_print_hex(spinloop); + prom_print_nl(); + prom_print(RELOC(" 3) *spinloop = 0x")); + prom_print_hex(*spinloop); + prom_print_nl(); + prom_print(RELOC(" 3) acknowledge = 0x")); + prom_print_hex(acknowledge); + prom_print_nl(); + prom_print(RELOC(" 3) *acknowledge = 0x")); + prom_print_hex(*acknowledge); + prom_print_nl(); + prom_print(RELOC(" 3) secondary_hold = 0x")); + prom_print_hex(secondary_hold); + prom_print_nl(); + prom_print(RELOC(" 3) cpuid = 0x")); + prom_print_hex(cpuid); + prom_print_nl(); +#endif + call_prom(RELOC("start-cpu"), 3, 0, node, secondary_hold, cpuid); + prom_print(RELOC("...")); + for ( i = 0 ; (i < 100000000) && + (*acknowledge == ((unsigned long)-1)); i++ ) ; +#ifdef DEBUG_PROM + { + unsigned long *p = 0x0; + prom_print(RELOC(" 4) 0x0 = 0x")); + prom_print_hex(*p); + prom_print_nl(); + } +#endif + if (*acknowledge == cpuid) { + prom_print(RELOC("ok\n")); + /* Set the number of active processors. */ + prom_naca->processorCount++; + } else { + prom_print(RELOC("failed: ")); + prom_print_hex(*acknowledge); + prom_print_nl(); + } + } + + +#ifdef DEBUG_PROM + prom_print(RELOC("prom_hold_cpus: end...")); +#endif +} + + +/* + * We enter here early on, when the Open Firmware prom is still + * handling exceptions and the MMU hash table for us. + */ + +__init +unsigned long +prom_init(unsigned long r3, unsigned long r4, unsigned long pp, + unsigned long r6, unsigned long r7, yaboot_debug_t *yaboot) +{ + int chrp = 0; + unsigned long mem; + ihandle prom_rtas, prom_mmu, prom_op; + unsigned long offset = reloc_offset(); + unsigned long msr = get_msr(); + #ifdef DEBUB_PROM // -aglitke + unsigned long sp = get_sp(); + #endif + long l; + char *p, *d; + int prom_version = 0; + unsigned long phys; + int retval; + u32 getprop_rval; + struct Naca *prom_naca = RELOC(naca); + struct prom_t *prom = PTRRELOC(&prom_data); + struct rtas_t *rtas = PTRRELOC(&rtas_data); + + /* Get a handle to the prom entry point before anything else */ + prom->entry = pp; + +#ifdef DEBUG_YABOOT + call_yaboot(yaboot->dummy); + call_yaboot(yaboot->printf, RELOC("offset = 0x%08x%08x\n"), LONG_MSW(offset), LONG_LSW(offset)); + call_yaboot(yaboot->printf, RELOC("msr = 0x%08x%08x\n"), LONG_MSW(msr), LONG_LSW(msr)); + // call_yaboot(yaboot->printf, RELOC("sp = 0x%08x%08x\n"), LONG_MSW(sp), LONG_LSW(sp)); +#endif + + /* Default */ + phys = KERNELBASE - offset; + +#ifdef DEBUG_YABOOT + call_yaboot(yaboot->printf, RELOC("phys = 0x%08x%08x\n"), LONG_MSW(phys), LONG_LSW(phys)); +#endif + + +#ifdef DEBUG_YABOOT + prom->yaboot = yaboot; + call_yaboot(yaboot->printf, RELOC("pp = 0x%08x%08x\n"), LONG_MSW(pp), LONG_LSW(pp)); + call_yaboot(yaboot->printf, RELOC("prom = 0x%08x%08x\n"), LONG_MSW(prom->entry), LONG_LSW(prom->entry)); +#endif + + /* First get a handle for the stdout device */ + prom->chosen = (ihandle)call_prom(RELOC("finddevice"), 1, 1, + RELOC("/chosen")); + +#ifdef DEBUG_YABOOT + call_yaboot(yaboot->printf, RELOC("prom->chosen = 0x%08x%08x\n"), LONG_MSW(prom->chosen), LONG_LSW(prom->chosen)); +#endif + + msr = get_msr(); + +#ifdef DEBUG_YABOOT + call_yaboot(yaboot->printf, RELOC("msr = 0x%08x%08x\n"), LONG_MSW(msr), LONG_LSW(msr)); +#endif + + if ((long)prom->chosen <= 0) + prom_exit(); + + if ((long)call_prom(RELOC("getprop"), 4, 1, prom->chosen, + RELOC("stdout"), &getprop_rval, + sizeof(prom->stdout)) <= 0) + prom_exit(); + + prom->stdout = getprop_rval; + +#ifdef DEBUG_YABOOT + if (prom->stdout == 0) { + call_yaboot(yaboot->printf, RELOC("prom->stdout = 0x%08x%08x\n"), LONG_MSW(prom->stdout), LONG_LSW(prom->stdout)); + } + + call_yaboot(yaboot->printf, RELOC("prom->stdout = 0x%08x%08x\n"), LONG_MSW(prom->stdout), LONG_LSW(prom->stdout)); +#endif + +#ifdef DEBUG_YABOOT + msr = get_msr(); + call_yaboot(yaboot->printf, RELOC("msr = 0x%08x%08x\n"), LONG_MSW(msr), LONG_LSW(msr)); + + call_yaboot(yaboot->printf, RELOC("Location: 0x11\n")); +#endif + + /* Get the full OF pathname of the stdout device */ + mem = (unsigned long) RELOC(klimit) - offset; + +#ifdef DEBUG_PROM + prom_print(RELOC("klimit = ")); + prom_print_hex(RELOC(klimit)); + prom_print_nl(); + prom_print(RELOC("offset = ")); + prom_print_hex(offset); + prom_print_nl(); + prom_print(RELOC("mem = ")); + prom_print_hex(mem); + prom_print_nl(); +#endif + + p = (char *) mem; + memset(p, 0, 256); + call_prom(RELOC("instance-to-path"), 3, 1, prom->stdout, p, 255); + RELOC(of_stdout_device) = PTRUNRELOC(p); + mem += strlen(p) + 1; + + /* Find the OF version */ + prom_op = call_prom(RELOC("finddevice"), 1, 1, RELOC("/openprom")); + prom_version = 0; + if (prom_op != (ihandle)-1) { + char model[64]; + long sz; + sz = (long)call_prom(RELOC("getprop"), 4, 1, prom_op, + RELOC("model"), model, 64); + if (sz > 0) { + char *c; + /* hack to skip the ibm chrp firmware # */ + if ( strncmp(model,RELOC("IBM"),3) ) { + for (c = model; *c; c++) + if (*c >= '0' && *c <= '9') { + prom_version = *c - '0'; + break; + } + } + else + chrp = 1; + } + } + if (prom_version >= 3) + prom_print(RELOC("OF Version 3 detected.\n")); + + /* Get the boot device and translate it to a full OF pathname. */ + p = (char *) mem; + l = (long) call_prom(RELOC("getprop"), 4, 1, prom->chosen, + RELOC("bootpath"), p, 1<<20); + if (l > 0) { + p[l] = 0; /* should already be null-terminated */ + RELOC(bootpath) = PTRUNRELOC(p); + mem += l + 1; + d = (char *) mem; + *d = 0; + call_prom(RELOC("canon"), 3, 1, p, d, 1<<20); + RELOC(bootdevice) = PTRUNRELOC(d); + mem = DOUBLEWORD_ALIGN(mem + strlen(d) + 1); + } + +#ifdef DEBUG_PROM + prom_print(RELOC("Find RTAS...\n")); +#endif + prom_rtas = call_prom(RELOC("finddevice"), 1, 1, RELOC("/rtas")); + if (prom_rtas != (ihandle) -1) { + call_prom(RELOC("getprop"), + 4, 1, prom_rtas, + RELOC("rtas-size"), + &getprop_rval, + sizeof(getprop_rval)); + rtas->size = getprop_rval; + prom_print(RELOC("instantiating rtas")); + if (rtas->size != 0) { + /* + * Ask OF for some space for RTAS. + * Actually OF has bugs so we just arbitrarily + * use memory at the 6MB point. + */ + // The new code... + mem = PAGE_ALIGN(mem); + rtas->base = mem + offset - KERNELBASE; + + mem += rtas->size; + prom_print(RELOC(" at 0x")); + prom_print_hex(rtas->base); + + rtas->event_scan.log = mem + offset; + call_prom(RELOC("getprop"), + 4, 1, prom_rtas, + RELOC("rtas-error-log-max"), + &getprop_rval, + sizeof(getprop_rval)); + rtas->event_scan.log_size = getprop_rval; + mem += getprop_rval; + + call_prom(RELOC("getprop"), + 4, 1, prom_rtas, + RELOC("rtas-event-scan-rate"), + &getprop_rval, + sizeof(getprop_rval)); + rtas->event_scan.rate = getprop_rval; + + prom_rtas = call_prom(RELOC("open"), + 1, 1, + RELOC("/rtas")); + prom_print(RELOC("...")); + + retval = (long)call_prom(RELOC("call-method"), 3, 2, + RELOC("instantiate-rtas"), + prom_rtas, + rtas->base); + + rtas->entry = (long)prom->args.rets[1]; + } + + if(rtas->entry <= 0) { + prom_print(RELOC(" failed\n")); + } else { + prom_print(RELOC(" done\n")); + } +#ifdef DEBUG_PROM + prom_print(RELOC("rtas->base = 0x")); + prom_print_hex(rtas->base); + prom_print_nl(); + prom_print(RELOC("rtas->entry = 0x")); + prom_print_hex(rtas->entry); + prom_print_nl(); + prom_print(RELOC("rtas->size = 0x")); + prom_print_hex(rtas->size); + prom_print_nl(); + prom_print(RELOC("rtas->event_scan.rate = 0x")); + prom_print_hex(rtas->event_scan.rate); + prom_print_nl(); + prom_print(RELOC("rtas->event_scan.log = 0x")); + prom_print_hex(rtas->event_scan.log); + prom_print_nl(); + prom_print(RELOC("rtas->event_scan.log_size = 0x")); + prom_print_hex(rtas->event_scan.log_size); + prom_print_nl(); +#endif + } + + /* Initialize some system info into the Naca early... */ + prom_get_naca_info(prom_naca); + +#ifdef DEBUG_PROM + prom_print(RELOC("naca->processorCount = 0x")); + prom_print_hex(prom_naca->processorCount); + prom_print_nl(); + + prom_print(RELOC("naca->physicalMemorySize = 0x")); + prom_print_hex(prom_naca->physicalMemorySize); + prom_print_nl(); + + prom_print(RELOC("naca->dCacheL1LineSize = 0x")); + prom_print_hex(prom_naca->dCacheL1LineSize); + prom_print_nl(); + + prom_print(RELOC("naca->dCacheL1LogLineSize = 0x")); + prom_print_hex(prom_naca->dCacheL1LogLineSize); + prom_print_nl(); + + prom_print(RELOC("naca->dCacheL1LinesPerPage = 0x")); + prom_print_hex(prom_naca->dCacheL1LinesPerPage); + prom_print_nl(); + + prom_print(RELOC("naca->iCacheL1LineSize = 0x")); + prom_print_hex(prom_naca->iCacheL1LineSize); + prom_print_nl(); + + prom_print(RELOC("naca->iCacheL1LogLineSize = 0x")); + prom_print_hex(prom_naca->iCacheL1LogLineSize); + prom_print_nl(); + + prom_print(RELOC("naca->iCacheL1LinesPerPage = 0x")); + prom_print_hex(prom_naca->iCacheL1LinesPerPage); + prom_print_nl(); + + prom_print(RELOC("naca->serialPortAddr = 0x")); + prom_print_hex(prom_naca->serialPortAddr); + prom_print_nl(); + + prom_print(RELOC("naca->interrupt_controller = 0x")); + prom_print_hex(prom_naca->interrupt_controller); + prom_print_nl(); +#endif + + /* If we are on an SMP machine, then we *MUST* do the + * following, regardless of whether we have an SMP + * kernel or not. + */ + if ( prom_naca->processorCount > 1 ) + prom_hold_cpus(mem); + + mem = check_display(mem); + +#ifdef DEBUG_PROM + prom_print(RELOC("copying OF device tree...")); +#endif + mem = copy_device_tree(mem, mem + (8<<20)); + + mem = prom_initialize_tce_table(mem); + + RELOC(klimit) = (char *) (mem + offset); + + /* If we are already running at 0xc0000000, we assume we were loaded by + * an OF bootloader which did set a BAT for us. This breaks OF translate + * so we force phys to be 0 + */ + if (offset == 0) + phys = 0; + else { + if ((long) call_prom(RELOC("getprop"), 4, 1, prom->chosen, + RELOC("mmu"), &RELOC(prom_mmu), sizeof(prom_mmu)) <= 0) { + prom_print(RELOC(" no MMU found\n")); + } else { + long retval; + + /* We assume the phys. address size is 3 cells */ + RELOC(prom_mmu) = *(u32 *)&RELOC(prom_mmu); + retval = (long)call_prom(RELOC("call-method"), 4, 4, + RELOC("translate"), + prom_mmu, + (void *)(KERNELBASE - offset), + (void *)1); + if ( retval != 0 ) { +#if 0 + if ((long)call_prom(RELOC("call-method"), 4, 4, + RELOC("translate"), + prom_mmu, + (void *)(KERNELBASE - offset), + (void *)1) != 0) { +#endif + prom_print(RELOC(" (translate failed) ")); + } else { + prom_print(RELOC(" (translate ok) ")); + phys = (unsigned long)prom->args.rets[3]; + } + } + } + + /* If OpenFirmware version >= 3, then use quiesce call */ + if (prom_version >= 3) { + prom_print(RELOC("Calling quiesce ...\n")); + call_prom(RELOC("quiesce"), 0, 0); + offset = reloc_offset(); + phys = KERNELBASE - offset; + } + +#ifdef DEBUG_PROM + msr = get_msr(); + prom_print_hex(LONG_MSW(msr)); + prom_print_hex(LONG_LSW(msr)); + prom_print_nl(); +#endif + + prom_print(RELOC("returning from prom_init\n")); + return phys; +} + + +static int +prom_set_color(ihandle ih, int i, int r, int g, int b) +{ + unsigned long offset = reloc_offset(); + + return (int)(long)call_prom(RELOC("call-method"), 6, 1, + RELOC("color!"), + ih, + (void *)(long) i, + (void *)(long) b, + (void *)(long) g, + (void *)(long) r ); +} + +/* + * 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 will probably fall over if we do that + * we've taken over the MMU. + * So we check whether we will need to open the display, + * and if so, open it now. + */ +__init +static unsigned long +check_display(unsigned long mem) +{ + phandle node; + ihandle ih; + int i; + unsigned long offset = reloc_offset(); + struct prom_t *prom = PTRRELOC(&prom_data); + char type[64], *path; + static unsigned char default_colors[] = { + 0x00, 0x00, 0x00, + 0x00, 0x00, 0xaa, + 0x00, 0xaa, 0x00, + 0x00, 0xaa, 0xaa, + 0xaa, 0x00, 0x00, + 0xaa, 0x00, 0xaa, + 0xaa, 0xaa, 0x00, + 0xaa, 0xaa, 0xaa, + 0x55, 0x55, 0x55, + 0x55, 0x55, 0xff, + 0x55, 0xff, 0x55, + 0x55, 0xff, 0xff, + 0xff, 0x55, 0x55, + 0xff, 0x55, 0xff, + 0xff, 0xff, 0x55, + 0xff, 0xff, 0xff + }; + + prom->disp_node = 0; + + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"), + type, sizeof(type)); + if (strcmp(type, RELOC("display")) != 0) + continue; + /* It seems OF doesn't null-terminate the path :-( */ + path = (char *) mem; + memset(path, 0, 256); + if ((long) call_prom(RELOC("package-to-path"), 3, 1, + node, path, 255) < 0) + continue; + prom_print(RELOC("opening display ")); + prom_print(path); + ih = call_prom(RELOC("open"), 1, 1, path); + if (ih == 0 || ih == (ihandle) -1) { + prom_print(RELOC("... failed\n")); + continue; + } + prom_print(RELOC("... ok\n")); + + if (prom->disp_node == 0) + prom->disp_node = node; + + /* Setup a useable color table when the appropriate + * method is available. Should update this to set-colors */ + for (i = 0; i < 32; i++) + if (prom_set_color(ih, i, RELOC(default_colors)[i*3], + RELOC(default_colors)[i*3+1], + RELOC(default_colors)[i*3+2]) != 0) + break; + +#ifdef CONFIG_FB + for (i = 0; i < LINUX_LOGO_COLORS; i++) + if (prom_set_color(ih, i + 32, + RELOC(linux_logo_red)[i], + RELOC(linux_logo_green)[i], + RELOC(linux_logo_blue)[i]) != 0) + break; +#endif /* CONFIG_FB */ + + /* + * If this display is the device that OF is using for stdout, + * move it to the front of the list. + */ + mem += strlen(path) + 1; + i = RELOC(prom_num_displays)++; + if (RELOC(of_stdout_device) != 0 && i > 0 + && strcmp(PTRRELOC(RELOC(of_stdout_device)), path) == 0) { + for (; i > 0; --i) + RELOC(prom_display_paths[i]) = RELOC(prom_display_paths[i-1]); + } + RELOC(prom_display_paths[i]) = PTRUNRELOC(path); + if (RELOC(prom_num_displays) >= FB_MAX) + break; + } + return DOUBLEWORD_ALIGN(mem); +} + +__init +static int +prom_next_node(phandle *nodep) +{ + phandle node; + unsigned long offset = reloc_offset(); + + if ((node = *nodep) != 0 + && (*nodep = call_prom(RELOC("child"), 1, 1, node)) != 0) + return 1; + if ((*nodep = call_prom(RELOC("peer"), 1, 1, node)) != 0) + return 1; + for (;;) { + if ((node = call_prom(RELOC("parent"), 1, 1, node)) == 0) + return 0; + if ((*nodep = call_prom(RELOC("peer"), 1, 1, node)) != 0) + return 1; + } +} + +/* + * Make a copy of the device tree from the PROM. + */ +__init +static unsigned long +copy_device_tree(unsigned long mem_start, unsigned long mem_end) +{ + phandle root; + unsigned long new_start; + struct device_node **allnextp; + unsigned long offset = reloc_offset(); + + root = call_prom(RELOC("peer"), 1, 1, (phandle)0); + if (root == (phandle)0) { + prom_print(RELOC("couldn't get device tree root\n")); + prom_exit(); + } + allnextp = &RELOC(allnodes); + mem_start = DOUBLEWORD_ALIGN(mem_start); + new_start = inspect_node(root, 0, mem_start, mem_end, &allnextp); + *allnextp = 0; + return new_start; +} + +__init +static unsigned long +inspect_node(phandle node, struct device_node *dad, + unsigned long mem_start, unsigned long mem_end, + struct device_node ***allnextpp) +{ + int l; + phandle child; + struct device_node *np; + struct property *pp, **prev_propp; + char *prev_name, *namep; + unsigned char *valp; + unsigned long offset = reloc_offset(); + + np = (struct device_node *) mem_start; + mem_start += sizeof(struct device_node); + memset(np, 0, sizeof(*np)); + np->node = node; + **allnextpp = PTRUNRELOC(np); + *allnextpp = &np->allnext; + if (dad != 0) { + np->parent = PTRUNRELOC(dad); + /* we temporarily use the `next' field as `last_child'. */ + if (dad->next == 0) + dad->child = PTRUNRELOC(np); + else + dad->next->sibling = PTRUNRELOC(np); + dad->next = np; + } + + /* get and store all properties */ + prev_propp = &np->properties; + prev_name = RELOC(""); + for (;;) { + pp = (struct property *) mem_start; + namep = (char *) (pp + 1); + pp->name = PTRUNRELOC(namep); + if ((long) call_prom(RELOC("nextprop"), 3, 1, node, prev_name, + namep) <= 0) + break; + mem_start = DOUBLEWORD_ALIGN((unsigned long)namep + strlen(namep) + 1); + prev_name = namep; + valp = (unsigned char *) mem_start; + pp->value = PTRUNRELOC(valp); + pp->length = (int)(long) + call_prom(RELOC("getprop"), 4, 1, node, namep, + valp, mem_end - mem_start); + if (pp->length < 0) + continue; +#ifdef MAX_PROPERTY_LENGTH + if (pp->length > MAX_PROPERTY_LENGTH) + continue; /* ignore this property */ +#endif + mem_start = DOUBLEWORD_ALIGN(mem_start + pp->length); + *prev_propp = PTRUNRELOC(pp); + prev_propp = &pp->next; + } + *prev_propp = 0; + + /* get the node's full name */ + l = (long) call_prom(RELOC("package-to-path"), 3, 1, node, + (char *) mem_start, mem_end - mem_start); + if (l >= 0) { + np->full_name = PTRUNRELOC((char *) mem_start); + *(char *)(mem_start + l) = 0; + mem_start = DOUBLEWORD_ALIGN(mem_start + l + 1); + } + + /* do all our children */ + child = call_prom(RELOC("child"), 1, 1, node); + while (child != (phandle)0) { + mem_start = inspect_node(child, np, mem_start, mem_end, + allnextpp); + child = call_prom(RELOC("peer"), 1, 1, child); + } + + return mem_start; +} + +/* + * finish_device_tree is called once things are running normally + * (i.e. with text and data mapped to the address they were linked at). + * It traverses the device tree and fills in the name, type, + * {n_}addrs and {n_}intrs fields of each node. + */ +__init +void +finish_device_tree(void) +{ + unsigned long mem = (unsigned long) klimit; + struct rtas_t *rtas = &rtas_data; + + /* All newworld machines now use the interrupt tree */ + struct device_node *np = allnodes; + + while(np) { + if (get_property(np, "interrupt-parent", 0)) { + pmac_newworld = 1; // This is true on the RS6000-260 + break; + } + np = np->allnext; + } + if (pmac_newworld) use_of_interrupt_tree = 1; + + mem = finish_node(allnodes, mem, NULL, 0, 0); + dev_tree_size = mem - (unsigned long) allnodes; + klimit = (char *) mem; + + rtas->dev = find_devices("rtas"); +} + + +__init +static unsigned long +finish_node(struct device_node *np, unsigned long mem_start, + interpret_func *ifunc, int naddrc, int nsizec) +{ + struct device_node *child; + int *ip; + + np->name = get_property(np, "name", 0); + np->type = get_property(np, "device_type", 0); + + /* get the device addresses and interrupts */ + if (ifunc != NULL) { + mem_start = ifunc(np, mem_start, naddrc, nsizec); + } + if (use_of_interrupt_tree) { + mem_start = finish_node_interrupts(np, mem_start); + } + + /* Look for #address-cells and #size-cells properties. */ + ip = (int *) get_property(np, "#address-cells", 0); + if (ip != NULL) + naddrc = *ip; + ip = (int *) get_property(np, "#size-cells", 0); + if (ip != NULL) + nsizec = *ip; + + /* the f50 sets the name to 'display' and 'compatible' to what we + * expect for the name -- Cort + */ + ifunc = NULL; + if (!strcmp(np->name, "display")) + np->name = get_property(np, "compatible", 0); + + if (!strcmp(np->name, "device-tree") || np->parent == NULL) + ifunc = interpret_root_props; + else if (np->type == 0) + ifunc = NULL; + else if (!strcmp(np->type, "pci") || !strcmp(np->type, "vci")) + ifunc = interpret_pci_props; + else if (!strcmp(np->type, "dbdma")) + ifunc = interpret_dbdma_props; + else if (!strcmp(np->type, "mac-io")) + ifunc = interpret_macio_props; + else if (!strcmp(np->type, "isa")) + ifunc = interpret_isa_props; + + for (child = np->child; child != NULL; child = child->sibling) + mem_start = finish_node(child, mem_start, ifunc, + naddrc, nsizec); + + return mem_start; +} + +/* This routine walks the interrupt tree for a given device node and gather + * all necessary informations according to the draft interrupt mapping + * for CHRP. The current version was only tested on Apple "Core99" machines + * and may not handle cascaded controllers correctly. + */ +__init +static unsigned long +finish_node_interrupts(struct device_node *np, unsigned long mem_start) +{ + /* Finish this node */ + unsigned int *isizep, *asizep, *interrupts, *map, *map_mask, *reg; + phandle *parent; + struct device_node *node, *parent_node; + int l, isize, ipsize, asize, map_size, regpsize; + + /* Currently, we don't look at all nodes with no "interrupts" property */ + + interrupts = (unsigned int *)get_property(np, "interrupts", &l); + if (interrupts == NULL) + return mem_start; + ipsize = l>>2; + + reg = (unsigned int *)get_property(np, "reg", &l); + regpsize = l>>2; + + /* We assume default interrupt cell size is 1 (bugus ?) */ + isize = 1; + node = np; + + do { + /* We adjust the cell size if the current parent contains an #interrupt-cells + * property */ + isizep = (unsigned int *)get_property(node, "#interrupt-cells", &l); + if (isizep) + isize = *isizep; + + /* We don't do interrupt cascade (ISA) for now, we stop on the first + * controller found + */ + if (get_property(node, "interrupt-controller", &l)) { + int i,j; + int cvt_irq; + + /* XXX on chrp, offset interrupt numbers for the + 8259 by 0, those for the openpic by 16 */ + // DRENG Condor -- this info is not actually known yet ... + cvt_irq = _machine == _MACH_chrp + && get_property(node, "interrupt-parent", NULL) == 0; + np->intrs = (struct interrupt_info *) mem_start; + np->n_intrs = ipsize / isize; + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *interrupts++; + if (cvt_irq) + np->intrs[i].line = openpic_to_irq(np->intrs[i].line); + np->intrs[i].sense = 1; + if (isize > 1) + np->intrs[i].sense = *interrupts++; + for (j=2; j>2; + map_mask = (unsigned int *)get_property(node, "interrupt-map-mask", &l); + asizep = (unsigned int *)get_property(node, "#address-cells", &l); + if (asizep && l == sizeof(unsigned int)) + asize = *asizep; + else + asize = 0; + found = 0; + while(map_size>0 && !found) { + found = 1; + for (i=0; i=regpsize) || ((mask & *map) != (mask & reg[i]))) + found = 0; + map++; + map_size--; + } + for (i=0; iparent; + } while(node); + + return mem_start; +} + +int +prom_n_addr_cells(struct device_node* np) +{ + int* ip; + do { + if (np->parent) + np = np->parent; + ip = (int *) get_property(np, "#address-cells", 0); + if (ip != NULL) + return *ip; + } while(np->parent); + return 0; +} + +int +prom_n_size_cells(struct device_node* np) +{ + int* ip; + do { + if (np->parent) + np = np->parent; + ip = (int *) get_property(np, "#size-cells", 0); + if (ip != NULL) + return *ip; + } while(np->parent); + return 0; +} + +__init +static unsigned long +interpret_pci_props(struct device_node *np, unsigned long mem_start, + int naddrc, int nsizec) +{ + struct address_range *adr; + struct pci_reg_property *pci_addrs; + int i, l, *ip, ml; + struct pci_intr_map *imp; + + pci_addrs = (struct pci_reg_property *) + get_property(np, "assigned-addresses", &l); + if (pci_addrs != 0 && l >= sizeof(struct pci_reg_property)) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= sizeof(struct pci_reg_property)) >= 0) { + /* XXX assumes PCI addresses mapped 1-1 to physical */ + adr[i].space = pci_addrs[i].addr.a_hi; + adr[i].address = pci_addrs[i].addr.a_lo; + adr[i].size = pci_addrs[i].size_lo; + ++i; + } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + if (use_of_interrupt_tree) + return mem_start; + + /* + * If the pci host bridge has an interrupt-map property, + * look for our node in it. + */ + if (np->parent != 0 && pci_addrs != 0 + && (imp = (struct pci_intr_map *) + get_property(np->parent, "interrupt-map", &ml)) != 0 + && (ip = (int *) get_property(np, "interrupts", &l)) != 0) { + unsigned int devfn = pci_addrs[0].addr.a_hi & 0xff00; + unsigned int cell_size; + struct device_node* np2; + /* This is hackish, but is only used for BootX booting */ + cell_size = sizeof(struct pci_intr_map); + np2 = np->parent; + while(np2) { + if (device_is_compatible(np2, "uni-north")) { + cell_size += 4; + break; + } + np2 = np2->parent; + } + np->n_intrs = 0; + np->intrs = (struct interrupt_info *) mem_start; + for (i = 0; (ml -= cell_size) >= 0; ++i) { + if (imp->addr.a_hi == devfn) { + np->intrs[np->n_intrs].line = imp->intr; + np->intrs[np->n_intrs].sense = 1; /* FIXME */ + ++np->n_intrs; + } + imp = (struct pci_intr_map *)(((unsigned long)imp) + + cell_size); + } + if (np->n_intrs == 0) + np->intrs = 0; + mem_start += np->n_intrs * sizeof(struct interrupt_info); + return mem_start; + } + + ip = (int *) get_property(np, "AAPL,interrupts", &l); + if (ip == 0) + ip = (int *) get_property(np, "interrupts", &l); + if (ip != 0) { + np->intrs = (struct interrupt_info *) mem_start; + np->n_intrs = l / sizeof(int); + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *ip++; + np->intrs[i].sense = 1; + } + } + + return mem_start; +} + +__init +static unsigned long +interpret_dbdma_props(struct device_node *np, unsigned long mem_start, + int naddrc, int nsizec) +{ + struct reg_property *rp; + struct address_range *adr; + unsigned long base_address; + int i, l, *ip; + struct device_node *db; + + base_address = 0; + for (db = np->parent; db != NULL; db = db->parent) { + if (!strcmp(db->type, "dbdma") && db->n_addrs != 0) { + base_address = db->addrs[0].address; + break; + } + } + + rp = (struct reg_property *) get_property(np, "reg", &l); + if (rp != 0 && l >= sizeof(struct reg_property)) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= sizeof(struct reg_property)) >= 0) { + adr[i].space = 0; + adr[i].address = rp[i].address + base_address; + adr[i].size = rp[i].size; + ++i; + } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + if (use_of_interrupt_tree) + return mem_start; + + ip = (int *) get_property(np, "AAPL,interrupts", &l); + if (ip == 0) + ip = (int *) get_property(np, "interrupts", &l); + if (ip != 0) { + np->intrs = (struct interrupt_info *) mem_start; + np->n_intrs = l / sizeof(int); + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *ip++; + np->intrs[i].sense = 1; + } + } + + return mem_start; +} + +__init +static unsigned long +interpret_macio_props(struct device_node *np, unsigned long mem_start, + int naddrc, int nsizec) +{ + struct reg_property *rp; + struct address_range *adr; + unsigned long base_address; + int i, l, keylargo, *ip; + struct device_node *db; + + base_address = 0; + for (db = np->parent; db != NULL; db = db->parent) { + if (!strcmp(db->type, "mac-io") && db->n_addrs != 0) { + base_address = db->addrs[0].address; + keylargo = device_is_compatible(db, "Keylargo"); + break; + } + } + + rp = (struct reg_property *) get_property(np, "reg", &l); + if (rp != 0 && l >= sizeof(struct reg_property)) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= sizeof(struct reg_property)) >= 0) { + adr[i].space = 0; + adr[i].address = rp[i].address + base_address; + adr[i].size = rp[i].size; + ++i; + } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + if (use_of_interrupt_tree) + return mem_start; + + ip = (int *) get_property(np, "interrupts", &l); + if (ip == 0) + ip = (int *) get_property(np, "AAPL,interrupts", &l); + if (ip != 0) { + np->intrs = (struct interrupt_info *) mem_start; + if (_machine == _MACH_Pmac) { + /* for the iMac */ + np->n_intrs = l / sizeof(int); + /* Hack for BootX on Core99 */ + if (keylargo) + np->n_intrs = np->n_intrs/2; + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *ip++; + if (keylargo) + np->intrs[i].sense = *ip++; + else + np->intrs[i].sense = 1; + } + } else { + /* CHRP machines */ + np->n_intrs = l / (2 * sizeof(int)); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = openpic_to_irq(*ip++); + np->intrs[i].sense = *ip++; + } + } + mem_start += np->n_intrs * sizeof(struct interrupt_info); + } + + return mem_start; +} + +__init +static unsigned long +interpret_isa_props(struct device_node *np, unsigned long mem_start, + int naddrc, int nsizec) +{ + struct isa_reg_property *rp; + struct address_range *adr; + int i, l, *ip; + + rp = (struct isa_reg_property *) get_property(np, "reg", &l); + if (rp != 0 && l >= sizeof(struct isa_reg_property)) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= sizeof(struct reg_property)) >= 0) { + adr[i].space = rp[i].space; + adr[i].address = rp[i].address + + (adr[i].space? 0: _ISA_MEM_BASE); + adr[i].size = rp[i].size; + ++i; + } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + if (use_of_interrupt_tree) + return mem_start; + + ip = (int *) get_property(np, "interrupts", &l); + if (ip != 0) { + np->intrs = (struct interrupt_info *) mem_start; + np->n_intrs = l / (2 * sizeof(int)); + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *ip++; + np->intrs[i].sense = *ip++; + } + } + + return mem_start; +} + +__init +static unsigned long +interpret_root_props(struct device_node *np, unsigned long mem_start, + int naddrc, int nsizec) +{ + struct address_range *adr; + int i, l, *ip; + unsigned int *rp; + int rpsize = (naddrc + nsizec) * sizeof(unsigned int); + + rp = (unsigned int *) get_property(np, "reg", &l); + if (rp != 0 && l >= rpsize) { + i = 0; + adr = (struct address_range *) mem_start; + while ((l -= rpsize) >= 0) { + adr[i].space = 0; + adr[i].address = rp[naddrc - 1]; + adr[i].size = rp[naddrc + nsizec - 1]; + ++i; + rp += naddrc + nsizec; + } + np->addrs = adr; + np->n_addrs = i; + mem_start += i * sizeof(struct address_range); + } + + if (use_of_interrupt_tree) + return mem_start; + + ip = (int *) get_property(np, "AAPL,interrupts", &l); + if (ip == 0) + ip = (int *) get_property(np, "interrupts", &l); + if (ip != 0) { + np->intrs = (struct interrupt_info *) mem_start; + np->n_intrs = l / sizeof(int); + mem_start += np->n_intrs * sizeof(struct interrupt_info); + for (i = 0; i < np->n_intrs; ++i) { + np->intrs[i].line = *ip++; + np->intrs[i].sense = 1; + } + } + + return mem_start; +} + +/* + * Work out the sense (active-low level / active-high edge) + * of each interrupt from the device tree. + */ +void __init +prom_get_irq_senses(unsigned char *senses, int off, int max) +{ + struct device_node *np; + int i, j; + + /* default to level-triggered */ + memset(senses, 1, max - off); + if (!use_of_interrupt_tree) + return; + + for (np = allnodes; np != 0; np = np->allnext) { + for (j = 0; j < np->n_intrs; j++) { + i = np->intrs[j].line; + if (i >= off && i < max) + senses[i-off] = np->intrs[j].sense; + } + } +} + +/* + * Construct and return a list of the device_nodes with a given name. + */ +__openfirmware +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 and return a list of the device_nodes with a given type. + */ +__openfirmware +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; +} + +/* Finds a device node given its PCI bus number, device number + * and function number + */ +__openfirmware +struct device_node * +find_pci_device_OFnode(unsigned char bus, unsigned char dev_fn) +{ + struct device_node* np; + unsigned int *reg; + int l; + + for (np = allnodes; np != 0; np = np->allnext) { + if (np->parent == NULL || np->parent->type == NULL + || strcmp(np->parent->type, "pci") != 0) + continue; + reg = (unsigned int *) get_property(np, "reg", &l); + if (reg == 0 || l < sizeof(struct reg_property)) + continue; + if (((reg[0] >> 8) & 0xff) == dev_fn && ((reg[0] >> 16) & 0xff) == bus) + break; + } + return np; +} + +/* + * Returns all nodes linked together + */ +__openfirmware +struct device_node * +find_all_nodes(void) +{ + struct device_node *head, **prevp, *np; + + prevp = &head; + for (np = allnodes; np != 0; np = np->allnext) { + *prevp = np; + prevp = &np->next; + } + *prevp = 0; + return head; +} + +/* Checks if the given "compat" string matches one of the strings in + * the device's "compatible" property + */ +__openfirmware +int +device_is_compatible(struct device_node *device, const char *compat) +{ + const char* cp; + int cplen, l; + + cp = (char *) get_property(device, "compatible", &cplen); + if (cp == NULL) + return 0; + while (cplen > 0) { + if (strncasecmp(cp, compat, strlen(compat)) == 0) + return 1; + l = strlen(cp) + 1; + cp += l; + cplen -= l; + } + + return 0; +} + + +/* + * Indicates whether the root node has a given value in its + * compatible property. + */ +__openfirmware +int +machine_is_compatible(const char *compat) +{ + struct device_node *root; + + root = find_path_device("/"); + if (root == 0) + return 0; + return device_is_compatible(root, compat); +} + +/* + * Construct and return a list of the device_nodes with a given type + * and compatible property. + */ +__openfirmware +struct device_node * +find_compatible_devices(const char *type, const char *compat) +{ + struct device_node *head, **prevp, *np; + + prevp = &head; + for (np = allnodes; np != 0; np = np->allnext) { + if (type != NULL + && !(np->type != 0 && strcasecmp(np->type, type) == 0)) + continue; + if (device_is_compatible(np, compat)) { + *prevp = np; + prevp = &np->next; + } + } + *prevp = 0; + return head; +} + +/* + * Find the device_node with a given full_name. + */ +__openfirmware +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 the device_node with a given phandle. + */ +__openfirmware +struct device_node * +find_phandle(phandle ph) +{ + struct device_node *np; + + for (np = allnodes; np != 0; np = np->allnext) + if (np->node == ph) + return np; + return NULL; +} + +/* + * Find a property with a given name for a given node + * and return the value. + */ +__openfirmware +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; +} + +/* + * Add a property to a node + */ +__openfirmware +void +prom_add_property(struct device_node* np, struct property* prop) +{ + struct property **next = &np->properties; + + prop->next = NULL; + while (*next) + next = &(*next)->next; + *next = prop; +} + +#if 0 +__openfirmware +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); + } + } +} +#endif + + +__init +void +abort() +{ +#ifdef CONFIG_XMON + xmon(NULL); +#endif + for (;;) + prom_exit(); +} + + +void +prom_peter(char c) +{ + unsigned long offset = reloc_offset(); + struct prom_t *prom = PTRRELOC(&prom_data); + + RELOC(function32b_ptr) = prom->entry; + call_function32b_ptr(RELOC("PETER1: location = '%c'\n"), c); +} + +void +prom_peter2(unsigned long i) { + unsigned long offset = reloc_offset(); + prom_print(RELOC("PETER2: location = ")); + prom_print_hex(i); + prom_print_nl(); +} + +void +prom_dump_mem(unsigned int *ptr, unsigned int num) +{ + int i; + unsigned long offset = reloc_offset(); + for(i=0; i < num ;i++) { + unsigned int *addr = ptr + i; + prom_print(RELOC("0x")); + prom_print_hex(addr); + prom_print(RELOC(": 0x")); + prom_print_hex(*addr); + prom_print_nl(); + } +} + + + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/ptrace.c linuxppc64_2_4/arch/ppc64/kernel/ptrace.c --- linux-2.4.5-ac18/arch/ppc64/kernel/ptrace.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/ptrace.c Mon May 7 12:47:01 2001 @@ -0,0 +1,368 @@ +/* + * linux/arch/ppc/kernel/ptrace.c + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/m68k/kernel/ptrace.c" + * Copyright (C) 1994 by Hamish Macdonald + * Taken from linux/kernel/ptrace.c and modified for M680x0. + * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds + * + * Modified by Cort Dougan (cort@hq.fsmlabs.com) + * and Paul Mackerras (paulus@linuxcare.com.au). + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file README.legal in the main directory of + * this archive for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * Set of msr bits that gdb can change on behalf of a process. + */ +#define MSR_DEBUGCHANGE (MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1) + +/* + * does not yet catch signals sent when the child dies. + * in exit.c or in signal.c. + */ + +/* + * Get contents of register REGNO in task TASK. + */ +static inline unsigned long get_reg(struct task_struct *task, int regno) +{ + if (regno < sizeof(struct pt_regs) / sizeof(unsigned long)) + return ((unsigned long *)task->thread.regs)[regno]; + return (0); +} + +/* + * Write contents of register REGNO in task TASK. + */ +static inline int put_reg(struct task_struct *task, int regno, + unsigned long data) +{ + if (regno <= PT_MQ) { + if (regno == PT_MSR) + data = (data & MSR_DEBUGCHANGE) + | (task->thread.regs->msr & ~MSR_DEBUGCHANGE); + ((unsigned long *)task->thread.regs)[regno] = data; + return 0; + } + return -EIO; +} + +static inline void +set_single_step(struct task_struct *task) +{ + struct pt_regs *regs = task->thread.regs; + regs->msr |= MSR_SE; +} + +static inline void +clear_single_step(struct task_struct *task) +{ + struct pt_regs *regs = task->thread.regs; + regs->msr &= ~MSR_SE; +} + +int sys_ptrace(long request, long pid, long addr, long data) +{ + struct task_struct *child; + int ret = -EPERM; + + lock_kernel(); + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + if (current->ptrace & PT_PTRACED) + goto out; + /* set the ptrace bit in the process flags. */ + current->ptrace |= PT_PTRACED; + ret = 0; + goto out; + } + ret = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + if (child) + get_task_struct(child); + read_unlock(&tasklist_lock); + if (!child) + goto out; + + ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + goto out_tsk; + + if (request == PTRACE_ATTACH) { + if (child == current) + goto out_tsk; + if ((!child->dumpable || + (current->uid != child->euid) || + (current->uid != child->suid) || + (current->uid != child->uid) || + (current->gid != child->egid) || + (current->gid != child->sgid) || + (!cap_issubset(child->cap_permitted, current->cap_permitted)) || + (current->gid != child->gid)) + && !capable(CAP_SYS_PTRACE)) + goto out_tsk; + /* the same process cannot be attached many times */ + if (child->ptrace & PT_PTRACED) + goto out_tsk; + child->ptrace |= PT_PTRACED; + + write_lock_irq(&tasklist_lock); + if (child->p_pptr != current) { + REMOVE_LINKS(child); + child->p_pptr = current; + SET_LINKS(child); + } + write_unlock_irq(&tasklist_lock); + + send_sig(SIGSTOP, child, 1); + ret = 0; + goto out_tsk; + } + ret = -ESRCH; + if (!(child->ptrace & PT_PTRACED)) + goto out_tsk; + if (child->state != TASK_STOPPED) { + if (request != PTRACE_KILL) + goto out_tsk; + } + if (child->p_pptr != current) + goto out_tsk; + + switch (request) { + /* when I and D space are separate, these will need to be fixed. */ + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: { + unsigned long tmp; + int copied; + + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + ret = -EIO; + if (copied != sizeof(tmp)) + break; + ret = put_user(tmp,(unsigned long *) data); + break; + } + + /* read the word at location addr in the USER area. */ + case PTRACE_PEEKUSR: { + unsigned long index, tmp; + + ret = -EIO; + /* convert to index and check */ + index = (unsigned long) addr >> 3; + if ((addr & 7) || index > PT_FPSCR) + break; + + if (index < PT_FPR0) { + tmp = get_reg(child, (int) index); + } else { + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + tmp = ((unsigned long *)child->thread.fpr)[index - PT_FPR0]; + } + ret = put_user(tmp,(unsigned long *) data); + break; + } + + /* If I and D space are separate, this will have to be fixed. */ + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: + ret = 0; + if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) + break; + ret = -EIO; + break; + + /* write the word at location addr in the USER area */ + case PTRACE_POKEUSR: { + unsigned long index; + + ret = -EIO; + /* convert to index and check */ + index = (unsigned long) addr >> 3; + if ((addr & 7) || index > PT_FPSCR) + break; + + if (index == PT_ORIG_R3) + break; + if (index < PT_FPR0) { + ret = put_reg(child, index, data); + } else { + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + ((unsigned long *)child->thread.fpr)[index - PT_FPR0] = data; + ret = 0; + } + break; + } + + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: { /* restart after signal. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + if (request == PTRACE_SYSCALL) + child->ptrace |= PT_TRACESYS; + else + child->ptrace &= ~PT_TRACESYS; + child->exit_code = data; + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + ret = 0; + break; + } + +/* + * make the child exit. Best I can do is send it a sigkill. + * perhaps it should be put in the status that it wants to + * exit. + */ + case PTRACE_KILL: { + ret = 0; + if (child->state == TASK_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + break; + } + + case PTRACE_SINGLESTEP: { /* set the trap flag. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + child->ptrace &= ~PT_TRACESYS; + set_single_step(child); + child->exit_code = data; + /* give it a chance to run. */ + wake_up_process(child); + ret = 0; + break; + } + + case PTRACE_DETACH: { /* detach a process that was attached. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + child->ptrace &= ~(PT_PTRACED|PT_TRACESYS); + child->exit_code = data; + write_lock_irq(&tasklist_lock); + REMOVE_LINKS(child); + child->p_pptr = child->p_opptr; + SET_LINKS(child); + write_unlock_irq(&tasklist_lock); + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + ret = 0; + break; + } + + case PPC_PTRACE_GETREGS: + { /* Get GPRs 0 - 31. */ + u64 tmp; + u64 cntr; + ret = 0; + for (cntr=0; cntr<32 && ret==0; ++cntr) + { + tmp = ((u64*)child->thread.regs)[cntr]; + ret = put_user(tmp, (u64*)(data+cntr)); + } + break; + } + + case PPC_PTRACE_SETREGS: + { /* Set GPRs 0 - 31. */ + u64 cntr; + ret = 0; + for (cntr=0; cntr<32 && ret==0; ++cntr) + { + ret = put_reg(child, cntr, *(u64*)(data+cntr)); + } + break; + } + + case PPC_PTRACE_GETFPREGS: + { /* Get FPRs 0 - 31. */ + u64 tmp; + u64 cntr; + ret = -EIO; + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + ret = 0; + for (cntr=0; cntr<32 && ret==0; ++cntr) + { + tmp = ((u64*)child->thread.fpr)[cntr]; + ret = put_user(tmp, (u64*)(data+cntr)); + } + break; + } + + case PPC_PTRACE_SETFPREGS: + { /* Get FPRs 0 - 31. */ + u64 cntr; + ret = -EIO; + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + for (cntr=0; cntr<32; ++cntr) + { + ((u64*)child->thread.fpr)[cntr] = *(u64*)(data+cntr); + } + ret = 0; + break; + } + + default: + ret = -EIO; + break; + } +out_tsk: + free_task_struct(child); +out: + unlock_kernel(); + return ret; +} + +void syscall_trace(void) +{ + if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) + != (PT_PTRACED|PT_TRACESYS)) + return; + current->exit_code = SIGTRAP; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + /* + * this isn't the same as continuing with a signal, but it will do + * for normal use. strace only continues with a signal if the + * stopping signal is not SIGTRAP. -brl + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } +} + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/ptrace32.c linuxppc64_2_4/arch/ppc64/kernel/ptrace32.c --- linux-2.4.5-ac18/arch/ppc64/kernel/ptrace32.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/ptrace32.c Wed May 23 15:21:33 2001 @@ -0,0 +1,439 @@ +/* + * linux/arch/ppc/kernel/ptrace32.c + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/m68k/kernel/ptrace.c" + * Copyright (C) 1994 by Hamish Macdonald + * Taken from linux/kernel/ptrace.c and modified for M680x0. + * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds + * + * Modified by Cort Dougan (cort@hq.fsmlabs.com) + * and Paul Mackerras (paulus@linuxcare.com.au). + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file README.legal in the main directory of + * this archive for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * Set of msr bits that gdb can change on behalf of a process. + */ +#define MSR_DEBUGCHANGE (MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1) + +/* + * does not yet catch signals sent when the child dies. + * in exit.c or in signal.c. + */ + +/* + * Get contents of register REGNO in task TASK. + */ +static inline unsigned long get_reg(struct task_struct *task, int regno) +{ + if (regno < sizeof(struct pt_regs) / sizeof(unsigned long)) + return ((unsigned long *)task->thread.regs)[regno]; + return (0); +} + +/* + * Write contents of register REGNO in task TASK. + * (Put DATA into task TASK's register REGNO.) + */ +static inline int put_reg(struct task_struct *task, int regno, unsigned long data) +{ + if (regno <= PT_MQ) + { + if (regno == PT_MSR) + data = (data & MSR_DEBUGCHANGE) | (task->thread.regs->msr & ~MSR_DEBUGCHANGE); + ((unsigned long *)task->thread.regs)[regno] = data; + return 0; + } + return -EIO; +} + +static inline void +set_single_step(struct task_struct *task) +{ + struct pt_regs *regs = task->thread.regs; + regs->msr |= MSR_SE; +} + +static inline void +clear_single_step(struct task_struct *task) +{ + struct pt_regs *regs = task->thread.regs; + regs->msr &= ~MSR_SE; +} + +int sys32_ptrace(long request, long pid, unsigned long addr, unsigned long data) +{ + struct task_struct *child; + int ret = -EPERM; + + lock_kernel(); + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + if (current->ptrace & PT_PTRACED) + goto out; + /* set the ptrace bit in the process flags. */ + current->ptrace |= PT_PTRACED; + ret = 0; + goto out; + } + ret = -ESRCH; + read_lock(&tasklist_lock); + child = find_task_by_pid(pid); + if (child) + get_task_struct(child); + read_unlock(&tasklist_lock); + if (!child) + goto out; + + ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + goto out_tsk; + + if (request == PTRACE_ATTACH) { + if (child == current) + goto out_tsk; + if ((!child->dumpable || + (current->uid != child->euid) || + (current->uid != child->suid) || + (current->uid != child->uid) || + (current->gid != child->egid) || + (current->gid != child->sgid) || + (!cap_issubset(child->cap_permitted, current->cap_permitted)) || + (current->gid != child->gid)) + && !capable(CAP_SYS_PTRACE)) + goto out_tsk; + /* the same process cannot be attached many times */ + if (child->ptrace & PT_PTRACED) + goto out_tsk; + child->ptrace |= PT_PTRACED; + + write_lock_irq(&tasklist_lock); + if (child->p_pptr != current) { + REMOVE_LINKS(child); + child->p_pptr = current; + SET_LINKS(child); + } + write_unlock_irq(&tasklist_lock); + + send_sig(SIGSTOP, child, 1); + ret = 0; + goto out_tsk; + } + ret = -ESRCH; + if (!(child->ptrace & PT_PTRACED)) + goto out_tsk; + if (child->state != TASK_STOPPED) { + if (request != PTRACE_KILL) + goto out_tsk; + } + if (child->p_pptr != current) + goto out_tsk; + + switch (request) + { + /* Read word at location ADDR */ + /* when I and D space are separate, these will need to be fixed. */ + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: + { + unsigned int tmp_mem_value; + int copied; + + copied = access_process_vm(child, addr, &tmp_mem_value, sizeof(tmp_mem_value), 0); + ret = -EIO; + if (copied != sizeof(tmp_mem_value)) + break; + ret = put_user(tmp_mem_value, (u32*)data); // copy 4 bytes of data into the user location specified by the 8 byte pointer in "data". + break; + } + + /* Read 4 bytes of the other process' storage */ + /* data is a pointer specifying where the user wants the 4 bytes copied into */ + /* addr is a pointer in the user's storage that contains an 8 byte address in the other process of the 4 bytes that is to be read */ + /* (this is run in a 32-bit process looking at a 64-bit process) */ + /* when I and D space are separate, these will need to be fixed. */ + case PPC_PTRACE_PEEKTEXT_3264: + case PPC_PTRACE_PEEKDATA_3264: + { + u32 tmp_mem_value; + int copied; + u32* addrOthers; + + ret = -EIO; + + /* Get the addr in the other process that we want to read */ + if (get_user(addrOthers,(u32**)addr) != 0) + break; + + copied = access_process_vm(child, (u64)addrOthers, &tmp_mem_value, sizeof(tmp_mem_value), 0); + if (copied != sizeof(tmp_mem_value)) + break; + ret = put_user(tmp_mem_value, (u32*)data); // copy 4 bytes of data into the user location specified by the 8 byte pointer in "data". + break; + } + + /* Read a register (specified by ADDR) out of the "user area" */ + case PTRACE_PEEKUSR: { + int index; + unsigned int reg32bits; + unsigned long tmp_reg_value; + + ret = -EIO; + /* convert to index and check */ + index = (unsigned long) addr >> 2; + if ((addr & 3) || index > PT_FPSCR32) + break; + + if (index < PT_FPR0) { + tmp_reg_value = get_reg(child, index); + } else { + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + /* the user space code considers the floating point to be + * an array of unsigned int (32 bits) - the index passed + * in is based on this assumption. + */ + tmp_reg_value = ((unsigned int *)child->thread.fpr)[index - PT_FPR0]; + } + reg32bits = tmp_reg_value; + ret = put_user(reg32bits, (u32*)data); // copy 4 bytes of data into the user location specified by the 8 byte pointer in "data". + break; + } + + /* Read 4 bytes out of the other process' pt_regs area */ + /* data is a pointer specifying where the user wants the 4 bytes copied into */ + /* addr is the offset into the other process' pt_regs structure that is to be read */ + /* (this is run in a 32-bit process looking at a 64-bit process) */ + case PPC_PTRACE_PEEKUSR_3264: + { + u32 index; + u32 reg32bits; + u64 tmp_reg_value; + u32 numReg; + u32 part; + + ret = -EIO; + /* Determine which register the user wants */ + index = (u64)addr >> 2; /* Divide addr by 4 */ + numReg = index / 2; + /* Determine which part of the register the user wants */ + if (index % 2) + part = 1; /* want the 2nd half of the register (right-most). */ + else + part = 0; /* want the 1st half of the register (left-most). */ + + /* Validate the input - check to see if address is on the wrong boundary or beyond the end of the user area */ + if ((addr & 3) || numReg > PT_FPSCR) + break; + + if (numReg >= PT_FPR0) + { + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + } + tmp_reg_value = get_reg(child, numReg); + reg32bits = ((u32*)&tmp_reg_value)[part]; + ret = put_user(reg32bits, (u32*)data); /* copy 4 bytes of data into the user location specified by the 8 byte pointer in "data". */ + break; + } + + /* Write the word at location ADDR */ + /* If I and D space are separate, this will have to be fixed. */ + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: { + unsigned int tmp_value_to_write; + tmp_value_to_write = data; + ret = 0; + if (access_process_vm(child, addr, &tmp_value_to_write, sizeof(tmp_value_to_write), 1) == sizeof(tmp_value_to_write)) + break; + ret = -EIO; + break; + } + + /* Write 4 bytes into the other process' storage */ + /* data is the 4 bytes that the user wants written */ + /* addr is a pointer in the user's storage that contains an 8 byte address in the other process where the 4 bytes that is to be written */ + /* (this is run in a 32-bit process looking at a 64-bit process) */ + /* when I and D space are separate, these will need to be fixed. */ + case PPC_PTRACE_POKETEXT_3264: + case PPC_PTRACE_POKEDATA_3264: + { + u32 tmp_value_to_write = data; + u32* addrOthers; + int bytesWritten; + + /* Get the addr in the other process that we want to write into */ + ret = -EIO; + if (get_user(addrOthers,(u32**)addr) != 0) + break; + + ret = 0; + bytesWritten = access_process_vm(child, (u64)addrOthers, &tmp_value_to_write, sizeof(tmp_value_to_write), 1); + if (bytesWritten == sizeof(tmp_value_to_write)) + break; + ret = -EIO; + break; + } + + /* Write DATA into location ADDR within the USER area */ + case PTRACE_POKEUSR: { + unsigned long index; + + ret = -EIO; + + /* convert to index and check */ + index = (unsigned long) addr >> 2; + if ((addr & 3) || index > PT_FPSCR32) + break; + + if (index == PT_ORIG_R3) + break; + + + if (index < PT_FPR0) { + ret = put_reg(child, index, data); + } else { + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + /* the user space code considers the floating point to be + * an array of unsigned int (32 bits) - the index passed + * in is based on this assumption. + */ + + ((unsigned int *)child->thread.fpr)[index - PT_FPR0] = data; + ret = 0; + } + break; + } + + /* Write 4 bytes into the other process' pt_regs area */ + /* data is the 4 bytes that the user wants written */ + /* addr is the offset into the other process' pt_regs structure that is to be written into */ + /* (this is run in a 32-bit process looking at a 64-bit process) */ + case PPC_PTRACE_POKEUSR_3264: + { + u32 index; + u32 numReg; + + ret = -EIO; + + /* Determine which register the user wants */ + index = (u64)addr >> 2; /* Divide addr by 4 */ + numReg = index / 2; + + /* Validate the input - check to see if address is on the wrong boundary or beyond the end of the user area */ + if ((addr & 3) || numReg > PT_FPSCR) + break; + /* Insure it is a register we let them change */ + if ((numReg == PT_ORIG_R3) || ((numReg > PT_MQ) && (numReg < PT_FPR0))) + break; + + if (numReg >= PT_FPR0) + { + if (child->thread.regs->msr & MSR_FP) + giveup_fpu(child); + } + + if (numReg == PT_MSR) + data = (data & MSR_DEBUGCHANGE) | (child->thread.regs->msr & ~MSR_DEBUGCHANGE); + + ((u32*)child->thread.regs)[index] = data; + ret = 0; + break; + } + + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: { /* restart after signal. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + if (request == PTRACE_SYSCALL) + child->ptrace |= PT_TRACESYS; + else + child->ptrace &= ~PT_TRACESYS; + child->exit_code = data; + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + ret = 0; + break; + } + +/* + * make the child exit. Best I can do is send it a sigkill. + * perhaps it should be put in the status that it wants to + * exit. + */ + case PTRACE_KILL: { + ret = 0; + if (child->state == TASK_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + break; + } + + case PTRACE_SINGLESTEP: { /* set the trap flag. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + child->ptrace &= ~PT_TRACESYS; + set_single_step(child); + child->exit_code = data; + /* give it a chance to run. */ + wake_up_process(child); + ret = 0; + break; + } + + case PTRACE_DETACH: { /* detach a process that was attached. */ + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + child->ptrace &= ~(PT_PTRACED|PT_TRACESYS); + child->exit_code = data; + write_lock_irq(&tasklist_lock); + REMOVE_LINKS(child); + child->p_pptr = child->p_opptr; + SET_LINKS(child); + write_unlock_irq(&tasklist_lock); + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + ret = 0; + break; + } + + default: + ret = -EIO; + break; + } +out_tsk: + free_task_struct(child); +out: + unlock_kernel(); + return ret; +} + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/rtas-eventscan.c linuxppc64_2_4/arch/ppc64/kernel/rtas-eventscan.c --- linux-2.4.5-ac18/arch/ppc64/kernel/rtas-eventscan.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/rtas-eventscan.c Tue May 22 14:59:22 2001 @@ -0,0 +1,213 @@ +/* + * arch/ppc64/kernel/rtas-eventscan.c + * + * Copyright (c) 2000 Tilmann Bitterberg + * (tilmann@bitterberg.de) + * + * Error processing of errors found by rtas even-scan routine + * which is done with every heartbeat. (chrp_setup.c) + */ + +#include + +#include /* for ppc_md */ +#include +#include + +static long rtas_handle_error_log(struct rtas_error_log *, long, char *); +static const char *rtas_errorlog_check_type (struct rtas_error_log *); + +/* ****************************************************************** */ + +#define rtas_errorlog_check_severity(x) \ + (_errlog_severity[x->severity]) +#define rtas_errorlog_check_target(x) \ + (_errlog_target[x->target]) +#define rtas_errorlog_check_initiator(x) \ + (_errlog_initiator[x->initiator]) +#define rtas_errorlog_check_extended(x) \ + (_errlog_extended[x->extended]) +#define rtas_errorlog_disect_extended(x) \ + do { /* implement me */ } while(0) + +/* ****************************************************************** */ + +long +rtas_event_scan(void) +{ + struct rtas_error_log *log = rtas_data.event_scan.log; + unsigned long size = rtas_data.event_scan.log_size; + char buf[1024]; + long cnt = 0; + + for(;;) { + long status = 1; + call_rtas( "event-scan", 4, 1, &status, EVENT_SCAN_ALL_EVENTS, + 0, __pa(log), size); + + if ( status == 1 ) /* No errors/events found */ + break; + + /* New error log returned */ + cnt += 1; + if (rtas_handle_error_log(log, status, buf) != 0) + printk("%s", buf); + + if (status == -1) { + /* Hardware error */ + panic("RTAS: event-scan reported hardware error\n"); + return 0; + } + } + ppc_md.heartbeat_count = ppc_md.heartbeat_reset; + return cnt; +} + + +/* ****************************************************************** */ +/* + * EVENT-SCAN + * The whole stuff below here doesn't take any action when it found + * an error, it just prints as much information as possible and + * then its up to the user to decide what to do. + * + * Returns 0 if no errors were found + * Returns 1 if there may be more errors + */ +static long +rtas_handle_error_log(struct rtas_error_log *log, long status, char *buffer) +{ +const char *_errlog_severity[] = { +#ifdef VERBOSE_ERRORS + "No Error\n\t\ +Should require no further information", + "Event\n\t\ +This is not really an error, it is an event. I use events\n\t\ +to communicate with RTAS back and forth.", + "Warning\n\t\ +Indicates a non-state-losing error, either fully recovered\n\t\ +by RTAS or not needing recovery. Ignore it.", + "Error sync\n\t\ +May only be fatal to a certain program or thread. Recovery\n\t\ +and continuation is possible, if I only had a handler for\n\t\ +this. Less serious", + "Error\n\t\ +Less serious, but still causing a loss of data and state.\n\t\ +I can't tell you exactly what to do, You have to decide\n\t\ +with help from the target and initiator field, what kind\n\t\ +of further actions may take place.", + "Fatal\n\t\ +Represent a permanent hardware failure and I believe this\n\t\ +affects my overall performance and behaviour. I would not\n\t\ +attempt to continue normal operation." +#else + "No Error", + "Event", + "Warning", + "Error sync", + "Error", + "Fatal" +#endif /* VERBOSE_ERRORS */ +}; + +const char *_errlog_extended[] = { +#ifdef VERBOSE_ERRORS + "Not present\n\t\ +Sad, the RTAS call didn't return an extended error log.", + "Present\n\t\ +The extended log is present and hopefully it contains a lot of\n\t\ +useful information, which leads to the solution of the problem." +#else + "Not present", + "Present" +#endif /* VERBOSE_ERRORS */ +}; + +const char *_errlog_initiator[] = { + "Unknown or not applicable", + "CPU", + "PCI", + "ISA", + "Memory", + "Power management" +}; + +const char *_errlog_target[] = { + "Unknown or not applicable", + "CPU", + "PCI", + "ISA", + "Memory", + "Power management" +}; + int n = 0; + + if (status == -1) { + n += sprintf ( buffer+n, KERN_WARNING "event-scan: " + "Unable to get errors. Do you a " + "favor and throw this box away\n"); + return n; + } + n += sprintf ( buffer+n, KERN_INFO "event-scan: " + "Error Log version %u\n", log->version); + + switch (log->disposition) { + case DISP_FULLY_RECOVERED: + /* there was an error, but everything is fine now */ + break; + case DISP_NOT_RECOVERED: + n += sprintf (buffer+n, KERN_WARNING "event-scan: " + "We have a really serious problem!\n"); + case DISP_LIMITED_RECOVERY: + n += sprintf (buffer+n, KERN_WARNING "event-scan: " + "Error classification\n"); + n += sprintf (buffer+n, KERN_WARNING "event-scan: " + "Severity : %s\n", + rtas_errorlog_check_severity (log)); + n += sprintf (buffer+n, KERN_WARNING "event-scan: " + "Initiator : %s\n", + rtas_errorlog_check_initiator (log)); + n += sprintf (buffer+n, KERN_WARNING "event-scan: " + "Target : %s\n", + rtas_errorlog_check_target (log)); + n += sprintf (buffer+n, KERN_WARNING "event-scan: " + "Type : %s\n", + rtas_errorlog_check_type (log)); + n += sprintf (buffer+n, KERN_WARNING "event-scan: " + "Ext. log : %s\n", + rtas_errorlog_check_extended (log)); + if (log->extended) + rtas_errorlog_disect_extended (log); + break; + default: + /* nothing */ + break; + } + return n; +} + +/* ****************************************************************** */ + +static const char * +rtas_errorlog_check_type (struct rtas_error_log *log) +{ + const char *_errlog_type[] = { + "unknown type", + "too many tries failed", + "TCE error", + "RTAS device failed", + "target timed out", + "parity error on data", /* 5 */ + "parity error on address", + "parity error on external cache", + "access to invalid address", + "uncorrectable ECC error", + "corrected ECC error" /* 10 */ + }; + if (log->type == TYPE_EPOW) + return "EPOW"; + if (log->type >= TYPE_PMGM_POWER_SW_ON) + return "PowerMGM Event (not handled right now)"; + return _errlog_type[log->type]; +} + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/rtas-proc.c linuxppc64_2_4/arch/ppc64/kernel/rtas-proc.c --- linux-2.4.5-ac18/arch/ppc64/kernel/rtas-proc.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/rtas-proc.c Wed May 30 18:54:55 2001 @@ -0,0 +1,794 @@ +/* + * arch/ppc64/kernel/rtas-proc.c + * Copyright (C) 2000 Tilmann Bitterberg + * (tilmann@bitterberg.de) + * + * RTAS (Runtime Abstraction Services) stuff + * Intention is to provide a clean user interface + * to use the RTAS. + * + * TODO: + * Split off a header file and maybe move it to a different + * location. Write Documentation on what the /proc/rtas/ entries + * actually do. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include /* for ppc_md */ +#include + +/* Token for Sensors */ +#define KEY_SWITCH 0x0001 +#define ENCLOSURE_SWITCH 0x0002 +#define THERMAL_SENSOR 0x0003 +#define LID_STATUS 0x0004 +#define POWER_SOURCE 0x0005 +#define BATTERY_VOLTAGE 0x0006 +#define BATTERY_REMAINING 0x0007 +#define BATTERY_PERCENTAGE 0x0008 +#define EPOW_SENSOR 0x0009 +#define BATTERY_CYCLESTATE 0x000a +#define BATTERY_CHARGING 0x000b + +/* IBM specific sensors */ +#define IBM_SURVEILLANCE 0x2328 /* 9000 */ +#define IBM_FANRPM 0x2329 /* 9001 */ +#define IBM_VOLTAGE 0x232a /* 9002 */ +#define IBM_DRCONNECTOR 0x232b /* 9003 */ +#define IBM_POWERSUPPLY 0x232c /* 9004 */ +#define IBM_INTQUEUE 0x232d /* 9005 */ + +/* Status return values */ +#define SENSOR_CRITICAL_HIGH 13 +#define SENSOR_WARNING_HIGH 12 +#define SENSOR_NORMAL 11 +#define SENSOR_WARNING_LOW 10 +#define SENSOR_CRITICAL_LOW 9 +#define SENSOR_SUCCESS 0 +#define SENSOR_HW_ERROR -1 +#define SENSOR_BUSY -2 +#define SENSOR_NOT_EXIST -3 +#define SENSOR_DR_ENTITY -9000 + +/* Location Codes */ +#define LOC_SCSI_DEV_ADDR 'A' +#define LOC_SCSI_DEV_LOC 'B' +#define LOC_CPU 'C' +#define LOC_DISKETTE 'D' +#define LOC_ETHERNET 'E' +#define LOC_FAN 'F' +#define LOC_GRAPHICS 'G' +/* reserved / not used 'H' */ +#define LOC_IO_ADAPTER 'I' +/* reserved / not used 'J' */ +#define LOC_KEYBOARD 'K' +#define LOC_LCD 'L' +#define LOC_MEMORY 'M' +#define LOC_NV_MEMORY 'N' +#define LOC_MOUSE 'O' +#define LOC_PLANAR 'P' +#define LOC_OTHER_IO 'Q' +#define LOC_PARALLEL 'R' +#define LOC_SERIAL 'S' +#define LOC_DEAD_RING 'T' +#define LOC_RACKMOUNTED 'U' /* for _u_nit is rack mounted */ +#define LOC_VOLTAGE 'V' +#define LOC_SWITCH_ADAPTER 'W' +#define LOC_OTHER 'X' +#define LOC_FIRMWARE 'Y' +#define LOC_SCSI 'Z' + +/* Tokens for indicators */ +#define TONE_FREQUENCY 0x0001 /* 0 - 1000 (HZ)*/ +#define TONE_VOLUME 0x0002 /* 0 - 100 (%) */ +#define SYSTEM_POWER_STATE 0x0003 +#define WARNING_LIGHT 0x0004 +#define DISK_ACTIVITY_LIGHT 0x0005 +#define HEX_DISPLAY_UNIT 0x0006 +#define BATTERY_WARNING_TIME 0x0007 +#define CONDITION_CYCLE_REQUEST 0x0008 +#define SURVEILLANCE_INDICATOR 0x2328 /* 9000 */ +#define DR_ACTION 0x2329 /* 9001 */ +#define DR_INDICATOR 0x232a /* 9002 */ +/* 9003 - 9004: Vendor specific */ +#define GLOBAL_INTERRUPT_QUEUE 0x232d /* 9005 */ +/* 9006 - 9999: Vendor specific */ + +/* other */ +#define MAX_SENSORS 17 /* I only know of 17 sensors */ +#define MAX_LINELENGTH 256 +#define SENSOR_PREFIX "ibm,sensor-" +#define cel_to_fahr(x) ((x*9/5)+32) + + +/* Globals */ +static struct proc_dir_entry *proc_rtas; +static struct rtas_sensors sensors; +static struct device_node *rtas; +static unsigned long power_on_time = 0; /* Save the time the user set */ +static char progress_led[MAX_LINELENGTH]; + +static unsigned long rtas_tone_frequency = 1000; +static unsigned long rtas_tone_volume = 0; + +/* ****************STRUCTS******************************************* */ +struct individual_sensor { + unsigned int token; + unsigned int quant; +}; + +struct rtas_sensors { + struct individual_sensor sensor[MAX_SENSORS]; + unsigned int quant; +}; + +/* ****************************************************************** */ +/* Declarations */ +static int ppc_rtas_sensor_read(char * buf, char ** start, off_t off, + int count, int *eof, void *data); +static ssize_t ppc_rtas_clock_read(struct file * file, char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_clock_write(struct file * file, const char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_progress_read(struct file * file, char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_progress_write(struct file * file, const char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_poweron_read(struct file * file, char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_poweron_write(struct file * file, const char * buf, + size_t count, loff_t *ppos); + +static ssize_t ppc_rtas_tone_freq_write(struct file * file, const char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_tone_freq_read(struct file * file, char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_tone_volume_write(struct file * file, const char * buf, + size_t count, loff_t *ppos); +static ssize_t ppc_rtas_tone_volume_read(struct file * file, char * buf, + size_t count, loff_t *ppos); + +struct file_operations ppc_rtas_poweron_operations = { + read: ppc_rtas_poweron_read, + write: ppc_rtas_poweron_write +}; +struct file_operations ppc_rtas_progress_operations = { + read: ppc_rtas_progress_read, + write: ppc_rtas_progress_write +}; + +struct file_operations ppc_rtas_clock_operations = { + read: ppc_rtas_clock_read, + write: ppc_rtas_clock_write +}; + +struct file_operations ppc_rtas_tone_freq_operations = { + read: ppc_rtas_tone_freq_read, + write: ppc_rtas_tone_freq_write +}; +struct file_operations ppc_rtas_tone_volume_operations = { + read: ppc_rtas_tone_volume_read, + write: ppc_rtas_tone_volume_write +}; + +int ppc_rtas_find_all_sensors (void); +int ppc_rtas_process_sensor(struct individual_sensor s, int state, + int error, char * buf); +char * ppc_rtas_process_error(int error); +int get_location_code(struct individual_sensor s, char * buf); +int check_location_string (char *c, char * buf); +int check_location (char *c, int idx, char * buf); + +/* ****************************************************************** */ +/* MAIN */ +/* ****************************************************************** */ +void proc_rtas_init(void) +{ + struct proc_dir_entry *entry; + + rtas = find_devices("rtas"); + if ((rtas == 0) || (_machine != _MACH_chrp)) { + return; + } + + proc_rtas = proc_mkdir("rtas", 0); + if (proc_rtas == 0) + return; + + /* /proc/rtas entries */ + + entry = create_proc_entry("progress", S_IRUGO|S_IWUSR, proc_rtas); + if (entry) entry->proc_fops = &ppc_rtas_progress_operations; + + entry = create_proc_entry("clock", S_IRUGO|S_IWUSR, proc_rtas); + if (entry) entry->proc_fops = &ppc_rtas_clock_operations; + + entry = create_proc_entry("poweron", S_IWUSR|S_IRUGO, proc_rtas); + if (entry) entry->proc_fops = &ppc_rtas_poweron_operations; + + create_proc_read_entry("sensors", S_IRUGO, proc_rtas, + ppc_rtas_sensor_read, NULL); + + entry = create_proc_entry("frequency", S_IWUSR|S_IRUGO, proc_rtas); + if (entry) entry->proc_fops = &ppc_rtas_tone_freq_operations; + + entry = create_proc_entry("volume", S_IWUSR|S_IRUGO, proc_rtas); + if (entry) entry->proc_fops = &ppc_rtas_tone_volume_operations; +} + +/* ****************************************************************** */ +/* POWER-ON-TIME */ +/* ****************************************************************** */ +static ssize_t ppc_rtas_poweron_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + struct rtc_time tm; + unsigned long nowtime; + char *dest; + int error; + + nowtime = simple_strtoul(buf, &dest, 10); + if (*dest != '\0' && *dest != '\n') { + printk("ppc_rtas_poweron_write: Invalid time\n"); + return count; + } + power_on_time = nowtime; /* save the time */ + + to_tm(nowtime, &tm); + + error = call_rtas("set-time-for-power-on", 7, 1, NULL, + tm.tm_year, tm.tm_mon, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, 0 /* nano */); + if (error != 0) + printk(KERN_WARNING "error: setting poweron time returned: %s\n", + ppc_rtas_process_error(error)); + return count; +} +/* ****************************************************************** */ +static ssize_t ppc_rtas_poweron_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + int n; + if (power_on_time == 0) + n = sprintf(buf, "Power on time not set\n"); + else + n = sprintf(buf, "%lu\n", power_on_time); + + if (*ppos >= strlen(buf)) + return 0; + if (n > strlen(buf) - *ppos) + n = strlen(buf) - *ppos; + if (n > count) + n = count; + *ppos += n; + return n; +} + +/* ****************************************************************** */ +/* PROGRESS */ +/* ****************************************************************** */ +static ssize_t ppc_rtas_progress_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + unsigned long hex; + + strcpy(progress_led, buf); /* save the string */ + /* Lets see if the user passed hexdigits */ + hex = simple_strtoul(buf, NULL, 10); + + ppc_md.progress ((char *)buf, hex); + return count; + + /* clear the line */ /* ppc_md.progress(" ", 0xffff);*/ +} +/* ****************************************************************** */ +static ssize_t ppc_rtas_progress_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + int n = 0; + if (progress_led != NULL) + n = sprintf (buf, "%s\n", progress_led); + if (*ppos >= strlen(buf)) + return 0; + if (n > strlen(buf) - *ppos) + n = strlen(buf) - *ppos; + if (n > count) + n = count; + *ppos += n; + return n; +} + +/* ****************************************************************** */ +/* CLOCK */ +/* ****************************************************************** */ +static ssize_t ppc_rtas_clock_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + struct rtc_time tm; + unsigned long nowtime; + char *dest; + int error; + + nowtime = simple_strtoul(buf, &dest, 10); + if (*dest != '\0' && *dest != '\n') { + printk("ppc_rtas_clock_write: Invalid time\n"); + return count; + } + + to_tm(nowtime, &tm); + error = call_rtas("set-time-of-day", 7, 1, NULL, + tm.tm_year, tm.tm_mon, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, 0); + if (error != 0) + printk(KERN_WARNING "error: setting the clock returned: %s\n", + ppc_rtas_process_error(error)); + return count; +} +/* ****************************************************************** */ +static ssize_t ppc_rtas_clock_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + unsigned int year, mon, day, hour, min, sec; + unsigned long *ret = kmalloc(4*8, GFP_KERNEL); + int n, error; + + error = call_rtas("get-time-of-day", 0, 8, ret); + + year = ret[0]; mon = ret[1]; day = ret[2]; + hour = ret[3]; min = ret[4]; sec = ret[5]; + + if (error != 0){ + printk(KERN_WARNING "error: reading the clock returned: %s\n", + ppc_rtas_process_error(error)); + n = sprintf (buf, "0"); + } else { + n = sprintf (buf, "%lu\n", mktime(year, mon, day, hour, min, sec)); + } + kfree(ret); + + if (*ppos >= strlen(buf)) + return 0; + if (n > strlen(buf) - *ppos) + n = strlen(buf) - *ppos; + if (n > count) + n = count; + *ppos += n; + return n; +} + +/* ****************************************************************** */ +/* SENSOR STUFF */ +/* ****************************************************************** */ +static int ppc_rtas_sensor_read(char * buf, char ** start, off_t off, + int count, int *eof, void *data) +{ + int i,j,n; + unsigned long ret; + int state, error; + char *buffer; + + if (count < 0) + return -EINVAL; + + /* May not be enough */ + buffer = kmalloc(buffer, MAX_LINELENGTH*MAX_SENSORS); + + if (!buffer) + return -ENOMEM; + + memset(buffer, 0, MAX_LINELENGTH*MAX_SENSORS); + + n = sprintf ( buffer , "RTAS (RunTime Abstraction Services) Sensor Information\n"); + n += sprintf ( buffer+n, "Sensor\t\tValue\t\tCondition\tLocation\n"); + n += sprintf ( buffer+n, "********************************************************\n"); + + if (ppc_rtas_find_all_sensors() != 0) { + n += sprintf ( buffer+n, "\nNo sensors are available\n"); + goto return_string; + } + + for (i=0; i= 0) { + error = call_rtas("get-sensor-state", 2, 2, &ret, + sensors.sensor[i].token, sensors.sensor[i].quant-j); + state = (int) ret; + n += ppc_rtas_process_sensor(sensors.sensor[i], state, error, buffer+n ); + n += sprintf (buffer+n, "\n"); + j--; + } /* while */ + } /* for */ + +return_string: + if (off >= strlen(buffer)) { + *eof = 1; + kfree(buffer); + return 0; + } + if (n > strlen(buffer) - off) + n = strlen(buffer) - off; + if (n > count) + n = count; + else + *eof = 1; + memcpy(buf, buffer + off, n); + *start = buf; + kfree(buffer); + return n; +} + +/* ****************************************************************** */ + +int ppc_rtas_find_all_sensors (void) +{ + unsigned long *utmp; + int len, i, j; + + utmp = (unsigned long *) get_property(rtas, "rtas-sensors", &len); + if (utmp == NULL) { + printk (KERN_ERR "error: could not get rtas-sensors\n"); + return 1; + } + + sensors.quant = len / 8; /* int + int */ + + for (i=0, j=0; j= llen) pos=0; + } + return n; +} +/* ****************************************************************** */ +/* INDICATORS - Tone Frequency */ +/* ****************************************************************** */ +static ssize_t ppc_rtas_tone_freq_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + unsigned long freq; + char *dest; + int error; + freq = simple_strtoul(buf, &dest, 10); + if (*dest != '\0' && *dest != '\n') { + printk("ppc_rtas_tone_freq_write: Invalid tone freqency\n"); + return count; + } + if (freq < 0) freq = 0; + rtas_tone_frequency = freq; /* save it for later */ + error = call_rtas("set-indicator", 3, 1, NULL, + TONE_FREQUENCY, 0, freq); + if (error != 0) + printk(KERN_WARNING "error: setting tone frequency returned: %s\n", + ppc_rtas_process_error(error)); + return count; +} +/* ****************************************************************** */ +static ssize_t ppc_rtas_tone_freq_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + int n; + n = sprintf(buf, "%lu\n", rtas_tone_frequency); + + if (*ppos >= strlen(buf)) + return 0; + if (n > strlen(buf) - *ppos) + n = strlen(buf) - *ppos; + if (n > count) + n = count; + *ppos += n; + return n; +} +/* ****************************************************************** */ +/* INDICATORS - Tone Volume */ +/* ****************************************************************** */ +static ssize_t ppc_rtas_tone_volume_write(struct file * file, const char * buf, + size_t count, loff_t *ppos) +{ + unsigned long volume; + char *dest; + int error; + volume = simple_strtoul(buf, &dest, 10); + if (*dest != '\0' && *dest != '\n') { + printk("ppc_rtas_tone_volume_write: Invalid tone volume\n"); + return count; + } + if (volume < 0) volume = 0; + if (volume > 100) volume = 100; + + rtas_tone_volume = volume; /* save it for later */ + error = call_rtas("set-indicator", 3, 1, NULL, + TONE_VOLUME, 0, volume); + if (error != 0) + printk(KERN_WARNING "error: setting tone volume returned: %s\n", + ppc_rtas_process_error(error)); + return count; +} +/* ****************************************************************** */ +static ssize_t ppc_rtas_tone_volume_read(struct file * file, char * buf, + size_t count, loff_t *ppos) +{ + int n; + n = sprintf(buf, "%lu\n", rtas_tone_volume); + + if (*ppos >= strlen(buf)) + return 0; + if (n > strlen(buf) - *ppos) + n = strlen(buf) - *ppos; + if (n > count) + n = count; + *ppos += n; + return n; +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/rtas.c linuxppc64_2_4/arch/ppc64/kernel/rtas.c --- linux-2.4.5-ac18/arch/ppc64/kernel/rtas.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/rtas.c Tue May 22 15:00:31 2001 @@ -0,0 +1,209 @@ +/* + * + * Procedures for interfacing to the RTAS on CHRP machines. + * + * Peter Bergner, IBM March 2001. + * Copyright (C) 2001 IBM. + * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * prom_init() is called very early on, before the kernel text + * and data have been mapped to KERNELBASE. At this point the code + * is running at whatever address it has been loaded at, so + * references to extern and static variables must be relocated + * explicitly. The procedure reloc_offset() returns the address + * we're currently running at minus the address we were linked at. + * (Note that strings count as static variables.) + * + * Because OF may have mapped I/O devices into the area starting at + * KERNELBASE, particularly on CHRP machines, we can't safely call + * OF once the kernel has been mapped to KERNELBASE. Therefore all + * OF calls should be done within prom_init(), and prom_init() + * and all routines called within it must be careful to relocate + * references as necessary. + * + * Note that the bss is cleared *after* prom_init runs, so we have + * to make sure that any static or extern variables it accesses + * are put in the data segment. + */ + + +struct rtas_t rtas_data = { 0, 0, 0, SPIN_LOCK_UNLOCKED, NULL, {0, 0, NULL}}; + +extern unsigned long reloc_offset(void); + +void +phys_call_rtas(int token, int nargs, int nret, ...) +{ + va_list list; + unsigned long offset = reloc_offset(); + struct rtas_args *rtas = PTRRELOC(&(get_paca()->xRtas)); + int i; + + rtas->token = token; + rtas->nargs = nargs; + rtas->nret = nret; + rtas->rets = (rtas_arg_t *)PTRRELOC(&(rtas->args[nargs])); + + va_start(list, nret); + for (i = 0; i < nargs; i++) + rtas->args[i] = (rtas_arg_t)LONG_LSW(va_arg(list, ulong)); + va_end(list); + + enter_rtas(rtas); +} + +void +phys_call_rtas_display_status(char c) +{ + unsigned long offset = reloc_offset(); + struct rtas_args *rtas = PTRRELOC(&(get_paca()->xRtas)); + + rtas->token = 10; + rtas->nargs = 1; + rtas->nret = 1; + rtas->rets = (rtas_arg_t *)PTRRELOC(&(rtas->args[1])); + rtas->args[0] = (int)c; + + enter_rtas(rtas); +} + +void +call_rtas_display_status(char c) +{ + struct rtas_args *rtas = &(get_paca()->xRtas); + + rtas->token = 10; + rtas->nargs = 1; + rtas->nret = 1; + rtas->rets = (rtas_arg_t *)&(rtas->args[1]); + rtas->args[0] = (int)c; + + enter_rtas((void *)__pa((unsigned long)rtas)); +} + + +/* #define DEBUG_RTAS */ +__openfirmware +long +call_rtas(const char *service, int nargs, int nret, + unsigned long *outputs, ...) +{ + va_list list; + int i; + unsigned long s; + struct rtas_args *rtas = &(get_paca()->xRtas); + int *tokp; + +#ifdef DEBUG_RTAS + udbg_printf("Entering call_rtas\n"); + udbg_printf("\tservice = '%s'\n", service); + udbg_printf("\tnargs = %d\n", nargs); + udbg_printf("\tnret = %d\n", nret); + udbg_printf("\t&outputs = 0x%lx\n", outputs); +#endif /* DEBUG_RTAS */ + + if (rtas_data.dev == NULL) { +#ifdef DEBUG_RTAS + udbg_printf("\tNo rtas device in device-tree...\n"); +#endif /* DEBUG_RTAS */ + return -1; + } + tokp = (int *) get_property(rtas_data.dev, service, NULL); +#ifdef DEBUG_RTAS + udbg_printf("\ttokp = 0x%lx\n", tokp); + udbg_printf("\ttok = 0x%lx\n", (tokp) ? *tokp : -1); +#endif /* DEBUG_RTAS */ + if (tokp == NULL) { + printk(KERN_ERR "No RTAS service called %s\n", service); + return -1; + } + rtas->token = *tokp; + rtas->nargs = nargs; + rtas->nret = nret; + rtas->rets = (rtas_arg_t *)&(rtas->args[nargs]); + va_start(list, outputs); + for (i = 0; i < nargs; ++i) { + rtas->args[i] = (rtas_arg_t)LONG_LSW(va_arg(list, ulong)); +#ifdef DEBUG_RTAS + udbg_printf("\tnarg[%d] = 0x%lx\n", i, rtas->args[i]); +#endif /* DEBUG_RTAS */ + } + va_end(list); + + for (i = 0; i < nret; ++i) + rtas->rets[i] = 0; + +#if 0 /* Gotta do something different here, use global lock for now... */ + spin_lock_irqsave(&rtas->lock, s); +#else + spin_lock_irqsave(&rtas_data.lock, s); +#endif +#ifdef DEBUG_RTAS + udbg_printf("\tentering rtas with 0x%lx\n", (void *)__pa((unsigned long)rtas)); +#endif /* DEBUG_RTAS */ + enter_rtas((void *)__pa((unsigned long)rtas)); +#ifdef DEBUG_RTAS + udbg_printf("\treturned from rtas ...\n"); +#endif /* DEBUG_RTAS */ +#if 0 /* Gotta do something different here, use global lock for now... */ + spin_unlock_irqrestore(&rtas->lock, s); +#else + spin_unlock_irqrestore(&rtas_data.lock, s); +#endif +#ifdef DEBUG_RTAS + for(i=0; i < nret ;i++) + udbg_printf("\tnret[%d] = 0x%lx\n", i, (ulong)rtas->rets[i]); +#endif /* DEBUG_RTAS */ + + if (nret > 1 && outputs != NULL) + for (i = 0; i < nret-1; ++i) + outputs[i] = rtas->rets[i+1]; + return (ulong)((nret > 0) ? rtas->rets[0] : 0); +} + + +void __chrp +rtas_restart(char *cmd) +{ + printk("RTAS system-reboot returned %d\n", + call_rtas("system-reboot", 0, 1, NULL)); + for (;;); +} + +void __chrp +rtas_power_off(void) +{ + /* allow power on only with power button press */ + printk("RTAS power-off returned %d\n", + call_rtas("power-off", 2, 1, NULL,0xffffffff,0xffffffff)); + for (;;); +} + +void __chrp +rtas_halt(void) +{ + rtas_power_off(); +} + + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/semaphore.c linuxppc64_2_4/arch/ppc64/kernel/semaphore.c --- linux-2.4.5-ac18/arch/ppc64/kernel/semaphore.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/semaphore.c Mon Jun 18 13:47:18 2001 @@ -0,0 +1,130 @@ +/* + * + * + * PowerPC-specific semaphore code. + * + * Copyright (C) 1999 Cort Dougan + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * April 2001 - Reworked by Paul Mackerras + * to eliminate the SMP races in the old version between the updates + * of `count' and `waking'. Now we use negative `count' values to + * indicate that some process(es) are waiting for the semaphore. + */ + +#include +#include +#include + +/* + * Atomically update sem->count. + * This does the equivalent of the following: + * + * old_count = sem->count; + * tmp = MAX(old_count, 0) + incr; + * sem->count = tmp; + * return old_count; + */ +static inline int __sem_update_count(struct semaphore *sem, int incr) +{ + int old_count, tmp; + + __asm__ __volatile__("\n" +"1: lwarx %0,0,%3\n" +" srawi %1,%0,31\n" +" andc %1,%0,%1\n" +" add %1,%1,%4\n" +" stwcx. %1,0,%3\n" +" bne 1b" + : "=&r" (old_count), "=&r" (tmp), "=m" (sem->count) + : "r" (&sem->count), "r" (incr), "m" (sem->count) + : "cc"); + + return old_count; +} + +void __up(struct semaphore *sem) +{ + /* + * Note that we incremented count in up() before we came here, + * but that was ineffective since the result was <= 0, and + * any negative value of count is equivalent to 0. + * This ends up setting count to 1, unless count is now > 0 + * (i.e. because some other cpu has called up() in the meantime), + * in which case we just increment count. + */ + __sem_update_count(sem, 1); + wake_up(&sem->wait); +} + +/* + * Note that when we come in to __down or __down_interruptible, + * we have already decremented count, but that decrement was + * ineffective since the result was < 0, and any negative value + * of count is equivalent to 0. + * Thus it is only when we decrement count from some value > 0 + * that we have actually got the semaphore. + */ +void __down(struct semaphore *sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + tsk->state = TASK_UNINTERRUPTIBLE; + add_wait_queue_exclusive(&sem->wait, &wait); + smp_wmb(); + + /* + * Try to get the semaphore. If the count is > 0, then we've + * got the semaphore; we decrement count and exit the loop. + * If the count is 0 or negative, we set it to -1, indicating + * that we are asleep, and then sleep. + */ + while (__sem_update_count(sem, -1) <= 0) { + schedule(); + tsk->state = TASK_UNINTERRUPTIBLE; + } + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; + + /* + * If there are any more sleepers, wake one of them up so + * that it can either get the semaphore, or set count to -1 + * indicating that there are still processes sleeping. + */ + wake_up(&sem->wait); +} + +int __down_interruptible(struct semaphore * sem) +{ + int retval = 0; + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + tsk->state = TASK_INTERRUPTIBLE; + add_wait_queue_exclusive(&sem->wait, &wait); + smp_wmb(); + + while (__sem_update_count(sem, -1) <= 0) { + if (signal_pending(current)) { + /* + * A signal is pending - give up trying. + * Set sem->count to 0 if it is negative, + * since we are no longer sleeping. + */ + __sem_update_count(sem, 0); + retval = -EINTR; + break; + } + schedule(); + tsk->state = TASK_INTERRUPTIBLE; + } + tsk->state = TASK_RUNNING; + remove_wait_queue(&sem->wait, &wait); + wake_up(&sem->wait); + return retval; +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/setup.c linuxppc64_2_4/arch/ppc64/kernel/setup.c --- linux-2.4.5-ac18/arch/ppc64/kernel/setup.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/setup.c Wed Aug 1 15:46:00 2001 @@ -0,0 +1,653 @@ +/* + * + * Common prep/pmac/chrp boot and setup code. + * + * Copyright (C) 2001 PPC64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern char *klimit; +/* extern void *stab; */ +extern HTAB htab_data; +extern unsigned long loops_per_jiffy; + +int have_of = 1; + +extern void chrp_init(unsigned long r3, + unsigned long r4, + unsigned long r5, + unsigned long r6, + unsigned long r7); + +extern void chrp_init_map_io_space( void ); +extern void iSeries_init( void ); +extern void iSeries_init_early( void ); +extern void mm_init_ppc64( void ); + +#ifdef CONFIG_XMON +extern void xmon_map_scc(void); +#endif + +char saved_command_line[256]; +unsigned char aux_device_present; +struct int_control_struct int_control = +{ + __no_use_cli, + __no_use_sti, + __no_use_restore_flags, + __no_use_save_flags +}; +struct ide_machdep_calls ppc_ide_md; +int parse_bootinfo(void); + +void parse_cmd_line(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7); + +unsigned long DMA_MODE_READ, DMA_MODE_WRITE; +int _machine = _MACH_chrp; /* DRENG prom.c needs this assumption, a better way needs to be found. */ + +#ifdef CONFIG_MAGIC_SYSRQ +unsigned long SYSRQ_KEY; +#endif /* CONFIG_MAGIC_SYSRQ */ + +struct machdep_calls ppc_md; +struct Naca *naca; + +/* + * 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 + * Maybe tie it to serial consoles, since this is really what + * these processors use on existing boards. -- Dan + */ +struct screen_info screen_info = { + 0, 25, /* orig-x, orig-y */ + 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 */ +}; + +/* + * These are used in binfmt_elf.c to put aux entries on the stack + * for each elf executable being started. + */ +int dcache_bsize; +int icache_bsize; +int ucache_bsize; + +/* + * Initialize the PPCDBG state. Called before relocation has been enabled. + */ +void ppcdbg_initialize(void) { + unsigned long offset = reloc_offset(); + struct Naca *reloc_naca = RELOC(naca); + + reloc_naca->debug_switch = PPC_DEBUG_DEFAULT; +} + +void naca_init(void) { + naca->xRamDisk = NULL; + naca->xRamDiskSize = 0; /* in pages */ +} + +/* + * Initialize a set of PACA's, one for each processor. + * + * At this point, relocation is on, but we have not done any other + * setup of the mm subsystem. + */ +void paca_init(void) { +#if 0 + int processorCount = naca->processorCount, i; + struct Paca *paca[]; + + /* Put the array of paca's on a page boundary & allocate 1/2 page of */ + /* storage for each. */ + klimit += (PAGE_SIZE-1) & PAGE_MASK; + naca->xPaca = paca[0] = klimit; + klimit += ((PAGE_SIZE>>1) * processorCount); + + for(i=0; ixPacaIndex = i; + } +#endif +} + +/* + * Determine the type of system this we are running on. + * For the PPC64 kernel, we assume chrp. + */ +void identify_machine(void) { + if ( itLpNaca.xLparInstalled == 1 ) + _machine = _MACH_iSeries; + else + _machine = _MACH_chrp; +} + +/* + * Do some initial setup of the system. The paramters are those which + * were passed in from the bootloader. + */ +void setup_system(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) { + + identify_machine(); + + switch (_machine) { + case _MACH_chrp: + parse_bootinfo(); + chrp_init_map_io_space(); + break; + case _MACH_iSeries: + iSeries_init_early(); + break; + default: + } + + udbg_init(); + + udbg_puts("\n-----------------------------------------------------\n"); + udbg_puts("Naca Info...\n\n"); + udbg_puts("naca = 0x"); + udbg_puthex((unsigned long)naca); + udbg_putc('\n'); + + udbg_puts("naca->processorCount = 0x"); + udbg_puthex(naca->processorCount); + udbg_putc('\n'); + + udbg_puts("naca->physicalMemorySize = 0x"); + udbg_puthex(naca->physicalMemorySize); + udbg_putc('\n'); + + udbg_puts("naca->dCacheL1LineSize = 0x"); + udbg_puthex(naca->dCacheL1LineSize); + udbg_putc('\n'); + + udbg_puts("naca->dCacheL1LogLineSize = 0x"); + udbg_puthex(naca->dCacheL1LogLineSize); + udbg_putc('\n'); + + udbg_puts("naca->dCacheL1LinesPerPage = 0x"); + udbg_puthex(naca->dCacheL1LinesPerPage); + udbg_putc('\n'); + + udbg_puts("naca->iCacheL1LineSize = 0x"); + udbg_puthex(naca->iCacheL1LineSize); + udbg_putc('\n'); + + udbg_puts("naca->iCacheL1LogLineSize = 0x"); + udbg_puthex(naca->iCacheL1LogLineSize); + udbg_putc('\n'); + + udbg_puts("naca->iCacheL1LinesPerPage = 0x"); + udbg_puthex(naca->iCacheL1LinesPerPage); + udbg_putc('\n'); + + udbg_puts("naca->serialPortAddr = 0x"); + udbg_puthex(naca->serialPortAddr); + udbg_putc('\n'); + + udbg_puts("naca->interrupt_controller = 0x"); + udbg_puthex(naca->interrupt_controller); + udbg_putc('\n'); + + udbg_printf("\nHTAB Info ...\n\n"); + udbg_printf("htab_data.htab = 0x%lx\n", htab_data.htab); + udbg_printf("htab_data.htabx = 0x%lx\n", htab_data.htabx); + udbg_printf("htab_data.num_ptegs = 0x%lx\n", htab_data.htab_num_ptegs); + + udbg_puts("\n-----------------------------------------------------\n"); + + + if ( _machine == _MACH_chrp ) { + finish_device_tree(); + chrp_init(r3, r4, r5, r6, r7); + } + + mm_init_ppc64(); + + switch (_machine) { + case _MACH_chrp: // DRENG _MACH_chrp needs to be _MACH_pSeries now + // The following relies on the device tree being fully configured. + parse_cmd_line(r3, r4, r5, r6, r7); + + break; + case _MACH_iSeries: + iSeries_init(); + break; + default: + // Need a way to die here-prob have to display something to the panel DRENG + } +} + +/* + * I really need to add multiple-console support... -- Cort + */ +int __init pmac_display_supported(char *name) +{ + return 0; +} +void __init pmac_find_display(void) +{ +} + +void machine_restart(char *cmd) +{ + ppc_md.restart(cmd); +} + +void machine_power_off(void) +{ + ppc_md.power_off(); +} + +void machine_halt(void) +{ + ppc_md.halt(); +} + +int get_cpuinfo(char *buffer) +{ + unsigned long len = 0; + unsigned long bogosum = 0; + unsigned long i; + unsigned int pvr; + unsigned short maj, min; + +#ifdef CONFIG_SMP +#define CPU_PRESENT(x) (cpu_callin_map[(x)]) +#else +#define CPU_PRESENT(x) ((x)==0) +#define smp_num_cpus 1 +#endif + + for ( i = 0; i < smp_num_cpus ; i++ ) + { + if ( !CPU_PRESENT(i) ) + continue; + if ( i ) + len += sprintf(len+buffer,"\n"); + len += sprintf(len+buffer,"processor\t: %lu\n",i); + len += sprintf(len+buffer,"cpu\t\t: "); + + pvr = naca->paca[i].pvr; + + switch (PVR_VER(pvr)) + { + case PV_PULSAR: + len += sprintf(len+buffer, "RS64-III (pulsar)\n"); + break; + case PV_POWER4: + len += sprintf(len+buffer, "POWER4 (gp)\n"); + break; + case PV_ICESTAR: + len += sprintf(len+buffer, "RS64-III (icestar)\n"); + break; + case PV_SSTAR: + len += sprintf(len+buffer, "RS64-IV (sstar)\n"); + break; + case PV_630: + len += sprintf(len+buffer, "POWER3 (630)\n"); + break; + case PV_630p: + len += sprintf(len+buffer, "POWER3 (630+)\n"); + break; + default: + len += sprintf(len+buffer, "Unknown (%08x)\n", pvr); + break; + } + + /* + * Assume here that all clock rates are the same in a + * smp system. -- Cort + */ + if ( _machine != _MACH_iSeries ) { + struct device_node *cpu_node; + int *fp; + + cpu_node = find_type_devices("cpu"); + if ( !cpu_node ) break; + { + int s; + for ( s = 0; (s < i) && cpu_node->next ; + s++, cpu_node = cpu_node->next ) + /* nothing */ ; + } + fp = (int *) get_property(cpu_node, "clock-frequency", NULL); + if ( !fp ) break; + len += sprintf(len+buffer, "clock\t\t: %dMHz\n", + *fp / 1000000); + } + + if (ppc_md.setup_residual != NULL) + { + len += ppc_md.setup_residual(buffer + len); + } + + maj = (pvr >> 8) & 0xFF; + min = pvr & 0xFF; + + len += sprintf(len+buffer, "revision\t: %hd.%hd\n", maj, min); + + len += sprintf(buffer+len, "bogomips\t: %lu.%02lu\n", + ((naca->loops_per_jiffy)+2500)/(500000/HZ), + ((naca->loops_per_jiffy)+2500)/(5000/HZ) % 100); + bogosum += naca->loops_per_jiffy; + } + +#ifdef CONFIG_SMP + if ( i ) + len += sprintf(buffer+len, "\n"); + len += sprintf(buffer+len,"total bogomips\t: %lu.%02lu\n", + (bogosum+2500)/(500000/HZ), + (bogosum+2500)/(5000/HZ) % 100); +#endif /* CONFIG_SMP */ + + + if (ppc_md.get_cpuinfo != NULL) + { + len += ppc_md.get_cpuinfo(buffer+len); + } + + return len; +} + +/* + * Fetch the cmd_line from open firmware. */ +void parse_cmd_line(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) { + struct device_node *chosen; + char *p; + +#ifdef CONFIG_BLK_DEV_INITRD + if (r3 && r4 && r4 != 0xdeadbeef) + { + if (r3 < KERNELBASE) + r3 += KERNELBASE; + initrd_start = r3; + initrd_end = r3 + r4; + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); + initrd_below_start_ok = 1; + } +#endif + + cmd_line[0] = 0; + chosen = find_devices("chosen"); + if (chosen != NULL) { + p = get_property(chosen, "bootargs", NULL); + if (p != NULL) + strncpy(cmd_line, p, sizeof(cmd_line)); + } + cmd_line[sizeof(cmd_line) - 1] = 0; + + /* Look for mem= option on command line */ + if (strstr(cmd_line, "mem=")) { + char *p, *q; + unsigned long maxmem = 0; + extern unsigned long __max_memory; + + for (q = cmd_line; (p = strstr(q, "mem=")) != 0; ) { + q = p + 4; + if (p > cmd_line && p[-1] != ' ') + continue; + maxmem = simple_strtoul(q, &q, 0); + if (*q == 'k' || *q == 'K') { + maxmem <<= 10; + ++q; + } else if (*q == 'm' || *q == 'M') { + maxmem <<= 20; + ++q; + } + } + __max_memory = maxmem; + } + ppc_md.progress("id mach: done", 0x200); +} + +int parse_bootinfo(void) +{ +#if 0 + /* DRENG this stuff is needs to be completly redone in the context of + * yaboot. Remove for now - I think we can wait on this for some time. + */ + struct bi_record *rec; + extern char __bss_start[]; + extern char *sysmap; + extern unsigned long sysmap_size; + + rec = (struct bi_record *)_ALIGN((ulong)__bss_start+(1<<20)-1,(1<<20)); + if ( rec->tag != BI_FIRST ) + { + /* + * This 0x10000 offset is a terrible hack but it will go away when + * we have the bootloader handle all the relocation and + * prom calls -- Cort + */ + rec = (struct bi_record *)_ALIGN((ulong)__bss_start+0x10000+(1<<20)-1,(1<<20)); + if ( rec->tag != BI_FIRST ) + return -1; + } + for ( ; rec->tag != BI_LAST ; + rec = (struct bi_record *)((ulong)rec + rec->size) ) + { + ulong *data = rec->data; + switch (rec->tag) + { + case BI_CMD_LINE: + memcpy(cmd_line, (void *)data, rec->size); + break; + case BI_SYSMAP: + sysmap = (char *)((data[0] >= (KERNELBASE)) ? data[0] : + (data[0]+KERNELBASE)); + sysmap_size = data[1]; + break; +#ifdef CONFIG_BLK_DEV_INITRD + case BI_INITRD: + initrd_start = data[0]; + initrd_end = data[0] + rec->size; + break; +#endif /* CONFIG_BLK_DEV_INITRD */ + } + } +#endif + return 0; +} + +void __init ppc_init(void) +{ + /* clear the progress line */ + ppc_md.progress(" ", 0xffff); + + if (ppc_md.init != NULL) { + ppc_md.init(); + } +} + +/* + * Called into from start_kernel, after lock_kernel has been called. + * Initializes bootmem, which is unsed to manage page allocation until + * mem_init is called. + */ +void __init setup_arch(char **cmdline_p) +{ + extern int panic_timeout; + extern char _etext[], _edata[]; + extern void do_init_bootmem(void); + +#ifdef CONFIG_XMON + xmon_map_scc(); + if (strstr(cmd_line, "xmon")) + xmon(0); +#endif /* CONFIG_XMON */ +#ifdef CONFIG_KDB + xmon_map_scc(); /* in kdb/start.c --need to rename TAI */ +#endif + ppc_md.progress("setup_arch:enter", 0x3eab); + +#if defined(CONFIG_KGDB) + kgdb_map_scc(); + set_debug_traps(); + breakpoint(); +#endif + /* + * Set cache line size based on type of cpu as a default. + * Systems with OF can look in the properties on the cpu node(s) + * for a possibly more accurate value. + */ + dcache_bsize = naca->dCacheL1LineSize; + icache_bsize = naca->iCacheL1LineSize; + + /* reboot on panic */ + panic_timeout = 180; + + init_mm.start_code = PAGE_OFFSET; + init_mm.end_code = (unsigned long) _etext; + init_mm.end_data = (unsigned long) _edata; + init_mm.brk = (unsigned long) klimit; + + /* Save unparsed command line copy for /proc/cmdline */ + strcpy(saved_command_line, cmd_line); + *cmdline_p = cmd_line; + + /* set up the bootmem stuff with available memory */ + do_init_bootmem(); + ppc_md.progress("setup_arch:bootmem", 0x3eab); + + ppc_md.setup_arch(); + + paging_init(); + ppc_md.progress("setup_arch: exit", 0x3eab); +} + +void ppc_generic_ide_fix_driveid(struct hd_driveid *id) +{ + int i; + unsigned short *stringcast; + + id->config = __le16_to_cpu(id->config); + id->cyls = __le16_to_cpu(id->cyls); + id->reserved2 = __le16_to_cpu(id->reserved2); + id->heads = __le16_to_cpu(id->heads); + id->track_bytes = __le16_to_cpu(id->track_bytes); + id->sector_bytes = __le16_to_cpu(id->sector_bytes); + id->sectors = __le16_to_cpu(id->sectors); + id->vendor0 = __le16_to_cpu(id->vendor0); + id->vendor1 = __le16_to_cpu(id->vendor1); + id->vendor2 = __le16_to_cpu(id->vendor2); + stringcast = (unsigned short *)&id->serial_no[0]; + for (i = 0; i < (20/2); i++) + stringcast[i] = __le16_to_cpu(stringcast[i]); + id->buf_type = __le16_to_cpu(id->buf_type); + id->buf_size = __le16_to_cpu(id->buf_size); + id->ecc_bytes = __le16_to_cpu(id->ecc_bytes); + stringcast = (unsigned short *)&id->fw_rev[0]; + for (i = 0; i < (8/2); i++) + stringcast[i] = __le16_to_cpu(stringcast[i]); + stringcast = (unsigned short *)&id->model[0]; + for (i = 0; i < (40/2); i++) + stringcast[i] = __le16_to_cpu(stringcast[i]); + id->dword_io = __le16_to_cpu(id->dword_io); + id->reserved50 = __le16_to_cpu(id->reserved50); + id->field_valid = __le16_to_cpu(id->field_valid); + id->cur_cyls = __le16_to_cpu(id->cur_cyls); + id->cur_heads = __le16_to_cpu(id->cur_heads); + id->cur_sectors = __le16_to_cpu(id->cur_sectors); + id->cur_capacity0 = __le16_to_cpu(id->cur_capacity0); + id->cur_capacity1 = __le16_to_cpu(id->cur_capacity1); + id->lba_capacity = __le32_to_cpu(id->lba_capacity); + id->dma_1word = __le16_to_cpu(id->dma_1word); + id->dma_mword = __le16_to_cpu(id->dma_mword); + id->eide_pio_modes = __le16_to_cpu(id->eide_pio_modes); + id->eide_dma_min = __le16_to_cpu(id->eide_dma_min); + id->eide_dma_time = __le16_to_cpu(id->eide_dma_time); + id->eide_pio = __le16_to_cpu(id->eide_pio); + id->eide_pio_iordy = __le16_to_cpu(id->eide_pio_iordy); + for (i = 0; i < 2; i++) + id->words69_70[i] = __le16_to_cpu(id->words69_70[i]); + for (i = 0; i < 4; i++) + id->words71_74[i] = __le16_to_cpu(id->words71_74[i]); + id->queue_depth = __le16_to_cpu(id->queue_depth); + for (i = 0; i < 4; i++) + id->words76_79[i] = __le16_to_cpu(id->words76_79[i]); + id->major_rev_num = __le16_to_cpu(id->major_rev_num); + id->minor_rev_num = __le16_to_cpu(id->minor_rev_num); + id->command_set_1 = __le16_to_cpu(id->command_set_1); + id->command_set_2 = __le16_to_cpu(id->command_set_2); + id->cfsse = __le16_to_cpu(id->cfsse); + id->cfs_enable_1 = __le16_to_cpu(id->cfs_enable_1); + id->cfs_enable_2 = __le16_to_cpu(id->cfs_enable_2); + id->csf_default = __le16_to_cpu(id->csf_default); + id->dma_ultra = __le16_to_cpu(id->dma_ultra); + id->word89 = __le16_to_cpu(id->word89); + id->word90 = __le16_to_cpu(id->word90); + id->CurAPMvalues = __le16_to_cpu(id->CurAPMvalues); + id->word92 = __le16_to_cpu(id->word92); + id->hw_config = __le16_to_cpu(id->hw_config); + for (i = 0; i < 32; i++) + id->words94_125[i] = __le16_to_cpu(id->words94_125[i]); + id->last_lun = __le16_to_cpu(id->last_lun); + id->word127 = __le16_to_cpu(id->word127); + id->dlf = __le16_to_cpu(id->dlf); + id->csfo = __le16_to_cpu(id->csfo); + for (i = 0; i < 26; i++) + id->words130_155[i] = __le16_to_cpu(id->words130_155[i]); + id->word156 = __le16_to_cpu(id->word156); + for (i = 0; i < 3; i++) + id->words157_159[i] = __le16_to_cpu(id->words157_159[i]); + for (i = 0; i < 96; i++) + id->words160_255[i] = __le16_to_cpu(id->words160_255[i]); +} + +void exception_trace(unsigned long trap) { + unsigned long x, srr0, srr1, reg20, reg1, reg21; + + asm("mflr %0" : "=r" (x) :); + asm("mfspr %0,0x1a" : "=r" (srr0) :); + asm("mfspr %0,0x1b" : "=r" (srr1) :); + asm("mr %0,1" : "=r" (reg1) :); + asm("mr %0,20" : "=r" (reg20) :); + asm("mr %0,21" : "=r" (reg21) :); + + udbg_puts("\n"); + udbg_puts("Took an exception : "); udbg_puthex(x); udbg_puts("\n"); + udbg_puts(" "); udbg_puthex(reg1); udbg_puts("\n"); + udbg_puts(" "); udbg_puthex(reg20); udbg_puts("\n"); + udbg_puts(" "); udbg_puthex(reg21); udbg_puts("\n"); + udbg_puts(" "); udbg_puthex(srr0); udbg_puts("\n"); + udbg_puts(" "); udbg_puthex(srr1); udbg_puts("\n"); +} + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/signal.c linuxppc64_2_4/arch/ppc64/kernel/signal.c --- linux-2.4.5-ac18/arch/ppc64/kernel/signal.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/signal.c Thu Jul 12 00:55:37 2001 @@ -0,0 +1,823 @@ +/* + * linux/arch/ppc64/kernel/signal.c + * + * + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/i386/kernel/signal.c" + * Copyright (C) 1991, 1992 Linus Torvalds + * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEBUG_SIG 0 + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#define GP_REGS_SIZE MIN(sizeof(elf_gregset_t), sizeof(struct pt_regs)) + +/* + * 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) + +int do_signal(sigset_t *oldset, struct pt_regs *regs); +extern long sys_wait4(pid_t pid, unsigned int *stat_addr, + int options, /*unsigned long*/ struct rusage *ru); + +int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) +{ + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + if (from->si_code < 0) + return __copy_to_user(to, from, sizeof(siginfo_t)); + else { + int err; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + /* First 32bits of unions are always present. */ + err |= __put_user(from->si_pid, &to->si_pid); + switch (from->si_code >> 16) { + case __SI_FAULT >> 16: + break; + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_uid, &to->si_uid); + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + return err; + } +} + +/* + * Atomically swap in the new signal mask, and wait for a signal. + */ +long sys_sigsuspend(old_sigset_t mask, int p2, int p3, int p4, int p6, int p7, + struct pt_regs *regs) +{ + sigset_t saveset; + + PPCDBG(PPCDBG_SYS64X, "sys_sigsuspend - running - pid=%ld current=%lx comm=%s \n", + current->pid, current, current->comm); + + + + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + regs->gpr[3] = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(&saveset, regs)) + /* + * If a signal handler needs to be called, + * do_signal() has set R3 to the signal number (the + * first argument of the signal handler), so don't + * overwrite that with EINTR ! + * In the other cases, do_signal() doesn't touch + * R3, so it's still set to -EINTR (see above). + */ + return regs->gpr[3]; + } +} + +long sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, int p3, int p4, int p6, + int p7, struct pt_regs *regs) +{ + sigset_t saveset, newset; + + + PPCDBG(PPCDBG_SYS64X, "sys_rt_sigsuspend - running - pid=%ld current=%lx comm=%s \n", + current->pid, current, current->comm); + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&newset, unewset, sizeof(newset))) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + regs->gpr[3] = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(&saveset, regs)) + return regs->gpr[3]; + } +} + + + +asmlinkage long sys_sigaltstack(const stack_t *uss, stack_t *uoss) +{ + struct pt_regs *regs = (struct pt_regs *) &uss; + + PPCDBG(PPCDBG_SYS64X, "sys_sigaltstack - running - pid=%ld current=%lx comm=%s \n", + current->pid, current, current->comm); + + return do_sigaltstack(uss, uoss, regs->gpr[1]); +} + +long sys_sigaction(int sig, const struct old_sigaction *act, + struct old_sigaction *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + PPCDBG(PPCDBG_SYS64X, "sys_sigaction - running - pid=%ld current=%lx comm=%s \n", + current->pid, current, current->comm); + + + + if (act) { + old_sigset_t mask; + if (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) + return -EFAULT; + __get_user(new_ka.sa.sa_flags, &act->sa_flags); + __get_user(mask, &act->sa_mask); + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) + return -EFAULT; + __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + + + + return ret; +} + +/* + * When we have signals to deliver, we set up on the + * user stack, going down from the original stack pointer: + * a sigregs struct + * one or more sigcontext structs + * a gap of __SIGNAL_FRAMESIZE bytes + * + * Each of these things must be a multiple of 16 bytes in size. + * + * XXX ultimately we will have to stack up a siginfo and ucontext + * for each rt signal. + */ +struct sigregs { + elf_gregset_t gp_regs; + double fp_regs[ELF_NFPREG]; + unsigned int tramp[2]; + /* 64 bit API allows for 288 bytes below sp before + decrementing it. */ + int abigap[72]; +}; + + + +struct rt_sigframe +{ + unsigned long _unused[2]; + struct siginfo *pinfo; + void *puc; + struct siginfo info; + struct ucontext uc; +}; + + +/* + * When we have rt signals to deliver, we set up on the + * user stack, going down from the original stack pointer: + * a sigregs struct + * one rt_sigframe struct (siginfo + ucontext) + * a gap of __SIGNAL_FRAMESIZE bytes + * + * Each of these things must be a multiple of 16 bytes in size. + * + */ + +extern long sys32_rt_sigreturn(struct pt_regs *regs); + +int sys_rt_sigreturn(struct pt_regs *regs) +{ + struct rt_sigframe *rt_sf; + struct sigcontext_struct sigctx; + struct sigregs *sr; + int ret; + elf_gregset_t saved_regs; /* an array of ELF_NGREG unsigned longs */ + sigset_t set; + stack_t st; + unsigned long prevsp; + + + PPCDBG(PPCDBG_SYS64X, "sys_rt_sigreturn - running - pid=%ld current=%lx comm=%s \n", + current->pid, current, current->comm); + + + /* If runnining a 32 bit process, then execute a 32 bit signal return */ + if (current->thread.flags & PPC_FLAG_32BIT) + { + ret = sys32_rt_sigreturn(regs); + PPCDBG(PPCDBG_SYS64NI, "sys_sigreturn - returned from sys32_sigreturn w/ %ld \n", ret); + return ret; + } + + rt_sf = (struct rt_sigframe *)(regs->gpr[1] + __SIGNAL_FRAMESIZE); + if (copy_from_user(&sigctx, &rt_sf->uc.uc_mcontext, sizeof(sigctx)) + || copy_from_user(&set, &rt_sf->uc.uc_sigmask, sizeof(set)) + || copy_from_user(&st, &rt_sf->uc.uc_stack, sizeof(st))) + goto badframe; + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + rt_sf++; /* Look at next rt_sigframe */ + if (rt_sf == (struct rt_sigframe *)(sigctx.regs)) { + /* Last stacked signal - restore registers - + * sigctx is initialized to point to the + * preamble frame (where registers are stored) + * see handle_signal() + */ + sr = (struct sigregs *) sigctx.regs; + if (regs->msr & MSR_FP ) + giveup_fpu(current); + if (copy_from_user(saved_regs, &sr->gp_regs, + sizeof(sr->gp_regs))) + goto badframe; + saved_regs[PT_MSR] = (regs->msr & ~MSR_USERCHANGE) + | (saved_regs[PT_MSR] & MSR_USERCHANGE); + memcpy(regs, saved_regs, GP_REGS_SIZE); + if (copy_from_user(current->thread.fpr, &sr->fp_regs, + sizeof(sr->fp_regs))) + goto badframe; + /* This function sets back the stack flags into + the current task structure. */ + sys_sigaltstack(&st, NULL); + + ret = regs->result; + } else { + /* More signals to go */ + /* Set up registers for next signal handler */ + regs->gpr[1] = (unsigned long)rt_sf - __SIGNAL_FRAMESIZE; + if (copy_from_user(&sigctx, &rt_sf->uc.uc_mcontext, sizeof(sigctx))) + goto badframe; + sr = (struct sigregs *) sigctx.regs; + regs->gpr[3] = ret = sigctx.signal; + /* Get the siginfo */ + get_user(regs->gpr[4], (unsigned long *)&rt_sf->pinfo); + /* Get the ucontext */ + get_user(regs->gpr[5], (unsigned long *)&rt_sf->puc); + regs->gpr[6] = (unsigned long) rt_sf; + + regs->link = (unsigned long) &sr->tramp; + regs->nip = sigctx.handler; + if (get_user(prevsp, &sr->gp_regs[PT_R1]) + || put_user(prevsp, (unsigned long *) regs->gpr[1])) + goto badframe; + } + return ret; + +badframe: + do_exit(SIGSEGV); +} + +static void +setup_rt_frame(struct pt_regs *regs, struct sigregs *frame, + signed long newsp) +{ + struct rt_sigframe *rt_sf = (struct rt_sigframe *) newsp; + /* Handler is *really* a pointer to the function descriptor for + * the signal routine. The first entry in the function + * descriptor is the entry address of signal and the second + * entry is the TOC value we need to use. + */ + struct funct_descr_entry { + unsigned long entry; + unsigned long toc; + }; + + + struct funct_descr_entry * funct_desc_ptr; + unsigned long temp_ptr; + + + /* Set up preamble frame */ + if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) + goto badframe; + if (regs->msr & MSR_FP) + giveup_fpu(current); + if (__copy_to_user(&frame->gp_regs, regs, GP_REGS_SIZE) + || __copy_to_user(&frame->fp_regs, current->thread.fpr, + ELF_NFPREG * sizeof(double)) + /* Set up to return from user space. + It calls the sc exception at offset 0x9999 + for sys_rt_sigreturn(). + */ + || __put_user(0x38006666UL, &frame->tramp[0]) /* li r0,0x6666 */ + || __put_user(0x44000002UL, &frame->tramp[1])) /* sc */ + goto badframe; + flush_icache_range((unsigned long) &frame->tramp[0], + (unsigned long) &frame->tramp[2]); + + /* Retrieve rt_sigframe from stack and + set up registers for signal handler + */ + newsp -= __SIGNAL_FRAMESIZE; + + if ( get_user(temp_ptr, &rt_sf->uc.uc_mcontext.handler)) + { + goto badframe; + } + + funct_desc_ptr = ( struct funct_descr_entry *) temp_ptr; + + if (put_user(regs->gpr[1], (unsigned long *)newsp) + || get_user(regs->nip, &funct_desc_ptr->entry) + || get_user(regs->gpr[2], &funct_desc_ptr->toc) + || get_user(regs->gpr[3], &rt_sf->uc.uc_mcontext.signal) + || get_user(regs->gpr[4], (unsigned long *)&rt_sf->pinfo) + || get_user(regs->gpr[5], (unsigned long *)&rt_sf->puc)) + goto badframe; + + regs->gpr[1] = newsp; + regs->gpr[6] = (unsigned long) rt_sf; + regs->link = (unsigned long) frame->tramp; + + + return; + +badframe: +#if DEBUG_SIG + printk("badframe in setup_rt_frame, regs=%p frame=%p newsp=%lx\n", + regs, frame, newsp); +#endif + do_exit(SIGSEGV); +} + +/* + * Do a signal return; undo the signal stack. + */ +extern long sys32_sigreturn(struct pt_regs *regs); +asmlinkage long sys_sigreturn(struct pt_regs *regs) +{ + struct sigcontext_struct *sc, sigctx; + struct sigregs *sr; + long ret; + elf_gregset_t saved_regs; /* an array of ELF_NGREG unsigned longs */ + sigset_t set; + unsigned long prevsp; + + PPCDBG(PPCDBG_SYS64NI, "sys_sigreturn - running - pid=%ld current=%lx comm=%s \n", + current->pid, current, current->comm); + + /* If runnining a 32 bit process, then execute a 32 bit signal return */ + if (current->thread.flags & PPC_FLAG_32BIT) + { + ret = sys32_sigreturn(regs); + PPCDBG(PPCDBG_SYS64NI, "sys_sigreturn - returned from sys32_sigreturn w/ %ld \n", ret); + return ret; + } + + sc = (struct sigcontext_struct *)(regs->gpr[1] + __SIGNAL_FRAMESIZE); + if (copy_from_user(&sigctx, sc, sizeof(sigctx))) + goto badframe; + + set.sig[0] = sigctx.oldmask; +#if _NSIG_WORDS > 1 + set.sig[1] = sigctx._unused[3]; +#endif + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + sc++; /* Look at next sigcontext */ + if (sc == (struct sigcontext_struct *)(sigctx.regs)) { + /* Last stacked signal - restore registers */ + sr = (struct sigregs *) sigctx.regs; + if (regs->msr & MSR_FP ) + giveup_fpu(current); + if (copy_from_user(saved_regs, &sr->gp_regs, + sizeof(sr->gp_regs))) + goto badframe; + saved_regs[PT_MSR] = (regs->msr & ~MSR_USERCHANGE) + | (saved_regs[PT_MSR] & MSR_USERCHANGE); + memcpy(regs, saved_regs, GP_REGS_SIZE); + + if (copy_from_user(current->thread.fpr, &sr->fp_regs, + sizeof(sr->fp_regs))) + goto badframe; + + ret = regs->result; + + } else { + /* More signals to go */ + regs->gpr[1] = (unsigned long)sc - __SIGNAL_FRAMESIZE; + if (copy_from_user(&sigctx, sc, sizeof(sigctx))) + goto badframe; + sr = (struct sigregs *) sigctx.regs; + regs->gpr[3] = ret = sigctx.signal; + regs->gpr[4] = (unsigned long) sc; + regs->link = (unsigned long) &sr->tramp; + regs->nip = sigctx.handler; + + if (get_user(prevsp, &sr->gp_regs[PT_R1]) + || put_user(prevsp, (unsigned long *) regs->gpr[1])) + goto badframe; + } + return ret; + +badframe: + do_exit(SIGSEGV); +} + +/* + * Set up a signal frame. + */ +static void +setup_frame(struct pt_regs *regs, struct sigregs *frame, + unsigned long newsp) +{ + + /* Handler is *really* a pointer to the function descriptor for + * the signal routine. The first entry in the function + * descriptor is the entry address of signal and the second + * entry is the TOC value we need to use. + */ + struct funct_descr_entry { + unsigned long entry; + unsigned long toc; + }; + + + struct funct_descr_entry * funct_desc_ptr; + unsigned long temp_ptr; + + struct sigcontext_struct *sc = (struct sigcontext_struct *) newsp; + + PPCDBG(PPCDBG_SIGNAL, "setup_frame - entered - regs=%p, frame=%p, newsp=%lx \n", regs, frame, newsp); + + if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) + goto badframe; + if (regs->msr & MSR_FP) + giveup_fpu(current); + if (__copy_to_user(&frame->gp_regs, regs, GP_REGS_SIZE) + || __copy_to_user(&frame->fp_regs, current->thread.fpr, + ELF_NFPREG * sizeof(double)) + || __put_user(0x38007777UL, &frame->tramp[0]) /* li r0,0x7777 */ + || __put_user(0x44000002UL, &frame->tramp[1])) /* sc */ + goto badframe; + flush_icache_range((unsigned long) &frame->tramp[0], + (unsigned long) &frame->tramp[2]); + + newsp -= __SIGNAL_FRAMESIZE; + if ( get_user(temp_ptr, &sc->handler)) + goto badframe; + + funct_desc_ptr = ( struct funct_descr_entry *) temp_ptr; + + if (put_user(regs->gpr[1], (unsigned long *)newsp) + || get_user(regs->nip, & funct_desc_ptr ->entry) + || get_user(regs->gpr[2],& funct_desc_ptr->toc) + || get_user(regs->gpr[3], &sc->signal)) + goto badframe; + regs->gpr[1] = newsp; + regs->gpr[4] = (unsigned long) sc; + regs->link = (unsigned long) frame->tramp; + + + PPCDBG(PPCDBG_SIGNAL, "setup_frame - returning - regs->gpr[1]=%lx, regs->gpr[4]=%lx, regs->link=%lx \n", + regs->gpr[1], regs->gpr[4], regs->link); + + return; + + badframe: + PPCDBG(PPCDBG_SIGNAL, "setup_frame - badframe in setup_frame, regs=%p frame=%p newsp=%lx\n", regs, frame, newsp); PPCDBG_ENTER_DEBUGGER(); +#if DEBUG_SIG + printk("badframe in setup_frame, regs=%p frame=%p newsp=%lx\n", + regs, frame, newsp); +#endif + do_exit(SIGSEGV); +} + +/* + * OK, we're invoking a handler + */ +static void +handle_signal(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs, + unsigned long *newspp, unsigned long frame) +{ + struct sigcontext_struct *sc; + struct rt_sigframe *rt_sf; + + if (regs->trap == 0x0C00 /* System Call! */ + && ((int)regs->result == -ERESTARTNOHAND || + ((int)regs->result == -ERESTARTSYS && + !(ka->sa.sa_flags & SA_RESTART)))) + regs->result = -EINTR; + /* Set up Signal Frame */ + + if (ka->sa.sa_flags & SA_SIGINFO) { + /* Put a Real Time Context onto stack */ + *newspp -= sizeof(*rt_sf); + rt_sf = (struct rt_sigframe *) *newspp; + if (verify_area(VERIFY_WRITE, rt_sf, sizeof(*rt_sf))) + goto badframe; + + + if (__put_user((unsigned long) ka->sa.sa_handler, &rt_sf->uc.uc_mcontext.handler) + || __put_user(&rt_sf->info, &rt_sf->pinfo) + || __put_user(&rt_sf->uc, &rt_sf->puc) + /* Put the siginfo */ + || __copy_to_user(&rt_sf->info, info, sizeof(*info)) + /* Create the ucontext */ + || __put_user(0, &rt_sf->uc.uc_flags) + || __put_user(0, &rt_sf->uc.uc_link) + || __put_user(current->sas_ss_sp, &rt_sf->uc.uc_stack.ss_sp) + || __put_user(sas_ss_flags(regs->gpr[1]), + &rt_sf->uc.uc_stack.ss_flags) + || __put_user(current->sas_ss_size, &rt_sf->uc.uc_stack.ss_size) + || __copy_to_user(&rt_sf->uc.uc_sigmask, oldset, sizeof(*oldset)) + /* mcontext.regs points to preamble register frame */ + || __put_user((struct pt_regs *)frame, &rt_sf->uc.uc_mcontext.regs) + || __put_user(sig, &rt_sf->uc.uc_mcontext.signal)) + goto badframe; + + } else { + /* Put another sigcontext on the stack */ + *newspp -= sizeof(*sc); + sc = (struct sigcontext_struct *) *newspp; + if (verify_area(VERIFY_WRITE, sc, sizeof(*sc))) + goto badframe; + + if (__put_user((unsigned long) ka->sa.sa_handler, &sc->handler) + || __put_user(oldset->sig[0], &sc->oldmask) +#if _NSIG_WORDS > 1 + || __put_user(oldset->sig[1], &sc->_unused[3]) +#endif + || __put_user((struct pt_regs *)frame, &sc->regs) + || __put_user(sig, &sc->signal)) + goto badframe; + } + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sigmask_lock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + } + return; + +badframe: +#if DEBUG_SIG + printk("badframe in handle_signal, regs=%p frame=%lx newsp=%lx\n", + regs, frame, *newspp); + printk("sc=%p sig=%d ka=%p info=%p oldset=%p\n", sc, sig, ka, info, oldset); +#endif + do_exit(SIGSEGV); +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + */ +extern int do_signal32(sigset_t *oldset, struct pt_regs *regs); +int do_signal(sigset_t *oldset, struct pt_regs *regs) +{ + siginfo_t info; + struct k_sigaction *ka; + unsigned long frame, newsp; + + /* + * If the current thread is 32 bit - invoke the + * 32 bit signal handling code + */ + if (current->thread.flags & PPC_FLAG_32BIT) + return do_signal32(oldset, regs); + + PPCDBG(PPCDBG_SIGNAL, "do_signal - running - pid=%ld current=%lx comm=%s \n", + current->pid, current, current->comm); + + + + if (!oldset) + oldset = ¤t->blocked; + + newsp = frame = 0; + + for (;;) { + unsigned long signr; + + PPCDBG(PPCDBG_SIGNAL, "do_signal - (pre) dequeueing signal - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + spin_lock_irq(¤t->sigmask_lock); + signr = dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->sigmask_lock); + PPCDBG(PPCDBG_SIGNAL, "do_signal - (aft) dequeueing signal - signal=%lx - pid=%ld current=%lx comm=%s \n", signr, current->pid, current, current->comm); + + if (!signr) + break; + + if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) { + /* Let the debugger run. */ + current->exit_code = signr; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + + /* We're back. Did the debugger cancel the sig? */ + if (!(signr = current->exit_code)) + continue; + current->exit_code = 0; + + /* The debugger continued. Ignore SIGSTOP. */ + if (signr == SIGSTOP) + continue; + + /* Update the siginfo structure. Is this good? */ + if (signr != info.si_signo) { + info.si_signo = signr; + info.si_errno = 0; + info.si_code = SI_USER; + info.si_pid = current->p_pptr->pid; + info.si_uid = current->p_pptr->uid; + } + + /* If the (new) signal is now blocked, requeue it. */ + if (sigismember(¤t->blocked, signr)) { + send_sig_info(signr, &info, current); + continue; + } + } + + ka = ¤t->sig->action[signr-1]; + + + PPCDBG(PPCDBG_SIGNAL, "do_signal - ka=%p, action handler=%lx \n", ka, ka->sa.sa_handler); + + if (ka->sa.sa_handler == SIG_IGN) { + PPCDBG(PPCDBG_SIGNAL, "do_signal - into SIG_IGN logic \n"); + if (signr != SIGCHLD) + continue; + /* Check for SIGCHLD: it's special. */ + while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0) + /* nothing */; + continue; + } + + if (ka->sa.sa_handler == SIG_DFL) { + int exit_code = signr; + PPCDBG(PPCDBG_SIGNAL, "do_signal - into SIG_DFL logic \n"); + + /* Init gets no signals it doesn't want. */ + if (current->pid == 1) + continue; + + switch (signr) { + case SIGCONT: case SIGCHLD: case SIGWINCH: + continue; + + case SIGTSTP: case SIGTTIN: case SIGTTOU: + if (is_orphaned_pgrp(current->pgrp)) + continue; + /* FALLTHRU */ + + case SIGSTOP: + current->state = TASK_STOPPED; + current->exit_code = signr; + if (!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) + notify_parent(current, SIGCHLD); + schedule(); + continue; + + case SIGQUIT: case SIGILL: case SIGTRAP: + case SIGABRT: case SIGFPE: case SIGSEGV: + case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ: + if (do_coredump(signr, regs)) + exit_code |= 0x80; + /* FALLTHRU */ + + default: + sigaddset(¤t->pending.signal, signr); + recalc_sigpending(current); + current->flags |= PF_SIGNALED; + do_exit(exit_code); + /* NOTREACHED */ + } + } + + if ( (ka->sa.sa_flags & SA_ONSTACK) + && (! on_sig_stack(regs->gpr[1]))) + newsp = (current->sas_ss_sp + current->sas_ss_size); + else + newsp = regs->gpr[1]; + newsp = frame = newsp - sizeof(struct sigregs); + + /* Whee! Actually deliver the signal. */ + + PPCDBG(PPCDBG_SIGNAL, "do_signal - GOING TO RUN SIGNAL HANDLER - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + handle_signal(signr, ka, &info, oldset, regs, &newsp, frame); + PPCDBG(PPCDBG_SIGNAL, "do_signal - after running signal handler - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + break; + } + + if (regs->trap == 0x0C00 /* System Call! */ && + ((int)regs->result == -ERESTARTNOHAND || + (int)regs->result == -ERESTARTSYS || + (int)regs->result == -ERESTARTNOINTR)) { + PPCDBG(PPCDBG_SIGNAL, "do_signal - going to back up & retry system call \n"); + regs->gpr[3] = regs->orig_gpr3; + regs->nip -= 4; /* Back up & retry system call */ + regs->result = 0; + } + + if (newsp == frame) + { + PPCDBG(PPCDBG_SIGNAL, "do_signal - returning w/ no signal delivered \n"); + return 0; /* no signals delivered */ + } + + + + if (ka->sa.sa_flags & SA_SIGINFO) + setup_rt_frame(regs, (struct sigregs *) frame, newsp); + else + setup_frame(regs, (struct sigregs *) frame, newsp); + PPCDBG(PPCDBG_SIGNAL, "do_signal - returning a signal was delivered \n"); + return 1; +} + + + + + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/signal32.c linuxppc64_2_4/arch/ppc64/kernel/signal32.c --- linux-2.4.5-ac18/arch/ppc64/kernel/signal32.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/signal32.c Wed Jun 27 16:34:56 2001 @@ -0,0 +1,1579 @@ +/* + * signal32.c: Support 32bit signal syscalls. + * + * Copyright (C) 2001 IBM + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * + * These routines maintain argument size conversion between 32bit and 64bit + * environment. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) +/* + * 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) + + + + +/* Use this to get at 32-bit user passed pointers. */ +/* Things to consider: the low-level assembly stub does + srl x, 0, x for first four arguments, so if you have + pointer to something in the first four arguments, just + declare it as a pointer, not u32. On the other side, + arguments from 5th onwards should be declared as u32 + for pointers, and need AA() around each usage. + A() macro should be used for places where you e.g. + have some internal variable u32 and just want to get + rid of a compiler warning. AA() has to be used in + places where you want to convert a function argument + to 32bit pointer or when you e.g. access pt_regs + structure and want to consider 32bit registers only. + - + */ +#define A(__x) ((unsigned long)(__x)) +#define AA(__x) \ +({ unsigned long __ret; \ + __asm__ ("clrldi %0, %0, 32" \ + : "=r" (__ret) \ + : "0" (__x)); \ + __ret; \ +}) + + + +struct timespec32 { + s32 tv_sec; + s32 tv_nsec; +}; + + + + +struct sigregs32 { + /***********************************************************************/ + /* the gp_regs array is 32 bit representation of the pt_regs structure */ + /* that was stored on the kernle stack during the system call that */ + /* was interrupted for the signal. */ + /* */ + /* Note that the entire pt_regs regs structure will fit in the gp_regs */ + /* structure because the ELF_NREG value is 48 for PPC and the pt_regs*/ + /* structure contains 44 registers */ + /* */ + /***********************************************************************/ + elf_gregset_t32 gp_regs; + double fp_regs[ELF_NFPREG]; + unsigned int tramp[2]; + /* Programs using the rs6000/xcoff abi can save up to 19 gp regs + and 18 fp regs below sp before decrementing it. */ + int abigap[56]; +}; + + +struct rt_sigframe_32 { + /* Unused space at start of frame to allow for storing of stack pointers */ + unsigned long _unused; + /* This is a 32 bit pointer in user address space + * it is a pointer to the siginfo stucture in the rt stack frame + */ + u32 pinfo; + /* This is a 32 bit pointer in user address space */ + /* it is a pointer to the user context in the rt stack frame */ + u32 puc; + struct siginfo32 info; + struct ucontext32 uc; +}; + + + + + +extern asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struct rusage * ru); + + +/****************************************************************************/ +/* Start of nonRT signal support */ +/* */ +/* sigset_t is 32 bits for non-rt signals */ +/* */ +/* System Calls */ +/* sigaction sys32_sigaction */ +/* sigpending sys32_sigpending */ +/* sigprocmask sys32_sigprocmask */ +/* sigreturn sys32_sigreturn */ +/* */ +/* Note sigsuspend has no special 32 bit routine - uses the 64 bit routine */ +/* */ +/* Other routines */ +/* setup_frame32 */ +/* */ +/****************************************************************************/ + + +asmlinkage long sys32_sigaction(int sig, struct old_sigaction32 *act, struct old_sigaction32 *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + PPCDBG(PPCDBG_SYS32, "sys32_sigaction - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + if (sig < 0) + { + sig = -sig; + } + + if (act) + { + old_sigset_t32 mask; + + ret = get_user((long)new_ka.sa.sa_handler, &act->sa_handler); + ret |= __get_user((long)new_ka.sa.sa_restorer, &act->sa_restorer); + ret |= __get_user(new_ka.sa.sa_flags, &act->sa_flags); + ret |= __get_user(mask, &act->sa_mask); + if (ret) + return ret; + PPCDBG(PPCDBG_SIGNAL, "sys32_sigaction flags =%lx \n", new_ka.sa.sa_flags); + + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) + { + ret = put_user((long)old_ka.sa.sa_handler, &oact->sa_handler); + ret |= __put_user((long)old_ka.sa.sa_restorer, &oact->sa_restorer); + ret |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + ret |= __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + + PPCDBG(PPCDBG_SYS32, "sys32_sigaction - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return ret; +} + + + + +extern asmlinkage long sys_sigpending(old_sigset_t *set); + +asmlinkage long sys32_sigpending(old_sigset_t32 *set) +{ + old_sigset_t s; + int ret; + mm_segment_t old_fs = get_fs(); + + PPCDBG(PPCDBG_SYS32, "sys32_sigpending - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + set_fs (KERNEL_DS); + ret = sys_sigpending(&s); + set_fs (old_fs); + if (put_user (s, set)) return -EFAULT; + + PPCDBG(PPCDBG_SYS32, "sys32_sigpending - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return ret; +} + + + + +extern asmlinkage long sys_sigprocmask(int how, old_sigset_t *set, old_sigset_t *oset); + +/* Note: it is necessary to treat how as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sigprocmask(u32 how, old_sigset_t32 *set, old_sigset_t32 *oset) +{ + old_sigset_t s; + int ret; + mm_segment_t old_fs = get_fs(); + + PPCDBG(PPCDBG_SYS32, "sys32_sigprocmask - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + if (set && get_user (s, set)) return -EFAULT; + set_fs (KERNEL_DS); + ret = sys_sigprocmask((int)how, set ? &s : NULL, oset ? &s : NULL); + set_fs (old_fs); + if (ret) return ret; + if (oset && put_user (s, oset)) return -EFAULT; + + PPCDBG(PPCDBG_SYS32, "sys32_sigprocmask - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return 0; +} + + + +/* + * When we have signals to deliver, we set up on the + * user stack, going down from the original stack pointer: + * a sigregs struct + * one or more sigcontext structs + * a gap of __SIGNAL_FRAMESIZE32 bytes + * + * Each of these things must be a multiple of 16 bytes in size. + * +*/ + + +/* + * Do a signal return; undo the signal stack. + */ +long sys32_sigreturn(struct pt_regs *regs) +{ + struct sigcontext32_struct *sc, sigctx; + struct sigregs32 *sr; + int ret; + elf_gregset_t32 saved_regs; /* an array of ELF_NGREG unsigned ints (32 bits) */ + sigset_t set; + unsigned int prevsp; + + + PPCDBG(PPCDBG_SIGNAL, "sys32_sigreturn - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + PPCDBG(PPCDBG_SIGNAL, "sys32_sigreturn - comm=%s reg1 =%lx \n", + current->comm, regs->gpr[1]); + + + sc = (struct sigcontext32_struct *)(regs->gpr[1] + __SIGNAL_FRAMESIZE32); + if (copy_from_user(&sigctx, sc, sizeof(sigctx))) + goto badframe; + + /* Note that PPC32 puts the upper 32 bits of the sigmask in the */ + /* unused part of the signal stackframe */ + set.sig[0] = sigctx.oldmask + ((long)(sigctx._unused[3])<< 32); + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + sc++; /* Look at next sigcontext */ + /* If the next sigcontext is actually the sigregs (frame) */ + /* - then no more sigcontexts on the user stack */ + if (sc == (struct sigcontext32_struct*)(u64)sigctx.regs) + { + PPCDBG(PPCDBG_SIGNAL, "sys32_sigreturn - no more sigcontexts found \n"); + /* Last stacked signal - restore registers */ + sr = (struct sigregs32*)(u64)sigctx.regs; + if (regs->msr & MSR_FP ) + giveup_fpu(current); + /* copy the 32 bit register values off the user stack */ + /* into the 32 bit register area */ + if (copy_from_user(saved_regs, &sr->gp_regs,sizeof(sr->gp_regs))) + goto badframe; + /**********************************************************************/ + /* The saved reg structure in the frame is an elf_grepset_t32, it is */ + /* a 32 bit register save of the registers in the pt_regs structure */ + /* that was stored on the kernel stack during the system call */ + /* when the system call was interrupted for the signal. Only 32 bits*/ + /* are saved because the sigcontext contains a pointer to the regs */ + /* and the sig context address is passed as a pointer to the signal */ + /* handler. */ + /* */ + /* The entries in the elf_grepset have the same index as the elements */ + /* in the pt_regs structure. */ + /* */ + /**********************************************************************/ + + saved_regs[PT_MSR] = (regs->msr & ~MSR_USERCHANGE) + | (saved_regs[PT_MSR] & MSR_USERCHANGE); + regs->gpr[0] = (u64)(saved_regs[0]) & 0xFFFFFFFF; + regs->gpr[1] = (u64)(saved_regs[1]) & 0xFFFFFFFF; + /**********************************************************************/ + /* Register 2 is the kernel toc - should be reset on any calls into */ + /* the kernel */ + /**********************************************************************/ + regs->gpr[2] = (u64)(saved_regs[2]) & 0xFFFFFFFF; + + regs->gpr[3] = (u64)(saved_regs[3]) & 0xFFFFFFFF; + regs->gpr[4] = (u64)(saved_regs[4]) & 0xFFFFFFFF; + regs->gpr[5] = (u64)(saved_regs[5]) & 0xFFFFFFFF; + regs->gpr[6] = (u64)(saved_regs[6]) & 0xFFFFFFFF; + regs->gpr[7] = (u64)(saved_regs[7]) & 0xFFFFFFFF; + regs->gpr[8] = (u64)(saved_regs[8]) & 0xFFFFFFFF; + regs->gpr[9] = (u64)(saved_regs[9]) & 0xFFFFFFFF; + regs->gpr[10] = (u64)(saved_regs[10]) & 0xFFFFFFFF; + regs->gpr[11] = (u64)(saved_regs[11]) & 0xFFFFFFFF; + regs->gpr[12] = (u64)(saved_regs[12]) & 0xFFFFFFFF; + regs->gpr[13] = (u64)(saved_regs[13]) & 0xFFFFFFFF; + regs->gpr[14] = (u64)(saved_regs[14]) & 0xFFFFFFFF; + regs->gpr[15] = (u64)(saved_regs[15]) & 0xFFFFFFFF; + regs->gpr[16] = (u64)(saved_regs[16]) & 0xFFFFFFFF; + regs->gpr[17] = (u64)(saved_regs[17]) & 0xFFFFFFFF; + regs->gpr[18] = (u64)(saved_regs[18]) & 0xFFFFFFFF; + regs->gpr[19] = (u64)(saved_regs[19]) & 0xFFFFFFFF; + regs->gpr[20] = (u64)(saved_regs[20]) & 0xFFFFFFFF; + regs->gpr[21] = (u64)(saved_regs[21]) & 0xFFFFFFFF; + regs->gpr[22] = (u64)(saved_regs[22]) & 0xFFFFFFFF; + regs->gpr[23] = (u64)(saved_regs[23]) & 0xFFFFFFFF; + regs->gpr[24] = (u64)(saved_regs[24]) & 0xFFFFFFFF; + regs->gpr[25] = (u64)(saved_regs[25]) & 0xFFFFFFFF; + regs->gpr[26] = (u64)(saved_regs[26]) & 0xFFFFFFFF; + regs->gpr[27] = (u64)(saved_regs[27]) & 0xFFFFFFFF; + regs->gpr[28] = (u64)(saved_regs[28]) & 0xFFFFFFFF; + regs->gpr[29] = (u64)(saved_regs[29]) & 0xFFFFFFFF; + regs->gpr[30] = (u64)(saved_regs[30]) & 0xFFFFFFFF; + regs->gpr[31] = (u64)(saved_regs[31]) & 0xFFFFFFFF; + /****************************************************/ + /* restore the non gpr registers */ + /****************************************************/ + regs->msr = (u64)(saved_regs[PT_MSR]) & 0xFFFFFFFF; + /* Insure that the interrupt mode is 64 bit, during 32 bit execution. + * (This is necessary because we only saved lower 32 bits of msr.) + */ + regs->msr = regs->msr | MSR_ISF; /* When this thread is interrupted it should run in 64 bit mode. */ + + regs->nip = (u64)(saved_regs[PT_NIP]) & 0xFFFFFFFF; + regs->orig_gpr3 = (u64)(saved_regs[PT_ORIG_R3]) & 0xFFFFFFFF; + regs->ctr = (u64)(saved_regs[PT_CTR]) & 0xFFFFFFFF; + regs->link = (u64)(saved_regs[PT_LNK]) & 0xFFFFFFFF; + regs->xer = (u64)(saved_regs[PT_XER]) & 0xFFFFFFFF; + regs->ccr = (u64)(saved_regs[PT_CCR]) & 0xFFFFFFFF; + regs->mq = (u64)(saved_regs[PT_MQ]) & 0xFFFFFFFF; + /******************************************************/ + /* the DAR and the DSISR are only relevant during a */ + /* data or instruction storage interrupt. The value */ + /* will be set to zero. */ + /******************************************************/ + regs->dar = 0; + regs->dsisr = 0; + regs->result = (u64)(saved_regs[PT_RESULT]) & 0xFFFFFFFF; + + if (copy_from_user(current->thread.fpr, &sr->fp_regs, sizeof(sr->fp_regs))) + goto badframe; + + ret = regs->result; + } + else + { + PPCDBG(PPCDBG_SIGNAL, "sys32_sigreturn - additional sigcontexts found \n"); + /* More signals to go */ + regs->gpr[1] = (unsigned long)sc - __SIGNAL_FRAMESIZE32; + if (copy_from_user(&sigctx, sc, sizeof(sigctx))) + goto badframe; + sr = (struct sigregs32*)(u64)sigctx.regs; + regs->gpr[3] = ret = sigctx.signal; + regs->gpr[4] = (unsigned long) sc; + regs->link = (unsigned long) &sr->tramp; + regs->nip = sigctx.handler; + + if (get_user(prevsp, &sr->gp_regs[PT_R1]) + || put_user(prevsp, (unsigned int*) regs->gpr[1])) + goto badframe; + } + + PPCDBG(PPCDBG_SIGNAL, "sys32_sigreturn - normal exit returning %ld - pid=%ld current=%lx comm=%s \n", ret, current->pid, current, current->comm); + return ret; + +badframe: + PPCDBG(PPCDBG_SYS32NI, "sys32_sigreturn - badframe - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + do_exit(SIGSEGV); +} + +/* + * Set up a signal frame. + */ +static void +setup_frame32(struct pt_regs *regs, struct sigregs32 *frame, + unsigned int newsp) +{ + struct sigcontext32_struct *sc = (struct sigcontext32_struct *)(u64)newsp; + + if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) + goto badframe; + if (regs->msr & MSR_FP) + giveup_fpu(current); + + PPCDBG(PPCDBG_SIGNAL, "setup32 - entered - newsp =%lx \n", newsp); + /***************************************************************/ + /* */ + /* Copy the register contents for the pt_regs structure on the */ + /* kernel stack to the elf_gregset_t32 structure on the user */ + /* stack. This is a copy of 64 bit register values to 32 bit */ + /* register values. The high order 32 bits of the 64 bit */ + /* registers are not needed since a 32 bit application is */ + /* running and the saved registers are the contents of the */ + /* user registers at the time of a system call. */ + /* */ + /* The values saved on the user stack will be restored into */ + /* the registers during the signal return processing */ + /* */ + /* Note the +1 is needed in order to get the lower 32 bits */ + /* of 64 bit register */ + /***************************************************************/ + if (__copy_to_user(&frame->gp_regs[0], (u32*)(®s->gpr[0])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[1], (u32*)(®s->gpr[1])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[2], (u32*)(®s->gpr[2])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[3], (u32*)(®s->gpr[3])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[4], (u32*)(®s->gpr[4])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[5], (u32*)(®s->gpr[5])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[6], (u32*)(®s->gpr[6])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[7], (u32*)(®s->gpr[7])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[8], (u32*)(®s->gpr[8])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[9], (u32*)(®s->gpr[9])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[10], (u32*)(®s->gpr[10])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[11], (u32*)(®s->gpr[11])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[12], (u32*)(®s->gpr[12])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[13], (u32*)(®s->gpr[13])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[14], (u32*)(®s->gpr[14])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[15], (u32*)(®s->gpr[15])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[16], (u32*)(®s->gpr[16])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[17], (u32*)(®s->gpr[17])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[18], (u32*)(®s->gpr[18])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[19], (u32*)(®s->gpr[19])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[20], (u32*)(®s->gpr[20])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[21], (u32*)(®s->gpr[21])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[22], (u32*)(®s->gpr[22])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[23], (u32*)(®s->gpr[23])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[24], (u32*)(®s->gpr[24])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[25], (u32*)(®s->gpr[25])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[26], (u32*)(®s->gpr[26])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[27], (u32*)(®s->gpr[27])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[28], (u32*)(®s->gpr[28])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[29], (u32*)(®s->gpr[29])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[30], (u32*)(®s->gpr[30])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[31], (u32*)(®s->gpr[31])+1, sizeof(u32))) + goto badframe; + + /*****************************************************************************/ + /* Copy the non gpr registers to the user stack */ + /*****************************************************************************/ + + if (__copy_to_user(&frame->gp_regs[PT_NIP], (u32*)(®s->gpr[PT_NIP])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_MSR], (u32*)(®s->gpr[PT_MSR])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_ORIG_R3], (u32*)(®s->gpr[PT_ORIG_R3])+1, + sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_CTR], (u32*)(®s->gpr[PT_CTR])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_LNK], (u32*)(®s->gpr[PT_LNK])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_XER], (u32*)(®s->gpr[PT_XER])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_CCR], (u32*)(®s->gpr[PT_CCR])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_MQ], (u32*)(®s->gpr[PT_MQ])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_RESULT], (u32*)(®s->gpr[PT_RESULT])+1, + sizeof(u32))) + goto badframe; + + + /*****************************************************************************/ + /* Now copy the floating point registers onto the user stack */ + /* */ + /* Also set up so on the completion of the signal handler, the sys_sigreturn */ + /* will get control to reset the stack */ + /*****************************************************************************/ + + + + PPCDBG(PPCDBG_SIGNAL, "setup32 - after copy to user \n"); + + if (__copy_to_user(&frame->fp_regs, current->thread.fpr, + ELF_NFPREG * sizeof(double)) + || __put_user(0x38007777U, &frame->tramp[0]) /* li r0,0x7777 */ + || __put_user(0x44000002U, &frame->tramp[1])) /* sc */ + goto badframe; + + PPCDBG(PPCDBG_SIGNAL, "setup32 - after copy to user2 \n"); + + + flush_icache_range((unsigned long) &frame->tramp[0], + (unsigned long) &frame->tramp[2]); + + PPCDBG(PPCDBG_SIGNAL, "setup32 - after flush icache range\n"); + + newsp -= __SIGNAL_FRAMESIZE32; + if (put_user(regs->gpr[1], (u32*)(u64)newsp) + || get_user(regs->nip, &sc->handler) + || get_user(regs->gpr[3], &sc->signal)) + goto badframe; + + PPCDBG(PPCDBG_SIGNAL, "setup32 - after copy to user 3 \n"); + + regs->gpr[1] = newsp & 0xFFFFFFFF; + /**************************************************************/ + /* first parameter to the signal handler is the signal number */ + /* - the value is in gpr3 */ + /* second parameter to the signal handler is the sigcontext */ + /* - set the value into gpr4 */ + /**************************************************************/ + regs->gpr[4] = (unsigned long) sc; + regs->link = (unsigned long) frame->tramp; + PPCDBG(PPCDBG_SIGNAL, "setup32 - before return trap address = %lx \n", frame->tramp); + return; + + badframe: + udbg_printf("setup_frame32 - badframe in setup_frame, regs=%p frame=%p newsp=%lx\n", regs, frame, newsp); PPCDBG_ENTER_DEBUGGER(); +#if DEBUG_SIG + printk("badframe in setup_frame32, regs=%p frame=%p newsp=%lx\n", + regs, frame, newsp); +#endif + do_exit(SIGSEGV); +} + + + + + +/****************************************************************************/ +/* Start of RT signal support */ +/* */ +/* sigset_t is 64 bits for rt signals */ +/* */ +/* System Calls */ +/* sigaction sys32_rt_sigaction */ +/* sigpending sys32_rt_sigpending */ +/* sigprocmask sys32_rt_sigprocmask */ +/* sigreturn sys32_rt_sigreturn */ +/* sigtimedwait sys32_rt_sigtimedwait */ +/* sigqueueinfo sys32_rt_sigqueueinfo */ +/* sigsuspend sys32_rt_sigsuspend */ +/* */ +/* Other routines */ +/* setup_rt_frame32 */ +/* siginfo64to32 */ +/* siginfo32to64 */ +/* */ +/* */ +/****************************************************************************/ + + +// This code executes after the rt signal handler in 32 bit mode has completed and +// returned +long sys32_rt_sigreturn(struct pt_regs * regs) +{ + struct rt_sigframe_32 *rt_stack_frame; + struct sigcontext32_struct sigctx; + struct sigregs32 *signalregs; + + int ret; + elf_gregset_t32 saved_regs; /* an array of 32 bit register values */ + sigset_t signal_set; + stack_t stack; + unsigned int previous_stack; + + + + ret = 0; + /* Adjust the inputted reg1 to point to the first rt signal frame */ + rt_stack_frame = (struct rt_sigframe_32 *)(regs->gpr[1] + __SIGNAL_FRAMESIZE32); + /* Copy the information from the user stack */ + if (copy_from_user(&sigctx, &rt_stack_frame->uc.uc_mcontext,sizeof(sigctx)) + || copy_from_user(&signal_set, &rt_stack_frame->uc.uc_sigmask,sizeof(signal_set)) + || copy_from_user(&stack,&rt_stack_frame->uc.uc_stack,sizeof(stack))) + { + /* unable to copy from user storage */ + goto badframe; + } + + PPCDBG(PPCDBG_SIGNAL, "sys32_rt_sigreturn - copied from user - signal_set=%lx \n", *(u64*)&signal_set); + + /* Unblock the signal that was processed + * After a signal handler runs - + * if the signal is blockable - the signal will be unblocked + * ( sigkill and sigstop are not blockable) + */ + sigdelsetmask(&signal_set, ~_BLOCKABLE); + /* update the current based on the sigmask found in the rt_stackframe */ + spin_lock_irq(¤t->sigmask_lock); + current->blocked = signal_set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + /* Set to point to the next rt_sigframe - this is used to determine whether this + * is the last signal to process + */ + rt_stack_frame ++; + + if (rt_stack_frame == (struct rt_sigframe_32 *)(u64)(sigctx.regs)) + { + signalregs = (struct sigregs32 *) (u64)sigctx.regs; + /* If currently owning the floating point - give them up */ + if (regs->msr & MSR_FP) + { + giveup_fpu(current); + } + if (copy_from_user(saved_regs,&signalregs->gp_regs,sizeof(signalregs->gp_regs))) + { + goto badframe; + } + /**********************************************************************/ + /* The saved reg structure in the frame is an elf_grepset_t32, it is */ + /* a 32 bit register save of the registers in the pt_regs structure */ + /* that was stored on the kernel stack during the system call */ + /* when the system call was interrupted for the signal. Only 32 bits*/ + /* are saved because the sigcontext contains a pointer to the regs */ + /* and the sig context address is passed as a pointer to the signal */ + /* handler. */ + /* */ + /* The entries in the elf_grepset have the same index as the elements */ + /* in the pt_regs structure. */ + /* */ + /**********************************************************************/ + + saved_regs[PT_MSR] = (regs->msr & ~MSR_USERCHANGE) + | (saved_regs[PT_MSR] & MSR_USERCHANGE); + regs->gpr[0] = (u64)(saved_regs[0]) & 0xFFFFFFFF; + regs->gpr[1] = (u64)(saved_regs[1]) & 0xFFFFFFFF; + /**********************************************************************/ + /* Register 2 is the kernel toc - should be reset on any calls into */ + /* the kernel */ + /**********************************************************************/ + regs->gpr[2] = (u64)(saved_regs[2]) & 0xFFFFFFFF; + + regs->gpr[3] = (u64)(saved_regs[3]) & 0xFFFFFFFF; + regs->gpr[4] = (u64)(saved_regs[4]) & 0xFFFFFFFF; + regs->gpr[5] = (u64)(saved_regs[5]) & 0xFFFFFFFF; + regs->gpr[6] = (u64)(saved_regs[6]) & 0xFFFFFFFF; + regs->gpr[7] = (u64)(saved_regs[7]) & 0xFFFFFFFF; + regs->gpr[8] = (u64)(saved_regs[8]) & 0xFFFFFFFF; + regs->gpr[9] = (u64)(saved_regs[9]) & 0xFFFFFFFF; + regs->gpr[10] = (u64)(saved_regs[10]) & 0xFFFFFFFF; + regs->gpr[11] = (u64)(saved_regs[11]) & 0xFFFFFFFF; + regs->gpr[12] = (u64)(saved_regs[12]) & 0xFFFFFFFF; + regs->gpr[13] = (u64)(saved_regs[13]) & 0xFFFFFFFF; + regs->gpr[14] = (u64)(saved_regs[14]) & 0xFFFFFFFF; + regs->gpr[15] = (u64)(saved_regs[15]) & 0xFFFFFFFF; + regs->gpr[16] = (u64)(saved_regs[16]) & 0xFFFFFFFF; + regs->gpr[17] = (u64)(saved_regs[17]) & 0xFFFFFFFF; + regs->gpr[18] = (u64)(saved_regs[18]) & 0xFFFFFFFF; + regs->gpr[19] = (u64)(saved_regs[19]) & 0xFFFFFFFF; + regs->gpr[20] = (u64)(saved_regs[20]) & 0xFFFFFFFF; + regs->gpr[21] = (u64)(saved_regs[21]) & 0xFFFFFFFF; + regs->gpr[22] = (u64)(saved_regs[22]) & 0xFFFFFFFF; + regs->gpr[23] = (u64)(saved_regs[23]) & 0xFFFFFFFF; + regs->gpr[24] = (u64)(saved_regs[24]) & 0xFFFFFFFF; + regs->gpr[25] = (u64)(saved_regs[25]) & 0xFFFFFFFF; + regs->gpr[26] = (u64)(saved_regs[26]) & 0xFFFFFFFF; + regs->gpr[27] = (u64)(saved_regs[27]) & 0xFFFFFFFF; + regs->gpr[28] = (u64)(saved_regs[28]) & 0xFFFFFFFF; + regs->gpr[29] = (u64)(saved_regs[29]) & 0xFFFFFFFF; + regs->gpr[30] = (u64)(saved_regs[30]) & 0xFFFFFFFF; + regs->gpr[31] = (u64)(saved_regs[31]) & 0xFFFFFFFF; + /****************************************************/ + /* restore the non gpr registers */ + /****************************************************/ + regs->msr = (u64)(saved_regs[PT_MSR]) & 0xFFFFFFFF; + + regs->nip = (u64)(saved_regs[PT_NIP]) & 0xFFFFFFFF; + regs->orig_gpr3 = (u64)(saved_regs[PT_ORIG_R3]) & 0xFFFFFFFF; + regs->ctr = (u64)(saved_regs[PT_CTR]) & 0xFFFFFFFF; + regs->link = (u64)(saved_regs[PT_LNK]) & 0xFFFFFFFF; + regs->xer = (u64)(saved_regs[PT_XER]) & 0xFFFFFFFF; + regs->ccr = (u64)(saved_regs[PT_CCR]) & 0xFFFFFFFF; + regs->mq = (u64)(saved_regs[PT_MQ]) & 0xFFFFFFFF; + /******************************************************/ + /* the DAR and the DSISR are only relevant during a */ + /* data or instruction storage interrupt. The value */ + /* will be set to zero. */ + /******************************************************/ + regs->dar = 0; + regs->dsisr = 0; + regs->result = (u64)(saved_regs[PT_RESULT]) & 0xFFFFFFFF; + + + } + else /* more signals to go */ + { + regs->gpr[1] = (u64)rt_stack_frame - __SIGNAL_FRAMESIZE32; + if (copy_from_user(&sigctx, &rt_stack_frame->uc.uc_mcontext,sizeof(sigctx))) + { + goto badframe; + } + signalregs = (struct sigregs32 *) (u64)sigctx.regs; + /* first parm to signal handler is the signal number */ + regs->gpr[3] = ret = sigctx.signal; + /* second parm is a pointer to sig info */ + get_user(regs->gpr[4], &rt_stack_frame->pinfo); + /* third parm is a pointer to the ucontext */ + get_user(regs->gpr[5], &rt_stack_frame->puc); + /* fourth parm is the stack frame */ + regs->gpr[6] = (u64)rt_stack_frame; + /* Set up link register to return to sigreturn when the */ + /* signal handler completes */ + regs->link = (u64)&signalregs->tramp; + /* Set next instruction to the start fo the signal handler */ + regs->nip = sigctx.handler; + /* Set the reg1 to look like a call to the signal handler */ + if (get_user(previous_stack,&signalregs->gp_regs[PT_R1]) + || put_user(previous_stack, (unsigned long *)regs->gpr[1])) + { + goto badframe; + } + + } + + return ret; + +badframe: + do_exit(SIGSEGV); +} + + + +asmlinkage long sys32_rt_sigaction(int sig, const struct sigaction32 *act, struct sigaction32 *oact, size_t sigsetsize) +{ + struct k_sigaction new_ka, old_ka; + int ret; + sigset32_t set32; + + PPCDBG(PPCDBG_SIGNAL, "sys32_rt_sigaction - entered - sig=%x \n", sig); + + if (act) + PPCDBG(PPCDBG_SYS32NI, " sys32_rt_sigaction flags = %lx \n" ,act->sa_flags); + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset32_t)) + return -EINVAL; + + if (act) { + ret = get_user((long)new_ka.sa.sa_handler, &act->sa_handler); + ret |= __copy_from_user(&set32, &act->sa_mask, + sizeof(sigset32_t)); + switch (_NSIG_WORDS) { + case 4: new_ka.sa.sa_mask.sig[3] = set32.sig[6] + | (((long)set32.sig[7]) << 32); + case 3: new_ka.sa.sa_mask.sig[2] = set32.sig[4] + | (((long)set32.sig[5]) << 32); + case 2: new_ka.sa.sa_mask.sig[1] = set32.sig[2] + | (((long)set32.sig[3]) << 32); + case 1: new_ka.sa.sa_mask.sig[0] = set32.sig[0] + | (((long)set32.sig[1]) << 32); + } + + + ret |= __get_user(new_ka.sa.sa_flags, &act->sa_flags); + + PPCDBG(PPCDBG_SIGNAL, " sys32_rt_sigaction flags(kernel) = %lx \n" ,new_ka.sa.sa_flags); + + if (ret) + return -EFAULT; + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + switch (_NSIG_WORDS) { + case 4: + set32.sig[7] = (old_ka.sa.sa_mask.sig[3] >> 32); + set32.sig[6] = old_ka.sa.sa_mask.sig[3]; + case 3: + set32.sig[5] = (old_ka.sa.sa_mask.sig[2] >> 32); + set32.sig[4] = old_ka.sa.sa_mask.sig[2]; + case 2: + set32.sig[3] = (old_ka.sa.sa_mask.sig[1] >> 32); + set32.sig[2] = old_ka.sa.sa_mask.sig[1]; + case 1: + set32.sig[1] = (old_ka.sa.sa_mask.sig[0] >> 32); + set32.sig[0] = old_ka.sa.sa_mask.sig[0]; + } + ret = put_user((long)old_ka.sa.sa_handler, &oact->sa_handler); + ret |= __copy_to_user(&oact->sa_mask, &set32, + sizeof(sigset32_t)); + ret |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + } + + + PPCDBG(PPCDBG_SIGNAL, "sys32_rt_sigaction - exiting - sig=%x \n", sig); + return ret; +} + + +extern asmlinkage long sys_rt_sigprocmask(int how, sigset_t *set, sigset_t *oset, + size_t sigsetsize); + +/* Note: it is necessary to treat how as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_rt_sigprocmask(u32 how, sigset32_t *set, sigset32_t *oset, size_t sigsetsize) +{ + sigset_t s; + sigset32_t s32; + int ret; + mm_segment_t old_fs = get_fs(); + + PPCDBG(PPCDBG_SIGNAL, "sys32_rt_sigprocmask - entered how=%x \n", (int)how); + + if (set) { + if (copy_from_user (&s32, set, sizeof(sigset32_t))) + return -EFAULT; + + PPCDBG(PPCDBG_SIGNAL, "sys32_rt_sigprocmask - *set=%lx \n", *(u64*)&s32); + + switch (_NSIG_WORDS) { + case 4: s.sig[3] = s32.sig[6] | (((long)s32.sig[7]) << 32); + case 3: s.sig[2] = s32.sig[4] | (((long)s32.sig[5]) << 32); + case 2: s.sig[1] = s32.sig[2] | (((long)s32.sig[3]) << 32); + case 1: s.sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32); + } + } + else + { + PPCDBG(PPCDBG_SIGNAL, " sys32_rt_sigprocmask mask set = NULL \n" ); + } + set_fs (KERNEL_DS); + ret = sys_rt_sigprocmask((int)how, set ? &s : NULL, oset ? &s : NULL, + sigsetsize); + set_fs (old_fs); + if (ret) return ret; + if (oset) { + switch (_NSIG_WORDS) { + case 4: s32.sig[7] = (s.sig[3] >> 32); s32.sig[6] = s.sig[3]; + case 3: s32.sig[5] = (s.sig[2] >> 32); s32.sig[4] = s.sig[2]; + case 2: s32.sig[3] = (s.sig[1] >> 32); s32.sig[2] = s.sig[1]; + case 1: s32.sig[1] = (s.sig[0] >> 32); s32.sig[0] = s.sig[0]; + } + if (copy_to_user (oset, &s32, sizeof(sigset32_t))) + return -EFAULT; + } + return 0; +} + + +extern asmlinkage long sys_rt_sigpending(sigset_t *set, size_t sigsetsize); + + + +asmlinkage long sys32_rt_sigpending(sigset32_t *set, __kernel_size_t32 sigsetsize) +{ + + sigset_t s; + sigset32_t s32; + int ret; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_rt_sigpending(&s, sigsetsize); + set_fs (old_fs); + if (!ret) { + switch (_NSIG_WORDS) { + case 4: s32.sig[7] = (s.sig[3] >> 32); s32.sig[6] = s.sig[3]; + case 3: s32.sig[5] = (s.sig[2] >> 32); s32.sig[4] = s.sig[2]; + case 2: s32.sig[3] = (s.sig[1] >> 32); s32.sig[2] = s.sig[1]; + case 1: s32.sig[1] = (s.sig[0] >> 32); s32.sig[0] = s.sig[0]; + } + if (copy_to_user (set, &s32, sizeof(sigset32_t))) + return -EFAULT; + } + return ret; +} + + + +siginfo_t32 * +siginfo64to32(siginfo_t32 *d, siginfo_t *s) +{ + memset (d, 0, sizeof(siginfo_t32)); + d->si_signo = s->si_signo; + d->si_errno = s->si_errno; + d->si_code = s->si_code; + if (s->si_signo >= SIGRTMIN) { + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + + d->si_int = s->si_int; + } else switch (s->si_signo) { + /* XXX: What about POSIX1.b timers */ + case SIGCHLD: + d->si_pid = s->si_pid; + d->si_status = s->si_status; + d->si_utime = s->si_utime; + d->si_stime = s->si_stime; + break; + case SIGSEGV: + case SIGBUS: + case SIGFPE: + case SIGILL: + d->si_addr = (long)(s->si_addr); + break; + case SIGPOLL: + d->si_band = s->si_band; + d->si_fd = s->si_fd; + break; + default: + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + break; + } + return d; +} + +extern asmlinkage long +sys_rt_sigtimedwait(const sigset_t *uthese, siginfo_t *uinfo, + const struct timespec *uts, size_t sigsetsize); + +asmlinkage long +sys32_rt_sigtimedwait(sigset32_t *uthese, siginfo_t32 *uinfo, + struct timespec32 *uts, __kernel_size_t32 sigsetsize) +{ + sigset_t s; + sigset32_t s32; + struct timespec t; + int ret; + mm_segment_t old_fs = get_fs(); + siginfo_t info; + siginfo_t32 info32; + + if (copy_from_user (&s32, uthese, sizeof(sigset32_t))) + return -EFAULT; + switch (_NSIG_WORDS) { + case 4: s.sig[3] = s32.sig[6] | (((long)s32.sig[7]) << 32); + case 3: s.sig[2] = s32.sig[4] | (((long)s32.sig[5]) << 32); + case 2: s.sig[1] = s32.sig[2] | (((long)s32.sig[3]) << 32); + case 1: s.sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32); + } + if (uts) { + ret = get_user (t.tv_sec, &uts->tv_sec); + ret |= __get_user (t.tv_nsec, &uts->tv_nsec); + if (ret) + return -EFAULT; + } + set_fs (KERNEL_DS); + if (uts) + { + ret = sys_rt_sigtimedwait(&s, &info, &t, sigsetsize); + } + else + { + ret = sys_rt_sigtimedwait(&s, &info, (struct timespec *)uts, sigsetsize); + } + + set_fs (old_fs); + if (ret >= 0 && uinfo) { + if (copy_to_user (uinfo, siginfo64to32(&info32, &info), + sizeof(siginfo_t32))) + return -EFAULT; + } + return ret; +} + + + +siginfo_t * +siginfo32to64(siginfo_t *d, siginfo_t32 *s) +{ + d->si_signo = s->si_signo; + d->si_errno = s->si_errno; + d->si_code = s->si_code; + if (s->si_signo >= SIGRTMIN) { + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + d->si_int = s->si_int; + + } else switch (s->si_signo) { + /* XXX: What about POSIX1.b timers */ + case SIGCHLD: + d->si_pid = s->si_pid; + d->si_status = s->si_status; + d->si_utime = s->si_utime; + d->si_stime = s->si_stime; + break; + case SIGSEGV: + case SIGBUS: + case SIGFPE: + case SIGILL: + d->si_addr = (void *)A(s->si_addr); + break; + case SIGPOLL: + d->si_band = s->si_band; + d->si_fd = s->si_fd; + break; + default: + d->si_pid = s->si_pid; + d->si_uid = s->si_uid; + break; + } + return d; +} + + +extern asmlinkage long sys_rt_sigqueueinfo(int pid, int sig, siginfo_t *uinfo); + +/* Note: it is necessary to treat pid and sig as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_rt_sigqueueinfo(u32 pid, u32 sig, siginfo_t32 *uinfo) +{ + siginfo_t info; + siginfo_t32 info32; + int ret; + mm_segment_t old_fs = get_fs(); + + if (copy_from_user (&info32, uinfo, sizeof(siginfo_t32))) + return -EFAULT; + /* XXX: Is this correct? */ + siginfo32to64(&info, &info32); + + set_fs (KERNEL_DS); + ret = sys_rt_sigqueueinfo((int)pid, (int)sig, &info); + set_fs (old_fs); + return ret; +} + + +int do_signal(sigset_t *oldset, struct pt_regs *regs); +int sys32_rt_sigsuspend(sigset32_t* unewset, size_t sigsetsize, int p3, int p4, int p6, int p7, struct pt_regs *regs) +{ + sigset_t saveset, newset; + + sigset32_t s32; + + PPCDBG(PPCDBG_SYS64X, "sys32_rt_sigsuspend - running - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + PPCDBG(PPCDBG_SIGNAL, "sys32_rt_sigsuspend - current block mask = %lx \n",current->blocked); + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&s32, unewset, sizeof(s32))) + return -EFAULT; + + /* Swap the 2 words of the 64-bit sigset_t (they are stored in the "wrong" endian in 32-bit user storage). */ + switch (_NSIG_WORDS) + { + case 4: newset.sig[3] = s32.sig[6] | (((long)s32.sig[7]) << 32); + case 3: newset.sig[2] = s32.sig[4] | (((long)s32.sig[5]) << 32); + case 2: newset.sig[1] = s32.sig[2] | (((long)s32.sig[3]) << 32); + case 1: newset.sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32); + } + + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + regs->gpr[3] = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(&saveset, regs)) + return regs->gpr[3]; + } +} + + + + + + + + +/* + * Set up a rt signal frame. + */ +static void +setup_rt_frame32(struct pt_regs *regs, struct sigregs32 *frame, + unsigned int newsp) +{ + + unsigned int copyreg4,copyreg5; + struct rt_sigframe_32 * rt_sf = (struct rt_sigframe_32 *) (u64)newsp; + + + if (verify_area(VERIFY_WRITE, frame, sizeof(*frame))) + goto badframe; + if (regs->msr & MSR_FP) + giveup_fpu(current); + /***************************************************************/ + /* */ + /* Copy the register contents for the pt_regs structure on the */ + /* kernel stack to the elf_gregset_t32 structure on the user */ + /* stack. This is a copy of 64 bit register values to 32 bit */ + /* register values. The high order 32 bits of the 64 bit */ + /* registers are not needed since a 32 bit application is */ + /* running and the saved registers are the contents of the */ + /* user registers at the time of a system call. */ + /* */ + /* The values saved on the user stack will be restored into */ + /* the registers during the signal return processing */ + /* */ + /* Note the +1 is needed in order to get the lower 32 bits */ + /* of 64 bit register */ + /***************************************************************/ + if (__copy_to_user(&frame->gp_regs[0], (u32*)(®s->gpr[0])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[1], (u32*)(®s->gpr[1])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[2], (u32*)(®s->gpr[2])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[3], (u32*)(®s->gpr[3])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[4], (u32*)(®s->gpr[4])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[5], (u32*)(®s->gpr[5])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[6], (u32*)(®s->gpr[6])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[7], (u32*)(®s->gpr[7])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[8], (u32*)(®s->gpr[8])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[9], (u32*)(®s->gpr[9])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[10], (u32*)(®s->gpr[10])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[11], (u32*)(®s->gpr[11])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[12], (u32*)(®s->gpr[12])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[13], (u32*)(®s->gpr[13])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[14], (u32*)(®s->gpr[14])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[15], (u32*)(®s->gpr[15])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[16], (u32*)(®s->gpr[16])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[17], (u32*)(®s->gpr[17])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[18], (u32*)(®s->gpr[18])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[19], (u32*)(®s->gpr[19])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[20], (u32*)(®s->gpr[20])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[21], (u32*)(®s->gpr[21])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[22], (u32*)(®s->gpr[22])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[23], (u32*)(®s->gpr[23])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[24], (u32*)(®s->gpr[24])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[25], (u32*)(®s->gpr[25])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[26], (u32*)(®s->gpr[26])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[27], (u32*)(®s->gpr[27])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[28], (u32*)(®s->gpr[28])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[29], (u32*)(®s->gpr[29])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[30], (u32*)(®s->gpr[30])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[31], (u32*)(®s->gpr[31])+1, sizeof(u32))) + goto badframe; + + /*****************************************************************************/ + /* Copy the non gpr registers to the user stack */ + /*****************************************************************************/ + + if (__copy_to_user(&frame->gp_regs[PT_NIP], (u32*)(®s->gpr[PT_NIP])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_MSR], (u32*)(®s->gpr[PT_MSR])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_ORIG_R3], (u32*)(®s->gpr[PT_ORIG_R3])+1, + sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_CTR], (u32*)(®s->gpr[PT_CTR])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_LNK], (u32*)(®s->gpr[PT_LNK])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_XER], (u32*)(®s->gpr[PT_XER])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_CCR], (u32*)(®s->gpr[PT_CCR])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_MQ], (u32*)(®s->gpr[PT_MQ])+1, sizeof(u32)) + || __copy_to_user(&frame->gp_regs[PT_RESULT], (u32*)(®s->gpr[PT_RESULT])+1, + sizeof(u32))) + goto badframe; + + + /*****************************************************************************/ + /* Now copy the floating point registers onto the user stack */ + /* */ + /* Also set up so on the completion of the signal handler, the sys_sigreturn */ + /* will get control to reset the stack */ + /*****************************************************************************/ + + + if (__copy_to_user(&frame->fp_regs, current->thread.fpr, + ELF_NFPREG * sizeof(double)) + || __put_user(0x38006666U, &frame->tramp[0]) /* li r0,0x6666 */ + || __put_user(0x44000002U, &frame->tramp[1])) /* sc */ + goto badframe; + + flush_icache_range((unsigned long) &frame->tramp[0], + (unsigned long) &frame->tramp[2]); + + + /* Retrieve rt_sigframe from stack and + set up registers for signal handler + */ + newsp -= __SIGNAL_FRAMESIZE32; + + + if (put_user((u32)(regs->gpr[1]), (unsigned int *)(u64)newsp) + || get_user(regs->nip, &rt_sf->uc.uc_mcontext.handler) + || get_user(regs->gpr[3], &rt_sf->uc.uc_mcontext.signal) + || get_user(copyreg4, &rt_sf->pinfo) + || get_user(copyreg5, &rt_sf->puc)) + goto badframe; + + regs->gpr[4] = copyreg4; + regs->gpr[5] = copyreg5; + + + regs->gpr[1] = newsp; + regs->gpr[6] = (unsigned long) rt_sf; + + + regs->link = (unsigned long) frame->tramp; + + return; + + +badframe: + udbg_printf("setup_frame32 - badframe in setup_frame, regs=%p frame=%p newsp=%lx\n", regs, frame, newsp); PPCDBG_ENTER_DEBUGGER(); +#if DEBUG_SIG + printk("badframe in setup_frame32, regs=%p frame=%p newsp=%lx\n", + regs, frame, newsp); +#endif + do_exit(SIGSEGV); +} + + +/* + * OK, we're invoking a handler + */ +static void +handle_signal32(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs, + unsigned int *newspp, unsigned int frame) +{ + struct sigcontext32_struct *sc; + struct rt_sigframe_32 *rt_stack_frame; + siginfo_t32 siginfo32bit; + + if (regs->trap == 0x0C00 /* System Call! */ + && ((int)regs->result == -ERESTARTNOHAND || + ((int)regs->result == -ERESTARTSYS && + !(ka->sa.sa_flags & SA_RESTART)))) + regs->result = -EINTR; + + /* Set up the signal frame */ + /* Determine if an real time frame - siginfo required */ + if (ka->sa.sa_flags & SA_SIGINFO) + { + siginfo64to32(&siginfo32bit,info); + *newspp -= sizeof(*rt_stack_frame); + rt_stack_frame = (struct rt_sigframe_32 *) (u64)(*newspp) ; + + if (verify_area(VERIFY_WRITE, rt_stack_frame, sizeof(*rt_stack_frame))) + { + goto badframe; + } + if (__put_user((u32)(u64)ka->sa.sa_handler, &rt_stack_frame->uc.uc_mcontext.handler) + || __put_user((u32)(u64)&rt_stack_frame->info, &rt_stack_frame->pinfo) + || __put_user((u32)(u64)&rt_stack_frame->uc, &rt_stack_frame->puc) + /* put the siginfo on the user stack */ + || __copy_to_user(&rt_stack_frame->info,&siginfo32bit,sizeof(siginfo32bit)) + /* set the ucontext on the user stack */ + || __put_user(0,&rt_stack_frame->uc.uc_flags) + || __put_user(0,&rt_stack_frame->uc.uc_link) + || __put_user(current->sas_ss_sp, &rt_stack_frame->uc.uc_stack.ss_sp) + || __put_user(sas_ss_flags(regs->gpr[1]), + &rt_stack_frame->uc.uc_stack.ss_flags) + || __put_user(current->sas_ss_size, &rt_stack_frame->uc.uc_stack.ss_size) + || __copy_to_user(&rt_stack_frame->uc.uc_sigmask, oldset,sizeof(*oldset)) + /* point the mcontext.regs to the pramble register frame */ + || __put_user(frame, &rt_stack_frame->uc.uc_mcontext.regs) + || __put_user(sig,&rt_stack_frame->uc.uc_mcontext.signal)) + { + goto badframe; + } + } + else + { + /* Put another sigcontext on the stack */ + *newspp -= sizeof(*sc); + sc = (struct sigcontext32_struct *)(u64)*newspp; + if (verify_area(VERIFY_WRITE, sc, sizeof(*sc))) + goto badframe; + + /* Note the upper 32 bits of the signal mask are stored in the */ + /* unused part of the signal stack frame */ + if (__put_user((u32)(u64)ka->sa.sa_handler, &sc->handler) + || __put_user(oldset->sig[0], &sc->oldmask) + || __put_user((oldset->sig[0] >> 32), &sc->_unused[3]) + || __put_user((unsigned int)frame, &sc->regs) + || __put_user(sig, &sc->signal)) + goto badframe; + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sigmask_lock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + } + } + return; + +badframe: +#if DEBUG_SIG + printk("badframe in handle_signal32, regs=%p frame=%lx newsp=%lx\n", + regs, frame, *newspp); + printk("sc=%p sig=%d ka=%p info=%p oldset=%p\n", sc, sig, ka, info, oldset); +#endif + do_exit(SIGSEGV); +} + + +/****************************************************************************/ +/* Start Alternate signal stack support */ +/* */ +/* */ +/* */ +/* System Calls */ +/* sigaltatck sys32_sigaltstack */ +/* */ +/****************************************************************************/ + + +asmlinkage int sys32_sigaltstack(u32 newstack, u32 oldstack, int p3, int p4, int p6, + int p7, struct pt_regs *regs) + +{ + stack_t uss, uoss; + int ret; + mm_segment_t old_fs; + unsigned long sp; + + + /* set sp to the user stack on entry to the system call */ + /* the system call router sets R9 to the saved registers */ + sp = regs->gpr[1]; + + PPCDBG(PPCDBG_SIGNAL," sys32 alt stack entered sp = %lx \n", sp); + + + /* Put new stack info in local 64 bit stack struct */ + if (newstack && (get_user((long)uss.ss_sp, &((stack_32_t *)(long)newstack)->ss_sp) || + __get_user(uss.ss_flags, &((stack_32_t *)(long)newstack)->ss_flags) || + __get_user(uss.ss_size, &((stack_32_t *)(long)newstack)->ss_size))) + return -EFAULT; + + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = do_sigaltstack(newstack ? &uss : NULL, oldstack ? &uoss : NULL, sp); + set_fs(old_fs); + /* Copy the stack information to the user output buffer */ + if (!ret && oldstack && (put_user((long)uoss.ss_sp, &((stack_32_t *)(long)oldstack)->ss_sp) || + __put_user(uoss.ss_flags, &((stack_32_t *)(long)oldstack)->ss_flags) || + __put_user(uoss.ss_size, &((stack_32_t *)(long)oldstack)->ss_size))) + return -EFAULT; + return ret; +} + + + +/****************************************************************************/ +/* Start of do_signal32 routine */ +/* */ +/* This routine gets control when a pemding signal needs to be processed */ +/* in the 32 bit target thread - */ +/* */ +/* It handles both rt and non-rt signals */ +/* */ +/****************************************************************************/ + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + */ + +int do_signal32(sigset_t *oldset, struct pt_regs *regs) +{ + siginfo_t info; + struct k_sigaction *ka; + unsigned int frame, newsp; + + if (!oldset) + oldset = ¤t->blocked; + + newsp = frame = 0; + + for (;;) { + unsigned long signr; + + spin_lock_irq(¤t->sigmask_lock); + signr = dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->sigmask_lock); + ifppcdebug(PPCDBG_SYS32) + { + if (signr) + udbg_printf("do_signal32 - processing signal=%2lx - pid=%ld, comm=%s \n", signr, current->pid, current->comm); + } + + if (!signr) + break; + + if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) { + /* Let the debugger run. */ + current->exit_code = signr; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + + /* We're back. Did the debugger cancel the sig? */ + if (!(signr = current->exit_code)) + continue; + current->exit_code = 0; + + /* The debugger continued. Ignore SIGSTOP. */ + if (signr == SIGSTOP) + continue; + + /* Update the siginfo structure. Is this good? */ + if (signr != info.si_signo) { + info.si_signo = signr; + info.si_errno = 0; + info.si_code = SI_USER; + info.si_pid = current->p_pptr->pid; + info.si_uid = current->p_pptr->uid; + } + + /* If the (new) signal is now blocked, requeue it. */ + if (sigismember(¤t->blocked, signr)) { + send_sig_info(signr, &info, current); + continue; + } + } + + ka = ¤t->sig->action[signr-1]; + + if (ka->sa.sa_handler == SIG_IGN) { + if (signr != SIGCHLD) + continue; + /* Check for SIGCHLD: it's special. */ + while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0) + /* nothing */; + continue; + } + + if (ka->sa.sa_handler == SIG_DFL) { + int exit_code = signr; + + /* Init gets no signals it doesn't want. */ + if (current->pid == 1) + continue; + + switch (signr) { + case SIGCONT: case SIGCHLD: case SIGWINCH: + continue; + + case SIGTSTP: case SIGTTIN: case SIGTTOU: + if (is_orphaned_pgrp(current->pgrp)) + continue; + /* FALLTHRU */ + + case SIGSTOP: + current->state = TASK_STOPPED; + current->exit_code = signr; + if (!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) + notify_parent(current, SIGCHLD); + schedule(); + continue; + + case SIGQUIT: case SIGILL: case SIGTRAP: + case SIGABRT: case SIGFPE: case SIGSEGV: + case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ: + if (do_coredump(signr, regs)) + exit_code |= 0x80; + /* FALLTHRU */ + + default: + sigaddset(¤t->pending.signal, signr); + recalc_sigpending(current); + current->flags |= PF_SIGNALED; + do_exit(exit_code); + /* NOTREACHED */ + } + } + + PPCDBG(PPCDBG_SIGNAL, " do signal :sigaction flags = %lx \n" ,ka->sa.sa_flags); + PPCDBG(PPCDBG_SIGNAL, " do signal :on sig stack = %lx \n" ,on_sig_stack(regs->gpr[1])); + PPCDBG(PPCDBG_SIGNAL, " do signal :reg1 = %lx \n" ,regs->gpr[1]); + PPCDBG(PPCDBG_SIGNAL, " do signal :alt stack = %lx \n" ,current->sas_ss_sp); + PPCDBG(PPCDBG_SIGNAL, " do signal :alt stack size = %lx \n" ,current->sas_ss_size); + + + + if ( (ka->sa.sa_flags & SA_ONSTACK) + && (! on_sig_stack(regs->gpr[1]))) + { + newsp = (current->sas_ss_sp + current->sas_ss_size); + } + else + newsp = regs->gpr[1]; + newsp = frame = newsp - sizeof(struct sigregs32); + + /* Whee! Actually deliver the signal. */ + handle_signal32(signr, ka, &info, oldset, regs, &newsp, frame); + break; + } + + if (regs->trap == 0x0C00 /* System Call! */ && + ((int)regs->result == -ERESTARTNOHAND || + (int)regs->result == -ERESTARTSYS || + (int)regs->result == -ERESTARTNOINTR)) { + regs->gpr[3] = regs->orig_gpr3; + regs->nip -= 4; /* Back up & retry system call */ + regs->result = 0; + } + + if (newsp == frame) + { + return 0; /* no signals delivered */ + } + // Invoke correct stack setup routine + if (ka->sa.sa_flags & SA_SIGINFO) + { + PPCDBG(PPCDBG_SIGNAL, " do sig - invoke setup rt signal newsp = %lx frame = %lx \n", + newsp, frame); + setup_rt_frame32(regs, (struct sigregs32*)(u64)frame, newsp); + } + else + { + PPCDBG(PPCDBG_SIGNAL, " do sig - invoke setup non rt signal newsp = %lx frame = %lx \n", + newsp, frame); + + setup_frame32(regs, (struct sigregs32*)(u64)frame, newsp); + } + + return 1; + +} + + + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/smp.c linuxppc64_2_4/arch/ppc64/kernel/smp.c --- linux-2.4.5-ac18/arch/ppc64/kernel/smp.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/smp.c Wed Aug 1 14:06:30 2001 @@ -0,0 +1,754 @@ +/* + * + * + * SMP support for ppc. + * + * Written by Cort Dougan (cort@cs.nmt.edu) borrowing a great + * deal of code from the sparc and intel versions. + * + * Copyright (C) 1999 Cort Dougan + * + * PowerPC-64 Support added by Dave Engebretsen, Peter Bergner, and + * Mike Corrigan {engebret|bergner|mikec}@us.ibm.com + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#define __KERNEL_SYSCALLS__ +#include +#include +/* #include */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "open_pic.h" + +int smp_threads_ready = 0; +volatile int smp_commenced = 0; +int smp_num_cpus = 1; +int smp_tb_synchronized = 0; +struct klock_info_struct klock_info = { KLOCK_CLEAR, 0 }; +extern atomic_t ipi_recv; +extern atomic_t ipi_sent; +spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED; +cycles_t cacheflush_time; +static int max_cpus __initdata = NR_CPUS; + +unsigned long cpu_online_map; + +/* this has to go in the data section because it is accessed from prom_init */ +int smp_hw_index[NR_CPUS] = {0}; + +volatile unsigned long cpu_callin_map[NR_CPUS] = {0,}; + +#define TB_SYNC_PASSES 4 +volatile unsigned long __initdata tb_sync_flag = 0; +volatile unsigned long __initdata tb_offset = 0; + +int start_secondary(void *); +extern int cpu_idle(void *unused); +void smp_call_function_interrupt(void); +void smp_message_pass(int target, int msg, unsigned long data, int wait); +u_int openpic_read(volatile u_int *addr); +static unsigned long iSeries_smp_message[NR_CPUS]; +extern struct Naca *naca; +extern struct Paca xPaca[]; + +/* Forward declarations */ +static void smp_iSeries_message_pass(int target, int msg, unsigned long data, int wait); +static int smp_iSeries_probe(void); +static void smp_iSeries_kick_cpu(int nr); +static void smp_iSeries_setup_cpu(int nr); + +static void smp_openpic_message_pass(int target, int msg, unsigned long data, int wait); +static int smp_chrp_probe(void); +static void smp_chrp_kick_cpu(int nr); +static void smp_chrp_setup_cpu(int cpu_nr); + +static void smp_xics_message_pass(int target, int msg, unsigned long data, int wait); +static int smp_xics_probe(void); +/* static void smp_xics_setup_cpu(int cpu_nr); */ + +/* + * XICS only has a single IPI, so encode the messages per CPU + */ +volatile unsigned long xics_ipi_message[NR_CPUS] = {0}; + +#define smp_message_pass(t,m,d,w) \ + do { if (smp_ops) \ + atomic_inc(&ipi_sent); \ + smp_ops->message_pass((t),(m),(d),(w)); \ + } while(0) + +static struct smp_ops_t { + void (*message_pass)(int target, int msg, unsigned long data, int wait); + int (*probe)(void); + void (*kick_cpu)(int nr); + void (*setup_cpu)(int nr); + +} *smp_ops; + +/* iSeries (iSeries) */ +static struct smp_ops_t iSeries_smp_ops = { + smp_iSeries_message_pass, + smp_iSeries_probe, + smp_iSeries_kick_cpu, + smp_iSeries_setup_cpu +}; + +/* CHRP with openpic */ +static struct smp_ops_t chrp_smp_ops = { + smp_openpic_message_pass, + smp_chrp_probe, + smp_chrp_kick_cpu, + smp_chrp_setup_cpu, +}; + +/* CHRP with new XICS interrupt controller */ +static struct smp_ops_t xics_smp_ops = { + smp_xics_message_pass, + smp_xics_probe, + smp_chrp_kick_cpu, + smp_chrp_setup_cpu, +}; + +#ifdef CONFIG_KDB +void smp_kdb_stop(void) +{ +} +#endif + +static inline void set_tb(unsigned int upper, unsigned int lower) +{ + mtspr(SPRN_TBWL, 0); + mtspr(SPRN_TBWU, upper); + mtspr(SPRN_TBWL, lower); +} + +void iSeries_smp_message_recv( struct pt_regs * regs ) +{ + int cpu = smp_processor_id(); + int msg; + + if ( smp_num_cpus < 2 ) + return; + + for ( msg = 0; msg < 4; ++msg ) + if ( test_and_clear_bit( msg, &iSeries_smp_message[cpu] ) ) + smp_message_recv( msg, regs ); + +} + +static void smp_iSeries_message_pass(int target, int msg, unsigned long data, int wait) +{ + int i; + for (i = 0; i < smp_num_cpus; ++i) { + if ( (target == MSG_ALL) || + (target == i) || + ((target == MSG_ALL_BUT_SELF) && (i != smp_processor_id())) ) { + set_bit( msg, &iSeries_smp_message[i] ); + HvCall_sendIPI(&(xPaca[i])); + } + } +} + +static int smp_iSeries_probe(void) +{ + unsigned i; + unsigned np; + struct ItLpPaca * lpPaca; + + np = 0; + for (i=0; i < maxPacas; ++i) { + lpPaca = xPaca[i].xLpPacaPtr; + if ( lpPaca->xDynProcStatus < 2 ) + ++np; + } + + smp_tb_synchronized = 1; + return np; +} + +static void smp_iSeries_kick_cpu(int nr) +{ + struct ItLpPaca * lpPaca; + /* Verify we have a Paca for processor nr */ + if ( ( nr <= 0 ) || + ( nr >= maxPacas ) ) + return; + /* Verify that our partition has a processor nr */ + lpPaca = xPaca[nr].xLpPacaPtr; + if ( lpPaca->xDynProcStatus >= 2 ) + return; + /* The processor is currently spinning, waiting + * for the xProcStart field to become non-zero + * After we set xProcStart, the processor will + * continue on to secondary_start in iSeries_head.S + */ + xPaca[nr].xProcStart = 1; +} + +static void smp_iSeries_setup_cpu(int nr) +{ +} + +static void +smp_openpic_message_pass(int target, int msg, unsigned long data, int wait) +{ + /* make sure we're sending something that translates to an IPI */ + if ( msg > 0x3 ){ + printk("SMP %d: smp_message_pass: unknown msg %d\n", + smp_processor_id(), msg); + return; + } + switch ( target ) + { + case MSG_ALL: + openpic_cause_IPI(msg, 0xffffffff); + break; + case MSG_ALL_BUT_SELF: + openpic_cause_IPI(msg, + 0xffffffff & ~(1 << smp_processor_id())); + break; + default: + openpic_cause_IPI(msg, 1<processorCount > 1) + openpic_request_IPIs(); + + return naca->processorCount; +} + +static void +smp_chrp_kick_cpu(int nr) +{ + /* Verify we have a Paca for processor nr */ + if ( ( nr <= 0 ) || + ( nr >= maxPacas ) ) + return; + + /* The processor is currently spinning, waiting + * for the xProcStart field to become non-zero + * After we set xProcStart, the processor will + * continue on to secondary_start in iSeries_head.S + */ + xPaca[nr].xProcStart = 1; +} + +static void +smp_chrp_setup_cpu(int cpu_nr) +{ + static atomic_t ready = ATOMIC_INIT(1); + static volatile int frozen = 0; + + if (cpu_nr == 0) { + /* wait for all the others */ + while (atomic_read(&ready) < smp_num_cpus) + barrier(); + atomic_set(&ready, 1); + /* freeze the timebase */ + call_rtas("freeze-time-base", 0, 1, NULL); + mb(); + frozen = 1; + set_tb(0, 0); + last_jiffy_stamp(0) = 0; + while (atomic_read(&ready) < smp_num_cpus) + barrier(); + /* thaw the timebase again */ + call_rtas("thaw-time-base", 0, 1, NULL); + mb(); + frozen = 0; + smp_tb_synchronized = 1; + } else { + atomic_inc(&ready); + while (!frozen) + barrier(); + set_tb(0, 0); + last_jiffy_stamp(0) = 0; + mb(); + atomic_inc(&ready); + while (frozen) + barrier(); + } + + if (OpenPIC_Addr) { + do_openpic_setup_cpu(); + } else { + if (cpu_nr > 0) + xics_setup_cpu(); + } +} + +static void +smp_xics_message_pass(int target, int msg, unsigned long data, int wait) +{ + int i; + + for (i = 0; i < smp_num_cpus; ++i) { + if (target == MSG_ALL || target == i + || (target == MSG_ALL_BUT_SELF + && i != smp_processor_id())) { + set_bit(msg, &xics_ipi_message[i]); + mb(); + xics_cause_IPI(i); + } + } +} + +static int +smp_xics_probe(void) +{ + return naca->processorCount; +} + +#if 0 +static void +smp_xics_setup_cpu(int cpu_nr) +{ + if (cpu_nr > 0) + xics_setup_cpu(); +} +#endif + + +void smp_local_timer_interrupt(struct pt_regs * regs) +{ + if (!--(get_paca()->prof_counter)) { + update_process_times(user_mode(regs)); + (get_paca()->prof_counter)=get_paca()->prof_multiplier; + } +} + +void smp_message_recv(int msg, struct pt_regs *regs) +{ + atomic_inc(&ipi_recv); + + switch( msg ) { + case PPC_MSG_CALL_FUNCTION: + smp_call_function_interrupt(); + break; + case PPC_MSG_RESCHEDULE: + current->need_resched = 1; + break; +#ifdef CONFIG_XMON + case PPC_MSG_XMON_BREAK: + xmon(regs); + break; +#endif /* CONFIG_XMON */ +#ifdef CONFIG_KDB + case PPC_MSG_XMON_BREAK: + /* This isn't finished yet, obviously -TAI */ + kdb(KDB_REASON_KEYBOARD,0, (kdb_eframe_t) regs); + break; +#endif + default: + printk("SMP %d: smp_message_recv(): unknown msg %d\n", + smp_processor_id(), msg); + break; + } +} + +void smp_send_reschedule(int cpu) +{ + /* + * This is only used if `cpu' is running an idle task, + * so it will reschedule itself anyway... + * + * This isn't the case anymore since the other CPU could be + * sleeping and won't reschedule until the next interrupt (such + * as the timer). + * -- Cort + */ + /* This is only used if `cpu' is running an idle task, + so it will reschedule itself anyway... */ + smp_message_pass(cpu, PPC_MSG_RESCHEDULE, 0, 0); +} + +#ifdef CONFIG_XMON +void smp_send_xmon_break(int cpu) +{ + smp_message_pass(cpu, PPC_MSG_XMON_BREAK, 0, 0); +} +#endif /* CONFIG_XMON */ + +static void stop_this_cpu(void *dummy) +{ + __cli(); + while (1) + ; +} + +void smp_send_stop(void) +{ + smp_call_function(stop_this_cpu, NULL, 1, 0); + smp_num_cpus = 1; +} + +/* + * Structure and data for smp_call_function(). This is designed to minimise + * static memory requirements. It also looks cleaner. + * Stolen from the i386 version. + */ +static spinlock_t call_lock = SPIN_LOCK_UNLOCKED; + +static struct call_data_struct { + void (*func) (void *info); + void *info; + atomic_t started; + atomic_t finished; + int wait; +} *call_data; + +/* + * This function sends a 'generic call function' IPI to all other CPUs + * in the system. + * + * [SUMMARY] Run a function on all other CPUs. + * The function to run. This must be fast and non-blocking. + * An arbitrary pointer to pass to the function. + * currently unused. + * If true, wait (atomically) until function has completed on other CPUs. + * [RETURNS] 0 on success, else a negative status code. Does not return until + * remote CPUs are nearly ready to execute <> or are or have executed. + * + * You must not call this function with disabled interrupts or from a + * hardware interrupt handler, you may call it from a bottom half handler. + */ +int smp_call_function (void (*func) (void *info), void *info, int nonatomic, + int wait) + +{ + struct call_data_struct data; + int ret = -1, cpus = smp_num_cpus-1; + int timeout; + + if (!cpus) + return 0; + + data.func = func; + data.info = info; + atomic_set(&data.started, 0); + data.wait = wait; + if (wait) + atomic_set(&data.finished, 0); + + spin_lock_bh(&call_lock); + call_data = &data; + /* Send a message to all other CPUs and wait for them to respond */ + smp_message_pass(MSG_ALL_BUT_SELF, PPC_MSG_CALL_FUNCTION, 0, 0); + + /* Wait for response */ + timeout = 4000000; + while (atomic_read(&data.started) != cpus) { + if (--timeout == 0) { + printk("smp_call_function on cpu %d: other cpus not responding (%d)\n", + smp_processor_id(), atomic_read(&data.started)); + xmon(0); + goto out; + } + barrier(); + udelay(1); + } + + if (wait) { + timeout = 1000000; + while (atomic_read(&data.finished) != cpus) { + if (--timeout == 0) { + printk("smp_call_function on cpu %d: other cpus not finishing (%d/%d)\n", + smp_processor_id(), atomic_read(&data.finished), atomic_read(&data.started)); + goto out; + } + barrier(); + udelay(1); + } + } + ret = 0; + + out: + spin_unlock_bh(&call_lock); + return ret; +} + +void smp_call_function_interrupt(void) +{ + void (*func) (void *info) = call_data->func; + void *info = call_data->info; + int wait = call_data->wait; + + /* + * Notify initiating CPU that I've grabbed the data and am + * about to execute the function + */ + atomic_inc(&call_data->started); + /* + * At this point the info structure may be out of scope unless wait==1 + */ + (*func)(info); + if (wait) + atomic_inc(&call_data->finished); +} + +void __init smp_boot_cpus(void) +{ + extern struct current_set_struct current_set[]; + struct Paca *paca; + extern void __secondary_start_chrp(void); + int i, cpu_nr; + struct task_struct *p; + unsigned long sp; + + printk("Entering SMP Mode...\n"); + PPCDBG(PPCDBG_SMP, "smp_boot_cpus: start. NR_CPUS = 0x%lx\n", NR_CPUS); + + smp_num_cpus = 1; + smp_store_cpu_info(0); + /* store this once in the naca since we can't at this time + * envision an SMP environment where different processors + * would have a different loops_per_jiffy value -- tgall + */ + naca->loops_per_jiffy=loops_per_jiffy; + + /* + * assume for now that the first cpu booted is + * cpu 0, the master -- Cort + */ + cpu_callin_map[0] = 1; + current->processor = 0; + + init_idle(); + + for (i = 0; i < NR_CPUS; i++) { + paca = &(naca->paca[i]); + paca->prof_counter=1; + paca->prof_multiplier = 1; + if(i != 0) { + /* + * Processor 0's segment table is statically + * initialized to real address 0x5000. The + * Other processor's tables are created and + * initialized here. + */ + paca->xStab_data.virt = + (unsigned long)btmalloc(PAGE_SIZE); + paca->xStab_data.real = ___pa(paca->xStab_data.virt); + paca->xHwProcNum = smp_hw_index[i]; + memset(paca->xStab_data.virt, 0, PAGE_SIZE); + } + PPCDBG(PPCDBG_SMP, + "\tProcessor %d, stab virt = 0x%lx, stab real = 0x%lx\n", + i, paca->xStab_data.virt, paca->xStab_data.real); + } + + /* + * XXX very rough, assumes 20 bus cycles to read a cache line, + * timebase increments every 4 bus cycles, 32kB L1 data cache. + */ + cacheflush_time = 5 * 1024; + + switch ( _machine ) { + case _MACH_chrp: + if (OpenPIC_Addr) { + smp_ops = &chrp_smp_ops; + } else { + smp_ops = &xics_smp_ops; + } + break; + case _MACH_iSeries: + smp_ops = &iSeries_smp_ops; + break; + default: + printk("SMP not supported on this machine.\n"); + return; + } + + /* Probe arch for CPUs */ + cpu_nr = smp_ops->probe(); + + /* + * only check for cpus we know exist. We keep the callin map + * with cpus at the bottom -- Cort + */ + if (cpu_nr > max_cpus) + cpu_nr = max_cpus; + for ( i = 1 ; i < cpu_nr; i++ ) { + int c; + struct pt_regs regs; + + /* create a process for the processor */ + /* we don't care about the values in regs since we'll + never reschedule the forked task. */ + /* We DO care about one bit in the pt_regs we + pass to do_fork. That is the MSR_FP bit in + regs.msr. If that bit is on, then do_fork + (via copy_thread) will call giveup_fpu. + giveup_fpu will get a pointer to our (current's) + last register savearea via current->thread.regs + and using that pointer will turn off the MSR_FP, + MSR_FE0 and MSR_FE1 bits. At this point, this + pointer is pointing to some arbitrary point within + our stack */ + + memset(®s, 0, sizeof(struct pt_regs)); + + if (do_fork(CLONE_VM|CLONE_PID, 0, ®s, 0) < 0) + panic("failed fork for CPU %d", i); + p = init_task.prev_task; + if (!p) + panic("No idle task for CPU %d", i); + + PPCDBG(PPCDBG_SMP,"\tProcessor %d, task = 0x%lx\n", i, p); + + del_from_runqueue(p); + unhash_process(p); + init_tasks[i] = p; + + p->processor = i; + p->has_cpu = 1; + current_set[i].task = p; + sp = ((unsigned long)p) + sizeof(union task_union) + - STACK_FRAME_OVERHEAD; + PPCDBG(PPCDBG_SMP, "\tstack pointer virt = 0x%lx\n", sp); + current_set[i].sp_real = ___pa(sp); + PPCDBG(PPCDBG_SMP,"\tstack pointer real = 0x%lx\n", + current_set[i].sp_real); + + /* wake up cpus */ + smp_ops->kick_cpu(i); + + /* + * wait to see if the cpu made a callin (is actually up). + * use this value that I found through experimentation. + * -- Cort + */ + for ( c = 5000; c && !cpu_callin_map[i] ; c-- ) + udelay(100); + + if ( cpu_callin_map[i] ) + { + printk("Processor %d found.\n", i); + PPCDBG(PPCDBG_SMP, "\tProcessor %d found.\n", i); + /* this sync's the decr's -- Cort */ + smp_num_cpus++; + } else { + printk("Processor %d is stuck.\n", i); + PPCDBG(PPCDBG_SMP, "\tProcessor %d is stuck.\n", i); + } + } + + /* Setup CPU 0 last (important) */ + smp_ops->setup_cpu(0); + + if (smp_num_cpus < 2) + smp_tb_synchronized = 1; +} + +void __init smp_commence(void) +{ + /* + * Lets the callin's below out of their loop. + */ + PPCDBG(PPCDBG_SMP, "smp_commence: start\n"); + wmb(); + smp_commenced = 1; +} + +void __init smp_callin(void) +{ + int cpu = current->processor; + + smp_store_cpu_info(cpu); + set_dec(tb_ticks_per_jiffy); + cpu_callin_map[cpu] = 1; + + smp_ops->setup_cpu(cpu); + + init_idle(); + + /* + * This cpu is now "online". Only set them online + * before they enter the loop below since write access + * to the below variable is _not_ guaranteed to be + * atomic. + * -- Cort + */ + cpu_callin_map[cpu] = 1; + + while(!smp_commenced) + barrier(); + __sti(); +} + +/* intel needs this */ +void __init initialize_secondary(void) +{ +} + +/* Activate a secondary processor. */ +int start_secondary(void *unused) +{ + int cpu; + + cpu = current->processor; + atomic_inc(&init_mm.mm_count); + current->active_mm = &init_mm; + smp_callin(); + + /* Go into the idle loop. */ + return cpu_idle(NULL); +} + +void __init smp_setup(char *str, int *ints) +{ +} + +int __init setup_profiling_timer(unsigned int multiplier) +{ + return 0; +} + +/* this function is called for each processor + */ +void __init smp_store_cpu_info(int id) +{ + naca->paca[id].pvr = _get_PVR(); +} + +static int __init maxcpus(char *str) +{ + get_option(&str, &max_cpus); + return 1; +} + +__setup("maxcpus=", maxcpus); diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/stab.c linuxppc64_2_4/arch/ppc64/kernel/stab.c --- linux-2.4.5-ac18/arch/ppc64/kernel/stab.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/stab.c Wed Jun 27 10:54:31 2001 @@ -0,0 +1,364 @@ +/* + * PowerPC64 Segment Translation Support. + * + * Dave Engebretsen and Mike Corrigan {engebret|mikejc}@us.ibm.com + * Copyright (c) 2001 Dave Engebretsen + * + * 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 +#include +#include +#include +#include +#include + +inline int make_ste(unsigned long stab, + unsigned long esid, unsigned long vsid); +inline void make_slbe(unsigned long esid, unsigned long vsid); +extern unsigned long reloc_offset(void); +extern struct Naca *naca; + +/* + * Initialize the segment table for this processor with the set + * of kernel addresses. + */ + +void stab_initialize(unsigned long skip_esid_c, unsigned long stab) { + unsigned long esid, last_esid, vsid; + + /* + * Put in STEs for the 0xC00000000 - 0xF00000000 segments. + */ + esid = KERNELBASE >> SID_SHIFT; + + /* + * If asked to skip loading esid 0xC00000000 then skip it. + */ + if ( skip_esid_c ) + esid += (REGION_STRIDE >> SID_SHIFT); + + last_esid = (KERNELBASE >> SID_SHIFT) + + ((REGION_COUNT - 2) * (REGION_STRIDE >> SID_SHIFT)); + + for (; esid <= last_esid; esid += (REGION_STRIDE >> SID_SHIFT)) { + /* Map the esid to an EA & find the vsid we need. */ + vsid = get_kernel_vsid(esid << SID_SHIFT); + if(!__is_processor(PV_POWER4)) { + make_ste(stab, esid, vsid); + } else { + make_slbe(esid, vsid ); + } + } +} + +/* + * Create a segment table entry for the given esid/vsid pair. + */ +inline int make_ste(unsigned long stab, + unsigned long esid, + unsigned long vsid) { + unsigned long entry, group, old_esid, castout_entry, i; + unsigned int global_entry; + STE *ste, *castout_ste; + + /* Search the primary group first. */ + global_entry = (esid & 0x1f) << 3; + ste = (STE *)(stab | ((esid & 0x1f) << 7)); + + /* + * Find an empty entry, if one exists. + */ + for(group = 0; group < 2; group++) { + for(entry = 0; entry < 8; entry++, ste++) { + if(!(ste->dw0.dw0.v)) { + ste->dw1.dw1.vsid = vsid; + /* Order VSID updte */ + __asm__ __volatile__ ("eieio" : : : "memory"); + ste->dw0.dw0.esid = esid; + ste->dw0.dw0.v = 1; + ste->dw0.dw0.kp = 1; + /* Order update */ + __asm__ __volatile__ ("sync" : : : "memory"); + + return(global_entry | entry); + } + } + /* Now search the secondary group. */ + global_entry = ((~esid) & 0x1f) << 3; + ste = (STE *)(stab | (((~esid) & 0x1f) << 7)); + } + + /* + * Could not find empty entry, pick one with a round robin selection. + * Search all entries in the two groups. Note that the first time + * we get here, we start with entry REGION_COUNT - 1 so the initializer + * can be common with the SLB castout code. + */ + + /* This assumes we never castout when initializing the stab. */ + PMC_SW_PROCESSOR(stab_capacity_castouts); + + castout_entry = get_paca()->xStab_data.next_round_robin; + for(i = 0; i < 16; i++) { + if(castout_entry < 8) { + global_entry = (esid & 0x1f) << 3; + ste = (STE *)(stab | ((esid & 0x1f) << 7)); + castout_ste = ste + castout_entry; + } else { + global_entry = ((~esid) & 0x1f) << 3; + ste = (STE *)(stab | (((~esid) & 0x1f) << 7)); + castout_ste = ste + (castout_entry - 8); + } + + if(REGION_ID((castout_ste->dw0.dw0.esid) << SID_SHIFT) == + USER_REGION_ID) { + /* Found a non-kernel entry to castout */ + break; + } + + castout_entry = (castout_entry + 1) & 0xf; + } + + get_paca()->xStab_data.next_round_robin = (castout_entry + 1) & 0xf; + + /* Modify the old entry to the new value. */ + + /* Force previous translations to complete. DRENG */ + __asm__ __volatile__ ("isync" : : : "memory" ); + + castout_ste->dw0.dw0.v = 0; + __asm__ __volatile__ ("sync" : : : "memory" ); /* Order update */ + castout_ste->dw1.dw1.vsid = vsid; + __asm__ __volatile__ ("eieio" : : : "memory" ); /* Order update */ + old_esid = castout_ste->dw0.dw0.esid; + castout_ste->dw0.dw0.esid = esid; + castout_ste->dw0.dw0.v = 1; + castout_ste->dw0.dw0.kp = 1; + __asm__ __volatile__ ("slbie %0" : : "r" (old_esid << SID_SHIFT)); + /* Ensure completion of slbie */ + __asm__ __volatile__ ("sync" : : : "memory" ); + + return(global_entry | (castout_entry & 0x7)); +} + +/* + * Create a segment buffer entry for the given esid/vsid pair. + */ +inline void make_slbe(unsigned long esid, unsigned long vsid) { + unsigned long entry, castout_entry; + slb_dword0 castout_esid_data; + union { + unsigned long word0; + slb_dword0 data; + } esid_data; + union { + unsigned long word0; + slb_dword1 data; + } vsid_data; + + /* + * Find an empty entry, if one exists. + */ + for(entry = 0; entry < naca->slb_size; entry++) { + __asm__ __volatile__("slbmfee %0,%1" + : "=r" (esid_data) : "r" (entry)); + if(!esid_data.data.v) { + /* + * Write the new SLB entry. + */ + vsid_data.word0 = 0; + vsid_data.data.vsid = vsid; + vsid_data.data.kp = 1; + + esid_data.word0 = 0; + esid_data.data.esid = esid; + esid_data.data.v = 1; + esid_data.data.index = entry; + + /* slbie not needed as no previous mapping existed. */ + /* Order update */ + __asm__ __volatile__ ("isync" : : : "memory"); + __asm__ __volatile__ ("slbmte %0,%1" + : : "r" (vsid_data), + "r" (esid_data)); + /* Order update */ + __asm__ __volatile__ ("isync" : : : "memory"); + return; + } + } + + /* + * Could not find empty entry, pick one with a round robin selection. + */ + + PMC_SW_PROCESSOR(stab_capacity_castouts); + + castout_entry = get_paca()->xStab_data.next_round_robin; + __asm__ __volatile__("slbmfee %0,%1" + : "=r" (castout_esid_data) + : "r" (castout_entry)); + + entry = castout_entry; + castout_entry++; + if(castout_entry >= naca->slb_size) { + castout_entry = REGION_COUNT - 1; + } + get_paca()->xStab_data.next_round_robin = castout_entry; + + /* Invalidate the old entry. */ + castout_esid_data.v = 0; /* Set the class to 0 */ + /* slbie not needed as the previous mapping is still valid. */ + __asm__ __volatile__("slbie %0" : : "r" (castout_esid_data)); + + /* + * Write the new SLB entry. + */ + vsid_data.word0 = 0; + vsid_data.data.vsid = vsid; + vsid_data.data.kp = 1; + + esid_data.word0 = 0; + esid_data.data.esid = esid; + esid_data.data.v = 1; + esid_data.data.index = entry; + + __asm__ __volatile__ ("isync" : : : "memory"); /* Order update */ + __asm__ __volatile__ ("slbmte %0,%1" + : : "r" (vsid_data), "r" (esid_data)); + __asm__ __volatile__ ("isync" : : : "memory" ); /* Order update */ +} + +/* + * Allocate a segment table entry for the given ea. + */ +int ste_allocate ( unsigned long ea, + unsigned long trap) +{ + unsigned long vsid, esid; + + PMC_SW_PROCESSOR(stab_faults); + + /* Check for invalid effective addresses. */ + if(IS_INVALID_EA(ea)) { + return 1; + } + + /* Kernel or user address? */ + if(REGION_ID(ea) >= KERNEL_REGION_ID) { + vsid = get_kernel_vsid( ea ); + } else { + struct mm_struct *mm = current->mm; + if ( mm ) { + vsid = get_vsid(mm->context, ea ); + } else { + return 1; + } + } + + esid = GET_ESID(ea); + if(trap == 0x380 || trap == 0x480) { + make_slbe(esid, vsid); + } else { + unsigned char top_entry, stab_entry, *segments, i; + + stab_entry = make_ste(get_paca()->xStab_data.virt, esid, vsid); + PMC_SW_PROCESSOR_A(stab_entry_use, stab_entry & 0xf); + + segments = get_paca()->xSegments; + top_entry = segments[0]; + if(top_entry < (STAB_CACHE_SIZE - 1)) { + top_entry++; + segments[top_entry] = stab_entry; + if(top_entry == STAB_CACHE_SIZE - 1) top_entry = 0xff; + segments[0] = top_entry; + } + } + + return(0); +} + +/* + * Flush all entries from the segment table of the current processor. + * Kernel and Bolted entries are not removed as we cannot tolerate + * faults on those addresses. + */ +void flush_stab(void) { + STE *stab = (STE *) get_paca()->xStab_data.virt; + unsigned char *segments = get_paca()->xSegments; + unsigned long flags, i; + + if(!__is_processor(PV_POWER4)) { + unsigned long entry; + STE *ste = stab; + + /* Never flush the first four entries (kernel segments) */ + ste += (REGION_COUNT-1); + + /* Force previous translations to complete. DRENG */ + __asm__ __volatile__ ("isync" : : : "memory"); + + __save_and_cli(flags); + if(segments[0] != 0xff) { + for(i = 1; i <= segments[0]; i++) { + ste = stab + segments[i]; + ste->dw0.dw0.v = 0; + PMC_SW_PROCESSOR(stab_invalidations); + } + } else { + /* Invalidate all entries except the four + * kernel segments. + */ + for(entry = REGION_COUNT - 1; + entry < (PAGE_SIZE / sizeof(STE)); + entry++, ste++) { + ste->dw0.dw0.v = 0; + PMC_SW_PROCESSOR(stab_invalidations); + } + } + + *((unsigned long *)segments) = 0; + __restore_flags(flags); + + /* Invalidate the SLB. */ + /* Force invals to complete. */ + __asm__ __volatile__ ("sync" : : : "memory"); + /* Flush the SLB. */ + __asm__ __volatile__ ("slbia" : : : "memory"); + /* Force flush to complete. */ + __asm__ __volatile__ ("sync" : : : "memory"); + } else { + /* Invalidate the entire SLB (except entry 1), and then put */ + /* back in the bolted range translation. */ + /* This only does the first bolted segment presently. DRENG */ + unsigned long msr, tmp; + + /* esid v idx */ + slb_dword0 esid_data = {BOLTEDBASE >> SID_SHIFT, 1, 0, 1}; + + /* vsid ks kp */ + slb_dword1 vsid_data = {0, 0, 1, 0, 0, 0, 0}; + vsid_data.vsid = get_kernel_vsid(BOLTEDBASE); + + PMC_SW_PROCESSOR(stab_invalidations); + + __asm__ __volatile__("\n\ + mfmsr %0 \n\ + rldicl %1,%0,48,1 \n\ + rldicl %1,%1,16,0 \n\ + mtmsrd %1 \n\ + isync \n\ + slbia \n\ + isync \n\ + slbmte %2,%3 \n\ + isync \n\ + mtmsrd %0" + : "=&r"(msr), "=&r"(tmp) + : "r" (vsid_data), "r" (esid_data) + : "memory"); + } +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/sys32.S linuxppc64_2_4/arch/ppc64/kernel/sys32.S --- linux-2.4.5-ac18/arch/ppc64/kernel/sys32.S Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/sys32.S Mon Jun 18 13:47:18 2001 @@ -0,0 +1,243 @@ +/* + * sys32.S: I-cache tricks for 32-bit compatability layer simple + * conversions. + * + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz) + * Copyright (C) 2000 Ken Aaker (kdaaker@rchland.vnet.ibm.com) + * For PPC ABI convention is parms in Regs 3-10. + * The router in entry.S clears the high 32 bits in the first + * 4 arguments (R3-R6). + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include "ppc_asm.h" +#include +#include + +/* NOTE: call as jump breaks return stack, we have to avoid that */ + + .text + +_GLOBAL(sys32_mmap) + clrldi r7, r7, 32 /* int fd parm */ + clrldi r8, r8, 32 /* off_t offset parm */ + b .sys_mmap + +_GLOBAL(sys32_lseek) + extsw r4,r4 /* sign extend off_t offset parm */ + b .sys_lseek + +_GLOBAL(sys32_chmod) +/* Ken Aaker.. hmmm maybe I don't need to do anything here */ + b .sys_chmod + +_GLOBAL(sys32_mknod) +/* Ken Aaker.. hmmm maybe I don't need to do anything here */ + b .sys_mknod + +_GLOBAL(sys32_sendto) + clrldi r7, r7, 32 /* struct sockaddr *addr parm */ + clrldi r8, r8, 32 /* int addr_len parm */ + b .sys_sendto + +_GLOBAL(sys32_recvfrom) + clrldi r7, r7, 32 /* struct sockaddr *addr parm */ + clrldi r8, r8, 32 /* int *addr_len parm */ + b .sys_recvfrom + +_GLOBAL(sys32_getsockopt) + clrldi r7, r7, 32 /* int *optlen parm */ + b .sys_getsockopt + +_GLOBAL(sys32_bdflush) + extsw r4,r4 /* sign extend long data parm */ + b .sys_bdflush + +_GLOBAL(sys32_mmap2) + clrldi r7, r7, 32 /* unsigned long fd parm */ + extsw r8, r8 /* off_t offset */ + b .sys_mmap + +_GLOBAL(sys32_socketcall) /* r3=call, r4=args */ + cmpwi r3, 1 + blt- .do_einval + cmpwi r3, 17 + bgt- .do_einval + subi r3, r3, 1 /* index into socketcall_table vectors and jmp */ + sldi r3, r3, 3 /* each entry is 8 bytes */ + LOADADDR(r10,.socketcall_table_begin) + ldx r10, r10, r3 + mtctr r10 + bctr + +/* Socket function vectored fix ups for 32 bit */ +_STATIC(do_sys_socket) /* sys_socket(int, int, int) */ + mr r10,r4 + lwa r3,0(r10) + lwa r4,4(r10) + lwa r5,8(r10) + b .sys_socket + +_STATIC(do_sys_bind) /* sys_bind(int fd, struct sockaddr *, int) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwa r5,8(r10) + b .sys_bind + +_STATIC(do_sys_connect) /* sys_connect(int, struct sockaddr *, int) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwa r5,8(r10) + b .sys_connect + +_STATIC(do_sys_listen) /* sys_listen(int, int) */ + mr r10,r4 + lwa r3,0(r10) + lwa r4,4(r10) + b .sys_listen + +_STATIC(do_sys_accept) /* sys_accept(int, struct sockaddr *, int *) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwz r5,8(r10) + b .sys_accept + +_STATIC(do_sys_getsockname) /* sys_getsockname(int, struct sockaddr *, int *) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwz r5,8(r10) + b .sys_getsockname + +_STATIC(do_sys_getpeername) /* sys_getpeername(int, struct sockaddr *, int *) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwz r5,8(r10) + b .sys_getpeername + +_STATIC(do_sys_socketpair) /* sys_socketpair(int, int, int, int *) */ + mr r10,r4 + lwa r3,0(r10) + lwa r4,4(r10) + lwa r5,8(r10) + lwz r6,12(r10) + b .sys_socketpair + +_STATIC(do_sys_send) /* sys_send(int, void *, size_t, unsigned int) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwz r5,8(r10) + lwz r6,12(r10) + b .sys_send + +_STATIC(do_sys_recv) /* sys_recv(int, void *, size_t, unsigned int) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwz r5,8(r10) + lwz r6,12(r10) + b .sys_recv + +_STATIC(do_sys_sendto) /* sys32_sendto(int, u32, __kernel_size_t32, unsigned int, u32, int) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwz r5,8(r10) + lwz r6,12(r10) + lwz r7,16(r10) + lwa r8,20(r10) + b .sys32_sendto + +_STATIC(do_sys_recvfrom) /* sys32_recvfrom(int, u32, __kernel_size_t32, unsigned int, u32, u32) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwz r5,8(r10) + lwz r6,12(r10) + lwz r7,16(r10) + lwz r8,20(r10) + b .sys32_recvfrom + +_STATIC(do_sys_shutdown) /* sys_shutdown(int, int) */ + mr r10,r4 + lwa r3,0(r10) + lwa r4,4(r10) + b .sys_shutdown + +_STATIC(do_sys_setsockopt) /* sys32_setsockopt(int, int, int, char *, int) */ + mr r10,r4 + lwa r3,0(r10) + lwa r4,4(r10) + lwa r5,8(r10) + lwz r6,12(r10) + lwa r7,16(r10) + b .sys32_setsockopt + +_STATIC(do_sys_getsockopt) /* sys32_getsockopt(int, int, int, u32, u32) */ + mr r10,r4 + lwa r3,0(r10) + lwa r4,4(r10) + lwa r5,8(r10) + lwz r6,12(r10) + lwz r7,16(r10) + b .sys32_getsockopt + +_STATIC(do_sys_sendmsg) /* sys32_sendmsg(int, struct msghdr32 *, unsigned int) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwa r5,8(r10) + b .sys32_sendmsg + +_STATIC(do_sys_recvmsg) /* sys32_recvmsg(int, struct msghdr32 *, unsigned int) */ + mr r10,r4 + lwa r3,0(r10) + lwz r4,4(r10) + lwa r5,8(r10) + b .sys32_recvmsg + +_STATIC(do_einval) + li r3,-EINVAL + b .ret_from_syscall_1 +_STATIC(do_efault) + li r3,-EFAULT + b .ret_from_syscall_1 + + .data + .align 8 +_GLOBAL(socketcall_table_begin) + .llong .do_sys_socket + .llong .do_sys_bind + .llong .do_sys_connect + .llong .do_sys_listen + .llong .do_sys_accept + .llong .do_sys_getsockname + .llong .do_sys_getpeername + .llong .do_sys_socketpair + .llong .do_sys_send + .llong .do_sys_recv + .llong .do_sys_sendto + .llong .do_sys_recvfrom + .llong .do_sys_shutdown + .llong .do_sys_setsockopt + .llong .do_sys_getsockopt + .llong .do_sys_sendmsg + .llong .do_sys_recvmsg +_GLOBAL(socketcall_table_end) + .section __ex_table,"a" + .align 3 + .llong .socketcall_table_begin + .llong 0 + .llong .socketcall_table_end + .llong .do_efault + .previous diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/sys_ppc32.c linuxppc64_2_4/arch/ppc64/kernel/sys_ppc32.c --- linux-2.4.5-ac18/arch/ppc64/kernel/sys_ppc32.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/sys_ppc32.c Tue Jul 10 16:11:40 2001 @@ -0,0 +1,4884 @@ +/* + * sys_ppc32.c: Conversion between 32bit and 64bit native syscalls. + * + * Copyright (C) 2001 IBM + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * + * These routines maintain argument size conversion between 32bit and 64bit + * environment. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +/* + * 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) + + + +/* For this source file, we want overflow handling. */ +#undef high2lowuid +#undef high2lowgid +#undef low2highuid +#undef low2highgid + +#define high2lowuid(uid) ((uid) > 65535) ? (u16)overflowuid : (u16)(uid) +#define high2lowgid(gid) ((gid) > 65535) ? (u16)overflowgid : (u16)(gid) +#define low2highuid(uid) ((uid) == (u16)-1) ? (uid_t)-1 : (uid_t)(uid) +#define low2highgid(gid) ((gid) == (u16)-1) ? (gid_t)-1 : (gid_t)(gid) + + + +/* Use this to get at 32-bit user passed pointers. */ +/* Things to consider: the low-level assembly stub does + srl x, 0, x for first four arguments, so if you have + pointer to something in the first four arguments, just + declare it as a pointer, not u32. On the other side, + arguments from 5th onwards should be declared as u32 + for pointers, and need AA() around each usage. + A() macro should be used for places where you e.g. + have some internal variable u32 and just want to get + rid of a compiler warning. AA() has to be used in + places where you want to convert a function argument + to 32bit pointer or when you e.g. access pt_regs + structure and want to consider 32bit registers only. + - + */ +#define A(__x) ((unsigned long)(__x)) +#define AA(__x) \ +({ unsigned long __ret; \ + __asm__ ("clrldi %0, %0, 32" \ + : "=r" (__ret) \ + : "0" (__x)); \ + __ret; \ +}) + + + +/* 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.. + * + * POSIX.1 2.4: an empty pathname is invalid (ENOENT). + */ +static inline int do_getname32(const char *filename, char *page) +{ + int retval; + + /* 32bit pointer will be always far below TASK_SIZE :)) */ + retval = strncpy_from_user((char *)page, (char *)filename, PAGE_SIZE); + if (retval > 0) { + if (retval < PAGE_SIZE) + return 0; + return -ENAMETOOLONG; + } else if (!retval) + retval = -ENOENT; + return retval; +} + +char * getname32(const char *filename) +{ + char *tmp, *result; + + result = ERR_PTR(-ENOMEM); + tmp = __getname(); + if (tmp) { + int retval = do_getname32(filename, tmp); + + result = tmp; + if (retval < 0) { + putname(tmp); + result = ERR_PTR(retval); + } + } + return result; +} + + + +extern asmlinkage long sys_utime(char * filename, struct utimbuf * times); + +struct utimbuf32 { + __kernel_time_t32 actime, modtime; +}; + +asmlinkage long sys32_utime(char * filename, struct utimbuf32 *times) +{ + struct utimbuf t; + mm_segment_t old_fs; + int ret; + char *filenam; + + PPCDBG(PPCDBG_SYS32NI, "sys32_utime - running - filename=%s, times=%p - pid=%ld, comm=%s \n", filename, times, current->pid, current->comm); + + if (!times) + return sys_utime(filename, NULL); + if (get_user(t.actime, ×->actime) || __get_user(t.modtime, ×->modtime)) + return -EFAULT; + filenam = getname32(filename); + + ret = PTR_ERR(filenam); + if (!IS_ERR(filenam)) { + old_fs = get_fs(); + set_fs (KERNEL_DS); + ret = sys_utime(filenam, &t); + set_fs (old_fs); + putname (filenam); + } + + return ret; +} + + + +struct iovec32 { u32 iov_base; __kernel_size_t32 iov_len; }; + +typedef ssize_t (*IO_fn_t)(struct file *, char *, size_t, loff_t *); + +static long do_readv_writev32(int type, struct file *file, + const struct iovec32 *vector, u32 count) +{ + unsigned long tot_len; + struct iovec iovstack[UIO_FASTIOV]; + struct iovec *iov=iovstack, *ivp; + struct inode *inode; + long retval, i; + IO_fn_t fn; + + /* First get the "struct iovec" from user memory and + * verify all the pointers + */ + if (!count) + return 0; + if(verify_area(VERIFY_READ, vector, sizeof(struct iovec32)*count)) + return -EFAULT; + if (count > UIO_MAXIOV) + return -EINVAL; + if (count > UIO_FASTIOV) { + iov = kmalloc(count*sizeof(struct iovec), GFP_KERNEL); + if (!iov) + return -ENOMEM; + } + + tot_len = 0; + i = count; + ivp = iov; + while(i > 0) { + u32 len; + u32 buf; + + __get_user(len, &vector->iov_len); + __get_user(buf, &vector->iov_base); + tot_len += len; + ivp->iov_base = (void *)A(buf); + ivp->iov_len = (__kernel_size_t) len; + vector++; + ivp++; + i--; + } + + inode = file->f_dentry->d_inode; + /* VERIFY_WRITE actually means a read, as we write to user space */ + retval = locks_verify_area((type == VERIFY_WRITE + ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE), + inode, file, file->f_pos, tot_len); + if (retval) { + if (iov != iovstack) + kfree(iov); + return retval; + } + + /* Then do the actual IO. Note that sockets need to be handled + * specially as they have atomicity guarantees and can handle + * iovec's natively + */ + if (inode->i_sock) { + int err; + err = sock_readv_writev(type, inode, file, iov, count, tot_len); + if (iov != iovstack) + kfree(iov); + return err; + } + + if (!file->f_op) { + if (iov != iovstack) + kfree(iov); + return -EINVAL; + } + /* VERIFY_WRITE actually means a read, as we write to user space */ + fn = file->f_op->read; + if (type == VERIFY_READ) + fn = (IO_fn_t) file->f_op->write; + ivp = iov; + while (count > 0) { + void * base; + int len, nr; + + base = ivp->iov_base; + len = ivp->iov_len; + ivp++; + count--; + nr = fn(file, base, len, &file->f_pos); + if (nr < 0) { + if (retval) + break; + retval = nr; + break; + } + retval += nr; + if (nr != len) + break; + } + if (iov != iovstack) + kfree(iov); + return retval; +} + +asmlinkage long sys32_readv(u32 fd, struct iovec32 *vector, u32 count) +{ + struct file *file; + long ret = -EBADF; + + PPCDBG(PPCDBG_SYS32, "sys32_readv - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + file = fget(fd); + if(!file) + goto bad_file; + + if (file->f_op && (file->f_mode & FMODE_READ) && + (file->f_op->readv || file->f_op->read)) + ret = do_readv_writev32(VERIFY_WRITE, file, vector, count); + fput(file); + +bad_file: + PPCDBG(PPCDBG_SYS32, "sys32_readv - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + return ret; +} + +asmlinkage long sys32_writev(u32 fd, struct iovec32 *vector, u32 count) +{ + struct file *file; + int ret = -EBADF; + + PPCDBG(PPCDBG_SYS32, "sys32_writev - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + file = fget(fd); + if(!file) + goto bad_file; + if (file->f_op && (file->f_mode & FMODE_WRITE) && + (file->f_op->writev || file->f_op->write)) + ret = do_readv_writev32(VERIFY_READ, file, vector, count); + fput(file); + +bad_file: + PPCDBG(PPCDBG_SYS32, "sys32_writev - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + return ret; +} + + + +static inline int get_flock(struct flock *kfl, struct flock32 *ufl) +{ + int err; + + err = get_user(kfl->l_type, &ufl->l_type); + err |= __get_user(kfl->l_whence, &ufl->l_whence); + err |= __get_user(kfl->l_start, &ufl->l_start); + err |= __get_user(kfl->l_len, &ufl->l_len); + err |= __get_user(kfl->l_pid, &ufl->l_pid); + return err; +} + +static inline int put_flock(struct flock *kfl, struct flock32 *ufl) +{ + int err; + + err = __put_user(kfl->l_type, &ufl->l_type); + err |= __put_user(kfl->l_whence, &ufl->l_whence); + err |= __put_user(kfl->l_start, &ufl->l_start); + err |= __put_user(kfl->l_len, &ufl->l_len); + err |= __put_user(kfl->l_pid, &ufl->l_pid); + return err; +} + +extern asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg); +asmlinkage long sys32_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + + PPCDBG(PPCDBG_SYS32NI, "sys32_fcntl - running - pid=%ld, comm=%s \n", current->pid, current->comm); + + switch (cmd) { + case F_GETLK: + case F_SETLK: + case F_SETLKW: + { + struct flock f; + mm_segment_t old_fs; + long ret; + + if(get_flock(&f, (struct flock32 *)arg)) + return -EFAULT; + old_fs = get_fs(); set_fs (KERNEL_DS); + ret = sys_fcntl(fd, cmd, (unsigned long)&f); + set_fs (old_fs); + if(put_flock(&f, (struct flock32 *)arg)) + return -EFAULT; + return ret; + } + default: + return sys_fcntl(fd, cmd, (unsigned long)arg); + } +} + + + + + + +struct ncp_mount_data32 { + int version; + unsigned int ncp_fd; + __kernel_uid_t32 mounted_uid; + __kernel_pid_t32 wdog_pid; + unsigned char mounted_vol[NCP_VOLNAME_LEN + 1]; + unsigned int time_out; + unsigned int retry_count; + unsigned int flags; + __kernel_uid_t32 uid; + __kernel_gid_t32 gid; + __kernel_mode_t32 file_mode; + __kernel_mode_t32 dir_mode; +}; + +static void *do_ncp_super_data_conv(void *raw_data) +{ + struct ncp_mount_data *n = (struct ncp_mount_data *)raw_data; + struct ncp_mount_data32 *n32 = (struct ncp_mount_data32 *)raw_data; + + n->dir_mode = n32->dir_mode; + n->file_mode = n32->file_mode; + n->gid = low2highgid(n32->gid); + n->uid = low2highuid(n32->uid); + memmove (n->mounted_vol, n32->mounted_vol, (sizeof (n32->mounted_vol) + 3 * sizeof (unsigned int))); + n->wdog_pid = n32->wdog_pid; + n->mounted_uid = low2highuid(n32->mounted_uid); + return raw_data; +} + +struct smb_mount_data32 { + int version; + __kernel_uid_t32 mounted_uid; + __kernel_uid_t32 uid; + __kernel_gid_t32 gid; + __kernel_mode_t32 file_mode; + __kernel_mode_t32 dir_mode; +}; + +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->version = s32->version; + s->mounted_uid = low2highuid(s32->mounted_uid); + s->uid = low2highuid(s32->uid); + s->gid = low2highgid(s32->gid); + s->file_mode = s32->file_mode; + s->dir_mode = s32->dir_mode; + return raw_data; +} + +static int copy_mount_stuff_to_kernel(const void *user, unsigned long *kernel) +{ + int i; + unsigned long page; + struct vm_area_struct *vma; + + *kernel = 0; + if(!user) + return 0; + vma = find_vma(current->mm, (unsigned long)user); + if(!vma || (unsigned long)user < vma->vm_start) + return -EFAULT; + if(!(vma->vm_flags & VM_READ)) + return -EFAULT; + i = vma->vm_end - (unsigned long) user; + if(PAGE_SIZE <= (unsigned long) i) + i = PAGE_SIZE - 1; + if(!(page = __get_free_page(GFP_KERNEL))) + return -ENOMEM; + if(copy_from_user((void *) page, user, i)) { + free_page(page); + return -EFAULT; + } + *kernel = page; + return 0; +} + +#define SMBFS_NAME "smbfs" +#define NCPFS_NAME "ncpfs" + +asmlinkage long sys32_mount(char *dev_name, char *dir_name, char *type, unsigned long new_flags, u32 data) +{ + unsigned long type_page = 0; + unsigned long data_page = 0; + unsigned long dev_page = 0; + unsigned long dir_page = 0; + int err, is_smb, is_ncp; + + PPCDBG(PPCDBG_SYS32, "sys32_mount - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + is_smb = is_ncp = 0; + + err = copy_mount_stuff_to_kernel((const void *)type, &type_page); + if (err) + goto out; + + if (!type_page) { + err = -EINVAL; + goto out; + } + + is_smb = !strcmp((char *)type_page, SMBFS_NAME); + is_ncp = !strcmp((char *)type_page, NCPFS_NAME); + + err = copy_mount_stuff_to_kernel((const void *)AA(data), &data_page); + if (err) + goto type_out; + + err = copy_mount_stuff_to_kernel(dev_name, &dev_page); + if (err) + goto data_out; + + err = copy_mount_stuff_to_kernel(dir_name, &dir_page); + if (err) + goto dev_out; + + if (!is_smb && !is_ncp) { + lock_kernel(); + err = do_mount((char*)dev_page, (char*)dir_page, + (char*)type_page, new_flags, (char*)data_page); + unlock_kernel(); + } else { + if (is_ncp) + do_ncp_super_data_conv((void *)data_page); + else + do_smb_super_data_conv((void *)data_page); + + lock_kernel(); + err = do_mount((char*)dev_page, (char*)dir_page, + (char*)type_page, new_flags, (char*)data_page); + unlock_kernel(); + } + free_page(dir_page); + +dev_out: + free_page(dev_page); + +data_out: + free_page(data_page); + +type_out: + free_page(type_page); + +out: + + PPCDBG(PPCDBG_SYS32, "sys32_mount - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return err; +} + + + +struct dqblk32 { + __u32 dqb_bhardlimit; + __u32 dqb_bsoftlimit; + __u32 dqb_curblocks; + __u32 dqb_ihardlimit; + __u32 dqb_isoftlimit; + __u32 dqb_curinodes; + __kernel_time_t32 dqb_btime; + __kernel_time_t32 dqb_itime; +}; + + +extern asmlinkage long sys_quotactl(int cmd, const char *special, int id, caddr_t addr); + +/* Note: it is necessary to treat cmd and id as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_quotactl(u32 cmd_parm, const char *special, u32 id_parm, unsigned long addr) +{ + int cmd = (int)cmd_parm; + int id = (int)id_parm; + int cmds = cmd >> SUBCMDSHIFT; + int err; + struct mem_dqblk d; + mm_segment_t old_fs; + char *spec; + + PPCDBG(PPCDBG_SYS32, "sys32_quotactl - entered - pid=%ld current=%lx comm=%s \n", + current->pid, current, current->comm); + + switch (cmds) { + case Q_GETQUOTA: + break; + case Q_SETQUOTA: + case Q_SETUSE: + case Q_SETQLIM: + if (copy_from_user (&d, (struct dqblk32 *)addr, + sizeof (struct dqblk32))) + return -EFAULT; + d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime; + d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime; + break; + default: + return sys_quotactl(cmd, special, + id, (caddr_t)addr); + } + 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)&d); + set_fs (old_fs); + putname (spec); + if (cmds == Q_GETQUOTA) { + __kernel_time_t b = d.dqb_btime, i = d.dqb_itime; + ((struct dqblk32 *)&d)->dqb_itime = i; + ((struct dqblk32 *)&d)->dqb_btime = b; + if (copy_to_user ((struct dqblk32 *)addr, &d, + sizeof (struct dqblk32))) + return -EFAULT; + } + + PPCDBG(PPCDBG_SYS32, "sys32_quotactl - exited - pid=%ld current=%lx comm=%s \n", + current->pid, current, current->comm); + + return err; +} + + + +/* readdir & getdents */ +#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) +#define ROUND_UP(x) (((x)+sizeof(u32)-1) & ~(sizeof(u32)-1)) + +struct old_linux_dirent32 { + u32 d_ino; + u32 d_offset; + unsigned short d_namlen; + /* unsigned char d_type; */ + char d_name[1]; +}; + +struct readdir_callback32 { + struct old_linux_dirent32 * dirent; + int count; +}; + +static int fillonedir(void * __buf, const char * name, int namlen, + off_t offset, ino_t ino, unsigned int d_type) +{ + struct readdir_callback32 * buf = (struct readdir_callback32 *) __buf; + struct old_linux_dirent32 * dirent; + + if (buf->count) + return -EINVAL; + buf->count++; + dirent = buf->dirent; + put_user(ino, &dirent->d_ino); + put_user(offset, &dirent->d_offset); + put_user(namlen, &dirent->d_namlen); + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); + return 0; +} + +asmlinkage int old32_readdir(unsigned int fd, struct old_linux_dirent32 *dirent, unsigned int count) +{ + int error = -EBADF; + struct file * file; + struct readdir_callback32 buf; + + file = fget(fd); + if (!file) + goto out; + + buf.count = 0; + buf.dirent = dirent; + + error = vfs_readdir(file, fillonedir, &buf); + if (error < 0) + goto out_putf; + error = buf.count; + +out_putf: + fput(file); +out: + return error; +} + +#if 0 +struct linux_dirent32 { + u32 d_ino; + u32 d_off; + unsigned short d_reclen; + char d_name[1]; +}; +#else +struct linux_dirent32 { + u32 d_ino; + u32 d_off; + unsigned short d_reclen; + /* unsigned char d_type; */ + char d_name[256]; +}; +#endif + +struct getdents_callback32 { + struct linux_dirent32 * current_dir; + struct linux_dirent32 * previous; + int count; + int error; +}; + +static int filldir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino, + unsigned int d_type) +{ + struct linux_dirent32 * dirent; + struct getdents_callback32 * buf = (struct getdents_callback32 *) __buf; + int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1); + + buf->error = -EINVAL; /* only used if we fail.. */ + if (reclen > buf->count) + return -EINVAL; + dirent = buf->previous; + if (dirent) + put_user(offset, &dirent->d_off); + dirent = buf->current_dir; + buf->previous = dirent; + put_user(ino, &dirent->d_ino); + put_user(reclen, &dirent->d_reclen); + /* put_user(d_type, &dirent->d_type); */ + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); + ((char *) dirent) += reclen; + buf->current_dir = dirent; + buf->count -= reclen; + return 0; +} + +asmlinkage long sys32_getdents(unsigned int fd, struct linux_dirent32 *dirent, unsigned int count) +{ + struct file * file; + struct linux_dirent32 * lastdirent; + struct getdents_callback32 buf; + int error = -EBADF; + + PPCDBG(PPCDBG_SYS32NI, "sys32_getdents - running - fd=%x, pid=%ld, comm=%s \n", fd, current->pid, current->comm); + + file = fget(fd); + if (!file) + goto out; + + buf.current_dir = dirent; + buf.previous = NULL; + buf.count = count; + buf.error = 0; + + error = vfs_readdir(file, filldir, &buf); + if (error < 0) + goto out_putf; + lastdirent = buf.previous; + error = buf.error; + if(lastdirent) { + put_user(file->f_pos, &lastdirent->d_off); + error = count - buf.count; + } +out_putf: + fput(file); + + out: + return error; +} +/* end of readdir & getdents */ + + + +/* 32-bit timeval and related flotsam. */ + +struct timeval32 +{ + int tv_sec, tv_usec; +}; + +struct itimerval32 +{ + struct timeval32 it_interval; + struct timeval32 it_value; +}; + + + + +/* + * Ooo, nasty. We need here to frob 32-bit unsigned longs to + * 64-bit unsigned longs. + */ +static inline int +get_fd_set32(unsigned long n, unsigned long *fdset, u32 *ufdset) +{ + if (ufdset) { + unsigned long odd; + + if (verify_area(VERIFY_WRITE, ufdset, n*sizeof(u32))) + return -EFAULT; + + odd = n & 1UL; + n &= ~1UL; + while (n) { + unsigned long h, l; + __get_user(l, ufdset); + __get_user(h, ufdset+1); + ufdset += 2; + *fdset++ = h << 32 | l; + n -= 2; + } + if (odd) + __get_user(*fdset, ufdset); + } else { + /* Tricky, must clear full unsigned long in the + * kernel fdset at the end, this makes sure that + * actually happens. + */ + memset(fdset, 0, ((n + 1) & ~1)*sizeof(u32)); + } + return 0; +} + +static inline void +set_fd_set32(unsigned long n, u32 *ufdset, unsigned long *fdset) +{ + unsigned long odd; + + if (!ufdset) + return; + + odd = n & 1UL; + n &= ~1UL; + while (n) { + unsigned long h, l; + l = *fdset++; + h = l >> 32; + __put_user(l, ufdset); + __put_user(h, ufdset+1); + ufdset += 2; + n -= 2; + } + if (odd) + __put_user(*fdset, ufdset); +} + + + +#define MAX_SELECT_SECONDS ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1) + +asmlinkage long sys32_select(int n, u32 *inp, u32 *outp, u32 *exp, u32 tvp_x) +{ + fd_set_bits fds; + struct timeval32 *tvp = (struct timeval32 *)AA(tvp_x); + char *bits; + unsigned long nn; + long timeout; + int ret, size; + + PPCDBG(PPCDBG_SYS32X, "sys32_select - entered - n=%x, inp=%p, outp=%p - pid=%ld comm=%s \n", n, inp, outp, current->pid, current->comm); + + timeout = MAX_SCHEDULE_TIMEOUT; + if (tvp) { + time_t sec, usec; + if ((ret = verify_area(VERIFY_READ, tvp, sizeof(*tvp))) + || (ret = __get_user(sec, &tvp->tv_sec)) + || (ret = __get_user(usec, &tvp->tv_usec))) + goto out_nofds; + + ret = -EINVAL; + if(sec < 0 || usec < 0) + goto out_nofds; + + if ((unsigned long) sec < MAX_SELECT_SECONDS) { + timeout = (usec + 1000000/HZ - 1) / (1000000/HZ); + timeout += sec * (unsigned long) HZ; + } + } + + ret = -EINVAL; + if (n < 0) + goto out_nofds; + if (n > current->files->max_fdset) + n = current->files->max_fdset; + + /* + * We need 6 bitmaps (in/out/ex for both incoming and outgoing), + * since we used fdset we need to allocate memory in units of + * long-words. + */ + ret = -ENOMEM; + size = FDS_BYTES(n); + bits = kmalloc(6 * size, GFP_KERNEL); + if (!bits) + goto out_nofds; + fds.in = (unsigned long *) bits; + fds.out = (unsigned long *) (bits + size); + fds.ex = (unsigned long *) (bits + 2*size); + fds.res_in = (unsigned long *) (bits + 3*size); + fds.res_out = (unsigned long *) (bits + 4*size); + fds.res_ex = (unsigned long *) (bits + 5*size); + + nn = (n + 8*sizeof(u32) - 1) / (8*sizeof(u32)); + if ((ret = get_fd_set32(nn, fds.in, inp)) || + (ret = get_fd_set32(nn, fds.out, outp)) || + (ret = get_fd_set32(nn, fds.ex, exp))) + goto out; + zero_fd_set(n, fds.res_in); + zero_fd_set(n, fds.res_out); + zero_fd_set(n, fds.res_ex); + + ret = do_select(n, &fds, &timeout); + + if (tvp && !(current->personality & STICKY_TIMEOUTS)) { + time_t sec = 0, usec = 0; + if (timeout) { + sec = timeout / HZ; + usec = timeout % HZ; + usec *= (1000000/HZ); + } + put_user(sec, &tvp->tv_sec); + put_user(usec, &tvp->tv_usec); + } + + if (ret < 0) + goto out; + if (!ret) { + ret = -ERESTARTNOHAND; + if (signal_pending(current)) + goto out; + ret = 0; + } + + set_fd_set32(nn, inp, fds.res_in); + set_fd_set32(nn, outp, fds.res_out); + set_fd_set32(nn, exp, fds.res_ex); + +out: + kfree(bits); + +out_nofds: + PPCDBG(PPCDBG_SYS32X, "sys32_select - exited - pid=%ld, comm=%s \n", current->pid, current->comm); + return ret; +} + + + + +/* + * Due to some executables calling the wrong select we sometimes + * get wrong args. This determines how the args are being passed + * (a single ptr to them all args passed) then calls + * sys_select() with the appropriate args. -- Cort + */ +/* Note: it is necessary to treat n as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage int ppc32_select(u32 n, u32* inp, u32* outp, u32* exp, u32 tvp_x) +{ + if ((unsigned int)n >= 4096) + panic("ppc32_select - wrong arguments were passed in \n"); + + return sys32_select((int)n, inp, outp, exp, tvp_x); +} + + + +static int cp_new_stat32(struct inode *inode, struct stat32 *statbuf) +{ + unsigned long ino, blksize, blocks; + kdev_t dev, rdev; + umode_t mode; + nlink_t nlink; + uid_t uid; + gid_t gid; + off_t size; + time_t atime, mtime, ctime; + int err; + + /* Stream the loads of inode data into the load buffer, + * then we push it all into the store buffer below. This + * should give optimal cache performance. + */ + ino = inode->i_ino; + dev = inode->i_dev; + mode = inode->i_mode; + nlink = inode->i_nlink; + uid = inode->i_uid; + gid = inode->i_gid; + rdev = inode->i_rdev; + size = inode->i_size; + atime = inode->i_atime; + mtime = inode->i_mtime; + ctime = inode->i_ctime; + blksize = inode->i_blksize; + blocks = inode->i_blocks; + + err = put_user(kdev_t_to_nr(dev), &statbuf->st_dev); + err |= put_user(ino, &statbuf->st_ino); + err |= put_user(mode, &statbuf->st_mode); + err |= put_user(nlink, &statbuf->st_nlink); + err |= put_user(high2lowuid(uid), &statbuf->st_uid); + err |= put_user(high2lowgid(gid), &statbuf->st_gid); + err |= put_user(kdev_t_to_nr(rdev), &statbuf->st_rdev); + err |= put_user(size, &statbuf->st_size); + err |= put_user(atime, &statbuf->st_atime); + err |= put_user(0, &statbuf->__unused1); + err |= put_user(mtime, &statbuf->st_mtime); + err |= put_user(0, &statbuf->__unused2); + err |= put_user(ctime, &statbuf->st_ctime); + err |= put_user(0, &statbuf->__unused3); + if (blksize) { + err |= put_user(blksize, &statbuf->st_blksize); + err |= put_user(blocks, &statbuf->st_blocks); + } else { + unsigned int tmp_blocks; + +#define D_B 7 +#define I_B (BLOCK_SIZE / sizeof(unsigned short)) + tmp_blocks = (size + BLOCK_SIZE - 1) / BLOCK_SIZE; + if (tmp_blocks > D_B) { + unsigned int indirect; + + indirect = (tmp_blocks - D_B + I_B - 1) / I_B; + tmp_blocks += indirect; + if (indirect > 1) { + indirect = (indirect - 1 + I_B - 1) / I_B; + tmp_blocks += indirect; + if (indirect > 1) + tmp_blocks++; + } + } + err |= put_user(BLOCK_SIZE, &statbuf->st_blksize); + err |= put_user((BLOCK_SIZE / 512) * tmp_blocks, &statbuf->st_blocks); +#undef D_B +#undef I_B + } + err |= put_user(0, &statbuf->__unused4[0]); + err |= put_user(0, &statbuf->__unused4[1]); + + return err; +} + +static __inline__ int +do_revalidate(struct dentry *dentry) +{ + struct inode * inode = dentry->d_inode; + if (inode->i_op && inode->i_op->revalidate) + return inode->i_op->revalidate(dentry); + return 0; +} + +asmlinkage long sys32_newstat(char* filename, struct stat32* statbuf) +{ + struct nameidata nd; + int error; + + PPCDBG(PPCDBG_SYS32X, "sys32_newstat - running - filename=%s, statbuf=%p, pid=%ld, comm=%s\n", filename, statbuf, current->pid, current->comm); + + error = user_path_walk(filename, &nd); + if (!error) { + error = do_revalidate(nd.dentry); + if (!error) + error = cp_new_stat32(nd.dentry->d_inode, statbuf); + path_release(&nd); + } + return error; +} + +asmlinkage long sys32_newlstat(char * filename, struct stat32 *statbuf) +{ + struct nameidata nd; + int error; + + PPCDBG(PPCDBG_SYS32X, "sys32_newlstat - running - fn=%s, pid=%ld, comm=%s\n", filename, current->pid, current->comm); + + error = user_path_walk_link(filename, &nd); + if (!error) { + error = do_revalidate(nd.dentry); + if (!error) + error = cp_new_stat32(nd.dentry->d_inode, statbuf); + + path_release(&nd); + } + return error; +} + +asmlinkage long sys32_newfstat(unsigned int fd, struct stat32 *statbuf) +{ + struct file *f; + int err = -EBADF; + + PPCDBG(PPCDBG_SYS32X, "sys32_newfstat - running - fd=%x, pid=%ld, comm=%s\n", fd, current->pid, current->comm); + + f = fget(fd); + if (f) { + struct dentry * dentry = f->f_dentry; + + err = do_revalidate(dentry); + if (!err) + err = cp_new_stat32(dentry->d_inode, statbuf); + fput(f); + } + return err; +} + +static inline int put_statfs (struct statfs32 *ubuf, struct statfs *kbuf) +{ + int err; + + err = put_user (kbuf->f_type, &ubuf->f_type); + err |= __put_user (kbuf->f_bsize, &ubuf->f_bsize); + err |= __put_user (kbuf->f_blocks, &ubuf->f_blocks); + err |= __put_user (kbuf->f_bfree, &ubuf->f_bfree); + err |= __put_user (kbuf->f_bavail, &ubuf->f_bavail); + err |= __put_user (kbuf->f_files, &ubuf->f_files); + err |= __put_user (kbuf->f_ffree, &ubuf->f_ffree); + err |= __put_user (kbuf->f_namelen, &ubuf->f_namelen); + err |= __put_user (kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]); + err |= __put_user (kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]); + return err; +} + +extern asmlinkage int sys_statfs(const char * path, struct statfs * buf); + +asmlinkage long sys32_statfs(const char * path, struct statfs32 *buf) +{ + int ret; + struct statfs s; + mm_segment_t old_fs = get_fs(); + char *pth; + + PPCDBG(PPCDBG_SYS32X, "sys32_statfs - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + 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); + putname (pth); + if (put_statfs(buf, &s)) + return -EFAULT; + } + + PPCDBG(PPCDBG_SYS32X, "sys32_statfs - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return ret; +} + +extern asmlinkage long sys_fstatfs(unsigned int fd, struct statfs * buf); + +asmlinkage long sys32_fstatfs(unsigned int fd, struct statfs32 *buf) +{ + int ret; + struct statfs s; + mm_segment_t old_fs = get_fs(); + + PPCDBG(PPCDBG_SYS32X, "sys32_fstatfs - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + set_fs (KERNEL_DS); + ret = sys_fstatfs(fd, &s); + set_fs (old_fs); + if (put_statfs(buf, &s)) + return -EFAULT; + + PPCDBG(PPCDBG_SYS32X, "sys32_fstatfs - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return ret; +} + + + +extern asmlinkage long sys_sysfs(int option, unsigned long arg1, unsigned long arg2); + +/* Note: it is necessary to treat option as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sysfs(u32 option, u32 arg1, u32 arg2) +{ + PPCDBG(PPCDBG_SYS32, "sys32_sysfs - running - pid=%ld, comm=%s\n", current->pid, current->comm); + return sys_sysfs((int)option, arg1, arg2); +} + + + + +extern unsigned long do_mremap(unsigned long addr, + unsigned long old_len, unsigned long new_len, + unsigned long flags, unsigned long new_addr); + +asmlinkage unsigned long sys32_mremap(unsigned long addr, unsigned long old_len, unsigned long new_len, + unsigned long flags, u32 __new_addr) +{ + unsigned long ret = -EINVAL; + unsigned long new_addr = AA(__new_addr); + + PPCDBG(PPCDBG_SYS32, "sys32_mremap - entered - pid=%ld current=%lx comm=%s\n", + current->pid, current, current->comm); + + + if (old_len > 0xf0000000UL || new_len > 0xf0000000UL) + goto out; + if (addr > 0xf0000000UL - old_len) + goto out; + down_write(¤t->mm->mmap_sem); + if (flags & MREMAP_FIXED) { + if (new_addr > 0xf0000000UL - new_len) + goto out_sem; + } else if (addr > 0xf0000000UL - new_len) { + ret = -ENOMEM; + if (!(flags & MREMAP_MAYMOVE)) + goto out_sem; + new_addr = get_unmapped_area (NULL, addr, new_len, 0, 0); + if (!new_addr) + goto out_sem; + flags |= MREMAP_FIXED; + } + ret = do_mremap(addr, old_len, new_len, flags, new_addr); +out_sem: + up_write(¤t->mm->mmap_sem); +out: + + PPCDBG(PPCDBG_SYS32, "sys32_mremap - exited - pid=%ld current=%lx comm=%s\n", + current->pid, current, current->comm); + + return ret; +} + + + +/* Handle adjtimex compatability. */ +struct timex32 { + u32 modes; + s32 offset, freq, maxerror, esterror; + s32 status, constant, precision, tolerance; + struct timeval32 time; + s32 tick; + s32 ppsfreq, jitter, shift, stabil; + s32 jitcnt, calcnt, errcnt, stbcnt; + s32 :32; s32 :32; s32 :32; s32 :32; + s32 :32; s32 :32; s32 :32; s32 :32; + s32 :32; s32 :32; s32 :32; s32 :32; +}; + +extern int do_adjtimex(struct timex *); + +asmlinkage long sys32_adjtimex(struct timex32 *utp) +{ + struct timex txc; + int ret; + + PPCDBG(PPCDBG_SYS32, "sys32_adjtimex - running - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + memset(&txc, 0, sizeof(struct timex)); + + if(get_user(txc.modes, &utp->modes) || + __get_user(txc.offset, &utp->offset) || + __get_user(txc.freq, &utp->freq) || + __get_user(txc.maxerror, &utp->maxerror) || + __get_user(txc.esterror, &utp->esterror) || + __get_user(txc.status, &utp->status) || + __get_user(txc.constant, &utp->constant) || + __get_user(txc.precision, &utp->precision) || + __get_user(txc.tolerance, &utp->tolerance) || + __get_user(txc.time.tv_sec, &utp->time.tv_sec) || + __get_user(txc.time.tv_usec, &utp->time.tv_usec) || + __get_user(txc.tick, &utp->tick) || + __get_user(txc.ppsfreq, &utp->ppsfreq) || + __get_user(txc.jitter, &utp->jitter) || + __get_user(txc.shift, &utp->shift) || + __get_user(txc.stabil, &utp->stabil) || + __get_user(txc.jitcnt, &utp->jitcnt) || + __get_user(txc.calcnt, &utp->calcnt) || + __get_user(txc.errcnt, &utp->errcnt) || + __get_user(txc.stbcnt, &utp->stbcnt)) + return -EFAULT; + + ret = do_adjtimex(&txc); + + if(put_user(txc.modes, &utp->modes) || + __put_user(txc.offset, &utp->offset) || + __put_user(txc.freq, &utp->freq) || + __put_user(txc.maxerror, &utp->maxerror) || + __put_user(txc.esterror, &utp->esterror) || + __put_user(txc.status, &utp->status) || + __put_user(txc.constant, &utp->constant) || + __put_user(txc.precision, &utp->precision) || + __put_user(txc.tolerance, &utp->tolerance) || + __put_user(txc.time.tv_sec, &utp->time.tv_sec) || + __put_user(txc.time.tv_usec, &utp->time.tv_usec) || + __put_user(txc.tick, &utp->tick) || + __put_user(txc.ppsfreq, &utp->ppsfreq) || + __put_user(txc.jitter, &utp->jitter) || + __put_user(txc.shift, &utp->shift) || + __put_user(txc.stabil, &utp->stabil) || + __put_user(txc.jitcnt, &utp->jitcnt) || + __put_user(txc.calcnt, &utp->calcnt) || + __put_user(txc.errcnt, &utp->errcnt) || + __put_user(txc.stbcnt, &utp->stbcnt)) + ret = -EFAULT; + + return ret; +} + + + +#ifdef CONFIG_MODULES + +extern asmlinkage unsigned long sys_create_module(const char *name_user, size_t size); + +asmlinkage unsigned long sys32_create_module(const char *name_user, __kernel_size_t32 size) +{ + + PPCDBG(PPCDBG_SYS32M, "sys32_create_module - running - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return sys_create_module(name_user, (size_t)size); +} + + + +extern asmlinkage long sys_init_module(const char *name_user, struct module *mod_user); + +asmlinkage long sys32_init_module(const char *name_user, struct module *mod_user) +{ + + PPCDBG(PPCDBG_SYS32, "sys32_init_module - running - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return sys_init_module(name_user, mod_user); +} + + + +extern asmlinkage long sys_delete_module(const char *name_user); + +asmlinkage long sys32_delete_module(const char *name_user) +{ + + PPCDBG(PPCDBG_SYS32, "sys32_delete_module - running - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return sys_delete_module(name_user); +} + + + +struct module_info32 { + u32 addr; + u32 size; + u32 flags; + s32 usecount; +}; + +/* Query various bits about modules. */ + +static inline long +get_mod_name(const char *user_name, char **buf) +{ + unsigned long page; + long retval; + + if ((unsigned long)user_name >= TASK_SIZE + && !segment_eq(get_fs (), KERNEL_DS)) + return -EFAULT; + + page = __get_free_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + retval = strncpy_from_user((char *)page, user_name, PAGE_SIZE); + if (retval > 0) { + if (retval < PAGE_SIZE) { + *buf = (char *)page; + return retval; + } + retval = -ENAMETOOLONG; + } else if (!retval) + retval = -EINVAL; + + free_page(page); + return retval; +} + +static inline void +put_mod_name(char *buf) +{ + free_page((unsigned long)buf); +} + +static __inline__ struct module *find_module(const char *name) +{ + struct module *mod; + + for (mod = module_list; mod ; mod = mod->next) { + if (mod->flags & MOD_DELETED) + continue; + if (!strcmp(mod->name, name)) + break; + } + + return mod; +} + +static int +qm_modules(char *buf, size_t bufsize, __kernel_size_t32 *ret) +{ + struct module *mod; + size_t nmod, space, len; + + nmod = space = 0; + + for (mod = module_list; mod->next != NULL; mod = mod->next, ++nmod) { + len = strlen(mod->name)+1; + if (len > bufsize) + goto calc_space_needed; + if (copy_to_user(buf, mod->name, len)) + return -EFAULT; + buf += len; + bufsize -= len; + space += len; + } + + if (put_user(nmod, ret)) + return -EFAULT; + else + return 0; + +calc_space_needed: + space += len; + while ((mod = mod->next)->next != NULL) + space += strlen(mod->name)+1; + + if (put_user(space, ret)) + return -EFAULT; + else + return -ENOSPC; +} + +static int +qm_deps(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret) +{ + size_t i, space, len; + + if (mod->next == NULL) + return -EINVAL; + if (!MOD_CAN_QUERY(mod)) + return put_user(0, ret); + + space = 0; + for (i = 0; i < mod->ndeps; ++i) { + const char *dep_name = mod->deps[i].dep->name; + + len = strlen(dep_name)+1; + if (len > bufsize) + goto calc_space_needed; + if (copy_to_user(buf, dep_name, len)) + return -EFAULT; + buf += len; + bufsize -= len; + space += len; + } + + return put_user(i, ret); + +calc_space_needed: + space += len; + while (++i < mod->ndeps) + space += strlen(mod->deps[i].dep->name)+1; + + if (put_user(space, ret)) + return -EFAULT; + else + return -ENOSPC; +} + +static int +qm_refs(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret) +{ + size_t nrefs, space, len; + struct module_ref *ref; + + if (mod->next == NULL) + return -EINVAL; + if (!MOD_CAN_QUERY(mod)) + if (put_user(0, ret)) + return -EFAULT; + else + return 0; + + space = 0; + for (nrefs = 0, ref = mod->refs; ref ; ++nrefs, ref = ref->next_ref) { + const char *ref_name = ref->ref->name; + + len = strlen(ref_name)+1; + if (len > bufsize) + goto calc_space_needed; + if (copy_to_user(buf, ref_name, len)) + return -EFAULT; + buf += len; + bufsize -= len; + space += len; + } + + if (put_user(nrefs, ret)) + return -EFAULT; + else + return 0; + +calc_space_needed: + space += len; + while ((ref = ref->next_ref) != NULL) + space += strlen(ref->ref->name)+1; + + if (put_user(space, ret)) + return -EFAULT; + else + return -ENOSPC; +} + +static inline int +qm_symbols(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret) +{ + size_t i, space, len; + struct module_symbol *s; + char *strings; + unsigned *vals; + + if (!MOD_CAN_QUERY(mod)) + if (put_user(0, ret)) + return -EFAULT; + else + return 0; + + space = mod->nsyms * 2*sizeof(u32); + + i = len = 0; + s = mod->syms; + + if (space > bufsize) + goto calc_space_needed; + + if (!access_ok(VERIFY_WRITE, buf, space)) + return -EFAULT; + + bufsize -= space; + vals = (unsigned *)buf; + strings = buf+space; + + for (; i < mod->nsyms ; ++i, ++s, vals += 2) { + len = strlen(s->name)+1; + if (len > bufsize) + goto calc_space_needed; + + if (copy_to_user(strings, s->name, len) + || __put_user(s->value, vals+0) + || __put_user(space, vals+1)) + return -EFAULT; + + strings += len; + bufsize -= len; + space += len; + } + + if (put_user(i, ret)) + return -EFAULT; + else + return 0; + +calc_space_needed: + for (; i < mod->nsyms; ++i, ++s) + space += strlen(s->name)+1; + + if (put_user(space, ret)) + return -EFAULT; + else + return -ENOSPC; +} + +static inline int +qm_info(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret) +{ + int error = 0; + + if (mod->next == NULL) + return -EINVAL; + + if (sizeof(struct module_info32) <= bufsize) { + struct module_info32 info; + info.addr = (unsigned long)mod; + info.size = mod->size; + info.flags = mod->flags; + info.usecount = + ((mod_member_present(mod, can_unload) + && mod->can_unload) + ? -1 : atomic_read(&mod->uc.usecount)); + + if (copy_to_user(buf, &info, sizeof(struct module_info32))) + return -EFAULT; + } else + error = -ENOSPC; + + if (put_user(sizeof(struct module_info32), ret)) + return -EFAULT; + + return error; +} + +/* Note: it is necessary to treat which as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_query_module(char *name_user, u32 which, char *buf, __kernel_size_t32 bufsize, u32 ret) +{ + struct module *mod; + int err; + + PPCDBG(PPCDBG_SYS32M, "sys32_query_module - entered - pid=%ld current=%lx comm=%s\n", + current->pid, current, current->comm); + + lock_kernel(); + if (name_user == 0) { + /* This finds "kernel_module" which is not exported. */ + for(mod = module_list; mod->next != NULL; mod = mod->next) + ; + } else { + long namelen; + char *name; + + if ((namelen = get_mod_name(name_user, &name)) < 0) { + err = namelen; + goto out; + } + err = -ENOENT; + if (namelen == 0) { + /* This finds "kernel_module" which is not exported. */ + for(mod = module_list; mod->next != NULL; mod = mod->next) + ; + } else if ((mod = find_module(name)) == NULL) { + put_mod_name(name); + goto out; + } + put_mod_name(name); + } + + switch ((int)which) + { + case 0: + err = 0; + break; + case QM_MODULES: + err = qm_modules(buf, bufsize, (__kernel_size_t32 *)AA(ret)); + break; + case QM_DEPS: + err = qm_deps(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); + break; + case QM_REFS: + err = qm_refs(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); + break; + case QM_SYMBOLS: + err = qm_symbols(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); + break; + case QM_INFO: + err = qm_info(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); + break; + default: + err = -EINVAL; + break; + } +out: + unlock_kernel(); + + PPCDBG(PPCDBG_SYS32, "sys32_query_module - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return err; +} + + + +struct kernel_sym32 { + u32 value; + char name[60]; +}; + +extern asmlinkage long sys_get_kernel_syms(struct kernel_sym *table); + +asmlinkage long sys32_get_kernel_syms(struct kernel_sym32 *table) +{ + int len, i; + struct kernel_sym *tbl; + mm_segment_t old_fs; + + PPCDBG(PPCDBG_SYS32, "sys32_get_kernel_syms - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + + len = sys_get_kernel_syms(NULL); + if (!table) return len; + tbl = kmalloc (len * sizeof (struct kernel_sym), GFP_KERNEL); + if (!tbl) return -ENOMEM; + old_fs = get_fs(); + set_fs (KERNEL_DS); + sys_get_kernel_syms(tbl); + set_fs (old_fs); + for (i = 0; i < len; i++, table += sizeof (struct kernel_sym32)) { + if (put_user (tbl[i].value, &table->value) || + copy_to_user (table->name, tbl[i].name, 60)) + break; + } + kfree (tbl); + + PPCDBG(PPCDBG_SYS32, "sys32_get_kernel_syms - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return i; +} + +#else /* CONFIG_MODULES */ + +asmlinkage unsigned long sys32_create_module(const char *name_user, size_t size) +{ + + PPCDBG(PPCDBG_SYS32, "sys32_create_module - running - pid=%ld, comm=%s\n", current->pid, current->comm); + + return -ENOSYS; +} + +asmlinkage long sys32_init_module(const char *name_user, struct module *mod_user) +{ + PPCDBG(PPCDBG_SYS32, "sys32_init_module - running - pid=%ld, comm=%s\n", current->pid, current->comm); + + return -ENOSYS; +} + +asmlinkage long sys32_delete_module(const char *name_user) +{ + PPCDBG(PPCDBG_SYS32, "sys32_delete_module - running - pid=%ld, comm=%s\n", current->pid, current->comm); + + return -ENOSYS; +} + +/* Note: it is necessary to treat which as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_query_module(const char *name_user, u32 which, char *buf, size_t bufsize, size_t *ret) +{ + PPCDBG(PPCDBG_SYS32, "sys32_query_module - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + /* Let the program know about the new interface. Not that it'll do them much good. */ + if ((int)which == 0) + return 0; + + PPCDBG(PPCDBG_SYS32, "sys32_query_module - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + return -ENOSYS; +} + +asmlinkage long sys32_get_kernel_syms(struct kernel_sym *table) +{ + PPCDBG(PPCDBG_SYS32, "sys32_get_kernel_syms - running - pid=%ld, comm=%s\n", current->pid, current->comm); + + return -ENOSYS; +} + +#endif /* CONFIG_MODULES */ + + + +/* Stuff for NFS server syscalls... */ +struct nfsctl_svc32 { + u16 svc32_port; + s32 svc32_nthreads; +}; + +struct nfsctl_client32 { + s8 cl32_ident[NFSCLNT_IDMAX+1]; + s32 cl32_naddr; + struct in_addr cl32_addrlist[NFSCLNT_ADDRMAX]; + s32 cl32_fhkeytype; + s32 cl32_fhkeylen; + u8 cl32_fhkey[NFSCLNT_KEYMAX]; +}; + +struct nfsctl_export32 { + s8 ex32_client[NFSCLNT_IDMAX+1]; + s8 ex32_path[NFS_MAXPATHLEN+1]; + __kernel_dev_t32 ex32_dev; + __kernel_ino_t32 ex32_ino; + s32 ex32_flags; + __kernel_uid_t32 ex32_anon_uid; + __kernel_gid_t32 ex32_anon_gid; +}; + +struct nfsctl_uidmap32 { + u32 ug32_ident; /* char * */ + __kernel_uid_t32 ug32_uidbase; + s32 ug32_uidlen; + u32 ug32_udimap; /* uid_t * */ + __kernel_uid_t32 ug32_gidbase; + s32 ug32_gidlen; + u32 ug32_gdimap; /* gid_t * */ +}; + +struct nfsctl_fhparm32 { + struct sockaddr gf32_addr; + __kernel_dev_t32 gf32_dev; + __kernel_ino_t32 gf32_ino; + s32 gf32_version; +}; + +struct nfsctl_fdparm32 { + struct sockaddr gd32_addr; + s8 gd32_path[NFS_MAXPATHLEN+1]; + s32 gd32_version; +}; + +struct nfsctl_fsparm32 { + struct sockaddr gd32_addr; + s8 gd32_path[NFS_MAXPATHLEN+1]; + s32 gd32_maxlen; +}; + +struct nfsctl_arg32 { + s32 ca32_version; /* safeguard */ + union { + struct nfsctl_svc32 u32_svc; + struct nfsctl_client32 u32_client; + struct nfsctl_export32 u32_export; + struct nfsctl_uidmap32 u32_umap; + struct nfsctl_fhparm32 u32_getfh; + struct nfsctl_fdparm32 u32_getfd; + struct nfsctl_fsparm32 u32_getfs; + } u; +#define ca32_svc u.u32_svc +#define ca32_client u.u32_client +#define ca32_export u.u32_export +#define ca32_umap u.u32_umap +#define ca32_getfh u.u32_getfh +#define ca32_getfd u.u32_getfd +#define ca32_getfs u.u32_getfs +#define ca32_authd u.u32_authd +}; + +union nfsctl_res32 { + __u8 cr32_getfh[NFS_FHSIZE]; + struct knfsd_fh cr32_getfs; +}; + +static int nfs_svc32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= __get_user(karg->ca_svc.svc_port, &arg32->ca32_svc.svc32_port); + err |= __get_user(karg->ca_svc.svc_nthreads, &arg32->ca32_svc.svc32_nthreads); + return err; +} + +static int nfs_clnt32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= copy_from_user(&karg->ca_client.cl_ident[0], + &arg32->ca32_client.cl32_ident[0], + NFSCLNT_IDMAX); + err |= __get_user(karg->ca_client.cl_naddr, &arg32->ca32_client.cl32_naddr); + err |= copy_from_user(&karg->ca_client.cl_addrlist[0], + &arg32->ca32_client.cl32_addrlist[0], + (sizeof(struct in_addr) * NFSCLNT_ADDRMAX)); + err |= __get_user(karg->ca_client.cl_fhkeytype, + &arg32->ca32_client.cl32_fhkeytype); + err |= __get_user(karg->ca_client.cl_fhkeylen, + &arg32->ca32_client.cl32_fhkeylen); + err |= copy_from_user(&karg->ca_client.cl_fhkey[0], + &arg32->ca32_client.cl32_fhkey[0], + NFSCLNT_KEYMAX); + return err; +} + +static int nfs_exp32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= copy_from_user(&karg->ca_export.ex_client[0], + &arg32->ca32_export.ex32_client[0], + NFSCLNT_IDMAX); + err |= copy_from_user(&karg->ca_export.ex_path[0], + &arg32->ca32_export.ex32_path[0], + NFS_MAXPATHLEN); + err |= __get_user(karg->ca_export.ex_dev, + &arg32->ca32_export.ex32_dev); + err |= __get_user(karg->ca_export.ex_ino, + &arg32->ca32_export.ex32_ino); + err |= __get_user(karg->ca_export.ex_flags, + &arg32->ca32_export.ex32_flags); + err |= __get_user(karg->ca_export.ex_anon_uid, + &arg32->ca32_export.ex32_anon_uid); + err |= __get_user(karg->ca_export.ex_anon_gid, + &arg32->ca32_export.ex32_anon_gid); + karg->ca_export.ex_anon_uid = high2lowuid(karg->ca_export.ex_anon_uid); + karg->ca_export.ex_anon_gid = high2lowgid(karg->ca_export.ex_anon_gid); + return err; +} + +static int nfs_uud32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + u32 uaddr; + int i; + int err; + + memset(karg, 0, sizeof(*karg)); + if(__get_user(karg->ca_version, &arg32->ca32_version)) + return -EFAULT; + karg->ca_umap.ug_ident = (char *)get_free_page(GFP_USER); + if(!karg->ca_umap.ug_ident) + return -ENOMEM; + err = __get_user(uaddr, &arg32->ca32_umap.ug32_ident); + if(strncpy_from_user(karg->ca_umap.ug_ident, + (char *)A(uaddr), PAGE_SIZE) <= 0) + return -EFAULT; + err |= __get_user(karg->ca_umap.ug_uidbase, + &arg32->ca32_umap.ug32_uidbase); + err |= __get_user(karg->ca_umap.ug_uidlen, + &arg32->ca32_umap.ug32_uidlen); + err |= __get_user(uaddr, &arg32->ca32_umap.ug32_udimap); + if (err) + return -EFAULT; + karg->ca_umap.ug_udimap = kmalloc((sizeof(uid_t) * karg->ca_umap.ug_uidlen), + GFP_USER); + if(!karg->ca_umap.ug_udimap) + return -ENOMEM; + for(i = 0; i < karg->ca_umap.ug_uidlen; i++) + err |= __get_user(karg->ca_umap.ug_udimap[i], + &(((__kernel_uid_t32 *)A(uaddr))[i])); + err |= __get_user(karg->ca_umap.ug_gidbase, + &arg32->ca32_umap.ug32_gidbase); + err |= __get_user(karg->ca_umap.ug_uidlen, + &arg32->ca32_umap.ug32_gidlen); + err |= __get_user(uaddr, &arg32->ca32_umap.ug32_gdimap); + if (err) + return -EFAULT; + karg->ca_umap.ug_gdimap = kmalloc((sizeof(gid_t) * karg->ca_umap.ug_uidlen), + GFP_USER); + if(!karg->ca_umap.ug_gdimap) + return -ENOMEM; + for(i = 0; i < karg->ca_umap.ug_gidlen; i++) + err |= __get_user(karg->ca_umap.ug_gdimap[i], + &(((__kernel_gid_t32 *)A(uaddr))[i])); + + return err; +} + +static int nfs_getfh32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= copy_from_user(&karg->ca_getfh.gf_addr, + &arg32->ca32_getfh.gf32_addr, + (sizeof(struct sockaddr))); + err |= __get_user(karg->ca_getfh.gf_dev, + &arg32->ca32_getfh.gf32_dev); + err |= __get_user(karg->ca_getfh.gf_ino, + &arg32->ca32_getfh.gf32_ino); + err |= __get_user(karg->ca_getfh.gf_version, + &arg32->ca32_getfh.gf32_version); + return err; +} + +static int nfs_getfd32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= copy_from_user(&karg->ca_getfd.gd_addr, + &arg32->ca32_getfd.gd32_addr, + (sizeof(struct sockaddr))); + err |= copy_from_user(&karg->ca_getfd.gd_path, + &arg32->ca32_getfd.gd32_path, + (NFS_MAXPATHLEN+1)); + err |= __get_user(karg->ca_getfd.gd_version, + &arg32->ca32_getfd.gd32_version); + return err; +} + +static int nfs_getfs32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= copy_from_user(&karg->ca_getfs.gd_addr, + &arg32->ca32_getfs.gd32_addr, + (sizeof(struct sockaddr))); + err |= copy_from_user(&karg->ca_getfs.gd_path, + &arg32->ca32_getfs.gd32_path, + (NFS_MAXPATHLEN+1)); + err |= __get_user(karg->ca_getfs.gd_maxlen, + &arg32->ca32_getfs.gd32_maxlen); + return err; +} + +/* This really doesn't need translations, we are only passing + * back a union which contains opaque nfs file handle data. + */ +static int nfs_getfh32_res_trans(union nfsctl_res *kres, union nfsctl_res32 *res32) +{ + return copy_to_user(res32, kres, sizeof(*res32)); +} + +/* Note: it is necessary to treat cmd_parm as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +int asmlinkage sys32_nfsservctl(u32 cmd_parm, struct nfsctl_arg32 *arg32, union nfsctl_res32 *res32) +{ + int cmd = (int)cmd_parm; + struct nfsctl_arg *karg = NULL; + union nfsctl_res *kres = NULL; + mm_segment_t oldfs; + int err; + + karg = kmalloc(sizeof(*karg), GFP_USER); + if(!karg) + return -ENOMEM; + if(res32) { + kres = kmalloc(sizeof(*kres), GFP_USER); + if(!kres) { + kfree(karg); + return -ENOMEM; + } + } + switch(cmd) { + case NFSCTL_SVC: + err = nfs_svc32_trans(karg, arg32); + break; + case NFSCTL_ADDCLIENT: + err = nfs_clnt32_trans(karg, arg32); + break; + case NFSCTL_DELCLIENT: + err = nfs_clnt32_trans(karg, arg32); + break; + case NFSCTL_EXPORT: + case NFSCTL_UNEXPORT: + err = nfs_exp32_trans(karg, arg32); + break; + /* This one is unimplemented, be we're ready for it. */ + case NFSCTL_UGIDUPDATE: + err = nfs_uud32_trans(karg, arg32); + break; + case NFSCTL_GETFH: + err = nfs_getfh32_trans(karg, arg32); + break; + case NFSCTL_GETFD: + err = nfs_getfd32_trans(karg, arg32); + break; + case NFSCTL_GETFS: + err = nfs_getfs32_trans(karg, arg32); + break; + default: + err = -EINVAL; + break; + } + if(err) + goto done; + oldfs = get_fs(); + set_fs(KERNEL_DS); + err = sys_nfsservctl(cmd, karg, kres); + set_fs(oldfs); + + if (err) + goto done; + + if((cmd == NFSCTL_GETFH) || + (cmd == NFSCTL_GETFD) || + (cmd == NFSCTL_GETFS)) + err = nfs_getfh32_res_trans(kres, res32); + +done: + if(karg) { + if(cmd == NFSCTL_UGIDUPDATE) { + if(karg->ca_umap.ug_ident) + kfree(karg->ca_umap.ug_ident); + if(karg->ca_umap.ug_udimap) + kfree(karg->ca_umap.ug_udimap); + if(karg->ca_umap.ug_gdimap) + kfree(karg->ca_umap.ug_gdimap); + } + kfree(karg); + } + if(kres) + kfree(kres); + return err; +} + + + +struct timespec32 { + s32 tv_sec; + s32 tv_nsec; +}; + +extern asmlinkage long sys_nanosleep(struct timespec *rqtp, struct timespec *rmtp); + +asmlinkage long sys32_nanosleep(struct timespec32 *rqtp, struct timespec32 *rmtp) +{ + struct timespec t; + int ret; + mm_segment_t old_fs = get_fs (); + + PPCDBG(PPCDBG_SYS32NI, "sys32_nanosleep - running - pid=%ld, comm=%s \n", current->pid, current->comm); + + if (get_user (t.tv_sec, &rqtp->tv_sec) || + __get_user (t.tv_nsec, &rqtp->tv_nsec)) + return -EFAULT; + set_fs (KERNEL_DS); + ret = sys_nanosleep(&t, rmtp ? &t : NULL); + set_fs (old_fs); + if (rmtp && ret == -EINTR) { + if (__put_user (t.tv_sec, &rmtp->tv_sec) || + __put_user (t.tv_nsec, &rmtp->tv_nsec)) + return -EFAULT; + } + + return ret; +} + + + + +/* These are here just in case some old sparc32 binary calls it. */ +asmlinkage long sys32_pause(void) +{ + + PPCDBG(PPCDBG_SYS32, "sys32_pause - running - pid=%ld, comm=%s \n", current->pid, current->comm); + + current->state = TASK_INTERRUPTIBLE; + schedule(); + + return -ERESTARTNOHAND; +} + + + +static inline long get_it32(struct itimerval *o, struct itimerval32 *i) +{ + return (!access_ok(VERIFY_READ, i, sizeof(*i)) || + (__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))); +} + +static inline long put_it32(struct itimerval32 *o, struct itimerval *i) +{ + return (!access_ok(VERIFY_WRITE, o, sizeof(*o)) || + (__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))); +} + +static inline long get_tv32(struct timeval *o, struct timeval32 *i) +{ + return (!access_ok(VERIFY_READ, i, sizeof(*i)) || + (__get_user(o->tv_sec, &i->tv_sec) | + __get_user(o->tv_usec, &i->tv_usec))); +} + +static inline long put_tv32(struct timeval32 *o, struct timeval *i) +{ + return (!access_ok(VERIFY_WRITE, o, sizeof(*o)) || + (__put_user(i->tv_sec, &o->tv_sec) | + __put_user(i->tv_usec, &o->tv_usec))); +} + + + + +extern int do_getitimer(int which, struct itimerval *value); + +/* Note: it is necessary to treat which as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_getitimer(u32 which, struct itimerval32 *it) +{ + struct itimerval kit; + int error; + + PPCDBG(PPCDBG_SYS32, "sys32_getitimer - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + error = do_getitimer((int)which, &kit); + if (!error && put_it32(it, &kit)) + error = -EFAULT; + + + PPCDBG(PPCDBG_SYS32, "sys32_getitimer - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + return error; +} + + + +extern int do_setitimer(int which, struct itimerval *, struct itimerval *); + +/* Note: it is necessary to treat which as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_setitimer(u32 which, struct itimerval32 *in, struct itimerval32 *out) +{ + struct itimerval kin, kout; + int error; + + PPCDBG(PPCDBG_SYS32, "sys32_setitimer - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + if (in) { + if (get_it32(&kin, in)) + return -EFAULT; + } else + memset(&kin, 0, sizeof(kin)); + + error = do_setitimer((int)which, &kin, out ? &kout : NULL); + if (error || !out) + return error; + if (put_it32(out, &kout)) + return -EFAULT; + + + PPCDBG(PPCDBG_SYS32, "sys32_setitimer - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + return 0; +} + + +#define RLIM_INFINITY32 0x7fffffff +#define RESOURCE32(x) ((x > RLIM_INFINITY32) ? RLIM_INFINITY32 : x) + +struct rlimit32 { + u32 rlim_cur; + u32 rlim_max; +}; + +extern asmlinkage long sys_getrlimit(unsigned int resource, struct rlimit *rlim); + +asmlinkage long sys32_getrlimit(unsigned int resource, struct rlimit32 *rlim) +{ + struct rlimit r; + int ret; + mm_segment_t old_fs = get_fs (); + + PPCDBG(PPCDBG_SYS32, "sys32_getrlimit - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + set_fs (KERNEL_DS); + ret = sys_getrlimit(resource, &r); + set_fs (old_fs); + if (!ret) { + ret = put_user (RESOURCE32(r.rlim_cur), &rlim->rlim_cur); + ret |= __put_user (RESOURCE32(r.rlim_max), &rlim->rlim_max); + } + + PPCDBG(PPCDBG_SYS32, "sys32_getrlimit - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return ret; +} + +/* Back compatibility for getrlimit. Needed for some apps. */ +asmlinkage long sys32_old_getrlimit(unsigned int resource, struct rlimit32* rlim) +{ + struct rlimit x; // 64-bit version of the resource limits. + struct rlimit32 x32; // 32-bit version of the resource limits. + long rc = 0; + + if (resource >= RLIM_NLIMITS) + { + PPCDBG(PPCDBG_SYS32NI, "sys32_old_getrlimit - specified resource is too large (%x) - pid=%ld, comm=%s\n", resource, current->pid, current->comm); + return -EINVAL; + } + + memcpy(&x, current->rlim+resource, sizeof(struct rlimit)); + + if(x.rlim_cur > RLIM_INFINITY32) + x32.rlim_cur = RLIM_INFINITY32; + else + x32.rlim_cur = x.rlim_cur; + + if(x.rlim_max > RLIM_INFINITY32) + x32.rlim_max = RLIM_INFINITY32; + else + x32.rlim_max = x.rlim_max; + + + rc = (copy_to_user(rlim, &x32, sizeof(x32))) ? (-EFAULT) : 0; + if (rc == 0) + { + PPCDBG(PPCDBG_SYS32NI, "sys32_old_getrlimit - current=%x, maximum=%x - pid=%ld, comm=%s\n", x32.rlim_cur, x32.rlim_max, current->pid, current->comm); + } + else + { + PPCDBG(PPCDBG_SYS32NI, "sys32_old_getrlimit - unable to copy into user's storage - pid=%ld, comm=%s\n", current->pid, current->comm); + } + return rc; +} + +extern asmlinkage long sys_setrlimit(unsigned int resource, struct rlimit *rlim); + +asmlinkage long sys32_setrlimit(unsigned int resource, struct rlimit32 *rlim) +{ + struct rlimit r; + int ret; + mm_segment_t old_fs = get_fs (); + + PPCDBG(PPCDBG_SYS32NI, "sys32_setrlimit - entered - pid=%ld comm=%s\n", current->pid, current->comm); + + if (resource >= RLIM_NLIMITS) return -EINVAL; + if (get_user (r.rlim_cur, &rlim->rlim_cur) || + __get_user (r.rlim_max, &rlim->rlim_max)) + return -EFAULT; + if (r.rlim_cur == RLIM_INFINITY32) + r.rlim_cur = RLIM_INFINITY; + if (r.rlim_max == RLIM_INFINITY32) + r.rlim_max = RLIM_INFINITY; + set_fs (KERNEL_DS); + ret = sys_setrlimit(resource, &r); + set_fs (old_fs); + + PPCDBG(PPCDBG_SYS32NI, "sys32_setrlimit - exited - pid=%ld comm=%s\n", current->pid, current->comm); + + return ret; +} + + +struct rusage32 { + struct timeval32 ru_utime; + struct timeval32 ru_stime; + s32 ru_maxrss; + s32 ru_ixrss; + s32 ru_idrss; + s32 ru_isrss; + s32 ru_minflt; + s32 ru_majflt; + s32 ru_nswap; + s32 ru_inblock; + s32 ru_oublock; + s32 ru_msgsnd; + s32 ru_msgrcv; + s32 ru_nsignals; + s32 ru_nvcsw; + s32 ru_nivcsw; +}; + +static int put_rusage (struct rusage32 *ru, struct rusage *r) +{ + int err; + + err = put_user (r->ru_utime.tv_sec, &ru->ru_utime.tv_sec); + err |= __put_user (r->ru_utime.tv_usec, &ru->ru_utime.tv_usec); + err |= __put_user (r->ru_stime.tv_sec, &ru->ru_stime.tv_sec); + err |= __put_user (r->ru_stime.tv_usec, &ru->ru_stime.tv_usec); + err |= __put_user (r->ru_maxrss, &ru->ru_maxrss); + err |= __put_user (r->ru_ixrss, &ru->ru_ixrss); + err |= __put_user (r->ru_idrss, &ru->ru_idrss); + err |= __put_user (r->ru_isrss, &ru->ru_isrss); + err |= __put_user (r->ru_minflt, &ru->ru_minflt); + err |= __put_user (r->ru_majflt, &ru->ru_majflt); + err |= __put_user (r->ru_nswap, &ru->ru_nswap); + err |= __put_user (r->ru_inblock, &ru->ru_inblock); + err |= __put_user (r->ru_oublock, &ru->ru_oublock); + err |= __put_user (r->ru_msgsnd, &ru->ru_msgsnd); + err |= __put_user (r->ru_msgrcv, &ru->ru_msgrcv); + err |= __put_user (r->ru_nsignals, &ru->ru_nsignals); + err |= __put_user (r->ru_nvcsw, &ru->ru_nvcsw); + err |= __put_user (r->ru_nivcsw, &ru->ru_nivcsw); + return err; +} + + +extern asmlinkage long sys_getrusage(int who, struct rusage *ru); + +/* Note: it is necessary to treat who as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_getrusage(u32 who, struct rusage32 *ru) +{ + struct rusage r; + int ret; + mm_segment_t old_fs = get_fs(); + + PPCDBG(PPCDBG_SYS32X, "sys32_getrusage - running - pid=%ld, comm=%s\n", current->pid, current->comm); + + set_fs (KERNEL_DS); + ret = sys_getrusage((int)who, &r); + set_fs (old_fs); + if (put_rusage (ru, &r)) + return -EFAULT; + + return ret; +} + + + + +struct sysinfo32 { + s32 uptime; + u32 loads[3]; + u32 totalram; + u32 freeram; + u32 sharedram; + u32 bufferram; + u32 totalswap; + u32 freeswap; + unsigned short procs; + char _f[22]; +}; + +extern asmlinkage long sys_sysinfo(struct sysinfo *info); + +asmlinkage long sys32_sysinfo(struct sysinfo32 *info) +{ + struct sysinfo s; + int ret, err; + mm_segment_t old_fs = get_fs (); + + PPCDBG(PPCDBG_SYS32, "sys32_sysinfo - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + set_fs (KERNEL_DS); + ret = sys_sysinfo(&s); + set_fs (old_fs); + err = put_user (s.uptime, &info->uptime); + err |= __put_user (s.loads[0], &info->loads[0]); + err |= __put_user (s.loads[1], &info->loads[1]); + err |= __put_user (s.loads[2], &info->loads[2]); + err |= __put_user (s.totalram, &info->totalram); + err |= __put_user (s.freeram, &info->freeram); + err |= __put_user (s.sharedram, &info->sharedram); + err |= __put_user (s.bufferram, &info->bufferram); + err |= __put_user (s.totalswap, &info->totalswap); + err |= __put_user (s.freeswap, &info->freeswap); + err |= __put_user (s.procs, &info->procs); + if (err) + return -EFAULT; + + PPCDBG(PPCDBG_SYS32, "sys32_sysinfo - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return ret; +} + + + + +/* 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 long sys32_gettimeofday(struct timeval32 *tv, struct timezone *tz) +{ + + PPCDBG(PPCDBG_SYS32X, "sys32_gettimeofday - running - pid=%ld, comm=%s\n", current->pid, current->comm); + + if (tv) { + struct timeval ktv; + do_gettimeofday(&ktv); + if (put_tv32(tv, &ktv)) + return -EFAULT; + } + if (tz) { + if (copy_to_user(tz, &sys_tz, sizeof(sys_tz))) + return -EFAULT; + } + + return 0; +} + + + +asmlinkage long sys32_settimeofday(struct timeval32 *tv, struct timezone *tz) +{ + struct timeval ktv; + struct timezone ktz; + + PPCDBG(PPCDBG_SYS32, "sys32_settimeofday - running - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + if (tv) { + if (get_tv32(&ktv, tv)) + return -EFAULT; + } + if (tz) { + if (copy_from_user(&ktz, tz, sizeof(ktz))) + return -EFAULT; + } + + return do_sys_settimeofday(tv ? &ktv : NULL, tz ? &ktz : NULL); +} + + + + +struct tms32 { + __kernel_clock_t32 tms_utime; + __kernel_clock_t32 tms_stime; + __kernel_clock_t32 tms_cutime; + __kernel_clock_t32 tms_cstime; +}; + +extern asmlinkage long sys_times(struct tms * tbuf); + +asmlinkage long sys32_times(struct tms32 *tbuf) +{ + struct tms t; + long ret; + mm_segment_t old_fs = get_fs (); + int err; + + PPCDBG(PPCDBG_SYS32, "sys32_times - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + set_fs (KERNEL_DS); + ret = sys_times(tbuf ? &t : NULL); + set_fs (old_fs); + if (tbuf) { + err = put_user (t.tms_utime, &tbuf->tms_utime); + err |= __put_user (t.tms_stime, &tbuf->tms_stime); + err |= __put_user (t.tms_cutime, &tbuf->tms_cutime); + err |= __put_user (t.tms_cstime, &tbuf->tms_cstime); + if (err) + ret = -EFAULT; + } + + PPCDBG(PPCDBG_SYS32, "sys32_times - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return ret; +} + + + + + +struct msgbuf32 { s32 mtype; char mtext[1]; }; + +struct ipc_perm32 +{ + key_t key; + __kernel_uid_t32 uid; + __kernel_gid_t32 gid; + __kernel_uid_t32 cuid; + __kernel_gid_t32 cgid; + __kernel_mode_t32 mode; + unsigned short seq; +}; + +struct semid_ds32 { + struct ipc_perm32 sem_perm; /* permissions .. see ipc.h */ + __kernel_time_t32 sem_otime; /* last semop time */ + __kernel_time_t32 sem_ctime; /* last change time */ + u32 sem_base; /* ptr to first semaphore in array */ + u32 sem_pending; /* pending operations to be processed */ + u32 sem_pending_last; /* last pending operation */ + u32 undo; /* undo requests on this array */ + unsigned short sem_nsems; /* no. of semaphores in array */ +}; + +struct semid64_ds32 { + struct ipc64_perm sem_perm; /* this structure is the same on ppc32 and ppc64 */ + unsigned int __pad1; + __kernel_time_t32 sem_otime; + unsigned int __pad2; + __kernel_time_t32 sem_ctime; + u32 sem_nsems; + u32 __unused1; + u32 __unused2; +}; + +struct msqid_ds32 +{ + struct ipc_perm32 msg_perm; + u32 msg_first; + u32 msg_last; + __kernel_time_t32 msg_stime; + __kernel_time_t32 msg_rtime; + __kernel_time_t32 msg_ctime; + u32 wwait; + u32 rwait; + unsigned short msg_cbytes; + unsigned short msg_qnum; + unsigned short msg_qbytes; + __kernel_ipc_pid_t32 msg_lspid; + __kernel_ipc_pid_t32 msg_lrpid; +}; + +struct msqid64_ds32 { + struct ipc64_perm msg_perm; + unsigned int __pad1; + __kernel_time_t32 msg_stime; + unsigned int __pad2; + __kernel_time_t32 msg_rtime; + unsigned int __pad3; + __kernel_time_t32 msg_ctime; + unsigned int msg_cbytes; + unsigned int msg_qnum; + unsigned int msg_qbytes; + __kernel_pid_t32 msg_lspid; + __kernel_pid_t32 msg_lrpid; + unsigned int __unused1; + unsigned int __unused2; +}; + + +struct shmid_ds32 { + struct ipc_perm32 shm_perm; + int shm_segsz; + __kernel_time_t32 shm_atime; + __kernel_time_t32 shm_dtime; + __kernel_time_t32 shm_ctime; + __kernel_ipc_pid_t32 shm_cpid; + __kernel_ipc_pid_t32 shm_lpid; + unsigned short shm_nattch; +}; + + +struct shmid64_ds32 { + struct ipc64_perm shm_perm; + unsigned int __pad1; + __kernel_time_t32 shm_atime; + unsigned int __pad2; + __kernel_time_t32 shm_dtime; + unsigned int __pad3; + __kernel_time_t32 shm_ctime; + __kernel_size_t32 shm_segsz; + __kernel_pid_t32 shm_cpid; + __kernel_pid_t32 shm_lpid; + unsigned int shm_nattch; + unsigned int __unused1; + unsigned int __unused2; +}; + + + +/* + * sys32_ipc + () is the de-multiplexer for the SysV IPC calls in 32bit emulation.. + * + * This is really horribly ugly. + */ +static long do_sys32_semctl(int first, int second, int third, void *uptr) +{ + union semun fourth; + u32 pad; + int err, err2; + mm_segment_t old_fs; + + if (!uptr) + return -EINVAL; + err = -EFAULT; + if (get_user (pad, (u32 *)uptr)) + return err; + if(third == SETVAL) + fourth.val = (int)pad; + else + fourth.__pad = (void *)A(pad); + switch (third & (~IPC_64)) { + + case IPC_INFO: + case IPC_RMID: + case SEM_INFO: + case GETVAL: + case GETPID: + case GETNCNT: + case GETZCNT: + case GETALL: + case SETALL: + case SETVAL: + err = sys_semctl (first, second, third, fourth); + break; + + case IPC_STAT: + case SEM_STAT: + if (third & IPC_64) + { + struct semid64_ds s; + struct semid64_ds32 *usp = (struct semid64_ds32 *)A(pad); + + fourth.__pad = &s; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_semctl (first, second, third, fourth); + set_fs (old_fs); + err2 = copy_to_user (&usp->sem_perm, &s.sem_perm, sizeof(struct ipc64_perm) + 2*sizeof(time_t)); + err2 |= __put_user (s.sem_nsems, &usp->sem_nsems); + if (err2) err = -EFAULT; + + } + else + { + struct semid64_ds s; + struct semid_ds32 *usp; + + usp = (struct semid_ds32 *)A(pad); + fourth.__pad = &s; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_semctl (first, second, third, fourth); + set_fs (old_fs); + err2 = put_user(s.sem_perm.key, &usp->sem_perm.key); + err2 |= __put_user(s.sem_perm.uid, &usp->sem_perm.uid); + err2 |= __put_user(s.sem_perm.gid, &usp->sem_perm.gid); + err2 |= __put_user(s.sem_perm.cuid, + &usp->sem_perm.cuid); + err2 |= __put_user (s.sem_perm.cgid, + &usp->sem_perm.cgid); + err2 |= __put_user (s.sem_perm.mode, + &usp->sem_perm.mode); + err2 |= __put_user (s.sem_perm.seq, &usp->sem_perm.seq); + err2 |= __put_user (s.sem_otime, &usp->sem_otime); + err2 |= __put_user (s.sem_ctime, &usp->sem_ctime); + err2 |= __put_user (s.sem_nsems, &usp->sem_nsems); + if (err2) + err = -EFAULT; + } + break; + + case IPC_SET: + if (third & IPC_64) + { + + + struct semid64_ds s; + struct semid64_ds32 *usp = (struct semid64_ds32 *)A(pad); + mm_segment_t old_fs; + + + err = get_user (s.sem_perm.uid, &usp->sem_perm.uid); + err |= __get_user (s.sem_perm.gid, &usp->sem_perm.gid); + err |= __get_user (s.sem_perm.mode, &usp->sem_perm.mode); + if (err) + goto out; + fourth.__pad = &s; + + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_semctl (first, second, third, fourth); + set_fs (old_fs); + + } + else + { + struct semid64_ds s; + struct semid_ds32 *usp; + + usp = (struct semid_ds32 *)A(pad); + err = get_user (s.sem_perm.uid, &usp->sem_perm.uid); + err |= __get_user (s.sem_perm.gid, &usp->sem_perm.gid); + err |= __get_user (s.sem_perm.mode, &usp->sem_perm.mode); + if (err) + goto out; + + fourth.__pad = &s; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_semctl (first, second, third, fourth); + set_fs (old_fs); + } + break; + + } + out: + return err; + + +} + +static int +do_sys32_msgsnd (int first, int second, int third, void *uptr) +{ + struct msgbuf *p = kmalloc (second + sizeof (struct msgbuf) + + 4, GFP_USER); + struct msgbuf32 *up = (struct msgbuf32 *)uptr; + mm_segment_t old_fs; + int err; + + if (!p) + return -ENOMEM; + err = get_user (p->mtype, &up->mtype); + err |= __copy_from_user (p->mtext, &up->mtext, second); + if (err) + goto out; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgsnd (first, p, second, third); + set_fs (old_fs); +out: + kfree (p); + return err; +} + +static int +do_sys32_msgrcv (int first, int second, int msgtyp, int third, + int version, void *uptr) +{ + struct msgbuf32 *up; + struct msgbuf *p; + mm_segment_t old_fs; + int err; + + if (!version) { + struct ipc_kludge *uipck = (struct ipc_kludge *)uptr; + struct ipc_kludge ipck; + + err = -EINVAL; + if (!uptr) + goto out; + err = -EFAULT; + if (copy_from_user (&ipck, uipck, sizeof (struct ipc_kludge))) + goto out; + uptr = (void *)A(ipck.msgp); + msgtyp = ipck.msgtyp; + } + err = -ENOMEM; + p = kmalloc (second + sizeof (struct msgbuf) + 4, GFP_USER); + if (!p) + goto out; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgrcv (first, p, second + 4, msgtyp, third); + set_fs (old_fs); + if (err < 0) + goto free_then_out; + up = (struct msgbuf32 *)uptr; + if (put_user (p->mtype, &up->mtype) || + __copy_to_user (&up->mtext, p->mtext, err)) + err = -EFAULT; +free_then_out: + kfree (p); +out: + return err; +} + +static int +do_sys32_msgctl (int first, int second, void *uptr) +{ + int err = -EINVAL, err2; + mm_segment_t old_fs; + + switch (second & (~IPC_64)) { + + case IPC_INFO: + case IPC_RMID: + case MSG_INFO: + err = sys_msgctl (first, second, (struct msqid_ds *)uptr); + break; + + case IPC_SET: + if (second & IPC_64) + { + struct msqid64_ds m; + struct msqid64_ds32 *up = (struct msqid64_ds32 *)uptr; + + + err = get_user (m.msg_perm.uid, &up->msg_perm.uid); + err |= __get_user (m.msg_perm.gid, &up->msg_perm.gid); + err |= __get_user (m.msg_perm.mode, &up->msg_perm.mode); + err |= __get_user (m.msg_qbytes, &up->msg_qbytes); + if (err) + goto out; + + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgctl (first, second, (struct msqid_ds *)&m); + set_fs (old_fs); + + + } + else + { + struct msqid_ds m; + struct msqid_ds32 *up = (struct msqid_ds32 *)uptr; + + + err = get_user (m.msg_perm.uid, &up->msg_perm.uid); + err |= __get_user (m.msg_perm.gid, &up->msg_perm.gid); + err |= __get_user (m.msg_perm.mode, &up->msg_perm.mode); + err |= __get_user (m.msg_qbytes, &up->msg_qbytes); + if (err) + break; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgctl (first, second, &m); + set_fs (old_fs); + } + break; + + case IPC_STAT: + case MSG_STAT: + if (second & IPC_64) + { + struct msqid64_ds m; + struct msqid64_ds32 *up = (struct msqid64_ds32 *)uptr; + + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgctl (first, second, (struct msqid_ds *)&m); + set_fs (old_fs); + err2 = copy_to_user(&up->msg_perm, &m.msg_perm, sizeof(struct ipc64_perm) + 3*sizeof(time_t)); + err2 |= __put_user (m.msg_cbytes, &up->msg_cbytes); + err2 |= __put_user (m.msg_qnum, &up->msg_qnum); + err2 |= __put_user (m.msg_qbytes, &up->msg_qbytes); + err2 |= __put_user (m.msg_lspid, &up->msg_lspid); + err2 |= __put_user (m.msg_lrpid, &up->msg_lrpid); + if (err2) + err = -EFAULT; + + } + else + { + struct msqid64_ds m64; + struct msqid_ds32 *up = (struct msqid_ds32 *)uptr; + + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgctl (first, second, (void *) &m64); + set_fs (old_fs); + err2 = put_user (m64.msg_perm.key, &up->msg_perm.key); + err2 |= __put_user(m64.msg_perm.uid, &up->msg_perm.uid); + err2 |= __put_user(m64.msg_perm.gid, &up->msg_perm.gid); + err2 |= __put_user(m64.msg_perm.cuid, &up->msg_perm.cuid); + err2 |= __put_user(m64.msg_perm.cgid, &up->msg_perm.cgid); + err2 |= __put_user(m64.msg_perm.mode, &up->msg_perm.mode); + err2 |= __put_user(m64.msg_perm.seq, &up->msg_perm.seq); + err2 |= __put_user(m64.msg_stime, &up->msg_stime); + err2 |= __put_user(m64.msg_rtime, &up->msg_rtime); + err2 |= __put_user(m64.msg_ctime, &up->msg_ctime); + err2 |= __put_user(m64.msg_cbytes, &up->msg_cbytes); + err2 |= __put_user(m64.msg_qnum, &up->msg_qnum); + err2 |= __put_user(m64.msg_qbytes, &up->msg_qbytes); + err2 |= __put_user(m64.msg_lspid, &up->msg_lspid); + err2 |= __put_user(m64.msg_lrpid, &up->msg_lrpid); + if (err2) + err = -EFAULT; + } + break; + + } +out: + return err; +} + + + +static int +do_sys32_shmat (int first, int second, int third, int version, void *uptr) +{ + unsigned long raddr; + u32 *uaddr = (u32 *)A((u32)third); + int err = -EINVAL; + + if (version == 1) + return err; + err = sys_shmat (first, uptr, second, &raddr); + if (err) + return err; + err = put_user (raddr, uaddr); + return err; +} + +static int +do_sys32_shmctl (int first, int second, void *uptr) +{ + int err = -EFAULT, err2; + struct shmid_ds s; + struct shmid64_ds s64; + mm_segment_t old_fs; + struct shm_info32 { + int used_ids; + u32 shm_tot, shm_rss, shm_swp; + u32 swap_attempts, swap_successes; + } *uip = (struct shm_info32 *)uptr; + struct shm_info si; + + switch (second & (~IPC_64)) { + + case IPC_INFO: + case IPC_RMID: + case SHM_LOCK: + case SHM_UNLOCK: + err = sys_shmctl (first, second, (struct shmid_ds *)uptr); + break; + case IPC_SET: + if (second & IPC_64) + { + + struct shmid64_ds32 *up = (struct shmid64_ds32 *)uptr; + + err = get_user (s.shm_perm.uid, &up->shm_perm.uid); + err |= __get_user (s.shm_perm.gid, &up->shm_perm.gid); + err |= __get_user (s.shm_perm.mode, &up->shm_perm.mode); + if (err) + goto out; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_shmctl (first, second, (struct shmid_ds *)&s); + set_fs (old_fs); + + } + else + { + struct shmid_ds32 *up = (struct shmid_ds32 *)uptr; + + err = get_user (s.shm_perm.uid, &up->shm_perm.uid); + err |= __get_user (s.shm_perm.gid, &up->shm_perm.gid); + err |= __get_user (s.shm_perm.mode, &up->shm_perm.mode); + if (err) + break; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_shmctl (first, second, &s); + set_fs (old_fs); + } + break; + + case IPC_STAT: + case SHM_STAT: + if (second & IPC_64) + { + + struct shmid64_ds32 *up = (struct shmid64_ds32 *)uptr; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_shmctl (first, second, (struct shmid_ds *)&s); + set_fs (old_fs); + if (err < 0) + goto out; + + err2 = copy_to_user (&up->shm_perm, &s.shm_perm, sizeof(struct ipc64_perm) + 3*sizeof(time_t)); + err2 |= __put_user (s.shm_segsz, &up->shm_segsz); + err2 |= __put_user (s.shm_nattch, &up->shm_nattch); + err2 |= __put_user (s.shm_cpid, &up->shm_cpid); + err2 |= __put_user (s.shm_lpid, &up->shm_lpid); + if (err2) + err = -EFAULT; + + } + else + { + struct shmid_ds32 *up = (struct shmid_ds32 *)uptr; + + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_shmctl (first, second, (void *) &s64); + set_fs (old_fs); + if (err < 0) + break; + err2 = put_user (s64.shm_perm.key, &up->shm_perm.key); + err2 |= __put_user (s64.shm_perm.uid, &up->shm_perm.uid); + err2 |= __put_user (s64.shm_perm.gid, &up->shm_perm.gid); + err2 |= __put_user (s64.shm_perm.cuid, + &up->shm_perm.cuid); + err2 |= __put_user (s64.shm_perm.cgid, + &up->shm_perm.cgid); + err2 |= __put_user (s64.shm_perm.mode, + &up->shm_perm.mode); + err2 |= __put_user (s64.shm_perm.seq, &up->shm_perm.seq); + err2 |= __put_user (s64.shm_atime, &up->shm_atime); + err2 |= __put_user (s64.shm_dtime, &up->shm_dtime); + err2 |= __put_user (s64.shm_ctime, &up->shm_ctime); + err2 |= __put_user (s64.shm_segsz, &up->shm_segsz); + err2 |= __put_user (s64.shm_nattch, &up->shm_nattch); + err2 |= __put_user (s64.shm_cpid, &up->shm_cpid); + err2 |= __put_user (s64.shm_lpid, &up->shm_lpid); + if (err2) + err = -EFAULT; + } + break; + + case SHM_INFO: + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_shmctl (first, second, (void *)&si); + set_fs (old_fs); + if (err < 0) + break; + err2 = put_user (si.used_ids, &uip->used_ids); + err2 |= __put_user (si.shm_tot, &uip->shm_tot); + err2 |= __put_user (si.shm_rss, &uip->shm_rss); + err2 |= __put_user (si.shm_swp, &uip->shm_swp); + err2 |= __put_user (si.swap_attempts, + &uip->swap_attempts); + err2 |= __put_user (si.swap_successes, + &uip->swap_successes); + if (err2) + err = -EFAULT; + break; + + } + out: + return err; +} + + + +/* Note: it is necessary to treat first_parm, second_parm, and third_parm as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_ipc(u32 call, u32 first_parm, u32 second_parm, u32 third_parm, u32 ptr, u32 fifth) +{ + int first = (int)first_parm; + int second = (int)second_parm; + int third = (int)third_parm; + int version, err; + + PPCDBG(PPCDBG_SYS32, "sys32_ipc - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + + switch (call) { + + case SEMOP: + /* struct sembuf is the same on 32 and 64bit :)) */ + err = sys_semop (first, (struct sembuf *)AA(ptr), + second); + break; + case SEMGET: + err = sys_semget (first, second, third); + break; + case SEMCTL: + err = do_sys32_semctl (first, second, third, + (void *)AA(ptr)); + break; + + case MSGSND: + err = do_sys32_msgsnd (first, second, third, + (void *)AA(ptr)); + break; + case MSGRCV: + err = do_sys32_msgrcv (first, second, fifth, third, + version, (void *)AA(ptr)); + break; + case MSGGET: + err = sys_msgget ((key_t) first, second); + break; + case MSGCTL: + err = do_sys32_msgctl (first, second, (void *)AA(ptr)); + break; + + case SHMAT: + err = do_sys32_shmat (first, second, third, + version, (void *)AA(ptr)); + break; + case SHMDT: + err = sys_shmdt ((char *)AA(ptr)); + break; + case SHMGET: + err = sys_shmget (first, second, third); + break; + case SHMCTL: + err = do_sys32_shmctl (first, second, (void *)AA(ptr)); + break; + default: + err = -EINVAL; + break; + } + + + PPCDBG(PPCDBG_SYS32, "sys32_ipc - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return err; +} + + + +/* stat syscall methods. */ +extern asmlinkage int sys_stat(char* filename, struct __old_kernel_stat* statbuf); + +static int cp_old_stat32(struct inode* inode, struct __old_kernel_stat32* statbuf) +{ + static int warncount = 5; + struct __old_kernel_stat32 tmp; + + if (warncount) { + warncount--; + printk("VFS: Warning: %s using old stat() call. Recompile your binary.\n", + current->comm); + } + + tmp.st_dev = kdev_t_to_nr(inode->i_dev); + tmp.st_ino = inode->i_ino; + tmp.st_mode = inode->i_mode; + tmp.st_nlink = inode->i_nlink; + SET_OLDSTAT_UID(tmp, inode->i_uid); + SET_OLDSTAT_GID(tmp, inode->i_gid); + tmp.st_rdev = kdev_t_to_nr(inode->i_rdev); + tmp.st_size = inode->i_size; + tmp.st_atime = inode->i_atime; + tmp.st_mtime = inode->i_mtime; + tmp.st_ctime = inode->i_ctime; + return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; +} + +asmlinkage long sys32_stat(char* filename, struct __old_kernel_stat32* statbuf) +{ + struct nameidata nd; + int error; + + PPCDBG(PPCDBG_SYS32X, "sys32_stat - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + error = user_path_walk(filename, &nd); + if (!error) { + error = do_revalidate(nd.dentry); + if (!error) + error = cp_old_stat32(nd.dentry->d_inode, statbuf); + path_release(&nd); + } + + PPCDBG(PPCDBG_SYS32X, "sys32_stat - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return error; +} + +asmlinkage long sys32_fstat(unsigned int fd, struct __old_kernel_stat32* statbuf) +{ + struct file *f; + int err = -EBADF; + + PPCDBG(PPCDBG_SYS32X, "sys32_fstat - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + f = fget(fd); + if (f) { + struct dentry * dentry = f->f_dentry; + + err = do_revalidate(dentry); + if (!err) + err = cp_old_stat32(dentry->d_inode, statbuf); + fput(f); + } + + PPCDBG(PPCDBG_SYS32X, "sys32_fstat - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return err; +} + +asmlinkage long sys32_lstat(char* filename, struct __old_kernel_stat32* statbuf) +{ + struct nameidata nd; + int error; + + PPCDBG(PPCDBG_SYS32X, "sys32_lstat - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + error = user_path_walk_link(filename, &nd); + if (!error) { + error = do_revalidate(nd.dentry); + if (!error) + error = cp_old_stat32(nd.dentry->d_inode, statbuf); + + path_release(&nd); + } + + PPCDBG(PPCDBG_SYS32X, "sys32_lstat - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return error; +} + +extern asmlinkage ssize_t sys_sendfile(int out_fd, int in_fd, off_t* offset, size_t count); + +/* Note: it is necessary to treat out_fd and in_fd as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sendfile(u32 out_fd, u32 in_fd, __kernel_off_t32* offset, u32 count) +{ + mm_segment_t old_fs = get_fs(); + int ret; + off_t of; + + PPCDBG(PPCDBG_SYS32, "sys32_sendfile - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + if (offset && get_user(of, offset)) + return -EFAULT; + + set_fs(KERNEL_DS); + ret = sys_sendfile((int)out_fd, (int)in_fd, offset ? &of : NULL, count); + set_fs(old_fs); + + if (!ret && offset && put_user(of, offset)) + return -EFAULT; + + + PPCDBG(PPCDBG_SYS32, "sys32_sendfile - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + return ret; +} + + + + +extern asmlinkage int sys_setsockopt(int fd, int level, int optname, char *optval, int optlen); + +asmlinkage long sys32_setsockopt(int fd, int level, int optname, char* optval, int optlen) +{ + + PPCDBG(PPCDBG_SYS32,"sys32_setsockopt - running - pid=%ld, comm=%s\n", current->pid, current->comm); + + if (optname == SO_ATTACH_FILTER) { + struct sock_fprog32 { + __u16 len; + __u32 filter; + } *fprog32 = (struct sock_fprog32 *)optval; + struct sock_fprog kfprog; + struct sock_filter *kfilter; + unsigned int fsize; + mm_segment_t old_fs; + __u32 uptr; + int ret; + + if (get_user(kfprog.len, &fprog32->len) || + __get_user(uptr, &fprog32->filter)) + return -EFAULT; + kfprog.filter = (struct sock_filter *)A(uptr); + fsize = kfprog.len * sizeof(struct sock_filter); + kfilter = (struct sock_filter *)kmalloc(fsize, GFP_KERNEL); + if (kfilter == NULL) + return -ENOMEM; + if (copy_from_user(kfilter, kfprog.filter, fsize)) { + kfree(kfilter); + return -EFAULT; + } + kfprog.filter = kfilter; + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_setsockopt(fd, level, optname, + (char *)&kfprog, sizeof(kfprog)); + set_fs(old_fs); + kfree(kfilter); + return ret; + } + return sys_setsockopt(fd, level, optname, optval, optlen); +} + + + + +#define MAX_SOCK_ADDR 128 /* 108 for Unix domain - 16 for IP, 16 for IPX, 24 for IPv6, about 80 for AX.25 */ +#define __CMSG32_NXTHDR(ctl, len, cmsg, cmsglen) __cmsg32_nxthdr((ctl),(len),(cmsg),(cmsglen)) +#define CMSG32_NXTHDR(mhdr, cmsg, cmsglen) cmsg32_nxthdr((mhdr), (cmsg), (cmsglen)) + +#define CMSG32_ALIGN(len) ( ((len)+sizeof(int)-1) & ~(sizeof(int)-1) ) + +#define CMSG32_DATA(cmsg) ((void *)((char *)(cmsg) + CMSG32_ALIGN(sizeof(struct cmsghdr32)))) +#define CMSG32_SPACE(len) (CMSG32_ALIGN(sizeof(struct cmsghdr32)) + CMSG32_ALIGN(len)) +#define CMSG32_LEN(len) (CMSG32_ALIGN(sizeof(struct cmsghdr32)) + (len)) +#define __CMSG32_FIRSTHDR(ctl,len) ((len) >= sizeof(struct cmsghdr32) ? \ + (struct cmsghdr32 *)(ctl) : \ + (struct cmsghdr32 *)NULL) +#define CMSG32_FIRSTHDR(msg) __CMSG32_FIRSTHDR((msg)->msg_control, (msg)->msg_controllen) + +struct msghdr32 +{ + u32 msg_name; + int msg_namelen; + u32 msg_iov; + __kernel_size_t32 msg_iovlen; + u32 msg_control; + __kernel_size_t32 msg_controllen; + unsigned msg_flags; +}; + +struct cmsghdr32 +{ + __kernel_size_t32 cmsg_len; + int cmsg_level; + int cmsg_type; +}; + +__inline__ struct cmsghdr32 *__cmsg32_nxthdr(void *__ctl, __kernel_size_t __size, + struct cmsghdr32 *__cmsg, int __cmsg_len) +{ + struct cmsghdr32 * __ptr; + + __ptr = (struct cmsghdr32 *)(((unsigned char *) __cmsg) + + CMSG32_ALIGN(__cmsg_len)); + if ((unsigned long)((char*)(__ptr+1) - (char *) __ctl) > __size) + return NULL; + + return __ptr; +} + +__inline__ struct cmsghdr32 *cmsg32_nxthdr (struct msghdr *__msg, + struct cmsghdr32 *__cmsg, + int __cmsg_len) +{ + return __cmsg32_nxthdr(__msg->msg_control, __msg->msg_controllen, + __cmsg, __cmsg_len); +} + +extern __inline__ struct socket *socki_lookup(struct inode *inode) +{ + return &inode->u.socket_i; +} + +extern __inline__ struct socket *sockfd_lookup(int fd, int *err) +{ + struct file *file; + struct inode *inode; + + if (!(file = fget(fd))) + { + *err = -EBADF; + return NULL; + } + + inode = file->f_dentry->d_inode; + if (!inode || !inode->i_sock || !socki_lookup(inode)) + { + *err = -ENOTSOCK; + fput(file); + return NULL; + } + + return socki_lookup(inode); +} + +extern __inline__ void sockfd_put(struct socket *sock) +{ + fput(sock->file); +} + +static inline int msghdr_from_user32_to_kern(struct msghdr *kmsg, struct msghdr32 *umsg) +{ + u32 tmp1, tmp2, tmp3; + int err; + + err = get_user(tmp1, &umsg->msg_name); + err |= __get_user(tmp2, &umsg->msg_iov); + err |= __get_user(tmp3, &umsg->msg_control); + if (err) + return -EFAULT; + + kmsg->msg_name = (void *)A(tmp1); + kmsg->msg_iov = (struct iovec *)A(tmp2); + kmsg->msg_control = (void *)A(tmp3); + + err = get_user(kmsg->msg_namelen, &umsg->msg_namelen); + err |= get_user(kmsg->msg_iovlen, &umsg->msg_iovlen); + err |= get_user(kmsg->msg_controllen, &umsg->msg_controllen); + err |= get_user(kmsg->msg_flags, &umsg->msg_flags); + + return err; +} + +static inline int iov_from_user32_to_kern(struct iovec *kiov, + struct iovec32 *uiov32, + int niov) +{ + int tot_len = 0; + + while(niov > 0) { + u32 len, buf; + + if(get_user(len, &uiov32->iov_len) || + get_user(buf, &uiov32->iov_base)) { + tot_len = -EFAULT; + break; + } + tot_len += len; + kiov->iov_base = (void *)A(buf); + kiov->iov_len = (__kernel_size_t) len; + uiov32++; + kiov++; + niov--; + } + return tot_len; +} + +/* I've named the args so it is easy to tell whose space the pointers are in. */ +static int verify_iovec32(struct msghdr *kern_msg, struct iovec *kern_iov, + char *kern_address, int mode) +{ + int tot_len; + + if(kern_msg->msg_namelen) { + if(mode==VERIFY_READ) { + int err = move_addr_to_kernel(kern_msg->msg_name, + kern_msg->msg_namelen, + kern_address); + if(err < 0) + return err; + } + kern_msg->msg_name = kern_address; + } else + kern_msg->msg_name = NULL; + + if(kern_msg->msg_iovlen > UIO_FASTIOV) { + kern_iov = kmalloc(kern_msg->msg_iovlen * sizeof(struct iovec), + GFP_KERNEL); + if(!kern_iov) + return -ENOMEM; + } + + tot_len = iov_from_user32_to_kern(kern_iov, + (struct iovec32 *)kern_msg->msg_iov, + kern_msg->msg_iovlen); + if(tot_len >= 0) + kern_msg->msg_iov = kern_iov; + else if(kern_msg->msg_iovlen > UIO_FASTIOV) + kfree(kern_iov); + + return tot_len; +} + +/* There is a lot of hair here because the alignment rules (and + * thus placement) of cmsg headers and length are different for + * 32-bit apps. -DaveM + */ +static int cmsghdr_from_user32_to_kern(struct msghdr *kmsg, + unsigned char *stackbuf, int stackbuf_size) +{ + struct cmsghdr32 *ucmsg; + struct cmsghdr *kcmsg, *kcmsg_base; + __kernel_size_t32 ucmlen; + __kernel_size_t kcmlen, tmp; + + kcmlen = 0; + kcmsg_base = kcmsg = (struct cmsghdr *)stackbuf; + ucmsg = CMSG32_FIRSTHDR(kmsg); + while(ucmsg != NULL) { + if(get_user(ucmlen, &ucmsg->cmsg_len)) + return -EFAULT; + + /* Catch bogons. */ + if(CMSG32_ALIGN(ucmlen) < + CMSG32_ALIGN(sizeof(struct cmsghdr32))) + return -EINVAL; + if((unsigned long)(((char *)ucmsg - (char *)kmsg->msg_control) + + ucmlen) > kmsg->msg_controllen) + return -EINVAL; + + tmp = ((ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))) + + CMSG_ALIGN(sizeof(struct cmsghdr))); + kcmlen += tmp; + ucmsg = CMSG32_NXTHDR(kmsg, ucmsg, ucmlen); + } + if(kcmlen == 0) + return -EINVAL; + + /* The kcmlen holds the 64-bit version of the control length. + * It may not be modified as we do not stick it into the kmsg + * until we have successfully copied over all of the data + * from the user. + */ + if(kcmlen > stackbuf_size) + kcmsg_base = kcmsg = kmalloc(kcmlen, GFP_KERNEL); + if(kcmsg == NULL) + return -ENOBUFS; + + /* Now copy them over neatly. */ + memset(kcmsg, 0, kcmlen); + ucmsg = CMSG32_FIRSTHDR(kmsg); + while(ucmsg != NULL) { + __get_user(ucmlen, &ucmsg->cmsg_len); + tmp = ((ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))) + + CMSG_ALIGN(sizeof(struct cmsghdr))); + kcmsg->cmsg_len = tmp; + __get_user(kcmsg->cmsg_level, &ucmsg->cmsg_level); + __get_user(kcmsg->cmsg_type, &ucmsg->cmsg_type); + + /* Copy over the data. */ + if(copy_from_user(CMSG_DATA(kcmsg), + CMSG32_DATA(ucmsg), + (ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))))) + goto out_free_efault; + + /* Advance. */ + kcmsg = (struct cmsghdr *)((char *)kcmsg + CMSG_ALIGN(tmp)); + ucmsg = CMSG32_NXTHDR(kmsg, ucmsg, ucmlen); + } + + /* Ok, looks like we made it. Hook it up and return success. */ + kmsg->msg_control = kcmsg_base; + kmsg->msg_controllen = kcmlen; + return 0; + +out_free_efault: + if(kcmsg_base != (struct cmsghdr *)stackbuf) + kfree(kcmsg_base); + return -EFAULT; +} + +asmlinkage long sys32_sendmsg(int fd, struct msghdr32* user_msg, unsigned int user_flags) +{ + struct socket *sock; + char address[MAX_SOCK_ADDR]; + struct iovec iov[UIO_FASTIOV]; + unsigned char ctl[sizeof(struct cmsghdr) + 20]; + unsigned char *ctl_buf = ctl; + struct msghdr kern_msg; + int err, total_len; + + PPCDBG(PPCDBG_SYS32, "sys32_sendmsg - entered - pid=%ld, comm=%s \n", current->pid, current->comm); + + if(msghdr_from_user32_to_kern(&kern_msg, user_msg)) + return -EFAULT; + if(kern_msg.msg_iovlen > UIO_MAXIOV) + return -EINVAL; + err = verify_iovec32(&kern_msg, iov, address, VERIFY_READ); + if (err < 0) + goto out; + total_len = err; + + if(kern_msg.msg_controllen) { + err = cmsghdr_from_user32_to_kern(&kern_msg, ctl, sizeof(ctl)); + if(err) + goto out_freeiov; + ctl_buf = kern_msg.msg_control; + } + kern_msg.msg_flags = user_flags; + + sock = sockfd_lookup(fd, &err); + if (sock != NULL) { + if (sock->file->f_flags & O_NONBLOCK) + kern_msg.msg_flags |= MSG_DONTWAIT; + err = sock_sendmsg(sock, &kern_msg, total_len); + sockfd_put(sock); + } + + /* N.B. Use kfree here, as kern_msg.msg_controllen might change? */ + if(ctl_buf != ctl) + kfree(ctl_buf); +out_freeiov: + if(kern_msg.msg_iov != iov) + kfree(kern_msg.msg_iov); +out: + + PPCDBG(PPCDBG_SYS32, "sys32_sendmsg - exited - pid=%ld, comm=%s \n", current->pid, current->comm); + return err; +} + +static void put_cmsg32(struct msghdr *kmsg, int level, int type, + int len, void *data) +{ + struct cmsghdr32 *cm = (struct cmsghdr32 *) kmsg->msg_control; + struct cmsghdr32 cmhdr; + int cmlen = CMSG32_LEN(len); + + if(cm == NULL || kmsg->msg_controllen < sizeof(*cm)) { + kmsg->msg_flags |= MSG_CTRUNC; + return; + } + + if(kmsg->msg_controllen < cmlen) { + kmsg->msg_flags |= MSG_CTRUNC; + cmlen = kmsg->msg_controllen; + } + cmhdr.cmsg_level = level; + cmhdr.cmsg_type = type; + cmhdr.cmsg_len = cmlen; + + if(copy_to_user(cm, &cmhdr, sizeof cmhdr)) + return; + if(copy_to_user(CMSG32_DATA(cm), data, cmlen - sizeof(struct cmsghdr32))) + return; + cmlen = CMSG32_SPACE(len); + kmsg->msg_control += cmlen; + kmsg->msg_controllen -= cmlen; +} + + +static void scm_detach_fds32(struct msghdr *kmsg, struct scm_cookie *scm) +{ + struct cmsghdr32 *cm = (struct cmsghdr32 *) kmsg->msg_control; + int fdmax = (kmsg->msg_controllen - sizeof(struct cmsghdr32)) / sizeof(int); + int fdnum = scm->fp->count; + struct file **fp = scm->fp->fp; + int *cmfptr; + int err = 0, i; + + if (fdnum < fdmax) + fdmax = fdnum; + + for (i = 0, cmfptr = (int *) CMSG32_DATA(cm); i < fdmax; i++, cmfptr++) { + int new_fd; + err = get_unused_fd(); + if (err < 0) + break; + new_fd = err; + err = put_user(new_fd, cmfptr); + if (err) { + put_unused_fd(new_fd); + break; + } + /* Bump the usage count and install the file. */ + get_file(fp[i]); + fd_install(new_fd, fp[i]); + } + + if (i > 0) { + int cmlen = CMSG32_LEN(i * sizeof(int)); + if (!err) + err = put_user(SOL_SOCKET, &cm->cmsg_level); + if (!err) + err = put_user(SCM_RIGHTS, &cm->cmsg_type); + if (!err) + err = put_user(cmlen, &cm->cmsg_len); + if (!err) { + cmlen = CMSG32_SPACE(i * sizeof(int)); + kmsg->msg_control += cmlen; + kmsg->msg_controllen -= cmlen; + } + } + if (i < fdnum) + kmsg->msg_flags |= MSG_CTRUNC; + + /* + * All of the files that fit in the message have had their + * usage counts incremented, so we just free the list. + */ + __scm_destroy(scm); +} + +/* In these cases we (currently) can just copy to data over verbatim + * because all CMSGs created by the kernel have well defined types which + * have the same layout in both the 32-bit and 64-bit API. One must add + * some special cased conversions here if we start sending control messages + * with incompatible types. + * + * SCM_RIGHTS and SCM_CREDENTIALS are done by hand in recvmsg32 right after + * we do our work. The remaining cases are: + * + * SOL_IP IP_PKTINFO struct in_pktinfo 32-bit clean + * IP_TTL int 32-bit clean + * IP_TOS __u8 32-bit clean + * IP_RECVOPTS variable length 32-bit clean + * IP_RETOPTS variable length 32-bit clean + * (these last two are clean because the types are defined + * by the IPv4 protocol) + * IP_RECVERR struct sock_extended_err + + * struct sockaddr_in 32-bit clean + * SOL_IPV6 IPV6_RECVERR struct sock_extended_err + + * struct sockaddr_in6 32-bit clean + * IPV6_PKTINFO struct in6_pktinfo 32-bit clean + * IPV6_HOPLIMIT int 32-bit clean + * IPV6_FLOWINFO u32 32-bit clean + * IPV6_HOPOPTS ipv6 hop exthdr 32-bit clean + * IPV6_DSTOPTS ipv6 dst exthdr(s) 32-bit clean + * IPV6_RTHDR ipv6 routing exthdr 32-bit clean + * IPV6_AUTHHDR ipv6 auth exthdr 32-bit clean + */ +static void cmsg32_recvmsg_fixup(struct msghdr *kmsg, unsigned long orig_cmsg_uptr) +{ + unsigned char *workbuf, *wp; + unsigned long bufsz, space_avail; + struct cmsghdr *ucmsg; + + bufsz = ((unsigned long)kmsg->msg_control) - orig_cmsg_uptr; + space_avail = kmsg->msg_controllen + bufsz; + wp = workbuf = kmalloc(bufsz, GFP_KERNEL); + if(workbuf == NULL) + goto fail; + + /* To make this more sane we assume the kernel sends back properly + * formatted control messages. Because of how the kernel will truncate + * the cmsg_len for MSG_TRUNC cases, we need not check that case either. + */ + ucmsg = (struct cmsghdr *) orig_cmsg_uptr; + while(((unsigned long)ucmsg) <= + (((unsigned long)kmsg->msg_control) - sizeof(struct cmsghdr))) { + struct cmsghdr32 *kcmsg32 = (struct cmsghdr32 *) wp; + int clen64, clen32; + + /* UCMSG is the 64-bit format CMSG entry in user-space. + * KCMSG32 is within the kernel space temporary buffer + * we use to convert into a 32-bit style CMSG. + */ + __get_user(kcmsg32->cmsg_len, &ucmsg->cmsg_len); + __get_user(kcmsg32->cmsg_level, &ucmsg->cmsg_level); + __get_user(kcmsg32->cmsg_type, &ucmsg->cmsg_type); + + clen64 = kcmsg32->cmsg_len; + copy_from_user(CMSG32_DATA(kcmsg32), CMSG_DATA(ucmsg), + clen64 - CMSG_ALIGN(sizeof(*ucmsg))); + clen32 = ((clen64 - CMSG_ALIGN(sizeof(*ucmsg))) + + CMSG32_ALIGN(sizeof(struct cmsghdr32))); + kcmsg32->cmsg_len = clen32; + + ucmsg = (struct cmsghdr *) (((char *)ucmsg) + CMSG_ALIGN(clen64)); + wp = (((char *)kcmsg32) + CMSG32_ALIGN(clen32)); + } + + /* Copy back fixed up data, and adjust pointers. */ + bufsz = (wp - workbuf); + copy_to_user((void *)orig_cmsg_uptr, workbuf, bufsz); + + kmsg->msg_control = (struct cmsghdr *) + (((char *)orig_cmsg_uptr) + bufsz); + kmsg->msg_controllen = space_avail - bufsz; + + kfree(workbuf); + return; + +fail: + /* If we leave the 64-bit format CMSG chunks in there, + * the application could get confused and crash. So to + * ensure greater recovery, we report no CMSGs. + */ + kmsg->msg_controllen += bufsz; + kmsg->msg_control = (void *) orig_cmsg_uptr; +} + +asmlinkage long sys32_recvmsg(int fd, struct msghdr32* user_msg, unsigned int user_flags) +{ + struct iovec iovstack[UIO_FASTIOV]; + struct msghdr kern_msg; + char addr[MAX_SOCK_ADDR]; + struct socket *sock; + struct iovec *iov = iovstack; + struct sockaddr *uaddr; + int *uaddr_len; + unsigned long cmsg_ptr; + int err, total_len, len = 0; + + PPCDBG(PPCDBG_SYS32, "sys32_recvmsg - entered - pid=%ld, comm=%s\n", current->pid, current->comm); + + if(msghdr_from_user32_to_kern(&kern_msg, user_msg)) + return -EFAULT; + if(kern_msg.msg_iovlen > UIO_MAXIOV) + return -EINVAL; + + uaddr = kern_msg.msg_name; + uaddr_len = &user_msg->msg_namelen; + err = verify_iovec32(&kern_msg, iov, addr, VERIFY_WRITE); + if (err < 0) + goto out; + total_len = err; + + cmsg_ptr = (unsigned long) kern_msg.msg_control; + kern_msg.msg_flags = 0; + + sock = sockfd_lookup(fd, &err); + if (sock != NULL) { + struct scm_cookie scm; + + if (sock->file->f_flags & O_NONBLOCK) + user_flags |= MSG_DONTWAIT; + memset(&scm, 0, sizeof(scm)); + err = sock->ops->recvmsg(sock, &kern_msg, total_len, + user_flags, &scm); + if(err >= 0) { + len = err; + if(!kern_msg.msg_control) { + if(sock->passcred || scm.fp) + kern_msg.msg_flags |= MSG_CTRUNC; + if(scm.fp) + __scm_destroy(&scm); + } else { + /* If recvmsg processing itself placed some + * control messages into user space, it's is + * using 64-bit CMSG processing, so we need + * to fix it up before we tack on more stuff. + */ + if((unsigned long) kern_msg.msg_control != cmsg_ptr) + cmsg32_recvmsg_fixup(&kern_msg, cmsg_ptr); + + /* Wheee... */ + if(sock->passcred) + put_cmsg32(&kern_msg, + SOL_SOCKET, SCM_CREDENTIALS, + sizeof(scm.creds), &scm.creds); + if(scm.fp != NULL) + scm_detach_fds32(&kern_msg, &scm); + } + } + sockfd_put(sock); + } + + if(uaddr != NULL && err >= 0) + err = move_addr_to_user(addr, kern_msg.msg_namelen, uaddr, uaddr_len); + if(cmsg_ptr != 0 && err >= 0) { + unsigned long ucmsg_ptr = ((unsigned long)kern_msg.msg_control); + __kernel_size_t32 uclen = (__kernel_size_t32) (ucmsg_ptr - cmsg_ptr); + err |= __put_user(uclen, &user_msg->msg_controllen); + } + if(err >= 0) + err = __put_user(kern_msg.msg_flags, &user_msg->msg_flags); + if(kern_msg.msg_iov != iov) + kfree(kern_msg.msg_iov); +out: + if(err < 0) + return err; + + PPCDBG(PPCDBG_SYS32, "sys32_recvmsg - exited - pid=%ld, comm=%s\n", current->pid, current->comm); + return len; +} + +/* + * count32() counts the number of arguments/envelopes + */ +static int count32(u32 * argv, int max) +{ + int i = 0; + + if (argv != NULL) { + for (;;) { + u32 p; int error; + + error = get_user(p,argv); + if (error) + return error; + if (!p) + break; + argv++; + if (++i > max) + return -E2BIG; + } + } + return i; +} + +/* + * 'copy_string32()' copies argument/envelope strings from user + * memory to free pages in kernel mem. These are in a format ready + * to be put directly into the top of new user memory. + */ +static int copy_strings32(int argc, u32 * argv, struct linux_binprm *bprm) +{ + while (argc-- > 0) + { + u32 str; + int len; + unsigned long pos; + + if (get_user(str, argv + argc) || + !str || + !(len = strnlen_user((char *)A(str), bprm->p))) + return -EFAULT; + + if (bprm->p < len) + return -E2BIG; + + bprm->p -= len; + + pos = bprm->p; + while (len) { + char *kaddr; + struct page *page; + int offset, bytes_to_copy, new, err; + + offset = pos % PAGE_SIZE; + page = bprm->page[pos / PAGE_SIZE]; + new = 0; + if (!page) { + page = alloc_page(GFP_USER); + bprm->page[pos / PAGE_SIZE] = page; + if (!page) + return -ENOMEM; + new = 1; + } + kaddr = (char *)kmap(page); + + if (new && offset) + memset(kaddr, 0, offset); + bytes_to_copy = PAGE_SIZE - offset; + if (bytes_to_copy > len) { + bytes_to_copy = len; + if (new) + memset(kaddr+offset+len, 0, + PAGE_SIZE-offset-len); + } + + err = copy_from_user(kaddr + offset, (char *)A(str), + bytes_to_copy); + flush_page_to_ram(page); + kunmap((unsigned long)kaddr); + + if (err) + return -EFAULT; + + pos += bytes_to_copy; + str += bytes_to_copy; + len -= bytes_to_copy; + } + } + return 0; +} + +/* + * sys32_execve() executes a new program. + */ +static int do_execve32(char * filename, u32 * argv, u32 * envp, struct pt_regs * regs) +{ + struct linux_binprm bprm; + struct file * file; + int retval; + int i; + + bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); + memset(bprm.page, 0, MAX_ARG_PAGES * sizeof(bprm.page[0])); + + file = open_exec(filename); + + retval = PTR_ERR(file); + if (IS_ERR(file)) + return retval; + + bprm.file = file; + bprm.filename = filename; + bprm.sh_bang = 0; + bprm.loader = 0; + bprm.exec = 0; + if ((bprm.argc = count32(argv, bprm.p / sizeof(u32))) < 0) { + allow_write_access(file); + fput(file); + return bprm.argc; + } + if ((bprm.envc = count32(envp, bprm.p / sizeof(u32))) < 0) { + allow_write_access(file); + fput(file); + return bprm.argc; + } + + retval = prepare_binprm(&bprm); + if (retval < 0) + goto out; + + retval = copy_strings_kernel(1, &bprm.filename, &bprm); + if (retval < 0) + goto out; + + bprm.exec = bprm.p; + retval = copy_strings32(bprm.envc, envp, &bprm); + if (retval < 0) + goto out; + + retval = copy_strings32(bprm.argc, argv, &bprm); + if (retval < 0) + goto out; + + retval = search_binary_handler(&bprm, regs); + if (retval >= 0) + /* execve success */ + return retval; + +out: + /* Something went wrong, return the inode and free the argument pages*/ + allow_write_access(bprm.file); + if (bprm.file) + fput(bprm.file); + + for (i=0 ; ipid, current->comm); + //PPCDBG(PPCDBG_SYS32NI, " a0=%lx, a1=%lx, a2=%lx, a3=%lx, a4=%lx, a5=%lx, regs=%p \n", a0, a1, a2, a3, a4, a5, regs); + } + + filename = getname((char *) a0); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + goto out; + if (regs->msr & MSR_FP) + giveup_fpu(current); + + error = do_execve32(filename, (u32*) a1, (u32*) a2, regs); + + if (error == 0) + current->ptrace &= ~PT_DTRACE; + putname(filename); + +out: + ifppcdebug(PPCDBG_SYS32) + { + udbg_printf("sys32_execve - exited - returning %x - pid=%ld \n", error, current->pid); + //udbg_printf("sys32_execve - at exit - regs->gpr[1]=%lx, gpr[3]=%lx, gpr[4]=%lx, gpr[5]=%lx, gpr[6]=%lx \n", regs->gpr[1], regs->gpr[3], regs->gpr[4], regs->gpr[5], regs->gpr[6]); + } + return error; +} + + + + +/* ld.so expects the auxiliary table to start on a 16-byte boundary, + * so this routine determines if the aux table in the stack meets this + * criteria and if not rearranges it on the stack so it does start on a + * 16-byte boundary. + */ +static inline void shove_aux_table32(unsigned long sp) +{ + int argc; + u_int p = 0; + + unsigned long e; + unsigned long aux_start, offset; + + /* Get the argc value. */ + if (__get_user(argc, (u_int*)sp)) + { + return; + } + + /* Skip over the arguments. */ + sp += sizeof(int) + (argc + 1) * sizeof(u_int); + + /* skip over the environment pointers */ + do + { + /* Get the value of the next 32-bit environment pointer. */ + if (__get_user(p, (u_int*)sp)) + { + return; + } + /* Advance the stack pointer to the address of the next 32-bit pointer. */ + sp += sizeof(u_int); + } while (p != 0); + + aux_start = sp; + /* skip to the end of the auxiliary table */ + do + { + /* Get the value of the next aux table entry. */ + if (__get_user(e, (u_int*)sp)) + { + return; + } + /* Advance the stack pointer to the address of the next aux table entry (from 32-bit pgm). */ + sp += 2 * sizeof(u_int); + } while (e != AT_NULL); + + /* Determine how far we have to move the aux table to get it onto a 16-byte boundary. */ + offset = ((aux_start + 15) & ~15) - aux_start; + + /* Move the aux table to the new 16-byte boundary area (if necessary). */ + if (offset != 0) + { + do + { + sp -= sizeof(u_int); + /* Read the entry into kernel stg then Write the entry back into the stack at the new location. */ + if (__get_user(e, (u_int*)sp) || __put_user(e, (u_int*)(sp + offset))) + { /* either the get or put failed. */ + return; + } + } while (sp > aux_start); + } +} + +/* Set up a thread for executing a new program. */ +void start_thread32(struct pt_regs* regs, unsigned long nip, unsigned long sp) +{ + set_fs(USER_DS); + regs->nip = nip; + regs->gpr[1] = sp; + regs->msr = MSR_USER32; + + shove_aux_table32(sp); + + if (last_task_used_math == current) + last_task_used_math = 0; + current->thread.fpscr = 0; +} + + + +extern asmlinkage int sys_prctl(int option, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5); + +/* Note: it is necessary to treat option as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_prctl(u32 option, u32 arg2, u32 arg3, u32 arg4, u32 arg5) +{ + PPCDBG(PPCDBG_SYS32, "sys32_prctl - running - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return sys_prctl((int)option, + (unsigned long) arg2, + (unsigned long) arg3, + (unsigned long) arg4, + (unsigned long) arg5); +} + + + + +extern asmlinkage int sys_sched_rr_get_interval(pid_t pid, struct timespec *interval); + +/* Note: it is necessary to treat pid as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage int sys32_sched_rr_get_interval(u32 pid, struct timespec32 *interval) +{ + struct timespec t; + int ret; + mm_segment_t old_fs = get_fs (); + + PPCDBG(PPCDBG_SYS32, "sys32_sched_rr_get_interval - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + set_fs (KERNEL_DS); + ret = sys_sched_rr_get_interval((int)pid, &t); + set_fs (old_fs); + if (put_user (t.tv_sec, &interval->tv_sec) || + __put_user (t.tv_nsec, &interval->tv_nsec)) + return -EFAULT; + + PPCDBG(PPCDBG_SYS32, "sys32_sched_rr_get_interval - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + return ret; +} + + + + +extern asmlinkage int sys_pciconfig_read(unsigned long bus, unsigned long dfn, unsigned long off, + unsigned long len, unsigned char *buf); + +asmlinkage int sys32_pciconfig_read(u32 bus, u32 dfn, u32 off, u32 len, u32 ubuf) +{ + + PPCDBG(PPCDBG_SYS32, "sys32_pciconfig_read - running - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); + + return sys_pciconfig_read((unsigned long) bus, + (unsigned long) dfn, + (unsigned long) off, + (unsigned long) len, + (unsigned char *)AA(ubuf)); +} + + + + +extern asmlinkage int sys_pciconfig_write(unsigned long bus, unsigned long dfn, unsigned long off, + unsigned long len, unsigned char *buf); + +asmlinkage int sys32_pciconfig_write(u32 bus, u32 dfn, u32 off, u32 len, u32 ubuf) +{ + + PPCDBG(PPCDBG_SYS32, "sys32_pciconfig_write - running - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + return sys_pciconfig_write((unsigned long) bus, + (unsigned long) dfn, + (unsigned long) off, + (unsigned long) len, + (unsigned char *)AA(ubuf)); +} + +extern asmlinkage int sys_newuname(struct new_utsname * name); + +asmlinkage int ppc64_newuname(struct new_utsname * name) +{ + int ret = sys_newuname(name); + + if (current->personality == PER_LINUX32 && !ret) { + ret = copy_to_user(name->machine, "ppc\0\0", 8); + } + return ret; +} + +extern asmlinkage long sys_personality(unsigned long); + +asmlinkage int sys32_personality(unsigned long personality) +{ + int ret; + if (current->personality == PER_LINUX32 && personality == PER_LINUX) + personality = PER_LINUX32; + ret = sys_personality(personality); + if (ret == PER_LINUX32) + ret = PER_LINUX; + return ret; +} + + + +extern asmlinkage long sys_access(const char * filename, int mode); + +/* Note: it is necessary to treat mode as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_access(const char * filename, u32 mode) +{ + return sys_access(filename, (int)mode); +} + + +extern asmlinkage int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6, struct pt_regs *regs); + +/* Note: it is necessary to treat p1, p2, p3, p4, p5, p7, and regs as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage int sys32_clone(u32 p1, u32 p2, u32 p3, u32 p4, u32 p5, u32 p6, struct pt_regs *regs) +{ + return sys_clone((int)p1, (int)p2, (int)p3, (int)p4, (int)p5, (int)p6, regs); +} + + +extern asmlinkage long sys_creat(const char * pathname, int mode); + +/* Note: it is necessary to treat mode as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_creat(const char * pathname, u32 mode) +{ + return sys_creat(pathname, (int)mode); +} + + +extern asmlinkage long sys_exit(int error_code); + +/* Note: it is necessary to treat error_code as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_exit(u32 error_code) +{ + return sys_exit((int)error_code); +} + + +extern asmlinkage long sys_wait4(pid_t pid, unsigned int * stat_addr, int options, struct rusage * ru); + +/* Note: it is necessary to treat pid and options as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_wait4(u32 pid, unsigned int * stat_addr, u32 options, struct rusage * ru) +{ + PPCDBG(PPCDBG_SYS32, "sys32_wait4 - running - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + if (!ru) + return sys_wait4((int)pid, stat_addr, options, NULL); + else { + struct rusage r; + int ret; + unsigned int status; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_wait4((int)pid, stat_addr ? &status : NULL, options, &r); + set_fs (old_fs); + if (put_rusage (ru, &r)) return -EFAULT; + if (stat_addr && put_user (status, stat_addr)) + return -EFAULT; + return ret; + } + +} + + +extern asmlinkage long sys_waitpid(pid_t pid, unsigned int * stat_addr, int options); + +/* Note: it is necessary to treat pid and options as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_waitpid(u32 pid, unsigned int * stat_addr, u32 options) +{ + return sys_waitpid((int)pid, stat_addr, (int)options); +} + + +extern asmlinkage int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6, struct pt_regs *regs); + +/* Note: it is necessary to treat p1, p2, p3, p4, p5, and p6 as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage int sys32_fork(u32 p1, u32 p2, u32 p3, u32 p4, u32 p5, u32 p6, struct pt_regs *regs) +{ + return sys_fork((int)p1, (int)p2, (int)p3, (int)p4, (int)p5, (int)p6, regs); +} + + +extern asmlinkage long sys_getgroups(int gidsetsize, gid_t *grouplist); + +/* Note: it is necessary to treat gidsetsize as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_getgroups(u32 gidsetsize, gid_t *grouplist) +{ + return sys_getgroups((int)gidsetsize, grouplist); +} + + +extern asmlinkage long sys_getpgid(pid_t pid); + +/* Note: it is necessary to treat pid as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_getpgid(u32 pid) +{ + return sys_getpgid((int)pid); +} + + +extern asmlinkage long sys_getpriority(int which, int who); + +/* Note: it is necessary to treat which and who as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_getpriority(u32 which, u32 who) +{ + return sys_getpriority((int)which, (int)who); +} + + +extern asmlinkage long sys_getsid(pid_t pid); + +/* Note: it is necessary to treat pid as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_getsid(u32 pid) +{ + return sys_getsid((int)pid); +} + + +extern asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on); + +/* Note: it is necessary to treat on as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_ioperm(unsigned long from, unsigned long num, u32 on) +{ + return sys_ioperm(from, num, (int)on); +} + + +extern asmlinkage int sys_iopl(int a1, int a2, int a3, int a4); + +/* Note: it is necessary to treat a1, a2, a3, and a4 as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage int sys32_iopl(u32 a1, u32 a2, u32 a3, u32 a4) +{ + return sys_iopl((int)a1, (int)a2, (int)a3, (int)a4); +} + + +extern asmlinkage long sys_kill(int pid, int sig); + +/* Note: it is necessary to treat pid and sig as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_kill(u32 pid, u32 sig) +{ + return sys_kill((int)pid, (int)sig); +} + + +extern asmlinkage long sys_mkdir(const char * pathname, int mode); + +/* Note: it is necessary to treat mode as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_mkdir(const char * pathname, u32 mode) +{ + return sys_mkdir(pathname, (int)mode); +} + + +extern asmlinkage long sys_mlockall(int flags); + +/* Note: it is necessary to treat flags as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_mlockall(u32 flags) +{ + return sys_mlockall((int)flags); +} + + +extern asmlinkage int sys_modify_ldt(int a1, int a2, int a3, int a4); + +/* Note: it is necessary to treat a1, a2, a3, and a4 as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage int sys32_modify_ldt(u32 a1, u32 a2, u32 a3, u32 a4) +{ + return sys_modify_ldt((int)a1, (int)a2, (int)a3, (int)a4); +} + + +extern asmlinkage long sys_msync(unsigned long start, size_t len, int flags); + +/* Note: it is necessary to treat flags as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_msync(unsigned long start, size_t len, u32 flags) +{ + return sys_msync(start, len, (int)flags); +} + + +extern asmlinkage long sys_nice(int increment); + +/* Note: it is necessary to treat increment as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_nice(u32 increment) +{ + return sys_nice((int)increment); +} + + +extern asmlinkage long sys_open(const char * filename, int flags, int mode); + +/* Note: it is necessary to treat flags and mode as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_open(const char * filename, int flags, int mode) +{ + return sys_open(filename, (int)flags, (int)mode); +} + + +extern asmlinkage long sys_readlink(const char * path, char * buf, int bufsiz); + +/* Note: it is necessary to treat bufsiz as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_readlink(const char * path, char * buf, u32 bufsiz) +{ + return sys_readlink(path, buf, (int)bufsiz); +} + + +extern asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void * arg); + +/* Note: it is necessary to treat magic1 and magic2 as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_reboot(u32 magic1, u32 magic2, unsigned int cmd, void * arg) +{ + return sys_reboot((int)magic1, (int)magic2, cmd, arg); +} + + +extern asmlinkage long sys_sched_get_priority_max(int policy); + +/* Note: it is necessary to treat option as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sched_get_priority_max(u32 policy) +{ + return sys_sched_get_priority_max((int)policy); +} + + +extern asmlinkage long sys_sched_get_priority_min(int policy); + +/* Note: it is necessary to treat policy as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sched_get_priority_min(u32 policy) +{ + return sys_sched_get_priority_min((int)policy); +} + + +extern asmlinkage long sys_sched_getparam(pid_t pid, struct sched_param *param); + +/* Note: it is necessary to treat pid as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sched_getparam(u32 pid, struct sched_param *param) +{ + return sys_sched_getparam((int)pid, param); +} + + +extern asmlinkage long sys_sched_getscheduler(pid_t pid); + +/* Note: it is necessary to treat pid as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sched_getscheduler(u32 pid) +{ + return sys_sched_getscheduler((int)pid); +} + + +extern asmlinkage long sys_sched_setparam(pid_t pid, struct sched_param *param); + +/* Note: it is necessary to treat pid as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sched_setparam(u32 pid, struct sched_param *param) +{ + return sys_sched_setparam((int)pid, param); +} + + +extern asmlinkage long sys_sched_setscheduler(pid_t pid, int policy, struct sched_param *param); + +/* Note: it is necessary to treat pid and policy as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sched_setscheduler(u32 pid, u32 policy, struct sched_param *param) +{ + return sys_sched_setscheduler((int)pid, (int)policy, param); +} + + +extern asmlinkage long sys_setdomainname(char *name, int len); + +/* Note: it is necessary to treat len as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_setdomainname(char *name, u32 len) +{ + return sys_setdomainname(name, (int)len); +} + + +extern asmlinkage long sys_setgroups(int gidsetsize, gid_t *grouplist); + +/* Note: it is necessary to treat gidsetsize as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_setgroups(u32 gidsetsize, gid_t *grouplist) +{ + return sys_setgroups((int)gidsetsize, grouplist); +} + + +extern asmlinkage long sys_sethostname(char *name, int len); + +/* Note: it is necessary to treat len as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_sethostname(char *name, u32 len) +{ + return sys_sethostname(name, (int)len); +} + + +extern asmlinkage long sys_setpgid(pid_t pid, pid_t pgid); + +/* Note: it is necessary to treat pid and pgid as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_setpgid(u32 pid, u32 pgid) +{ + return sys_setpgid((int)pid, (int)pgid); +} + + +extern asmlinkage long sys_setpriority(int which, int who, int niceval); + +/* Note: it is necessary to treat which, who, and niceval as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_setpriority(u32 which, u32 who, u32 niceval) +{ + return sys_setpriority((int)which, (int)who, (int)niceval); +} + + +extern asmlinkage long sys_ssetmask(int newmask); + +/* Note: it is necessary to treat newmask as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_ssetmask(u32 newmask) +{ + return sys_ssetmask((int) newmask); +} + + +extern asmlinkage long sys_swapon(const char * specialfile, int swap_flags); + +/* Note: it is necessary to treat swap_flags as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_swapon(const char * specialfile, u32 swap_flags) +{ + return sys_swapon(specialfile, (int)swap_flags); +} + + +extern asmlinkage long sys_syslog(int type, char * buf, int len); + +/* Note: it is necessary to treat type and len as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_syslog(u32 type, char * buf, u32 len) +{ + return sys_syslog((int)type, buf, (int)len); +} + + +extern asmlinkage long sys_umask(int mask); + +/* Note: it is necessary to treat mask as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_umask(u32 mask) +{ + return sys_umask((int)mask); +} + + +extern asmlinkage long sys_umount(char * name, int flags); + +/* Note: it is necessary to treat flags as an unsigned int, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage long sys32_umount(char * name, u32 flags) +{ + return sys_umount(name, (int)flags); +} + + +extern asmlinkage int sys_vfork(int p1, int p2, int p3, int p4, int p5, int p6, struct pt_regs *regs); + +/* Note: it is necessary to treat p1, p2, p3, p4, p5, and p6 as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage int sys32_vfork(u32 p1, u32 p2, u32 p3, u32 p4, u32 p5, u32 p6, struct pt_regs *regs) +{ + return sys_vfork((int)p1, (int)p2, (int)p3, (int)p4, (int)p5, (int)p6, regs); +} + + +extern asmlinkage int sys_vm86(int a1, int a2, int a3, int a4); + +/* Note: it is necessary to treat a1, a2, a3, and a4 as unsigned ints, + * with the corresponding cast to a signed int to insure that the + * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) + * and the register representation of a signed int (msr in 64-bit mode) is performed. + */ +asmlinkage int sys32_vm86(u32 a1, u32 a2, u32 a3, u32 a4) +{ + return sys_vm86((int)a1, (int)a2, (int)a3, (int)a4); +} + + + + + +extern asmlinkage ssize_t sys_pread(unsigned int fd, char * buf, + size_t count, loff_t pos); + +extern asmlinkage ssize_t sys_pwrite(unsigned int fd, const char * buf, + size_t count, loff_t pos); + +typedef __kernel_ssize_t32 ssize_t32; + +asmlinkage ssize_t32 sys32_pread(unsigned int fd, char *ubuf, + __kernel_size_t32 count, u32 reg6, u32 poshi, u32 poslo) +{ + return sys_pread(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo)); +} + +asmlinkage ssize_t32 sys32_pwrite(unsigned int fd, char *ubuf, + __kernel_size_t32 count, u32 reg6 ,u32 poshi, u32 poslo) +{ + return sys_pwrite(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo)); +} + + +extern asmlinkage long sys_truncate(const char * path, unsigned long length); +extern asmlinkage long sys_ftruncate(unsigned int fd, unsigned long length); + +asmlinkage int sys32_truncate64(const char * path, u32 reg4, unsigned long high, unsigned long low) +{ + if ((int)high < 0) + return -EINVAL; + else + return sys_truncate(path, (high << 32) | low); +} + +asmlinkage int sys32_ftruncate64(unsigned int fd, u32 reg4, unsigned long high, unsigned long low) +{ + if ((int)high < 0) + return -EINVAL; + else + return sys_ftruncate(fd, (high << 32) | low); +} + + + +asmlinkage long sys32_fcntl64(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + if (cmd >= F_GETLK64 && cmd <= F_SETLKW64) + return sys_fcntl(fd, cmd + F_GETLK - F_GETLK64, arg); + return sys32_fcntl(fd, cmd, arg); +} + + + + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/syscalls.c linuxppc64_2_4/arch/ppc64/kernel/syscalls.c --- linux-2.4.5-ac18/arch/ppc64/kernel/syscalls.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/syscalls.c Sun Jun 24 22:00:18 2001 @@ -0,0 +1,288 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +void +check_bugs(void) +{ +} + +asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on) +{ + printk(KERN_ERR "sys_ioperm()\n"); + return -EIO; +} + +int sys_iopl(int a1, int a2, int a3, int a4) +{ + printk(KERN_ERR "sys_iopl(%x, %x, %x, %x)!\n", a1, a2, a3, a4); + return (-ENOSYS); +} + +int sys_vm86(int a1, int a2, int a3, int a4) +{ + printk(KERN_ERR "sys_vm86(%x, %x, %x, %x)!\n", a1, a2, a3, a4); + return (-ENOSYS); +} + +int sys_modify_ldt(int a1, int a2, int a3, int a4) +{ + printk(KERN_ERR "sys_modify_ldt(%x, %x, %x, %x)!\n", a1, a2, a3, a4); + return (-ENOSYS); +} + +/* + * sys_ipc() is the de-multiplexer for the SysV IPC calls.. + * + * This is really horribly ugly. + */ +asmlinkage int +sys_ipc (uint call, int first, int second, long third, void *ptr, long fifth) +{ + int version, ret; + + PPCDBG(PPCDBG_SYS64X, "sys_ipc - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + + 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; + } + 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; + } + default: + ret = sys_msgrcv (first, (struct msgbuf *) ptr, + second, fifth, third); + break; + } + 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 */ + if (!segment_eq(get_fs(), get_ds())) + break; + ret = sys_shmat (first, (char *) ptr, second, + (ulong *) third); + break; + } + 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; + } + + + PPCDBG(PPCDBG_SYS64X, "sys_ipc - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + return ret; +} + +/* + * 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(int *fildes) +{ + int fd[2]; + int error; + + PPCDBG(PPCDBG_SYS64X, "sys_pipe - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + error = do_pipe(fd); + if (!error) { + if (copy_to_user(fildes, fd, 2*sizeof(int))) + error = -EFAULT; + } + + PPCDBG(PPCDBG_SYS64X, "sys_pipe - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + return error; +} + +asmlinkage unsigned long sys_mmap(unsigned long addr, size_t len, + unsigned long prot, unsigned long flags, + unsigned long fd, off_t offset) +{ + struct file * file = NULL; + unsigned long ret = -EBADF; + + PPCDBG(PPCDBG_SYS64X, "sys_mmap - entered - addr=%lx, len=%lx - pid=%ld, comm=%s \n", addr, len, current->pid, current->comm); + + if (!(flags & MAP_ANONYMOUS)) { + if (!(file = fget(fd))) + goto out; + } + + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + down_write(¤t->mm->mmap_sem); + ret = do_mmap(file, addr, len, prot, flags, offset); + up_write(¤t->mm->mmap_sem); + if (file) + fput(file); + +out: + + PPCDBG(PPCDBG_SYS64X, "sys_mmap - exited - ret=%x \n", ret); + + return ret; +} + +asmlinkage int sys_pause(void) +{ + + PPCDBG(PPCDBG_SYS64X, "sys_pause - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + current->state = TASK_INTERRUPTIBLE; + schedule(); + + PPCDBG(PPCDBG_SYS64X, "sys_pause - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + return -ERESTARTNOHAND; +} + +static int __init set_fakeppc(char *str) +{ + if (*str) + return 0; + init_task.personality = PER_LINUX32; + return 1; +} +__setup("fakeppc", set_fakeppc); + +asmlinkage int sys_uname(struct old_utsname * name) +{ + int err = -EFAULT; + + PPCDBG(PPCDBG_SYS64X, "sys_uname - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + down_read(&uts_sem); + if (name && !copy_to_user(name, &system_utsname, sizeof (*name))) + err = 0; + up_read(&uts_sem); + + PPCDBG(PPCDBG_SYS64X, "sys_uname - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + return err; +} + +asmlinkage int sys_olduname(struct oldold_utsname * name) +{ + int error; + + PPCDBG(PPCDBG_SYS64X, "sys_olduname - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + + if (!name) + return -EFAULT; + if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname))) + return -EFAULT; + + down_read(&uts_sem); + 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); + up_read(&uts_sem); + + error = error ? -EFAULT : 0; + + PPCDBG(PPCDBG_SYS64X, "sys_olduname - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); + return error; +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/time.c linuxppc64_2_4/arch/ppc64/kernel/time.c --- linux-2.4.5-ac18/arch/ppc64/kernel/time.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/time.c Wed Jul 18 03:07:24 2001 @@ -0,0 +1,472 @@ +/* + * + * 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. + * MPC8xx/MBX changes by Dan Malek (dmalek@jlc.net). + * Converted for 64-bit by Mike Corrigan + * + * First round of bugfixes by Gabriel Paubert (paubert@iram.es) + * to make clock more stable (2.4.0-test5). The only thing + * that this code assumes is that the timebases have been synchronized + * by firmware on SMP and are never stopped (never do sleep + * on SMP then, nap and doze are OK). + * + * TODO (not necessarily in this file): + * - improve precision and reproducibility of timebase frequency + * measurement at boot time. + * - get rid of xtime_lock for gettimeofday (generic kernel problem + * to be implemented on all architectures for SMP scalability and + * eventually implementing gettimeofday without entering the kernel). + * - put all time/clock related variables in a single structure + * to minimize number of cache lines touched by gettimeofday() + * - for astronomical applications: add a new function to get + * non ambiguous timestamps even around leap seconds. This needs + * a new timestamp format and a good name. + * + * + * The following comment is partially obsolete (at least the long wait + * is no more a valid reason): + * Since the MPC8xx has a programmable interrupt timer, I decided to + * use that rather than the decrementer. Two reasons: 1.) the clock + * frequency is low, causing 2.) a long wait in the timer interrupt + * while ((d = get_dec()) == dval) + * loop. The MPC8xx can be driven from a variety of input clocks, + * so a number of assumptions have been made here because the kernel + * parameter HZ is a constant. We assume (correctly, today :-) that + * the MPC8xx on the MBX board is driven from a 32.768 kHz crystal. + * This is then divided by 4, providing a 8192 Hz clock into the PIT. + * Since it is not possible to get a nice 100 Hz clock out of this, without + * creating a software PLL, I have set HZ to 128. -- Dan + * + * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 + * "A Kernel Model for Precision Timekeeping" by Dave Mills + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +void smp_local_timer_interrupt(struct pt_regs *); + +/* keep track of when we need to update the rtc */ +time_t last_rtc_update; +extern rwlock_t xtime_lock; +extern int piranha_simulator; + +unsigned long tb_ticks_per_jiffy; +unsigned long tb_ticks_per_usec; +unsigned long tb_to_us; +unsigned long tb_last_stamp; +spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; + +extern unsigned long wall_jiffies; + +static long time_offset = 0; + +extern unsigned long prof_cpu_mask; +extern unsigned int * prof_buffer; +extern unsigned long prof_len; +extern unsigned long prof_shift; +extern char _stext; + +static inline void ppc_do_profile (unsigned long nip) +{ + if (!prof_buffer) + return; + + /* + * Only measure the CPUs specified by /proc/irq/prof_cpu_mask. + * (default is all CPUs.) + */ + if (!((1<>= prof_shift; + /* + * Don't ignore out-of-bounds EIP values silently, + * put them into the last histogram slot, so if + * present, they will show up as a sharp peak. + */ + if (nip > prof_len-1) + nip = prof_len-1; + atomic_inc((atomic_t *)&prof_buffer[nip]); +} + +/* + * For iSeries shared processors, we have to let the hypervisor + * set the hardware decrementer. We set a virtual decrementer + * in the ItLpPaca and call the hypervisor if the virtual + * decrementer is less than the current value in the hardware + * decrementer. (almost always the new decrementer value will + * be greater than the current hardware decementer so the hypervisor + * call will not be needed) + * + * When we yield the processor in idle.c (needed for shared processors) + * we cannot yield for too long or the 32-bit tbl may wrap and then + * we would lose jiffies. In addition, if we yield for too long, + * we might be pretty far off on timing for device drivers and such. + * When the hypervisor returns to us after a yield we need to + * determine whether decrementers might have been missed. If so + * we need to drive the timer_interrupt code to catch up (using + * the tb) + * + */ + + + +/* + * timer_interrupt - gets called when the decrementer overflows, + * with interrupts disabled. + * We set it up to overflow again in 1/HZ seconds. + */ +int timer_interrupt(struct pt_regs * regs) +{ + int next_dec; + unsigned long cpu = smp_processor_id(); + unsigned long jiffy_stamp = last_jiffy_stamp(cpu); + + irq_enter(cpu); + + if (!user_mode(regs)) + ppc_do_profile(instruction_pointer(regs)); + + do { + jiffy_stamp += tb_ticks_per_jiffy; + if (smp_processor_id()) continue; + /* We are in an interrupt, no need to save/restore flags */ + write_lock(&xtime_lock); + tb_last_stamp = jiffy_stamp; + do_timer(regs); + + /* + * update the rtc when needed, this should be performed on the + * right fraction of a second. Half or full second ? + * Full second works on mk48t59 clocks, others need testing. + * Note that this update is basically only used through + * the adjtimex system calls. Setting the HW clock in + * any other way is a /dev/rtc and userland business. + * This is still wrong by -0.5/+1.5 jiffies because of the + * timer interrupt resolution and possible delay, but here we + * hit a quantization limit which can only be solved by higher + * resolution timers and decoupling time management from timer + * interrupts. This is also wrong on the clocks + * which require being written at the half second boundary. + * We should have an rtc call that only sets the minutes and + * seconds like on Intel to avoid problems with non UTC clocks. + */ + if ( (time_status & STA_UNSYNC) == 0 && + xtime.tv_sec - last_rtc_update >= 659 && + abs(xtime.tv_usec - (1000000-1000000/HZ)) < 500000/HZ && + jiffies - wall_jiffies == 1) { + if (ppc_md.set_rtc_time(xtime.tv_sec+1 + time_offset) == 0) + last_rtc_update = xtime.tv_sec+1; + else + /* Try again one minute later */ + last_rtc_update += 60; + } + write_unlock(&xtime_lock); + } while((next_dec = tb_ticks_per_jiffy - tb_ticks_since(jiffy_stamp)) < 0); +/* } while((next_dec = tb_ticks_per_jiffy - tb_delta(&jiffy_stamp)) < 0); */ + set_dec(next_dec); + last_jiffy_stamp(cpu) = jiffy_stamp; + +#ifdef CONFIG_SMP + smp_local_timer_interrupt(regs); +#endif + + if (ppc_md.heartbeat && !ppc_md.heartbeat_count--) + ppc_md.heartbeat(); + + irq_exit(cpu); + return 1; /* lets ret_from_int know we can do checks */ +} + +/* + * This version of gettimeofday has microsecond resolution. + */ +void do_gettimeofday(struct timeval *tv) +{ + unsigned long flags; + unsigned long delta, lost_ticks, usec, sec; + + read_lock_irqsave(&xtime_lock, flags); + sec = xtime.tv_sec; + usec = xtime.tv_usec; + /* tb_last_stamp is the last simulated tb value computed + * during a decrementer interrupt on processor 0 + */ + delta = tb_ticks_since(tb_last_stamp); +#ifdef CONFIG_SMP + /* As long as timebases are not in sync, gettimeofday can only + * have jiffy resolution on SMP. (except iSeries where the + * timebases are synchronized by the hypervisor) + */ + if (_machine != _MACH_iSeries) + delta = 0; +#endif /* CONFIG_SMP */ + lost_ticks = jiffies - wall_jiffies; + read_unlock_irqrestore(&xtime_lock, flags); + + /* Convert the lost ticks into micro-seconds. */ + usec += ( ( tb_ticks_per_jiffy * lost_ticks ) + delta ) / tb_ticks_per_usec; +/* usec += mulhwu(tb_to_us, tb_ticks_per_jiffy * lost_ticks + delta); */ + while (usec > 1000000) { + sec++; + usec -= 1000000; + } + tv->tv_sec = sec; + tv->tv_usec = usec; +} + +void do_settimeofday(struct timeval *tv) +{ + unsigned long flags; + long int tb_delta, new_usec, new_sec; + + write_lock_irqsave(&xtime_lock, flags); + /* Updating the RTC is not the job of this code. If the time is + * stepped under NTP, the RTC will be update after STA_UNSYNC + * is cleared. Tool like clock/hwclock either copy the RTC + * to the system time, in which case there is no point in writing + * to the RTC again, or write to the RTC but then they don't call + * settimeofday to perform this operation. Note also that + * we don't touch the decrementer since: + * a) it would lose timer interrupt synchronization on SMP + * (if it is working one day) + * b) it could make one jiffy spuriously shorter or longer + * which would introduce another source of uncertainty potentially + * harmful to relatively short timers. + */ + + /* This works perfectly on SMP only if the tb are in sync but + * guarantees an error < 1 jiffy even if they are off by eons, + * still reasonable when gettimeofday resolution is 1 jiffy. + */ + tb_delta = tb_ticks_since(last_jiffy_stamp(smp_processor_id())); + tb_delta += (jiffies - wall_jiffies) * tb_ticks_per_jiffy; + new_sec = tv->tv_sec; + new_usec = tv->tv_usec - tb_delta / tb_ticks_per_usec; +/* new_usec = tv->tv_usec - mulhwu(tb_to_us, tb_delta); */ + while (new_usec <0) { + new_sec--; + new_usec += 1000000; + } + xtime.tv_usec = new_usec; + xtime.tv_sec = new_sec; + + /* In case of a large backwards jump in time with NTP, we want the + * clock to be updated as soon as the PLL is again in lock. + */ + last_rtc_update = new_sec - 658; + + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_state = TIME_ERROR; /* p. 24, (a) */ + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; + write_unlock_irqrestore(&xtime_lock, flags); +} + +extern int do_sys_settimeofday(struct timeval *tv, struct timezone *tz); + +void __init time_init(void) +{ + time_t sec, old_sec; + unsigned long old_stamp, stamp, elapsed; + /* This function is only called on the boot processor */ + unsigned long flags; + + if (ppc_md.time_init != NULL) + time_offset = ppc_md.time_init(); + + ppc_md.calibrate_decr(); + + /* Now that the decrementer is calibrated, it can be used in case the + * clock is stuck, but the fact that we have to handle the 601 + * makes things more complex. Repeatedly read the RTC until the + * next second boundary to try to achieve some precision... + */ + stamp = get_tb(); + sec = ppc_md.get_rtc_time(); + elapsed = 0; + if ( ! piranha_simulator ) { + do { + old_stamp = stamp; + old_sec = sec; + stamp = get_tb(); + elapsed += stamp - old_stamp; + sec = ppc_md.get_rtc_time(); + } while ( sec == old_sec && elapsed < 2*HZ*tb_ticks_per_jiffy); + if (sec==old_sec) { + printk("Warning: real time clock seems stuck!\n"); + } + } + write_lock_irqsave(&xtime_lock, flags); + xtime.tv_sec = sec; + last_jiffy_stamp(0) = tb_last_stamp = stamp; + xtime.tv_usec = 0; + /* No update now, we just read the time from the RTC ! */ + last_rtc_update = xtime.tv_sec; + write_unlock_irqrestore(&xtime_lock, flags); + /* Not exact, but the timer interrupt takes care of this */ + set_dec(tb_ticks_per_jiffy); + + /* If platform provided a timezone (pmac), we correct the time + * using do_sys_settimeofday() which in turn calls warp_clock() + */ + if (time_offset) { + struct timezone tz; + tz.tz_minuteswest = -time_offset / 60; + tz.tz_dsttime = 0; + do_sys_settimeofday(NULL, &tz); + } +} + +#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 +}; + +/* + * This only works for the Gregorian calendar - i.e. after 1752 (in the UK) + */ +void GregorianDay(struct rtc_time * tm) +{ + int leapsToDate; + int lastYear; + int day; + int MonthOffset[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + + lastYear=tm->tm_year-1; + + /* + * Number of leap corrections to apply up to end of last year + */ + leapsToDate = lastYear/4 - lastYear/100 + lastYear/400; + + /* + * This year is a leap year if it is divisible by 4 except when it is + * divisible by 100 unless it is divisible by 400 + * + * e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 will be + */ + if((tm->tm_year%4==0) && + ((tm->tm_year%100!=0) || (tm->tm_year%400==0)) && + (tm->tm_mon>2)) + { + /* + * We are past Feb. 29 in a leap year + */ + day=1; + } + else + { + day=0; + } + + day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] + + tm->tm_mday; + + tm->tm_wday=day%7; +} + +void to_tm(int tim, struct rtc_time * tm) +{ + register int i; + 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; + + /* + * Determine the day of week + */ + GregorianDay(tm); +} + +/* Auxiliary function to compute scaling factors */ +/* Actually the choice of a timebase running at 1/4 the of the bus + * frequency giving resolution of a few tens of nanoseconds is quite nice. + * It makes this computation very precise (27-28 bits typically) which + * is optimistic considering the stability of most processor clock + * oscillators and the precision with which the timebase frequency + * is measured but does not harm. + */ +/*unsigned mulhwu_scale_factor(unsigned inscale, unsigned outscale) { */ +/* unsigned mlt=0, tmp, err; */ + /* No concern for performance, it's done once: use a stupid + * but safe and compact method to find the multiplier. + */ +/* + for (tmp = 1U<<31; tmp != 0; tmp >>= 1) { + if (mulhwu(inscale, mlt|tmp) < outscale) mlt|=tmp; + } +*/ + /* We might still be off by 1 for the best approximation. + * A side effect of this is that if outscale is too large + * the returned value will be zero. + * Many corner cases have been checked and seem to work, + * some might have been forgotten in the test however. + */ +/* + err = inscale*(mlt+1); + if (err <= inscale/2) mlt++; + return mlt; + } +*/ diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/time.h linuxppc64_2_4/arch/ppc64/kernel/time.h --- linux-2.4.5-ac18/arch/ppc64/kernel/time.h Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/time.h Fri May 4 17:13:59 2001 @@ -0,0 +1 @@ + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/traps.c linuxppc64_2_4/arch/ppc64/kernel/traps.c --- linux-2.4.5-ac18/arch/ppc64/kernel/traps.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/traps.c Fri Jul 13 11:47:34 2001 @@ -0,0 +1,368 @@ +/* + * linux/arch/ppc/kernel/traps.c + * + * 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) + */ + +/* + * This file handles the architecture-dependent parts of hardware exceptions + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_KDB +#include +#endif + +#include +#include +#include +#include +#include +#include + +extern int fix_alignment(struct pt_regs *); +extern void bad_page_fault(struct pt_regs *, unsigned long); + +#ifdef CONFIG_XMON +extern void xmon(struct pt_regs *regs); +extern int xmon_bpt(struct pt_regs *regs); +extern int xmon_sstep(struct pt_regs *regs); +extern int xmon_iabr_match(struct pt_regs *regs); +extern int xmon_dabr_match(struct pt_regs *regs); +extern void (*xmon_fault_handler)(struct pt_regs *regs); +#endif + +#ifdef CONFIG_XMON +void (*debugger)(struct pt_regs *regs) = xmon; +int (*debugger_bpt)(struct pt_regs *regs) = xmon_bpt; +int (*debugger_sstep)(struct pt_regs *regs) = xmon_sstep; +int (*debugger_iabr_match)(struct pt_regs *regs) = xmon_iabr_match; +int (*debugger_dabr_match)(struct pt_regs *regs) = xmon_dabr_match; +void (*debugger_fault_handler)(struct pt_regs *regs); +#else +#ifdef CONFIG_KGDB +void (*debugger)(struct pt_regs *regs); +int (*debugger_bpt)(struct pt_regs *regs); +int (*debugger_sstep)(struct pt_regs *regs); +int (*debugger_iabr_match)(struct pt_regs *regs); +int (*debugger_dabr_match)(struct pt_regs *regs); +void (*debugger_fault_handler)(struct pt_regs *regs); +#endif +#endif +/* + * Trap & Exception support + */ + +void +_exception(int signr, struct pt_regs *regs) +{ + if (!user_mode(regs)) + { + show_regs(regs); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + debugger(regs); +#endif +#if defined(CONFIG_KDB) + kdb(KDB_REASON_OOPS, 0, (kdb_eframe_t) regs); +#endif + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Exception in kernel pc %lx signal %d",regs->nip,signr); +#if defined(CONFIG_PPCDBG) && (defined(CONFIG_XMON) || defined(CONFIG_KGDB)) + /* Allow us to catch SIGILLs for 64-bit app/glibc debugging. -Peter */ + } else if (signr == SIGILL) { + ifppcdebug(PPCDBG_SIGNALXMON) + debugger(regs); +#endif + } + force_sig(signr, current); +} + +void +SystemResetException(struct pt_regs *regs) +{ + udbg_printf("System Reset in kernel mode.\n"); + printk("System Reset in kernel mode.\n"); +#if defined(CONFIG_XMON) + xmon(regs); +#endif + for(;;); +} + + +void +MachineCheckException(struct pt_regs *regs) +{ + if ( !user_mode(regs) ) + { +#if defined(CONFIG_8xx) && defined(CONFIG_PCI) + /* the qspan pci read routines can cause machine checks -- Cort */ + bad_page_fault(regs, regs->dar); + return; +#endif +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_fault_handler) { + debugger_fault_handler(regs); + return; + } +#endif +#ifdef CONFIG_KDB + if (kdb(KDB_REASON_FAULT, 0, regs)) + return; +#endif + printk("Machine check in kernel mode.\n"); + printk("Caused by (from SRR1=%lx): ", regs->msr); + switch (regs->msr & 0xF0000) { + case 0x80000: + printk("Machine check signal\n"); + break; + case 0x40000: + printk("Transfer error ack signal\n"); + break; + case 0x20000: + printk("Data parity error signal\n"); + break; + case 0x10000: + printk("Address parity error signal\n"); + break; + default: + printk("Unknown values in msr\n"); + } + show_regs(regs); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + debugger(regs); +#endif +#ifdef CONFIG_KDB + if (kdb(KDB_REASON_FAULT, 0, regs)) + return ; +#endif + print_backtrace((unsigned long *)regs->gpr[1]); + panic("machine check"); + } + _exception(SIGSEGV, regs); +} + +void +SMIException(struct pt_regs *regs) +{ +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + { + debugger(regs); + return; + } +#endif +#ifdef CONFIG_KDB + { + kdb(KDB_REASON_OOPS, 0, regs); + return; + } +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("System Management Interrupt"); +} + +void +UnknownException(struct pt_regs *regs) +{ + printk("Bad trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); + _exception(SIGTRAP, regs); +} + +void +InstructionBreakpoint(struct pt_regs *regs) +{ +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_iabr_match(regs)) + return; +#endif +#ifdef CONFIG_KDB + if (kdb(KDB_REASON_BREAK, 0, regs)) + return ; +#endif + _exception(SIGTRAP, regs); +} + +void +RunModeException(struct pt_regs *regs) +{ + _exception(SIGTRAP, regs); +} + +void +ProgramCheckException(struct pt_regs *regs) +{ +#if defined(CONFIG_4xx) + unsigned int esr = mfspr(SPRN_ESR); + + if (esr & ESR_PTR) { +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_bpt(regs)) + return; +#endif +#ifdef CONFIG_KDB + if (kdb(KDB_REASON_BREAK, 0, regs)) + return; +#endif + _exception(SIGTRAP, regs); + } else { + _exception(SIGILL, regs); + } +#else + if (regs->msr & 0x100000) { + /* IEEE FP exception */ + _exception(SIGFPE, regs); + } else if (regs->msr & 0x20000) { + /* trap exception */ +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_bpt(regs)) + return; +#endif +#ifdef CONFIG_KDB + if (kdb(KDB_REASON_BREAK, 0, regs)) + return; +#endif + _exception(SIGTRAP, regs); + } else { + _exception(SIGILL, regs); + } +#endif +} + +void +SingleStepException(struct pt_regs *regs) +{ + regs->msr &= ~MSR_SE; /* Turn off 'trace' bit */ +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_sstep(regs)) + return; +#endif +#ifdef CONFIG_KDB + if (kdb(KDB_REASON_DEBUG, 0, regs)) + return; +#endif + _exception(SIGTRAP, regs); +} + +/* Dummy handler for Performance Monitor */ + +void +PerformanceMonitorException(struct pt_regs *regs) +{ + _exception(SIGTRAP, regs); +} + +void +AlignmentException(struct pt_regs *regs) +{ + int fixed; + + fixed = fix_alignment(regs); + if (fixed == 1) { + ifppcdebug(PPCDBG_ALIGNFIXUP) + if (!user_mode(regs)) + PPCDBG(PPCDBG_ALIGNFIXUP, "fix alignment at %lx\n", regs->nip); + regs->nip += 4; /* skip over emulated instruction */ + return; + } + if (fixed == -EFAULT) { + /* fixed == -EFAULT means the operand address was bad */ + if (user_mode(regs)) + force_sig(SIGSEGV, current); + else + bad_page_fault(regs, regs->dar); + return; + } + _exception(SIGBUS, regs); +} + +void +StackOverflow(struct pt_regs *regs) +{ + printk(KERN_CRIT "Kernel stack overflow in process %p, r1=%lx\n", + current, regs->gpr[1]); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + debugger(regs); +#endif +#if defined(CONFIG_KDB) + kdb(KDB_REASON_OOPS, 0, regs); +#endif + show_regs(regs); + print_backtrace((unsigned long *)regs->gpr[1]); + panic("kernel stack overflow"); +} + +void +trace_syscall(struct pt_regs *regs) +{ + 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]); +} + +#ifdef CONFIG_8xx +void +SoftwareEmulation(struct pt_regs *regs) +{ + extern int do_mathemu(struct pt_regs *); + int errcode; + + if (!user_mode(regs)) { + show_regs(regs); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + debugger(regs); +#endif +#ifdef CONFIG_KDB + if (kdb(KDB_REASON_FAULT, 0, regs)) + return ; +#endif + print_backtrace((unsigned long *)regs->gpr[1]); + panic("Kernel Mode Software FPU Emulation"); + } + +#ifdef CONFIG_MATH_EMULATION + if ((errcode = do_mathemu(regs))) { +#else + if ((errcode = Soft_emulate_8xx(regs))) { +#endif + if (errcode > 0) + _exception(SIGFPE, regs); + else if (errcode == -EFAULT) + _exception(SIGSEGV, regs); + else + _exception(SIGILL, regs); + } +} +#endif + +void +TAUException(struct pt_regs *regs) +{ + printk("TAU trap at PC: %lx, SR: %lx, vector=%lx\n", + regs->nip, regs->msr, regs->trap); +} + +void __init trap_init(void) +{ +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/udbg.c linuxppc64_2_4/arch/ppc64/kernel/udbg.c --- linux-2.4.5-ac18/arch/ppc64/kernel/udbg.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/udbg.c Mon Jun 4 10:40:32 2001 @@ -0,0 +1,265 @@ +/* + * NS16550 Serial Port (uart) debugging stuff. + * + * c 2001 PPC 64 Team, IBM Corp + * + * NOTE: I am trying to make this code avoid any static data references to + * simplify debugging early boot. We'll see how that goes... + * + * To use this call udbg_init() first. It will init the uart to 9600 8N1. + * You may need to update the COM1 define if your uart is at a different addr. + * + * 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 +#include +#include +#define WANT_PPCDBG_TAB /* Only defined here */ +#include +#include + +extern struct Naca *naca; +extern int _machine; + +struct NS16550 { + /* this struct must be packed */ + unsigned char rbr; /* 0 */ + unsigned char ier; /* 1 */ + unsigned char fcr; /* 2 */ + unsigned char lcr; /* 3 */ + unsigned char mcr; /* 4 */ + unsigned char lsr; /* 5 */ + unsigned char msr; /* 6 */ + unsigned char scr; /* 7 */ +}; + +#define thr rbr +#define iir fcr +#define dll rbr +#define dlm ier +#define dlab lcr + +#define LSR_DR 0x01 /* Data ready */ +#define LSR_OE 0x02 /* Overrun */ +#define LSR_PE 0x04 /* Parity error */ +#define LSR_FE 0x08 /* Framing error */ +#define LSR_BI 0x10 /* Break */ +#define LSR_THRE 0x20 /* Xmit holding register empty */ +#define LSR_TEMT 0x40 /* Xmitter empty */ +#define LSR_ERR 0x80 /* Error */ + +void *comport1; + +static inline void eieio(void) { asm volatile ("eieio" : :); } + +void +udbg_init(void) +{ + if ( _machine != _MACH_iSeries ) { + volatile struct NS16550 *port = (struct NS16550 *)comport1; + port->lcr = 0x00; eieio(); + port->ier = 0xFF; eieio(); + port->ier = 0x00; eieio(); + port->lcr = 0x80; eieio(); /* Access baud rate */ + port->dll = 12; eieio(); /* 1 = 115200, 2 = 57600, 3 = 38400, 12 = 9600 baud */ + port->dlm = 0; eieio(); /* dll >> 8 which should be zero for fast rates; */ + port->lcr = 0x03; eieio(); /* 8 data, 1 stop, no parity */ + port->mcr = 0x03; eieio(); /* RTS/DTR */ + port->fcr = 0x07; eieio(); /* Clear & enable FIFOs */ + } +} + + +void +udbg_putc(unsigned char c) +{ + if ( _machine != _MACH_iSeries ) { + volatile struct NS16550 *port = (struct NS16550 *)comport1; + while ((port->lsr & LSR_THRE) == 0) + /* wait for idle */; + port->thr = c; eieio(); + if (c == '\n') { + /* Also put a CR. This is for convenience. */ + while ((port->lsr & LSR_THRE) == 0) + /* wait for idle */; + port->thr = '\r'; eieio(); + } + } +} + +unsigned char +udbg_getc(void) +{ + if ( _machine != _MACH_iSeries ) { + volatile struct NS16550 *port = (struct NS16550 *)comport1; + while ((port->lsr & LSR_DR) == 0) + /* wait for char */; + return port->rbr; + } + return 0; +} + +void +udbg_puts(const char *s) +{ + if ( _machine != _MACH_iSeries ) { + char c; + if ( s && *s != '\0' ) + while ( ( c = *s++ ) != '\0' ) + udbg_putc(c); + else + udbg_puts("NULL"); + } +} + +void +udbg_puthex(unsigned long val) +{ + if ( _machine != _MACH_iSeries ) { + int i, nibbles = sizeof(val)*2; + unsigned char buf[sizeof(val)*2+1]; + for (i = nibbles-1; i >= 0; i--) { + buf[i] = (val & 0xf) + '0'; + if (buf[i] > '9') + buf[i] += ('a'-'0'-10); + val >>= 4; + } + buf[nibbles] = '\0'; + udbg_puts(buf); + } +} + +void +udbg_printSP(const char *s) +{ + if ( _machine != _MACH_iSeries ) { + unsigned long sp; + asm("mr %0,1" : "=r" (sp) :); + if (s) + udbg_puts(s); + udbg_puthex(sp); + } +} + +void +udbg_printf(const char *fmt, ...) +{ + if ( _machine != _MACH_iSeries ) { + unsigned char buf[256]; + + va_list args; + va_start(args, fmt); + + vsprintf(buf, fmt, args); + udbg_puts(buf); + + va_end(args); + } +} + +/* Special print used by PPCDBG() macro */ +void +udbg_ppcdbg(unsigned long flags, const char *fmt, ...) +{ + if ( _machine != _MACH_iSeries ) { + unsigned long active_debugs = flags & naca->debug_switch; + if ( active_debugs ) { + va_list ap; + unsigned char buf[256]; + unsigned long i, len = 0; + + for(i=0; i < PPCDBG_NUM_FLAGS ;i++) { + if (((1U << i) & active_debugs) && + trace_names[i]) { + len += strlen(trace_names[i]); + udbg_puts(trace_names[i]); + break; + } + } + sprintf(buf, " [%s]: ", current->comm); + len += strlen(buf); + udbg_puts(buf); + + while(len < 18) { + udbg_putc(' '); + len++; + } + + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + udbg_puts(buf); + va_end(ap); + } + } +} + +unsigned long +udbg_ifdebug(unsigned long flags) +{ + return (flags & naca->debug_switch); +} + +/* Print out an area of memory. + * The msg will be printed, followed by a newline, followed by the + * memory dump. + */ +void +udbg_dump(char *msg, void *addr, unsigned len) +{ + if ( _machine != _MACH_iSeries ) { + int i; + int j; + int numrows; + unsigned char c; + unsigned char tc; + char *buff = addr; + + if (msg) { + udbg_puts(msg); + udbg_puts("\n"); + } + + if (!buff) { + udbg_puts(" Danger, Will Robinson, Null Pointer\n"); + return; + } + numrows = (len + 15) / 16; + + for (i = 0; i < numrows; i++) { + udbg_printf("%016lx ", &buff[i*16]); + for (j = 0; j < 16; j++) { + if (j + i * 16 >= len) { + udbg_puts(" "); + } else { + udbg_printf("%02x", (unsigned char)buff[j + i * 16]); + } /* endif */ + if (j % 8 == 7) { + udbg_puts(" "); + } /* endif */ + } /* endfor */ + + udbg_puts(" a|"); + + for (j = 0; j < 16; j++) { + if (j + i * 16 >= len) { + udbg_putc(' '); + } else { + c = (unsigned char)buff[j + i * 16]; + tc = c; + if (tc >= 32 && tc < 127 && tc != ' ') { + udbg_putc(tc); + } else { + udbg_putc('.'); + } /* endif */ + } /* endif */ + } /* endfor */ + udbg_putc('|'); + udbg_putc('\n'); + } /* endfor */ + } +} + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/viopath.c linuxppc64_2_4/arch/ppc64/kernel/viopath.c --- linux-2.4.5-ac18/arch/ppc64/kernel/viopath.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/viopath.c Fri May 4 17:13:59 2001 @@ -0,0 +1,711 @@ +/* + * arch/ppc64/viopath.c + * + * iSeries Virtual I/O Message Path code + * + * Author: Dave Boutcher + * (C) Copyright 2000 IBM Corporation + * + * 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) anyu later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef _HVLPEVENT_H +#include +#endif +#ifndef _HVLPConfig_H +#include +#endif +#ifndef _HVCallCfg_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _VIO_H +#include +#endif +#ifndef _MF_H +#include +#endif +#ifndef _ISERIES_PROC_H +#include +#endif + +/* Status of the path to each other partition in the system. + * This is overkill, since we will only ever establish connections + * to our hosting partition and the primary partition on the system. + * But this allows for other support in the future. + */ +static struct viopathStatus { + int isOpen:1; /* Did we open the path? */ + int isActive:1; /* Do we have a mon msg outstanding */ +#if defined(CONFIG_VIOCONS) + int xCharUsers; +#endif +#if defined(CONFIG_VIODASD) + int xBlockUsers; +#endif +#if defined(CONFIG_VIOCD) + int xCDUsers; +#endif +#if defined(CONFIG_VIOTAPE) + int xTapeUsers; +#endif + int xConfigUsers; + HvLpInstanceId mSourceInst; + HvLpInstanceId mTargetInst; + int numberAllocated; + spinlock_t statuslock; + unsigned long statuslockFlags; +} viopathStatus[HVMAXARCHITECTEDLPS]; + +/* We use this structure to handle asynchronous responses. The caller + * blocks on the semaphore and the handler posts the semaphore. + */ +struct doneAllocParms_t { + struct semaphore *sem; + int number; +}; + +/* Put a sequence number in each mon msg. The value is not + * important. Start at something other than 0 just for + * readability. wrapping this is ok. + */ +static u8 viomonseq = 22; + +/* Our hosting logical partition. We get this at startup + * time, and different modules access this variable directly. + */ +HvLpIndex viopath_hostLp; + +/* For each kind of incoming event we set a pointer to a + * routine to call. + */ +static vio_event_handler_t *vio_handleCharEvent; +static vio_event_handler_t *vio_handleBlockEvent; +static vio_event_handler_t *vio_handleCDEvent; +static vio_event_handler_t *vio_handleTapeEvent; + +/* + * For each kind of event we allocate a buffer that is + * guaranteed not to cross a page boundary + */ +void * vio_char_event_buffer; +void * vio_block_event_buffer; +void * vio_cd_event_buffer; +void * vio_tape_event_buffer; + +/*************************************************************************** + * A page to build an lp event in + ***************************************************************************/ +unsigned long VIOReqPage; + +/*************************************************************************** + * Handle reads from the proc file system + ***************************************************************************/ +static int proc_read(char *buf, char **start, off_t offset, + int blen, int *eof, void *data) +{ + HvLpEvent_Rc hvrc; + DECLARE_MUTEX_LOCKED(Semaphore); + dma_addr_t dmaa = pci_map_single( NULL, buf, PAGE_SIZE, PCI_DMA_FROMDEVICE); + int len = PAGE_SIZE; + + if (len > blen) + len = blen; + + memset(buf,0x00,len); + hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_config | vioconfigget, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_ImmediateAck, + viopath_sourceinst(viopath_hostLp), + viopath_targetinst(viopath_hostLp), + (u64)(unsigned long)&Semaphore, + VIOVERSION << 16, + ((u64)dmaa) << 32, + len, + 0, + 0); + if (hvrc != HvLpEvent_Rc_Good) + { + printk("viopath hv error on op %d\n",(int)hvrc); + } + + down(&Semaphore); + + pci_unmap_single( NULL, dmaa, PAGE_SIZE, PCI_DMA_FROMDEVICE); + + *eof = 1; + return strlen(buf); +} + +/*************************************************************************** + * Handle writes to our proc file system + ***************************************************************************/ +static int proc_write(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + printk("viopath: in proc_write, got %ld bytes starting with %c\n", + count, buffer[0]); + return count; +} + +/*************************************************************************** + * setup our proc file system entries + ***************************************************************************/ +void vio_proc_init(struct proc_dir_entry *iSeries_proc) +{ + struct proc_dir_entry *ent; + ent = create_proc_entry("config", S_IFREG|S_IRUSR, iSeries_proc); + if (!ent) return; + ent->nlink = 1; + ent->data = NULL; + ent->read_proc = proc_read; + ent->write_proc = proc_write; +} + +/* Boot time initialization of the vio path code. Needs + * to be called before any VIO components start + */ +void viopath_init(void) +{ + int i; + memset(viopathStatus,0x00,sizeof(viopathStatus)); + for (i=0; ixFlags.xFunction == HvLpEvent_Function_Int) + { + printk("viopath: got monitor INT event\n"); + remoteLp = event->xSourceLp; + if (!viopathStatus[remoteLp].isActive) + sendMonMsg(remoteLp); + } + else + { + remoteLp = event->xTargetLp; + printk("viopath: got monitor ACK event %d, sinst %d, tinst %d\n", + (int)event->xCorrelationToken, + event->xSourceInstanceId, + event->xTargetInstanceId); + /* Other partition went away! */ + if ((event->xSourceInstanceId != viopathStatus[remoteLp].mSourceInst) || + (event->xTargetInstanceId != viopathStatus[remoteLp].mTargetInst)) + { + printk("viopath: ignoring ack....mismatched instances\n"); + } + else + { + printk("viopath: closing path %d\n",remoteLp); + + viopathStatus[remoteLp].isActive = 0; + + if (vio_handleBlockEvent!= NULL) + (*vio_handleBlockEvent)(NULL); + + if (vio_handleCharEvent != NULL) + (*vio_handleCharEvent)(NULL); + + if (vio_handleCDEvent != NULL) + (*vio_handleCDEvent)(NULL); + + if (vio_handleTapeEvent != NULL) + (*vio_handleTapeEvent)(NULL); + } + } +} + +void vio_setBlockHandler(vio_event_handler_t *beh) +{ + vio_handleBlockEvent = *beh; +} + +void vio_clearBlockHandler(void) +{ + vio_handleBlockEvent = NULL; +} + +void vio_setCharHandler(vio_event_handler_t *ceh) +{ + vio_handleCharEvent = *ceh; +} + +void vio_clearCharHandler(void) +{ + vio_handleCharEvent = NULL; +} + +void vio_setCDHandler(vio_event_handler_t *ceh) +{ + vio_handleCDEvent = *ceh; +} + +void vio_clearCDHandler(void) +{ + vio_handleCDEvent = NULL; +} + +void vio_setTapeHandler(vio_event_handler_t *ceh) +{ + vio_handleTapeEvent = *ceh; +} + +void vio_clearTapeHandler(void) +{ + vio_handleTapeEvent = NULL; +} + +static void vio_handleEvent(struct HvLpEvent *event, struct pt_regs *regs) +{ + HvLpIndex remoteLp; + if (event->xFlags.xFunction == HvLpEvent_Function_Int) + { + remoteLp = event->xSourceLp; + if (event->xSourceInstanceId != viopathStatus[remoteLp].mTargetInst) + { + printk("viopath: int msg rcvd, source inst (%d) doesnt match (%d)\n", + viopathStatus[remoteLp].mTargetInst, + event->xSourceInstanceId); + return; + } + + if (event->xTargetInstanceId != viopathStatus[remoteLp].mSourceInst) + { + printk("viopath: int msg rcvd, target inst (%d) doesnt match (%d)\n", + viopathStatus[remoteLp].mSourceInst, + event->xTargetInstanceId); + return; + } + } + else + { + remoteLp = event->xTargetLp; + if (event->xSourceInstanceId != viopathStatus[remoteLp].mSourceInst) + { + printk("viopath: ack msg rcvd, source inst (%d) doesnt match (%d)\n", + viopathStatus[remoteLp].mSourceInst, + event->xSourceInstanceId); + return; + } + + if (event->xTargetInstanceId != viopathStatus[remoteLp].mTargetInst) + { + printk("viopath: ack msg rcvd, target inst (%d) doesnt match (%d)\n", + viopathStatus[remoteLp].mTargetInst, + event->xTargetInstanceId); + return; + } + } + + + switch (event->xSubtype & VIOMAJOR_SUBTYPE_MASK) { + case viomajorsubtype_config: + up((struct semaphore *)event->xCorrelationToken); + break; + + case viomajorsubtype_monitor: + handleMonitorEvent(event); + break; + case viomajorsubtype_blockio: + if (vio_handleBlockEvent) + { + (*vio_handleBlockEvent)(event); + } + else + { + printk("vio: unexpected virtual blockio event\n"); + if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) + { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } + break; + case viomajorsubtype_chario: + if (vio_handleCharEvent) + { + (*vio_handleCharEvent)(event); + } + else + { + printk("vio: unexpected virtual chario event\n"); + if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) + { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } + break; + case viomajorsubtype_cdio: + if (vio_handleCDEvent) + { + (*vio_handleCDEvent)(event); + } + else + { + printk("vio: unexpected virtual cd event\n"); + if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) + { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } + break; + case viomajorsubtype_tape: + if (vio_handleTapeEvent) + { + (*vio_handleTapeEvent)(event); + } + else + { + printk("vio: unexpected virtual tape event\n"); + if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) + { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } + break; + default: + printk("vio: unexpected virtual io event subtype %d\n",event->xSubtype); + if (event->xFlags.xAckInd == HvLpEvent_AckInd_DoAck) + { + event->xRc = HvLpEvent_Rc_InvalidSubtype; + HvCallEvent_ackLpEvent(event); + } + } +} + + +static void viopath_donealloc(void *parm, int number) +{ + struct doneAllocParms_t *doneAllocParmsp = (struct doneAllocParms_t *)parm; + doneAllocParmsp->number = number; + up(doneAllocParmsp->sem); +} + +int viopath_open(HvLpIndex remoteLp, int subtype) +{ + HvLpEvent_Rc hvrc; + struct doneAllocParms_t doneAllocParms; + DECLARE_MUTEX_LOCKED(Semaphore); + + if ((remoteLp >= HvMaxArchitectedLps) || (remoteLp == HvLpIndexInvalid)) + return -EINVAL; + + if (VIOReqPage == 0) + { + /* Get a page to build read/write LP events in */ + VIOReqPage = get_free_page(GFP_KERNEL); + if (!VIOReqPage) + { + printk("viopath: error allocating I/O memory\n"); + return -ENOMEM; + } + + vio_char_event_buffer = (void *)VIOReqPage; + vio_block_event_buffer = (void *)(VIOReqPage+256); + vio_cd_event_buffer = (void *)(VIOReqPage+512); + vio_tape_event_buffer = (void *)(VIOReqPage+768); + } + + if ( (0) +#if defined(CONFIG_VIODASD) + || (subtype == viomajorsubtype_blockio) +#endif +#if defined(CONFIG_VIOCONS) + || (subtype == viomajorsubtype_chario) +#endif +#if defined(CONFIG_VIOCD) + || (subtype == viomajorsubtype_cdio) +#endif +#if defined(CONFIG_VIOTAPE) + || (subtype == viomajorsubtype_tape) +#endif + ) + { + viopath_statuslock(remoteLp); + switch (subtype) { +#if defined(CONFIG_VIODASD) + case viomajorsubtype_blockio: + viopathStatus[remoteLp].xBlockUsers++; + break; +#endif +#if defined(CONFIG_VIOCONS) + case viomajorsubtype_chario: + viopathStatus[remoteLp].xCharUsers++; + break; +#endif +#if defined(CONFIG_VIOCD) + case viomajorsubtype_cdio: + viopathStatus[remoteLp].xCDUsers++; + break; +#endif +#if defined(CONFIG_VIOTAPE) + case viomajorsubtype_tape: + viopathStatus[remoteLp].xTapeUsers++; + break; +#endif + default: + } + if (!viopathStatus[remoteLp].isOpen) + { + HvCallEvent_openLpEventPath(remoteLp, HvLpEvent_Type_VirtualIo); + + viopathStatus[remoteLp].mSourceInst = HvCallEvent_getSourceLpInstanceId(remoteLp, HvLpEvent_Type_VirtualIo); + viopathStatus[remoteLp].mTargetInst = HvCallEvent_getTargetLpInstanceId(remoteLp, HvLpEvent_Type_VirtualIo); + + printk("viopath: open, setting sinst %d, tinst %d\n", + viopathStatus[remoteLp].mSourceInst, + viopathStatus[remoteLp].mTargetInst); + + doneAllocParms.sem = &Semaphore; + + mf_allocateLpEvents(remoteLp, + HvLpEvent_Type_VirtualIo, + 250, /* TODO: Put a sizeof VIOLpEvent in here! */ + 25, /* TODO: Work out the real number */ + &viopath_donealloc, + &doneAllocParms); + + down(&Semaphore); + + viopathStatus[remoteLp].numberAllocated = doneAllocParms.number; + + HvLpEvent_registerHandler(HvLpEvent_Type_VirtualIo, &vio_handleEvent); + + viopathStatus[remoteLp].isOpen = 1; + + hvrc = HvCallEvent_signalLpEventFast(remoteLp, + HvLpEvent_Type_VirtualIo, + viomajorsubtype_monitor, + HvLpEvent_AckInd_DoAck, + HvLpEvent_AckType_DeferredAck, + viopathStatus[remoteLp].mSourceInst, + viopathStatus[remoteLp].mTargetInst, + 0, 0, 0, 0, 0, 0); + + if (hvrc == HvLpEvent_Rc_Good) + { + viopathStatus[remoteLp].isActive = 1; + } + else + { + viopathStatus[remoteLp].isActive = 0; + } + } + viopath_statusunlock(remoteLp); + + return 0; + } + else /* invalid subtype */ + { + printk("viopath: invalid path subtype %d\n",subtype); + return -EINVAL; + } +} + +int viopath_close(HvLpIndex remoteLp, int subtype) +{ + printk("viopath: close(%d,%4.4x)\n",remoteLp,subtype); + + if ((remoteLp >= HvMaxArchitectedLps) || (remoteLp == HvLpIndexInvalid)) + return -EINVAL; + + if ( (0) +#if defined(CONFIG_VIODASD) + || (subtype == viomajorsubtype_blockio) +#endif +#if defined(CONFIG_VIOCONS) + || (subtype == viomajorsubtype_chario) +#endif +#if defined(CONFIG_VIOCD) + || (subtype == viomajorsubtype_cdio) +#endif +#if defined(CONFIG_VIOTAPE) + || (subtype == viomajorsubtype_tape) +#endif + ) + { + viopath_statuslock(remoteLp); + switch (subtype) { +#if defined(CONFIG_VIODASD) + case viomajorsubtype_blockio: + viopathStatus[remoteLp].xBlockUsers--; + break; +#endif +#if defined(CONFIG_VIOCONS) + case viomajorsubtype_chario: + viopathStatus[remoteLp].xCharUsers--; + break; +#endif +#if defined(CONFIG_VIOCD) + case viomajorsubtype_cdio: + viopathStatus[remoteLp].xCDUsers--; + break; +#endif +#if defined(CONFIG_VIOTAPE) + case viomajorsubtype_tape: + viopathStatus[remoteLp].xTapeUsers--; + break; +#endif + default: + } + + if ((viopathStatus[remoteLp].isOpen) +#if defined(CONFIG_VIODASD) + && (viopathStatus[remoteLp].xBlockUsers == 0) +#endif +#if defined(CONFIG_VIOCONS) + && (viopathStatus[remoteLp].xCharUsers == 0) +#endif +#if defined(CONFIG_VIOCD) + && (viopathStatus[remoteLp].xCDUsers == 0) +#endif +#if defined(CONFIG_VIOTAPE) + && (viopathStatus[remoteLp].xTapeUsers == 0) +#endif + ) + { + printk("viopath: closing event path\n"); + HvCallEvent_closeLpEventPath(remoteLp, HvLpEvent_Type_VirtualIo); + viopathStatus[remoteLp].isOpen = 0; + viopathStatus[remoteLp].isActive = 0; + } + viopath_statusunlock(remoteLp); + + return 0; + } + else /* invalid subtype */ + { + printk("viopath: invalid path subtype %d\n",subtype); + return -EINVAL; + } +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/xics.c linuxppc64_2_4/arch/ppc64/kernel/xics.c --- linux-2.4.5-ac18/arch/ppc64/kernel/xics.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/xics.c Wed Jun 20 01:09:17 2001 @@ -0,0 +1,344 @@ +/* + * arch/ppc/kernel/xics.c + * + * Copyright 2000 IBM Corporation. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "i8259.h" +#include "xics.h" +#include + +extern struct Naca *naca; + +void xics_enable_irq(u_int irq); +void xics_disable_irq(u_int irq); +void xics_mask_and_ack_irq(u_int irq); +void xics_end_irq(u_int irq); + +struct hw_interrupt_type xics_pic = { + " XICS ", + NULL, + NULL, + xics_enable_irq, + xics_disable_irq, + xics_mask_and_ack_irq, + xics_end_irq +}; + +struct hw_interrupt_type xics_8259_pic = { + " XICS/8259", + NULL, + NULL, + NULL, + NULL, + xics_mask_and_ack_irq, + NULL +}; + +#define XICS_IPI 2 +#define XICS_IRQ_8259_CASCADE 0x1c +#define XICS_IRQ_OFFSET 0x10 +#define XICS_PCI_IRQ_START 0x10 + +#define XICS_IRQ_SPURIOUS 0 + +#define DEFAULT_SERVER 0 +#define DEFAULT_PRIORITY 0 + +struct xics_ipl { + union { + u32 word; + u8 bytes[4]; + } xirr_poll; + union { + u32 word; + u8 bytes[4]; + } xirr; + u32 dummy; + union { + u32 word; + u8 bytes[4]; + } qirr; +}; + +struct xics_info { + volatile struct xics_ipl * per_cpu[NR_CPUS]; +}; + +struct xics_info xics_info; + +#define xirr_info(n_cpu) (xics_info.per_cpu[n_cpu]->xirr.word) +#define cppr_info(n_cpu) (xics_info.per_cpu[n_cpu]->xirr.bytes[0]) +#define poll_info(n_cpu) (xics_info.per_cpu[n_cpu]->xirr_poll.word) +#define qirr_info(n_cpu) (xics_info.per_cpu[n_cpu]->qirr.bytes[0]) + +unsigned long long intr_base = 0; +unsigned int xics_irq_8259_cascade = 0; + +struct xics_interrupt_node { + unsigned long long addr; + unsigned long long size; +} inodes[NR_CPUS*2]; + +void +xics_enable_irq( + u_int irq + ) +{ + unsigned long status; + long call_status; + + irq -= XICS_IRQ_OFFSET; + if (irq == XICS_IPI) + return; + call_status = call_rtas("ibm,set-xive", 3, 1, (unsigned long*)&status, + irq, DEFAULT_SERVER, DEFAULT_PRIORITY); + if( call_status != 0 ) { + printk("xics_enable_irq: irq=%x: call_rtas failed; retn=%x, status=%x\n", + irq, call_status, status); + return; + } +} + +void +xics_disable_irq( + u_int irq + ) +{ + unsigned long status; + long call_status; + + irq -= XICS_IRQ_OFFSET; + call_status = call_rtas("ibm,int-off", 1, 1, (unsigned long*)&status, + irq); + if( call_status != 0 ) { + printk("xics_disable_irq: irq=%x: call_rtas failed, retn=%x\n", + irq, call_status); + return; + } +} + +void +xics_end_irq( + u_int irq + ) +{ + int cpu = smp_processor_id(); + + cppr_info(cpu) = 0; /* actually the value overwritten by ack */ + iosync(); + xirr_info(cpu) = (0xff<<24) | (irq-XICS_IRQ_OFFSET); + iosync(); +} + +void +xics_mask_and_ack_irq( + u_int irq + ) +{ + int cpu = smp_processor_id(); + + if( irq < XICS_IRQ_OFFSET ) { + i8259_pic.ack(irq); + iosync(); + xirr_info(cpu) = (0xff<<24) | XICS_IRQ_8259_CASCADE; + iosync(); + } + else { + cppr_info(cpu) = 0xff; + iosync(); + } +} + +int +xics_get_irq(struct pt_regs *regs) +{ + u_int cpu = smp_processor_id(); + u_int vec; + int irq; + + vec = xirr_info(cpu); + /* (vec >> 24) == old priority */ + vec &= 0x00ffffff; + /* for sanity, this had better be < NR_IRQS - 16 */ + if( vec == XICS_IRQ_8259_CASCADE ) { + irq = i8259_irq(cpu); + if(irq == -1) { + /* Spurious cascaded interrupt. Still must ack xics */ + xics_end_irq(XICS_IRQ_OFFSET + XICS_IRQ_8259_CASCADE); + } + } else if( vec == XICS_IRQ_SPURIOUS ) + irq = -1; + else + irq = vec + XICS_IRQ_OFFSET; + return irq; +} + +int +xics_to_irq(int line) +{ + int irq; + + irq = line + XICS_IRQ_OFFSET; + return irq; +} + +#ifdef CONFIG_SMP +void xics_ipi_action(int irq, void *dev_id, struct pt_regs *regs) +{ + extern volatile unsigned long xics_ipi_message[]; + int cpu = smp_processor_id(); + + while (xics_ipi_message[cpu]) { + if (test_and_clear_bit(PPC_MSG_CALL_FUNCTION, &xics_ipi_message[cpu])) { + mb(); + smp_message_recv(PPC_MSG_CALL_FUNCTION, regs); + } + if (test_and_clear_bit(PPC_MSG_RESCHEDULE, &xics_ipi_message[cpu])) { + mb(); + smp_message_recv(PPC_MSG_RESCHEDULE, regs); + } + } + qirr_info(cpu) = 0xff; +} + +void xics_cause_IPI(int cpu) +{ + qirr_info(cpu) = 0; +} + +void xics_setup_cpu(void) +{ + int cpu = smp_processor_id(); + + cppr_info(cpu) = 0xff; + iosync(); +} +#endif /* CONFIG_SMP */ + +void +xics_init_IRQ( void ) +{ + int i; + unsigned long intr_size = 0; + struct device_node *np; + uint *ireg, ilen, indx=0; + + np = find_type_devices("PowerPC-External-Interrupt-Presentation"); + if (!np) { + printk(KERN_WARNING "Can't find Interrupt Presentation\n"); + udbg_printf("Can't find Interrupt Presentation\n"); + while (1); + } +nextnode: + ireg = (uint *)get_property(np, "ibm,interrupt-server-ranges", 0); + if (ireg) { + /* + * set node starting index for this node + */ + indx = *ireg; + } + + ireg = (uint *)get_property(np, "reg", &ilen); + if (!ireg) { + printk(KERN_WARNING "Can't find Interrupt Reg Property\n"); + udbg_printf("Can't find Interrupt Reg Property\n"); + while (1); + } + + while (ilen) { + inodes[indx].addr = (unsigned long long)*ireg++ << 32; + ilen -= sizeof(uint); + inodes[indx].addr |= *ireg++; + ilen -= sizeof(uint); + inodes[indx].size = (unsigned long long)*ireg++ << 32; + ilen -= sizeof(uint); + inodes[indx].size |= *ireg++; + ilen -= sizeof(uint); + indx++; + if (indx >= NR_CPUS) break; + } + + np = np->next; + if ((indx < NR_CPUS) && np) goto nextnode; + + /* + * XXX Assume for now that nodes are in order + * We could (and should) get the "interrupt-server-ranges" property + * for each "presentation" node, and for a given CPU, look at + * the CPU's "ibm,ppc-interrupt-server#s" property to see which + * "presentation" node this CPU is routed to. But for now, the + * server numbers appear to match the physical cpu index, and + * are sequentially assigned to the nodes in order. + * XXX Also a gross assumption in the reload handler (hashtable.S) + * that all CPU's XICS nodes addresses have the same upper 16 bits + */ + intr_base = inodes[0].addr; + intr_size = (ulong)inodes[0].size; + + np = find_type_devices("interrupt-controller"); + if (!np) { + printk(KERN_WARNING "Can't find ISA Interrupt Controller\n"); + udbg_printf("Can't find ISA Interrupt Controller\n"); + while (1); + } + ireg = (uint *) get_property(np, "interrupts", 0); + if (!ireg) { + printk(KERN_WARNING "Can't find ISA Interrupts Property\n"); + udbg_printf("Can't find ISA Interrupts Property\n"); + while (1); + } + + xics_irq_8259_cascade = *ireg; + +#ifdef CONFIG_SMP + for (i = 0; i < naca->processorCount; ++i) { + xics_info.per_cpu[i] = + ioremap((ulong)inodes[smp_hw_index[i]].addr, + (ulong)inodes[smp_hw_index[i]].size); + } +#else + xics_info.per_cpu[0] = ioremap((ulong)intr_base, intr_size); +#endif /* CONFIG_SMP */ + + xics_8259_pic.enable = i8259_pic.enable; + xics_8259_pic.disable = i8259_pic.disable; + for (i = 0; i < 16; ++i) + irq_desc[i].handler = &xics_8259_pic; + for (; i < NR_IRQS; ++i) + irq_desc[i].handler = &xics_pic; + + cppr_info(0) = 0xff; + iosync(); + if (request_irq(XICS_IRQ_8259_CASCADE + XICS_IRQ_OFFSET, no_action, + 0, "8259 cascade", 0)) + printk(KERN_ERR "xics_init_IRQ: couldn't get 8259 cascade\n"); + i8259_init(); + +#ifdef CONFIG_SMP + request_irq(XICS_IPI + XICS_IRQ_OFFSET, xics_ipi_action, 0, "IPI", 0); + irq_desc[XICS_IPI+XICS_IRQ_OFFSET].status |= IRQ_PER_CPU; +#endif +} + +void xics_isa_init(void) +{ + return; + if (request_irq(XICS_IRQ_8259_CASCADE + XICS_IRQ_OFFSET, no_action, + 0, "8259 cascade", 0)) + printk(KERN_ERR "xics_init_IRQ: couldn't get 8259 cascade\n"); + i8259_init(); +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/kernel/xics.h linuxppc64_2_4/arch/ppc64/kernel/xics.h --- linux-2.4.5-ac18/arch/ppc64/kernel/xics.h Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/kernel/xics.h Tue Jun 26 17:16:50 2001 @@ -0,0 +1,24 @@ +/* + * arch/ppc/kernel/xics.h + * + * Copyright 2000 IBM Corporation. + * + * 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. + */ + +#ifndef _PPC_KERNEL_XICS_H +#define _PPC_KERNEL_XICS_H + +#include "local_irq.h" + +extern struct hw_interrupt_type xics_pic; +extern struct hw_interrupt_type xics_8259_pic; + +void xics_init_IRQ(void); +int xics_get_irq(struct pt_regs *); +void xics_isa_init(void); + +#endif /* _PPC_KERNEL_XICS_H */ diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/lib/Makefile linuxppc64_2_4/arch/ppc64/lib/Makefile --- linux-2.4.5-ac18/arch/ppc64/lib/Makefile Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/lib/Makefile Mon May 7 16:05:35 2001 @@ -0,0 +1,11 @@ +# +# Makefile for ppc64-specific library files.. +# + +USE_STANDARD_AS_RULE := true + +O_TARGET = lib.o + +obj-y := checksum.o string.o strcase.o + +include $(TOPDIR)/Rules.make diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/lib/checksum.S linuxppc64_2_4/arch/ppc64/lib/checksum.S --- linux-2.4.5-ac18/arch/ppc64/lib/checksum.S Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/lib/checksum.S Fri May 4 17:13:58 2001 @@ -0,0 +1,231 @@ +/* + * 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 +#include +#include +#include "../kernel/ppc_asm.tmpl" + + .text + +/* + * ip_fast_csum(r3=buf, r4=len) -- Optimized for IP header + * len is in words and is always >= 5. + * + * In practice len == 5, but this is not guaranteed. So this code does not + * attempt to use doubleword instructions. + */ +_GLOBAL(ip_fast_csum) + lwz r0,0(r3) + lwzu r5,4(r3) + addic. r4,r4,-2 + addc r0,r0,r5 + mtctr r4 + blelr- +1: lwzu r4,4(r3) + adde r0,r0,r4 + bdnz 1b + addze r0,r0 /* add in final carry */ + rldicl r4,r0,32,0 /* fold two 32-bit halves together */ + add r0,r0,r4 + srdi r0,r0,32 + 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(r3=saddr, r4=daddr, r5=len, r6=proto, r7=sum) + * No real gain trying to do this specially for 64 bit, but + * the 32 bit addition may spill into the upper bits of + * the doubleword so we still must fold it down from 64. + */ +_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 + rldicl r4,r0,32,0 /* fold 64 bit value */ + add r0,r4,r0 + srdi r0,r0,32 + 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). + * + * This code assumes at least halfword alignment, though the length + * can be any number of bytes. The sum is accumulated in r5. + * + * csum_partial(r3=buff, r4=len, r5=sum) + */ +_GLOBAL(csum_partial) + subi r3,r3,8 /* we'll offset by 8 for the loads */ + srdi. r6,r4,3 /* divide by 8 for doubleword count */ + addic r5,r5,0 /* clear carry */ + beq 3f /* if we're doing < 8 bytes */ + andi. r0,r3,2 /* aligned on a word boundary already? */ + beq+ 1f + lhz r6,8(r3) /* do 2 bytes to get aligned */ + addi r3,r3,2 + subi r4,r4,2 + addc r5,r5,r6 + srdi. r6,r4,3 /* recompute number of doublewords */ + beq 3f /* any left? */ +1: mtctr r6 +2: ldu r6,8(r3) /* main sum loop */ + adde r5,r5,r6 + bdnz 2b + andi. r4,r4,7 /* compute bytes left to sum after doublewords */ +3: cmpi 0,r4,4 /* is at least a full word left? */ + blt 4f + lwz r6,8(r3) /* sum this word */ + addi r3,r3,4 + subi r4,r4,4 + adde r5,r5,r6 +4: cmpi 0,r4,2 /* is at least a halfword left? */ + blt+ 5f + lhz r6,8(r3) /* sum this halfword */ + addi r3,r3,2 + subi r4,r4,2 + adde r5,r5,r6 +5: cmpi 0,r4,1 /* is at least a byte left? */ + bne+ 6f + lbz r6,8(r3) /* sum this byte */ + slwi r6,r6,8 /* this byte is assumed to be the upper byte of a halfword */ + adde r5,r5,r6 +6: addze r5,r5 /* add in final carry */ + rldicl r4,r5,32,0 /* fold two 32-bit halves together */ + add r3,r4,r5 + srdi r3,r3,32 + 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. + * + * This code needs to be reworked to take advantage of 64 bit sum+copy. + * However, due to tokenring halfword alignment problems this will be very + * tricky. For now we'll leave it until we instrument it somehow. + * + * csum_partial_copy_generic(r3=src, r4=dst, r5=len, r6=sum, r7=src_err, r8=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 (unlikely with 64-bit regs) */ + rldicl r4,r3,32,0 /* fold 64 bit value */ + add r3,r4,r3 + srdi r3,r3,32 + blr + +/* These shouldn't go in the fixup section, since that would + cause the ex_table addresses to get out of order. */ + + .globl src_error_1 +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 + .globl src_error_2 +src_error_2: + li r6,0 +96: stwu r6,4(r4) + bdnz 96b +3: andi. r5,r5,3 + beq src_error + .globl src_error_3 +src_error_3: + li r6,0 + mtctr r5 + addi r4,r4,3 +97: stbu r6,1(r4) + bdnz 97b + .globl src_error +src_error: + cmpi 0,r7,0 + beq 1f + li r6,-EFAULT + stw r6,0(r7) +1: addze r3,r0 + blr + + .globl dst_error +dst_error: + cmpi 0,r8,0 + beq 1f + li r6,-EFAULT + stw r6,0(r8) +1: addze r3,r0 + blr + +.section __ex_table,"a" + .align 3 + .llong 81b,src_error_1 + .llong 91b,dst_error + .llong 82b,src_error_2 + .llong 92b,dst_error + .llong 83b,src_error_3 + .llong 93b,dst_error + .llong 84b,src_error_3 + .llong 94b,dst_error + .llong 95b,dst_error + .llong 96b,dst_error + .llong 97b,dst_error diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/lib/strcase.c linuxppc64_2_4/arch/ppc64/lib/strcase.c --- linux-2.4.5-ac18/arch/ppc64/lib/strcase.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/lib/strcase.c Mon May 7 12:48:05 2001 @@ -0,0 +1,31 @@ +/* + * c 2001 PPC 64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include + +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; +} + +int strncasecmp(const char *s1, const char *s2, int n) +{ + int c1, c2; + + do { + c1 = tolower(*s1++); + c2 = tolower(*s2++); + } while ((--n > 0) && c1 == c2 && c1 != 0); + return c1 - c2; +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/lib/string.S linuxppc64_2_4/arch/ppc64/lib/string.S --- linux-2.4.5-ac18/arch/ppc64/lib/string.S Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/lib/string.S Fri May 4 17:13:58 2001 @@ -0,0 +1,661 @@ +/* + * 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 +#include +#include + +#define CACHE_LINE_SIZE 128 +#define LG_CACHE_LINE_SIZE 7 +#define MAX_COPY_PREFETCH 1 + +#define COPY_16_BYTES \ + lwz r7,4(r4); \ + lwz r8,8(r4); \ + lwz r9,12(r4); \ + lwzu r10,16(r4); \ + stw r7,4(r6); \ + stw r8,8(r6); \ + stw r9,12(r6); \ + stwu r10,16(r6) + +#define COPY_16_BYTES_WITHEX(n) \ +8 ## n ## 0: \ + lwz r7,4(r4); \ +8 ## n ## 1: \ + lwz r8,8(r4); \ +8 ## n ## 2: \ + lwz r9,12(r4); \ +8 ## n ## 3: \ + lwzu r10,16(r4); \ +8 ## n ## 4: \ + stw r7,4(r6); \ +8 ## n ## 5: \ + stw r8,8(r6); \ +8 ## n ## 6: \ + stw r9,12(r6); \ +8 ## n ## 7: \ + stwu r10,16(r6) + +#define COPY_16_BYTES_EXCODE(n) \ +9 ## n ## 0: \ + addi r5,r5,-(16 * n); \ + b 104f; \ +9 ## n ## 1: \ + addi r5,r5,-(16 * n); \ + b 105f; \ +.section __ex_table,"a"; \ + .align 3; \ + .llong 8 ## n ## 0b,9 ## n ## 0b; \ + .llong 8 ## n ## 1b,9 ## n ## 0b; \ + .llong 8 ## n ## 2b,9 ## n ## 0b; \ + .llong 8 ## n ## 3b,9 ## n ## 0b; \ + .llong 8 ## n ## 4b,9 ## n ## 1b; \ + .llong 8 ## n ## 5b,9 ## n ## 1b; \ + .llong 8 ## n ## 6b,9 ## n ## 1b; \ + .llong 8 ## n ## 7b,9 ## n ## 1b; \ +.text + +CACHELINE_BYTES = CACHE_LINE_SIZE +LG_CACHELINE_BYTES = LG_CACHE_LINE_SIZE +CACHELINE_MASK = (CACHE_LINE_SIZE-1) + +_GLOBAL(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 + +_GLOBAL(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 + +_GLOBAL(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 + +_GLOBAL(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 + +_GLOBAL(strlen) + addi r4,r3,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + bne 1b + subf r3,r3,r4 + blr + +/* + * Use dcbz on the complete cache lines in the destination + * to set them to zero. This requires that the destination + * area is cacheable. -- paulus + */ +_GLOBAL(cacheable_memzero) + mr r5,r4 + li r4,0 + 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 + clrlwi r7,r6,32-LG_CACHELINE_BYTES + add r8,r7,r5 + srwi r9,r8,LG_CACHELINE_BYTES + addic. r9,r9,-1 /* total number of complete cachelines */ + ble 2f + xori r0,r7,CACHELINE_MASK & ~3 + srwi. r0,r0,2 + beq 3f + mtctr r0 +4: stwu r4,4(r6) + bdnz 4b +3: mtctr r9 + li r7,4 +10: dcbz r7,r6 + addi r6,r6,CACHELINE_BYTES + bdnz 10b + clrlwi r5,r8,32-LG_CACHELINE_BYTES + addi r5,r5,4 +2: srwi r0,r5,2 + 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 + +_GLOBAL(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 + srwi r0,r5,2 + 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 + +_GLOBAL(bcopy) + mr r6,r3 + mr r3,r4 + mr r4,r6 + b .memcpy + +/* + * This version uses dcbz on the complete cache lines in the + * destination area to reduce memory traffic. This requires that + * the destination area is cacheable. + * We only use this version if the source and dest don't overlap. + * -- paulus. + */ +_GLOBAL(cacheable_memcpy) + add r7,r3,r5 /* test if the src & dst overlap */ + add r8,r4,r5 + cmplw 0,r4,r7 + cmplw 1,r3,r8 + crand 0,0,4 /* cr0.lt &= cr1.lt */ + blt .memcpy /* if regions overlap */ + + addi r4,r4,-4 + addi r6,r3,-4 + neg r0,r3 + andi. r0,r0,CACHELINE_MASK /* # bytes to start of cache line */ + beq 58f + + cmplw 0,r5,r0 /* is this more than total to do? */ + blt 63f /* if not much to do */ + andi. r8,r0,3 /* get it word-aligned first */ + subf r5,r0,r5 + mtctr r8 + beq+ 61f +70: lbz r9,4(r4) /* do some bytes */ + stb r9,4(r6) + addi r4,r4,1 + addi r6,r6,1 + bdnz 70b +61: srwi. r0,r0,2 + mtctr r0 + beq 58f +72: lwzu r9,4(r4) /* do some words */ + stwu r9,4(r6) + bdnz 72b + +58: srwi. r0,r5,LG_CACHELINE_BYTES /* # complete cachelines */ + clrlwi r5,r5,32-LG_CACHELINE_BYTES + li r11,4 + mtctr r0 + beq 63f +53: + dcbz r11,r6 + COPY_16_BYTES +#if CACHE_LINE_SIZE >= 32 + COPY_16_BYTES +#if CACHE_LINE_SIZE >= 64 + COPY_16_BYTES + COPY_16_BYTES +#if CACHE_LINE_SIZE >= 128 + COPY_16_BYTES + COPY_16_BYTES + COPY_16_BYTES + COPY_16_BYTES +#endif +#endif +#endif + bdnz 53b + +63: srwi. r0,r5,2 + mtctr r0 + beq 64f +30: lwzu r0,4(r4) + stwu r0,4(r6) + bdnz 30b + +64: andi. r0,r5,3 + mtctr r0 + beq+ 65f +40: lbz r0,4(r4) + stb r0,4(r6) + addi r4,r4,1 + addi r6,r6,1 + bdnz 40b +65: blr + +_GLOBAL(memmove) + cmplw 0,r3,r4 + bgt .backwards_memcpy + /* fall through */ + +_GLOBAL(memcpy) + srwi. r7,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 + +_GLOBAL(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 + +_GLOBAL(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) + 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 + +_GLOBAL(__copy_tofrom_user) + addi r4,r4,-4 + addi r6,r3,-4 + neg r0,r3 + andi. r0,r0,CACHELINE_MASK /* # bytes to start of cache line */ + beq 58f + + cmplw 0,r5,r0 /* is this more than total to do? */ + blt 63f /* if not much to do */ + andi. r8,r0,3 /* get it word-aligned first */ + mtctr r8 + beq+ 61f +70: lbz r9,4(r4) /* do some bytes */ +71: stb r9,4(r6) + addi r4,r4,1 + addi r6,r6,1 + bdnz 70b +61: subf r5,r0,r5 + srwi. r0,r0,2 + mtctr r0 + beq 58f +72: lwzu r9,4(r4) /* do some words */ +73: stwu r9,4(r6) + bdnz 72b + +58: srwi. r0,r5,LG_CACHELINE_BYTES /* # complete cachelines */ + clrlwi r5,r5,32-LG_CACHELINE_BYTES + li r11,4 + beq 63f + + /* Here we decide how far ahead to prefetch the source */ +#if MAX_COPY_PREFETCH > 1 + /* Heuristically, for large transfers we prefetch + MAX_COPY_PREFETCH cachelines ahead. For small transfers + we prefetch 1 cacheline ahead. */ + cmpwi r0,MAX_COPY_PREFETCH + li r7,1 + li r3,4 + ble 111f + li r7,MAX_COPY_PREFETCH +111: mtctr r7 +112: dcbt r3,r4 + addi r3,r3,CACHELINE_BYTES + bdnz 112b +#else /* MAX_COPY_PREFETCH == 1 */ + li r3,CACHELINE_BYTES + 4 + dcbt r11,r4 +#endif /* MAX_COPY_PREFETCH */ + + mtctr r0 +53: + dcbt r3,r4 + dcbz r11,r6 +/* had to move these to keep extable in order */ + .section __ex_table,"a" + .align 3 + .llong 70b,100f + .llong 71b,101f + .llong 72b,102f + .llong 73b,103f + .llong 53b,105f + .text +/* the main body of the cacheline loop */ + COPY_16_BYTES_WITHEX(0) +#if CACHE_LINE_SIZE >= 32 + COPY_16_BYTES_WITHEX(1) +#if CACHE_LINE_SIZE >= 64 + COPY_16_BYTES_WITHEX(2) + COPY_16_BYTES_WITHEX(3) +#if CACHE_LINE_SIZE >= 128 + COPY_16_BYTES_WITHEX(4) + COPY_16_BYTES_WITHEX(5) + COPY_16_BYTES_WITHEX(6) + COPY_16_BYTES_WITHEX(7) +#endif +#endif +#endif + bdnz 53b + +63: srwi. r0,r5,2 + mtctr r0 + beq 64f +30: lwzu r0,4(r4) +31: stwu r0,4(r6) + bdnz 30b + +64: andi. r0,r5,3 + mtctr r0 + beq+ 65f +40: lbz r0,4(r4) +41: stb r0,4(r6) + addi r4,r4,1 + addi r6,r6,1 + bdnz 40b +65: li r3,0 + blr + +/* read fault, initial single-byte copy */ +100: li r4,0 + b 90f +/* write fault, initial single-byte copy */ +101: li r4,1 +90: subf r5,r8,r5 + li r3,0 + b 99f +/* read fault, initial word copy */ +102: li r4,0 + b 91f +/* write fault, initial word copy */ +103: li r4,1 +91: li r3,2 + b 99f + +/* + * this stuff handles faults in the cacheline loop and branches to either + * 104f (if in read part) or 105f (if in write part), after updating r5 + */ + COPY_16_BYTES_EXCODE(0) +#if CACHE_LINE_SIZE >= 32 + COPY_16_BYTES_EXCODE(1) +#if CACHE_LINE_SIZE >= 64 + COPY_16_BYTES_EXCODE(2) + COPY_16_BYTES_EXCODE(3) +#if CACHE_LINE_SIZE >= 128 + COPY_16_BYTES_EXCODE(4) + COPY_16_BYTES_EXCODE(5) + COPY_16_BYTES_EXCODE(6) + COPY_16_BYTES_EXCODE(7) +#endif +#endif +#endif + +/* read fault in cacheline loop */ +104: li r4,0 + b 92f +/* fault on dcbz (effectively a write fault) */ +/* or write fault in cacheline loop */ +105: li r4,1 +92: li r3,LG_CACHELINE_BYTES + b 99f +/* read fault in final word loop */ +108: li r4,0 + b 93f +/* write fault in final word loop */ +109: li r4,1 +93: andi. r5,r5,3 + li r3,2 + b 99f +/* read fault in final byte loop */ +110: li r4,0 + b 94f +/* write fault in final byte loop */ +111: li r4,1 +94: li r5,0 + li r3,0 +/* + * At this stage the number of bytes not copied is + * r5 + (ctr << r3), and r4 is 0 for read or 1 for write. + */ +99: mfctr r0 + slw r3,r0,r3 + add r3,r3,r5 + cmpwi 0,r4,0 + bne 120f +/* for read fault, clear out the destination: r3 bytes starting at 4(r6) */ + srwi. r0,r3,2 + li r9,0 + mtctr r0 + beq 113f +112: stwu r9,4(r6) + bdnz 112b +113: andi. r0,r3,3 + mtctr r0 + beq 120f +114: stb r9,4(r6) + addi r6,r6,1 + bdnz 114b +120: blr + + .section __ex_table,"a" + .align 3 + .llong 30b,108b + .llong 31b,109b + .llong 40b,110b + .llong 41b,111b + .llong 112b,120b + .llong 114b,120b + .text + +_GLOBAL(__clear_user) + addi r6,r3,-4 + li r3,0 + li r5,0 + cmplwi 0,r4,4 + blt 7f + /* clear a single word */ +11: stwu r5,4(r6) + beqlr + /* clear word sized chunks */ + andi. r0,r6,3 + add r4,r0,r4 + subf r6,r0,r6 + srwi r0,r4,2 + mtctr r0 + bdz 6f +1: stwu r5,4(r6) + bdnz 1b +6: andi. r4,r4,3 + /* clear byte sized chunks */ +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 3 + .llong 11b,99b + .llong 1b,99b + .llong 8b,99b + .text + +_GLOBAL(__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 3 + .llong 1b,99b + .text + +/* r3 = str, r4 = len (> 0), r5 = top (highest addr) */ +_GLOBAL(__strnlen_user) + addi r7,r3,-1 + subf r6,r7,r5 /* top+1 - str */ + cmplw 0,r4,r6 + bge 0f + mr r6,r4 +0: mtctr r6 /* ctr = min(len, top - str) */ +1: lbzu r0,1(r7) /* get next byte */ + cmpwi 0,r0,0 + bdnzf 2,1b /* loop if --ctr != 0 && byte != 0 */ + addi r7,r7,1 + subf r3,r3,r7 /* number of bytes we have looked at */ + beqlr /* return if we found a 0 byte */ + cmpw 0,r3,r4 /* did we look at all len bytes? */ + blt 99f /* if not, must have hit top */ + addi r3,r4,1 /* return len + 1 to indicate no null found */ + blr +99: li r3,0 /* bad address, return 0 */ + blr + + .section __ex_table,"a" + .align 3 + .llong 1b,99b diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/mm/Makefile linuxppc64_2_4/arch/ppc64/mm/Makefile --- linux-2.4.5-ac18/arch/ppc64/mm/Makefile Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/mm/Makefile Fri May 4 17:13:59 2001 @@ -0,0 +1,16 @@ +# +# Makefile for the linux ppc-specific parts of the memory manager. +# +# 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... + +EXTRA_CFLAGS = -mno-minimal-toc + +O_TARGET := mm.o + +obj-y := fault.o init.o mem_pieces.o extable.o btmalloc.o imalloc.o + +include $(TOPDIR)/Rules.make diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/mm/btmalloc.c linuxppc64_2_4/arch/ppc64/mm/btmalloc.c --- linux-2.4.5-ac18/arch/ppc64/mm/btmalloc.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/mm/btmalloc.c Tue Jun 19 23:28:34 2001 @@ -0,0 +1,232 @@ +/* + * C 2001 PPC 64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include + +#include +#include +#include +#include + +extern pgd_t *bolted_pgd; +extern struct vmalloc_struct btmalloc_ops; +extern struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags, + struct vmalloc_struct *v); +extern pte_t *find_linux_pte(pgd_t *pgdir, unsigned long ea); +extern void build_valid_hpte(unsigned long vsid, unsigned long ea, + unsigned long pa, pte_t *ptep, unsigned hpteflags, + HPTEX_flags hptexflags ); + +/* + * This lock guarantees that only one task will be attempting + * to do pmd_alloc or pte_alloc for the bolted page table + */ +static spinlock_t bolted_pte_lock = SPIN_LOCK_UNLOCKED; + +pmd_t *pmd_alloc_b(pgd_t *pgd, unsigned long address) +{ + pmd_t *pmd; + void *page; + unsigned long ea, pa, vsid; + struct vm_struct *area; + HPTEX_flags hptexflags; + + if (!pgd_present(*pgd)) { + spin_unlock(&bolted_pte_lock); + page = (void *)__get_free_page(GFP_KERNEL); + spin_lock(&bolted_pte_lock); + if (!page) + return NULL; + + /* + * Because we dropped the lock, we should re-check the + * entry, as somebody else could have populated it.. + */ + if (pgd_present(*pgd)) { + __free_pages(page, 0); + goto out; + } + + clear_page(page); + /* allocate space in the bolted address range */ + area = __get_vm_area(PAGE_SIZE, 0, &btmalloc_ops); + ea = (unsigned long)area->addr; + pa = __pa(page); + vsid = get_kernel_vsid(ea); + *(char *)&hptexflags = 0; + hptexflags.bolted = 1; + hptexflags.valid = 1; + build_valid_hpte(vsid, ea, pa, NULL, + _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX, hptexflags); + pmd = (pmd_t *)ea; + pgd_set(pgd, pmd); + } +out: + return pmd_offset(pgd, address); +} + +pte_t *pte_alloc_b(pmd_t *pmd, unsigned long address) +{ + pte_t *pte; + void *page; + struct vm_struct *area; + unsigned long ea, pa, vsid; + HPTEX_flags hptexflags; + + if (!pmd_present(*pmd)) { + spin_unlock(&bolted_pte_lock); + page = (void *)__get_free_page(GFP_KERNEL); + spin_lock(&bolted_pte_lock); + if (!page) + return NULL; + + /* + * Because we dropped the lock, we should re-check the + * entry, as somebody else could have populated it.. + */ + if (pmd_present(*pmd)) { + __free_pages(page, 0); + goto out; + } + + clear_page(page); + /* allocate space in the bolted address range */ + area = __get_vm_area(PAGE_SIZE, 0, &btmalloc_ops); + ea = (unsigned long)area->addr; + pa = __pa(page); + vsid = get_kernel_vsid(ea); + *(char *)&hptexflags = 0; + hptexflags.bolted = 1; + hptexflags.valid = 1; + build_valid_hpte(vsid, ea, pa, NULL, + _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX, hptexflags); + pte = (pte_t *)ea; + pmd_set(pmd, pte); + } +out: + return pte_offset(pmd, address); +} + +static unsigned long btmalloc_start(void) +{ + return BTMALLOC_START; +} + +static unsigned long btmalloc_end(void) +{ + return BTMALLOC_END; +} + +static int btmalloc_area_pages(unsigned long address, unsigned long size, + int gfp_mask, pgprot_t prot); +static void btfree_area_pages(unsigned long address, unsigned long size); + +struct vmalloc_struct btmalloc_ops = { + lock: RW_LOCK_UNLOCKED, + start: btmalloc_start, + end: btmalloc_end, + alloc_area_pages: btmalloc_area_pages, + free_area_pages: btfree_area_pages, +}; + +static void btfree_area_pages(unsigned long address, unsigned long size) +{ + pte_t *ptep; + pte_t pte; + unsigned long num; + + num = (size >> PAGE_SHIFT); + + while(num--) { + ptep = find_linux_pte(bolted_pgd, address); + if (!ptep) { + write_unlock(&btmalloc_ops.lock); + panic("btfree_area_pages - page being freed (%016lx) is not bolted", address); + } + pte = fetch_pte(ptep); + pte_clear(ptep); + __free_pages(pte_page(pte), 0); + address += PAGE_SIZE; + } +} + +static int btmalloc_area_pages(unsigned long address, unsigned long size, + int gfp_mask, pgprot_t prot) +{ + pgd_t *pgdp; + pmd_t *pmdp; + pte_t *ptep; + unsigned long eaddr; + unsigned long pa, vsid, pg_count, page; + HPTEX_flags hptexflags; + + pg_count = (size >> PAGE_SHIFT); + + /* Create a Linux page table entry and an HPTE for each page */ + for(page = 0; page < pg_count; page++) { + pa = get_free_page(GFP_KERNEL) - PAGE_OFFSET; + eaddr = address + (page * PAGE_SIZE); + + /* + * We need a lock here to avoid multiple tasks doing + * pmd_alloc_b and/or pte_alloc_b simultaneously + */ + spin_lock(&bolted_pte_lock); + + /* + * Get a pointer to the linux page table entry for this page + * allocating pmd or pte pages along the way as needed + */ + pgdp = pgd_offset_b(eaddr); + pmdp = pmd_alloc_b(pgdp, eaddr); + ptep = pte_alloc_b(pmdp, eaddr); + + /* + * At this point we are done allocating any new pmd/pte pages + * We own the pte (ptep) by nature of owning the eaddr so we + * no longer need the lock + */ + spin_unlock(&bolted_pte_lock); + + /* Clear any old hpte and set the new linux pte */ + set_pte(ptep, mk_pte_phys(pa & PAGE_MASK, PAGE_KERNEL)); + + vsid = get_kernel_vsid(eaddr); + + /* initialize the hptex flags to zero. */ + *(char*)&hptexflags = 0; + + /* indicate that the page is bolted (won't be removed from hpt*/ + hptexflags.bolted = 1; + + /* indicate that the hpte is valid. */ + hptexflags.valid = 1; + + build_valid_hpte(vsid, eaddr, pa, ptep, + _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX, hptexflags); + } + + return 0; +} + +void btfree(void *addr) +{ + __vfree(addr, &btmalloc_ops); +} + +void *btmalloc(unsigned long size) +{ + return ___vmalloc(size, 0, 0, &btmalloc_ops); +} + +long btread(char *buf, char *addr, unsigned long count) +{ + return __vread(buf, addr, count, &btmalloc_ops); +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/mm/extable.c linuxppc64_2_4/arch/ppc64/mm/extable.c --- linux-2.4.5-ac18/arch/ppc64/mm/extable.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/mm/extable.c Mon May 7 12:48:27 2001 @@ -0,0 +1,48 @@ +/* + * linux/arch/ppc/mm/extable.c + * + * from linux/arch/i386/mm/extable.c + * + * 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 +#include + +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) +{ + 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; + } + return 0; +} + +unsigned long +search_exception_table(unsigned long addr) +{ + unsigned long ret; + + ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr); + if (ret) return ret; + + return 0; +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/mm/fault.c linuxppc64_2_4/arch/ppc64/mm/fault.c --- linux-2.4.5-ac18/arch/ppc64/mm/fault.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/mm/fault.c Mon Jun 4 13:44:30 2001 @@ -0,0 +1,237 @@ +/* + * arch/ppc/mm/fault.c + * + * 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. + * + * Modified for PPC64 by Dave Engebretsen (engebret@ibm.com) + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) +extern void (*debugger)(struct pt_regs *); +extern void (*debugger_fault_handler)(struct pt_regs *); +extern int (*debugger_dabr_match)(struct pt_regs *); +int debugger_kernel_faults = 1; +#endif + +unsigned long htab_reloads = 0; /* updated by head.S:hash_page() */ +unsigned long htab_evicts = 0; /* updated by head.S:hash_page() */ +unsigned long pte_misses = 0; /* updated by do_page_fault() */ +unsigned long pte_errors = 0; /* updated by do_page_fault() */ +unsigned int probingmem = 0; + +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); + +/* + * For 600- and 800-family processors, 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 vm_area_struct * vma; + struct mm_struct *mm = current->mm; + siginfo_t info; + unsigned long code = SEGV_MAPERR; + unsigned long is_write = error_code & 0x02000000; + unsigned long mm_fault_return; + + PPCDBG(PPCDBG_MM, "Entering do_page_fault: addr = 0x%16.16lx, error_code = %lx\n\tregs_trap = %lx, srr0 = %lx, srr1 = %lx\n", address, error_code, regs->trap, get_srr0(), get_srr1()); + /* + * Fortunately the bit assignments in SRR1 for an instruction + * fault and DSISR for a data fault are mostly the same for the + * bits we are interested in. But there are some bits which + * indicate errors in DSISR but can validly be set in SRR1. + */ + if (regs->trap == 0x400) + error_code &= 0x48200000; + +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_fault_handler && regs->trap == 0x300) { + debugger_fault_handler(regs); + return; + } + if (error_code & 0x00400000) { + /* DABR match */ + if (debugger_dabr_match(regs)) + return; + } +#endif /* CONFIG_XMON || CONFIG_KGDB */ + + if (in_interrupt() || mm == NULL) { + bad_page_fault(regs, address); + return; + } + down_read(&mm->mmap_sem); + vma = find_vma(mm, address); + PPCDBG(PPCDBG_MM, "\tdo_page_fault: vma = 0x%16.16lx\n", vma); + if (!vma) { + PPCDBG(PPCDBG_MM, "\tdo_page_fault: !vma\n"); + goto bad_area; + } + PPCDBG(PPCDBG_MM, "\tdo_page_fault: vma->vm_start = 0x%16.16lx, vma->vm_flags = 0x%16.16lx\n", vma->vm_start, vma->vm_flags); + if (vma->vm_start <= address) { + goto good_area; + } + if (!(vma->vm_flags & VM_GROWSDOWN)) { + PPCDBG(PPCDBG_MM, "\tdo_page_fault: vma->vm_flags = %lx, %lx\n", vma->vm_flags, VM_GROWSDOWN); + goto bad_area; + } + if (expand_stack(vma, address)) { + PPCDBG(PPCDBG_MM, "\tdo_page_fault: expand_stack\n"); + goto bad_area; + } + +good_area: + code = SEGV_ACCERR; + + /* a write */ + if (is_write) { + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area; + /* a read */ + } else { + /* protection fault */ + if (error_code & 0x08000000) + goto bad_area; + if (!(vma->vm_flags & (VM_READ | VM_EXEC))) + goto bad_area; + } + + /* + * If for any reason at all we couldn't handle the fault, + * make sure we exit gracefully rather than endlessly redo + * the fault. + */ + PPCDBG(PPCDBG_MM, "\tdo_page_fault: calling handle_mm_fault\n"); + mm_fault_return = handle_mm_fault(mm, vma, address, is_write); + PPCDBG(PPCDBG_MM, "\tdo_page_fault: handle_mm_fault = 0x%lx\n", + mm_fault_return); + switch(mm_fault_return) { + case 1: + current->min_flt++; + break; + case 2: + current->maj_flt++; + break; + case 0: + goto do_sigbus; + default: + goto out_of_memory; + } + + up_read(&mm->mmap_sem); + /* + * keep track of tlb+htab misses that are good addrs but + * just need pte's created via handle_mm_fault() + * -- Cort + */ + pte_misses++; + return; + +bad_area: + up_read(&mm->mmap_sem); + pte_errors++; + + /* User mode accesses cause a SIGSEGV */ + if (user_mode(regs)) { + info.si_signo = SIGSEGV; + info.si_errno = 0; + info.si_code = code; + info.si_addr = (void *) address; + PPCDBG(PPCDBG_SIGNAL, "Bad addr in user: 0x%lx\n", address); +#ifdef CONFIG_XMON + ifppcdebug(PPCDBG_SIGNALXMON) + PPCDBG_ENTER_DEBUGGER(); +#endif + + force_sig_info(SIGSEGV, &info, current); + return; + } + + bad_page_fault(regs, address); + return; + +/* + * We ran out of memory, or some other thing happened to us that made + * us unable to handle the page fault gracefully. + */ +out_of_memory: + up_read(&mm->mmap_sem); + printk("VM: killing process %s\n", current->comm); + if (user_mode(regs)) + do_exit(SIGKILL); + bad_page_fault(regs, address); + return; + +do_sigbus: + up_read(&mm->mmap_sem); + info.si_signo = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_ADRERR; + info.si_addr = (void *)address; + force_sig_info (SIGBUS, &info, current); + if (!user_mode(regs)) + bad_page_fault(regs, address); +} + +/* + * bad_page_fault is called when we have a bad access from the kernel. + * It is called from do_page_fault above and from some of the procedures + * in traps.c. + */ +void +bad_page_fault(struct pt_regs *regs, unsigned long address) +{ + unsigned long fixup; + + /* Are we prepared to handle this fault? */ + if ((fixup = search_exception_table(regs->nip)) != 0) { + regs->nip = fixup; + return; + } + + /* kernel has accessed a bad area */ + show_regs(regs); +#if defined(CONFIG_XMON) || defined(CONFIG_KGDB) + if (debugger_kernel_faults) + debugger(regs); +#endif + print_backtrace( (unsigned long *)regs->gpr[1] ); + panic("kernel access of bad area pc %lx lr %lx address %lX tsk %s/%d", + regs->nip,regs->link,address,current->comm,current->pid); +} + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/mm/imalloc.c linuxppc64_2_4/arch/ppc64/mm/imalloc.c --- linux-2.4.5-ac18/arch/ppc64/mm/imalloc.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/mm/imalloc.c Mon Jun 4 20:06:03 2001 @@ -0,0 +1,69 @@ +/* + * c 2001 PPC 64 Team, IBM Corp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include + +#include +#include + +rwlock_t imlist_lock = RW_LOCK_UNLOCKED; +struct vm_struct * imlist = NULL; + +struct vm_struct *get_im_area(unsigned long size) { + unsigned long addr; + struct vm_struct **p, *tmp, *area; + + area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL); + if (!area) + return NULL; + addr = IMALLOC_START; + write_lock(&imlist_lock); + for (p = &imlist; (tmp = *p) ; p = &tmp->next) { + if (size + addr < (unsigned long) tmp->addr) + break; + addr = tmp->size + (unsigned long) tmp->addr; + if (addr > IMALLOC_END-size) { + write_unlock(&imlist_lock); + kfree(area); + return NULL; + } + } + area->flags = 0; + area->addr = (void *)addr; + area->size = size; + area->next = *p; + *p = area; + write_unlock(&imlist_lock); + return area; +} + +void ifree(void * addr) { + struct vm_struct **p, *tmp; + + if (!addr) + return; + if ((PAGE_SIZE-1) & (unsigned long) addr) { + printk(KERN_ERR "Trying to ifree() bad address (%p)\n", addr); + return; + } + write_lock(&imlist_lock); + for (p = &imlist ; (tmp = *p) ; p = &tmp->next) { + if (tmp->addr == addr) { + *p = tmp->next; + kfree(tmp); + write_unlock(&imlist_lock); + return; + } + } + write_unlock(&imlist_lock); + printk(KERN_ERR "Trying to ifree() nonexistent area (%p)\n", addr); +} + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/mm/init.c linuxppc64_2_4/arch/ppc64/mm/init.c --- linux-2.4.5-ac18/arch/ppc64/mm/init.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/mm/init.c Tue Jul 3 00:00:35 2001 @@ -0,0 +1,702 @@ +/* + * + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) + * and Cort Dougan (PReP) (cort@cs.nmt.edu) + * Copyright (C) 1996 Paul Mackerras + * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). + * + * Derived from "arch/i386/mm/init.c" + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * + * Dave Engebretsen + * Rework for PPC64 port. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_BLK_DEV_INITRD +#include /* for initrd_* */ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mem_pieces.h" +#include + +#include + +#define PGTOKB(pages) (((pages) * PAGE_SIZE) >> 10) + +#ifdef CONFIG_PPC_ISERIES +#include +#endif + +struct mmu_context_stack_t mmu_context_stack; +int mem_init_done; +unsigned long ioremap_bot = IMALLOC_BASE; + +static unsigned long *end_of_DRAM; +static int init_bootmem_done; +static int boot_mapsize; +static unsigned long totalram_pages; +static struct device_node *memory_node; + +extern pgd_t swapper_pg_dir[]; +extern char __init_begin, __init_end; +extern char __chrp_begin, __chrp_end; +extern char __openfirmware_begin, __openfirmware_end; +extern struct _of_tce_table of_tce_table[]; +extern char _start[], _end[]; +extern char _stext[], etext[]; +extern struct task_struct *current_set[NR_CPUS]; +extern struct Naca *naca; +extern u32 msChunks[]; + +void mm_init_ppc64(void); + +void make_pte(HPTE * htab, HPTEX * htabx, + unsigned long va, unsigned long pa, + int mode, unsigned long hash_mask); + +void mem_pieces_print( struct mem_pieces * ); + +unsigned long *pmac_find_end_of_memory(void); +extern unsigned long *find_end_of_memory(void); + +extern pgd_t ioremap_dir[]; +extern pgd_t bolted_dir[]; +pgd_t * ioremap_pgd = (pgd_t *)&ioremap_dir; +pgd_t * bolted_pgd = (pgd_t *)&bolted_dir; + +static void map_io_page(unsigned long va, unsigned long pa, int flags); +void set_phys_avail(struct mem_pieces *mp); +extern void die_if_kernel(char *,struct pt_regs *,long); + +static struct mem_pieces phys_mem; +struct mem_pieces phys_avail; +char *klimit = _end; + +HPTE *Hash=0; +unsigned long Hash_size=0; +unsigned long _SDR1=0; +unsigned long _ASR=0; + +/* max amount of RAM to use */ +unsigned long __max_memory; + +/* This is declared as we are using the more or less generic + * include/asm-ppc64/tlb.h file -- tgall + */ +mmu_gather_t mmu_gathers[NR_CPUS]; + +int do_check_pgt_cache(int low, int high) +{ + int freed = 0; + if(pgtable_cache_size > high) { + do { + if(pgd_quicklist) + free_pgd_slow(get_pgd_fast()), freed++; + if(pmd_quicklist) + free_pmd_slow(get_pmd_fast()), freed++; + if(pte_quicklist) + { + free_pte_slow(pte_alloc_one_fast(NULL,0)); + freed++; + } + } while(pgtable_cache_size > low); + } + return freed; +} + +void show_mem(void) +{ + int i,free = 0,total = 0,reserved = 0; + int shared = 0, cached = 0; + struct task_struct *p; + + printk("Mem-info:\n"); + show_free_areas(); + printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); + i = max_mapnr; + while (i-- > 0) { + total++; + if (PageReserved(mem_map+i)) + reserved++; + else if (PageSwapCache(mem_map+i)) + cached++; + else if (!atomic_read(&mem_map[i].count)) + free++; + else + shared += atomic_read(&mem_map[i].count) - 1; + } + printk("%d pages of RAM\n",total); + printk("%d free pages\n",free); + printk("%d reserved pages\n",reserved); + printk("%d pages shared\n",shared); + printk("%d pages swap cached\n",cached); + printk("%d pages in page table cache\n",(int)pgtable_cache_size); + show_buffers(); + printk("%-8s %3s %8s %8s %8s %9s %8s", "Process", "Pid", + "Ctx", "Ctx<<4", "Last Sys", "pc", "task"); +#ifdef CONFIG_SMP + printk(" %3s", "CPU"); +#endif /* CONFIG_SMP */ + printk("\n"); + for_each_task(p) + { + printk("%-8.8s %3d %8ld %8ld %8ld %c%08lx %08lx ", + p->comm,p->pid, + (p->mm)?p->mm->context:0, + (p->mm)?(p->mm->context<<4):0, + p->thread.last_syscall, + (p->thread.regs)?user_mode(p->thread.regs) ? 'u' : 'k' : '?', + (p->thread.regs)?p->thread.regs->nip:0, + (ulong)p); + { + int iscur = 0; +#ifdef CONFIG_SMP + printk("%3d ", p->processor); + if ( (p->processor != NO_PROC_ID) && + (p == current_set[p->processor]) ) + { + iscur = 1; + printk("current"); + } +#else + if ( p == current ) + { + iscur = 1; + printk("current"); + } + + if ( p == last_task_used_math ) + { + if ( iscur ) + printk(","); + printk("last math"); + } +#endif /* CONFIG_SMP */ + printk("\n"); + } + } +} + +void si_meminfo(struct sysinfo *val) +{ + val->totalram = totalram_pages; + val->sharedram = 0; + val->freeram = nr_free_pages(); + val->bufferram = atomic_read(&buffermem_pages); + val->totalhigh = 0; + val->freehigh = 0; + val->mem_unit = PAGE_SIZE; +} + +void * +ioremap(unsigned long addr, unsigned long size) +{ + return __ioremap(addr, size, _PAGE_NO_CACHE); +} + +extern struct vm_struct * get_im_area( unsigned long size ); + +void * +__ioremap(unsigned long addr, unsigned long size, unsigned long flags) +{ + unsigned long pa, ea, i; + + /* + * Choose an address to map it to. + * Once the imalloc system is running, we use it. + * Before that, we map using addresses going + * up from ioremap_bot. imalloc will use + * the addresses from ioremap_bot through + * IMALLOC_END (0xE000001fffffffff) + * + */ + pa = addr & PAGE_MASK; + size = PAGE_ALIGN(addr + size) - pa; + + // MIKEC: Do we still need to support this? + /* + * If the address lies within the first 16 MB, assume it's in ISA + * memory space + */ + if (pa < 16*1024*1024) + pa += _ISA_MEM_BASE; + + /* + * Don't allow anybody to remap normal RAM that we're using. + * mem_init() sets high_memory so only do the check after that. + */ + if ( mem_init_done && (pa < virt_to_phys(high_memory)) ) + { + printk("__ioremap(): phys addr %0lx is RAM lr %p\n", pa, + __builtin_return_address(0)); + return NULL; + } + + if (size == 0) + return NULL; + + if (mem_init_done) { + struct vm_struct *area; + area = get_im_area(size); + if (area == 0) + return NULL; + ea = (unsigned long)(area->addr); + } + else { + ea = ioremap_bot; + ioremap_bot += size; + } + + if ((flags & _PAGE_PRESENT) == 0) + flags |= pgprot_val(PAGE_KERNEL); + if (flags & (_PAGE_NO_CACHE | _PAGE_WRITETHRU)) + flags |= _PAGE_GUARDED; + + for (i = 0; i < size; i += PAGE_SIZE) { + map_io_page(ea+i, pa+i, flags); + } + + return (void *) (ea + (addr & ~PAGE_MASK)); +} + +void iounmap(void *addr) +{ + /* DRENG / PPPBBB todo */ +} + +unsigned long iopa(unsigned long addr) +{ + pmd_t *pd; + pte_t *pg; + + /* Do we have a page table? */ + if (init_mm.pgd == NULL) + return 0; + + /* Use upper 10 bits of addr to index the first level map */ + pd = (pmd_t *) (init_mm.pgd + (addr >> PGDIR_SHIFT)); + if (pmd_none(*pd)) + return 0; + + /* Use middle 10 bits of addr to index the second-level map */ + pg = pte_offset(pd, addr); + return (pte_val(*pg) & PAGE_MASK) | (addr & ~PAGE_MASK); +} + +/* + * map_io_page currently only called by __ioremap + * map_io_page adds an entry to the ioremap page table + * and adds an entry to the HPT, possibly bolting it + */ +static void map_io_page(unsigned long ea, unsigned long pa, int flags) +{ + pgd_t *pgdp; + pmd_t *pmdp; + pte_t *ptep; + unsigned long vsid; + + if(mem_init_done) { + spin_lock(&init_mm.page_table_lock); + pgdp = pgd_offset_i(ea); + pmdp = pmd_alloc(&init_mm, pgdp, ea); + ptep = pte_alloc(&init_mm, pmdp, ea); + set_pte(ptep, mk_pte_phys(pa & PAGE_MASK, __pgprot(flags))); + spin_unlock(&init_mm.page_table_lock); + } else { + /* If the mm subsystem is not fully up, we cannot create a linux + * page table entry for this mapping. Simply bolt an entry in the + * hardware page table. + */ + vsid = get_kernel_vsid(ea); + make_pte(htab_data.htab, htab_data.htabx, + (vsid << 28) | (ea & 0xFFFFFFF), // va (NOT the ea) + pa, + _PAGE_NO_CACHE | _PAGE_GUARDED | PP_RWXX, + htab_data.htab_hash_mask); + } +} + +void +local_flush_tlb_all(void) +{ +} + +void +local_flush_tlb_mm(struct mm_struct *mm) +{ +} + +void +local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr) +{ +} + +void +local_flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) +{ +} + + +static void get_mem_prop(char *, struct mem_pieces *); + +/* + * Read in a property describing some pieces of memory. + */ + +static void __init get_mem_prop(char *name, struct mem_pieces *mp) +{ + struct reg_property *rp; + int s; + + rp = (struct reg_property *) get_property(memory_node, name, &s); + if (rp == NULL) { + printk(KERN_ERR "error: couldn't get %s property on /memory\n", + name); + abort(); + } + mp->n_regions = s / sizeof(mp->regions[0]); + memcpy(mp->regions, rp, s); + + /* Make sure the pieces are sorted. */ + mem_pieces_sort(mp); + mem_pieces_coalesce(mp); +} + + +void __init free_initmem(void) +{ + unsigned long a; + unsigned long num_freed_pages = 0; +#define FREESEC(START,END,CNT) do { \ + a = (unsigned long)(&START); \ + for (; a < (unsigned long)(&END); a += PAGE_SIZE) { \ + clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); \ + set_page_count(mem_map+MAP_NR(a), 1); \ + free_page(a); \ + CNT++; \ + } \ +} while (0) + + FREESEC(__init_begin,__init_end,num_freed_pages); + + printk ("Freeing unused kernel memory: %ldk init\n", + PGTOKB(num_freed_pages)); +} + +#ifdef CONFIG_BLK_DEV_INITRD +void free_initrd_mem(unsigned long start, unsigned long end) +{ + for (; start < end; start += PAGE_SIZE) { + ClearPageReserved(mem_map + MAP_NR(start)); + set_page_count(mem_map+MAP_NR(start), 1); + free_page(start); + totalram_pages++; + } + printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10); +} +#endif + + +/* Reserve all contexts < FIRST_USER_CONTEXT for kernel use. + * The range of contexts [FIRST_USER_CONTEXT, NUM_USER_CONTEXT) + * are stored on a stack for easy allocation and deallocation. + */ +void +init_context_stack(void) +{ + mm_context_t top; + + mmu_context_stack.lock = SPIN_LOCK_UNLOCKED; + mmu_context_stack.top = FIRST_USER_CONTEXT; + for(top=0; top < FIRST_USER_CONTEXT ;top++) { + mmu_context_stack.stack[top] = NO_CONTEXT; + } + for(top=FIRST_USER_CONTEXT; top < NUM_USER_CONTEXT ;top++) { + mmu_context_stack.stack[top] = top; + } +} + + +/* + * Do very early mm setup. + */ +void __init mm_init_ppc64(void) { + int i; + + ppc_md.progress("MM:init", 0); + + phys_mem.n_regions = 1; + phys_mem.regions[0].address = 0; + phys_mem.regions[0].size = naca->physicalMemorySize; + + ifppcdebug(PPCDBG_MMINIT) { + udbg_puts("naca = "); + udbg_puthex((unsigned long)naca); udbg_putc('\n'); + udbg_puts("naca->physicalMemorySize = "); + udbg_puthex(naca->physicalMemorySize); udbg_putc('\n'); + } + set_phys_avail(&phys_mem); + + end_of_DRAM = htab_data.last_kernel_address + 1; + + /* Reserve all contexts < FIRST_USER_CONTEXT for kernel use. + * The range of contexts [FIRST_USER_CONTEXT, NUM_USER_CONTEXT) + * are stored on a stack for easy allocation and deallocation. + */ + init_context_stack(); + + /* This should really be done earlier. On iSeries, this + * is completed much earlier. When non-contiguous memory + * is supported for pSeries then this should be done + * early. + */ + + if ( _machine != _MACH_iSeries ) { + for(i = 0; i < 4096*4; i++) { + msChunks[i] = i; + } + } + + ppc_md.progress("MM:exit", 0x211); +} + + + +/* + * Initialize the bootmem system and give it all the memory we + * have available. + */ +void __init do_init_bootmem(void) +{ + unsigned long start, bitmap_bytes; + int i; + + PPCDBG(PPCDBG_MMINIT, "do_init_bootmem: start\n"); + /* + * Find an area to use for the bootmem bitmap. Calculate the size of + * bitmap required as (Total Memory) / PAGE_SIZE / BITS_PER_BYTE. + * Add 1 additional page in case the address isn't page-aligned. + */ + bitmap_bytes = (((naca->physicalMemorySize) >> PAGE_SHIFT) >> 3) + + PAGE_SIZE; + + start = 0; + for (i = 0; i < phys_avail.n_regions; ++i) { + unsigned long a = phys_avail.regions[i].address; + unsigned long s = phys_avail.regions[i].size; + if (s >= bitmap_bytes) { + start = a; + break; + } + } + + if(start == 0) { + udbg_printf("do_init_bootmem: failed to allocate a bitmap.\n"); + udbg_printf("\tbitmap_bytes = 0x%lx.\n", bitmap_bytes); + PPCDBG_ENTER_DEBUGGER(); + } + + start = PAGE_ALIGN(start); + + PPCDBG(PPCDBG_MMINIT, "\tstart = 0x%lx\n", start); + PPCDBG(PPCDBG_MMINIT, "\tbitmap_bytes = 0x%lx\n", bitmap_bytes); + PPCDBG(PPCDBG_MMINIT, "\tphysicalMemorySize = 0x%lx\n", naca->physicalMemorySize); + + boot_mapsize = init_bootmem(start >> PAGE_SHIFT, + __pa(end_of_DRAM) >> PAGE_SHIFT); + + PPCDBG(PPCDBG_MMINIT, "\tboot_mapsize = 0x%lx\n", boot_mapsize); + + /* Hack...remove bottom 16MB for ISA. Not needed w/TCEs DRENG. */ + mem_pieces_remove(&phys_avail, 0, (16<<20), 1); + + /* remove the bootmem bitmap from the available memory */ + mem_pieces_remove(&phys_avail, start, boot_mapsize, 1); + + /* add everything in phys_avail into the bootmem map */ + for (i = 0; i < phys_avail.n_regions; ++i) { + free_bootmem(phys_avail.regions[i].address, + phys_avail.regions[i].size); + } + + PPCDBG(PPCDBG_MMINIT, "phys_avail: \n"); + mem_pieces_print(&phys_avail); + + init_bootmem_done = 1; +} + +/* + * paging_init() sets up the page tables - in fact we've already done this. + */ +void __init paging_init(void) +{ + unsigned long zones_size[MAX_NR_ZONES], i; + + /* + * All pages are DMA-able so we put them all in the DMA zone. + */ + zones_size[0] = ((unsigned long)end_of_DRAM - KERNELBASE) >> PAGE_SHIFT; + for (i = 1; i < MAX_NR_ZONES; i++) + zones_size[i] = 0; + free_area_init(zones_size); +} + +void __init mem_init(void) +{ + extern char *sysmap; + extern unsigned long sysmap_size; + unsigned long addr; + int codepages = 0; + int datapages = 0; + int initpages = 0; + unsigned long va_rtas_base = (unsigned long)__va(rtas_data.base); + max_mapnr = max_low_pfn; + high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); + num_physpages = max_mapnr; /* RAM is assumed contiguous */ + + totalram_pages += free_all_bootmem(); + + ifppcdebug(PPCDBG_MMINIT) { + udbg_printf("mem_init: totalram_pages = 0x%lx\n", totalram_pages); + udbg_printf("mem_init: va_rtas_base = 0x%lx\n", va_rtas_base); + udbg_printf("mem_init: va_rtas_end = 0x%lx\n", PAGE_ALIGN(va_rtas_base+rtas_data.size)); + udbg_printf("mem_init: pinned start = 0x%lx\n", __va(0)); + udbg_printf("mem_init: pinned end = 0x%lx\n", PAGE_ALIGN((unsigned long)klimit)); + } + + if ( sysmap_size ) + for (addr = (unsigned long)sysmap; + addr < PAGE_ALIGN((unsigned long)sysmap+sysmap_size) ; + addr += PAGE_SIZE) + SetPageReserved(mem_map + MAP_NR(addr)); + + for (addr = PAGE_OFFSET; addr < (unsigned long)end_of_DRAM; + addr += PAGE_SIZE) { + if (!PageReserved(mem_map + MAP_NR(addr))) + continue; + if (addr < (ulong) etext) + codepages++; + + else if (addr >= (unsigned long)&__init_begin + && addr < (unsigned long)&__init_end) + initpages++; + else if (addr < (ulong) klimit) + datapages++; + } + + printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%08lx,%08lx]\n", + (unsigned long)nr_free_pages()<< (PAGE_SHIFT-10), + codepages<< (PAGE_SHIFT-10), datapages<< (PAGE_SHIFT-10), + initpages<< (PAGE_SHIFT-10), + PAGE_OFFSET, (unsigned long) end_of_DRAM); + mem_init_done = 1; + +#ifdef CONFIG_PPC_ISERIES + create_virtual_bus_tce_table(); +#endif +} + +/* + * Set phys_avail to phys_mem less the kernel text/data/bss. + */ +void __init +set_phys_avail(struct mem_pieces *mp) +{ + unsigned long kstart, ksize, i; + + PPCDBG(PPCDBG_MMINIT, "set_phys_avail: start\n"); + + /* + * Initially, available physical memory is equivalent to all + * physical memory. + */ + phys_avail = *mp; + + /* + * Map out the kernel text/data/bss from the available physical + * memory. + */ + kstart = __pa(_stext); /* should be 0 */ + ksize = PAGE_ALIGN(klimit - _stext); + + mem_pieces_remove(&phys_avail, kstart, ksize, 0); + +#if defined(CONFIG_BLK_DEV_INITRD) + /* Remove the init RAM disk from the available memory. */ + if (initrd_start) { + mem_pieces_remove(&phys_avail, __pa(initrd_start), + initrd_end - initrd_start, 1); + } +#endif /* CONFIG_BLK_DEV_INITRD */ + +#if 1 + i = 0; + while(of_tce_table[i].node) { + PPCDBG(PPCDBG_TCE, "\ttce_table[%d].node = 0x%lx\n", + i, of_tce_table[i].node); + PPCDBG(PPCDBG_TCE, "\ttce_table[%d].base = 0x%lx\n", + i, of_tce_table[i].base); + PPCDBG(PPCDBG_TCE, "\ttce_table[%d].size = 0x%lx\n", + i, of_tce_table[i].size); + + mem_pieces_remove(&phys_avail, of_tce_table[i].base - KERNELBASE, + of_tce_table[i].size, 0); + + i++; + } +#endif + + PPCDBG(PPCDBG_MMINIT, "phys_mem: \n"); + mem_pieces_print(&phys_mem); + PPCDBG(PPCDBG_MMINIT, "phys_avail: \n"); + mem_pieces_print(&phys_avail); + + PPCDBG(PPCDBG_MMINIT, "set_phys_avail: done\n"); +} + +unsigned long _virt_to_absolute( unsigned long ea ) +{ + unsigned long pa,abs; + + pa = ___pa( ea ); + abs = (((unsigned long)msChunks[(pa>>18)])<<18) + ((unsigned long)(pa & 0x3ffff)); + return abs; +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/mm/mem_pieces.c linuxppc64_2_4/arch/ppc64/mm/mem_pieces.c --- linux-2.4.5-ac18/arch/ppc64/mm/mem_pieces.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/mm/mem_pieces.c Wed Jun 6 03:23:24 2001 @@ -0,0 +1,188 @@ +/* + * Copyright (c) 1996 Paul Mackerras + * Changes to accomodate Power Macintoshes. + * Cort Dougan + * Rewrites. + * Grant Erickson + * General rework and split from mm/init.c. + * Dave Engebretsen + * PPC 64b rework. + * + * Module name: mem_pieces.c + * + * Description: + * Routines and data structures for manipulating and representing + * phyiscal memory extents (i.e. address/length pairs). + * + */ + +#include +#include +#include +#include +#include + +#include "mem_pieces.h" + +#include + +extern struct mem_pieces phys_avail; + +void mem_pieces_print(struct mem_pieces *); + +/* + * Scan a region for a piece of a given size with the required alignment. + */ +void __init * +mem_pieces_find(unsigned long size, unsigned int align) +{ + long i; + unsigned long a, e; + struct mem_pieces *mp = &phys_avail; + + for (i = 0; i < mp->n_regions; ++i) { + a = mp->regions[i].address; + e = a + mp->regions[i].size; + a = (a + align - 1) & -align; + if (a + size <= e) { + mem_pieces_remove(mp, a, size, 1); + return __va(a); + } + } + panic("Couldn't find %lu bytes at %u alignment\n", size, align); + + return NULL; +} + +/* + * Remove some memory from an array of pieces + */ +void __init +mem_pieces_remove(struct mem_pieces *mp, + unsigned long start, + unsigned long size, + int must_exist) +{ + long i, j; + unsigned long end, rs, re; + struct reg_property *rp; + + end = start + size; + for (i = 0, rp = mp->regions; i < mp->n_regions; ++i, ++rp) { + if (end > rp->address && start < rp->address + rp->size) + break; + } + if (i >= mp->n_regions) { + if (must_exist) + printk("mem_pieces_remove: [%lx,%lx] not in any region\n", start, end); + return; + } + for (; i < mp->n_regions && end > rp->address; ++i, ++rp) { + rs = rp->address; + re = rs + rp->size; + if (must_exist && (start < rs || end > re)) { + printk("mem_pieces_remove: bad overlap [%lx,%lx] with", + start, end); + mem_pieces_print(mp); + must_exist = 0; + } + if (start > rs) { + rp->size = start - rs; + if (end < re) { + /* need to split this entry */ + if (mp->n_regions >= MEM_PIECES_MAX) + panic("eek... mem_pieces overflow"); + for (j = mp->n_regions; j > i + 1; --j) + mp->regions[j] = mp->regions[j-1]; + ++mp->n_regions; + rp[1].address = end; + rp[1].size = re - end; + } + } else { + if (end < re) { + rp->address = end; + rp->size = re - end; + } else { + /* need to delete this entry */ + for (j = i; j < mp->n_regions - 1; ++j) + mp->regions[j] = mp->regions[j+1]; + --mp->n_regions; + --i; + --rp; + } + } + } +} + +void __init +mem_pieces_print(struct mem_pieces *mp) +{ + int i; + + PPCDBG(PPCDBG_MMINIT, "\tmem_pieces_print\n"); + + for (i = 0; i < mp->n_regions; ++i) { + PPCDBG(PPCDBG_MMINIT, "\t\t[0x%lx, 0x%lx)\n", + mp->regions[i].address, + mp->regions[i].address + mp->regions[i].size); + } +} + +/* + * Add some memory to an array of pieces + */ +void __init +mem_pieces_append(struct mem_pieces *mp, + unsigned long start, + unsigned long size) +{ + struct reg_property *rp; + + if (mp->n_regions >= MEM_PIECES_MAX) + return; + rp = &mp->regions[mp->n_regions++]; + rp->address = start; + rp->size = size; +} + +void __init +mem_pieces_sort(struct mem_pieces *mp) +{ + unsigned long a, s; + long i, j; + + for (i = 1; i < mp->n_regions; ++i) { + a = mp->regions[i].address; + s = mp->regions[i].size; + for (j = i - 1; j >= 0; --j) { + if (a >= mp->regions[j].address) + break; + mp->regions[j+1] = mp->regions[j]; + } + mp->regions[j+1].address = a; + mp->regions[j+1].size = s; + } +} + +void __init +mem_pieces_coalesce(struct mem_pieces *mp) +{ + unsigned long a, s, ns; + long i, j, d; + + d = 0; + for (i = 0; i < mp->n_regions; i = j) { + a = mp->regions[i].address; + s = mp->regions[i].size; + for (j = i + 1; j < mp->n_regions + && mp->regions[j].address - a <= s; ++j) { + ns = mp->regions[j].address + mp->regions[j].size - a; + if (ns > s) + s = ns; + } + mp->regions[d].address = a; + mp->regions[d].size = s; + ++d; + } + mp->n_regions = d; +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/mm/mem_pieces.h linuxppc64_2_4/arch/ppc64/mm/mem_pieces.h --- linux-2.4.5-ac18/arch/ppc64/mm/mem_pieces.h Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/mm/mem_pieces.h Fri May 4 17:13:59 2001 @@ -0,0 +1,52 @@ +/* + * Copyright (c) 1996 Paul Mackerras + * Changes to accomodate Power Macintoshes. + * Cort Dougan + * Rewrites. + * Grant Erickson + * General rework and split from mm/init.c. + * Dave Engebretsen + * PPC 64b rework. + * + * Module name: mem_pieces.h + * + * Description: + * Routines and data structures for manipulating and representing + * phyiscal memory extents (i.e. address/length pairs). + * + */ + +#ifndef __MEM_PIECES_H__ +#define __MEM_PIECES_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Type Definitions */ + +#define MEM_PIECES_MAX 32 + +struct mem_pieces { + int n_regions; + struct reg_property regions[MEM_PIECES_MAX]; +}; + +/* Function Prototypes */ + +extern void *mem_pieces_find(unsigned long size, unsigned int align); +extern void mem_pieces_remove(struct mem_pieces *mp, unsigned long start, + unsigned long size, int must_exist); +extern void mem_pieces_append(struct mem_pieces *mp, unsigned long start, + unsigned long size); +extern void mem_pieces_coalesce(struct mem_pieces *mp); +extern void mem_pieces_sort(struct mem_pieces *mp); + +#ifdef __cplusplus +} +#endif + +#endif /* __MEM_PIECES_H__ */ diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/mymacros.h linuxppc64_2_4/arch/ppc64/mymacros.h --- linux-2.4.5-ac18/arch/ppc64/mymacros.h Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/mymacros.h Thu Jul 5 15:41:33 2001 @@ -0,0 +1,36 @@ + +#undef PPC64_ELF_SECTIONS +#ifndef PPC64_ELF_SECTIONS + +/* The toolchain cannot handle this yet, so comment them *all* out. */ +#ifdef __section__ +# undef __section__ +# define __section__(X) +#endif + +#if 0 + /* These are commented out in asm-ppc64/init.h */ + __chrp, __chrpdata, __chrpfunc(x), __openfirmware, + __openfirmwaredata, __openfirmwarefunc(x) +#endif + +#endif /* PPC64_ELF_SECTIONS */ + +#if 0 +#define PIRANHA +#else +#undef PIRANHA +#endif + +#define DEBUG_DELAY + +#if 1 +/* For 64-bit apps, temporarily reduce the size of the address space + * available to user application. This allow us to use strace without + * having to compile a strace64 program. This shouldn't affect anyone + * other than Steve Munroe, Peter Bergner. I will back this hack out + * later... -Peter + */ +#define PPC64_32B_ADDR_SPACE +#endif + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/vmlinux.lds linuxppc64_2_4/arch/ppc64/vmlinux.lds --- linux-2.4.5-ac18/arch/ppc64/vmlinux.lds Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/vmlinux.lds Wed Jun 6 14:49:42 2001 @@ -0,0 +1,133 @@ +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) & 0xFFFFFFFFFFFFF000; + .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 = .; + + __start___ksymtab = .; /* Kernel symbol table */ + __ksymtab : { *(__ksymtab) } + __stop___ksymtab = .; + + . = ALIGN(32); + .data.cacheline_aligned : { *(.data.cacheline_aligned) } + + . = ALIGN(4096); + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { + *(.data.init); + __vtop_table_begin = .; + *(.vtop_fixup); + __vtop_table_end = .; + __ptov_table_begin = .; + *(.ptov_fixup); + __ptov_table_end = .; + } + . = ALIGN(16); + __setup_start = .; + .setup.init : { *(.setup.init) } + __setup_end = .; + __initcall_start = .; + .initcall.init : { *(.initcall.init) } + __initcall_end = .; + + + . = ALIGN(4096); + __init_end = .; + + . = ALIGN(4096); + __chrp_begin = .; + .text.chrp : { *(.text.chrp) } + .data.chrp : { *(.data.chrp) } + . = ALIGN(4096); + __chrp_end = .; + + . = ALIGN(4096); + __openfirmware_begin = .; + .text.openfirmware : { *(.text.openfirmware) } + .data.openfirmware : { *(.data.openfirmware) } + . = ALIGN(4096); + __openfirmware_end = .; + + + . = ALIGN(4096); + __toc_start = .; + .toc : + { + *(.toc) + } + . = ALIGN(4096); + __toc_end = .; + + __bss_start = .; + .bss : + { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + } + + _end = . ; + PROVIDE (end = .); +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/xmon/Makefile linuxppc64_2_4/arch/ppc64/xmon/Makefile --- linux-2.4.5-ac18/arch/ppc64/xmon/Makefile Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/xmon/Makefile Fri May 4 17:13:59 2001 @@ -0,0 +1,9 @@ +# Makefile for xmon + +EXTRA_CFLAGS = -mno-minimal-toc + +O_TARGET = x.o + +obj-y := start.o xmon.o ppc-dis.o ppc-opc.o subr_prf.o setjmp.o + +include $(TOPDIR)/Rules.make diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/xmon/adb.c linuxppc64_2_4/arch/ppc64/xmon/adb.c --- linux-2.4.5-ac18/arch/ppc64/xmon/adb.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/xmon/adb.c Mon May 7 12:48:56 2001 @@ -0,0 +1,217 @@ +/* + * 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 "nonstdio.h" +#include "privinst.h" + +#define scanhex xmon_scanhex +#define skipbl xmon_skipbl + +#define ADB_B (*(volatile unsigned char *)0xf3016000) +#define ADB_SR (*(volatile unsigned char *)0xf3017400) +#define ADB_ACR (*(volatile unsigned char *)0xf3017600) +#define ADB_IFR (*(volatile unsigned char *)0xf3017a00) + +static inline void eieio(void) { asm volatile ("eieio" : :); } + +#define N_ADB_LOG 1000 +struct adb_log { + unsigned char b; + unsigned char ifr; + unsigned char acr; + unsigned int time; +} adb_log[N_ADB_LOG]; +int n_adb_log; + +void +init_adb_log(void) +{ + adb_log[0].b = ADB_B; + adb_log[0].ifr = ADB_IFR; + adb_log[0].acr = ADB_ACR; + adb_log[0].time = get_dec(); + n_adb_log = 0; +} + +void +dump_adb_log(void) +{ + unsigned t, t0; + struct adb_log *ap; + int i; + + ap = adb_log; + t0 = ap->time; + for (i = 0; i <= n_adb_log; ++i, ++ap) { + t = t0 - ap->time; + printf("b=%x ifr=%x acr=%x at %d.%.7d\n", ap->b, ap->ifr, ap->acr, + t / 1000000000, (t % 1000000000) / 100); + } +} + +void +adb_chklog(void) +{ + struct adb_log *ap = &adb_log[n_adb_log + 1]; + + ap->b = ADB_B; + ap->ifr = ADB_IFR; + ap->acr = ADB_ACR; + if (ap->b != ap[-1].b || (ap->ifr & 4) != (ap[-1].ifr & 4) + || ap->acr != ap[-1].acr) { + ap->time = get_dec(); + ++n_adb_log; + } +} + +int +adb_bitwait(int bmask, int bval, int fmask, int fval) +{ + int i; + struct adb_log *ap; + + for (i = 10000; i > 0; --i) { + adb_chklog(); + ap = &adb_log[n_adb_log]; + if ((ap->b & bmask) == bval && (ap->ifr & fmask) == fval) + return 0; + } + return -1; +} + +int +adb_wait(void) +{ + if (adb_bitwait(0, 0, 4, 4) < 0) { + printf("adb: ready wait timeout\n"); + return -1; + } + return 0; +} + +void +adb_readin(void) +{ + int i, j; + unsigned char d[64]; + + if (ADB_B & 8) { + printf("ADB_B: %x\n", ADB_B); + return; + } + i = 0; + adb_wait(); + j = ADB_SR; + eieio(); + ADB_B &= ~0x20; + eieio(); + for (;;) { + if (adb_wait() < 0) + break; + d[i++] = ADB_SR; + eieio(); + if (ADB_B & 8) + break; + ADB_B ^= 0x10; + eieio(); + } + ADB_B |= 0x30; + if (adb_wait() == 0) + j = ADB_SR; + for (j = 0; j < i; ++j) + printf("%.2x ", d[j]); + printf("\n"); +} + +int +adb_write(unsigned char *d, int i) +{ + int j; + unsigned x; + + if ((ADB_B & 8) == 0) { + printf("r: "); + adb_readin(); + } + for (;;) { + ADB_ACR = 0x1c; + eieio(); + ADB_SR = d[0]; + eieio(); + ADB_B &= ~0x20; + eieio(); + if (ADB_B & 8) + break; + ADB_ACR = 0xc; + eieio(); + ADB_B |= 0x20; + eieio(); + adb_readin(); + } + adb_wait(); + for (j = 1; j < i; ++j) { + ADB_SR = d[j]; + eieio(); + ADB_B ^= 0x10; + eieio(); + if (adb_wait() < 0) + break; + } + ADB_ACR = 0xc; + eieio(); + x = ADB_SR; + eieio(); + ADB_B |= 0x30; + return j; +} + +void +adbcmds(void) +{ + char cmd; + unsigned rtcu, rtcl, dec, pdec, x; + int i, j; + unsigned char d[64]; + + cmd = skipbl(); + switch (cmd) { + case 't': + for (;;) { + rtcl = get_rtcl(); + rtcu = get_rtcu(); + dec = get_dec(); + printf("rtc u=%u l=%u dec=%x (%d = %d.%.7d)\n", + rtcu, rtcl, dec, pdec - dec, (pdec - dec) / 1000000000, + ((pdec - dec) % 1000000000) / 100); + pdec = dec; + if (cmd == 'x') + break; + while (xmon_read(stdin, &cmd, 1) != 1) + ; + } + break; + case 'r': + init_adb_log(); + while (adb_bitwait(8, 0, 0, 0) == 0) + adb_readin(); + break; + case 'w': + i = 0; + while (scanhex(&x)) + d[i++] = x; + init_adb_log(); + j = adb_write(d, i); + printf("sent %d bytes\n", j); + while (adb_bitwait(8, 0, 0, 0) == 0) + adb_readin(); + break; + case 'l': + dump_adb_log(); + break; + } +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/xmon/ansidecl.h linuxppc64_2_4/arch/ppc64/xmon/ansidecl.h --- linux-2.4.5-ac18/arch/ppc64/xmon/ansidecl.h Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/xmon/ansidecl.h Fri May 4 17:13:59 2001 @@ -0,0 +1,141 @@ +/* ANSI and traditional C compatability macros + Copyright 1991, 1992 Free Software Foundation, Inc. + This file is part of the GNU C Library. + +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. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +/* ANSI and traditional C compatibility macros + + ANSI C is assumed if __STDC__ is #defined. + + Macro ANSI C definition Traditional C definition + ----- ---- - ---------- ----------- - ---------- + PTR `void *' `char *' + LONG_DOUBLE `long double' `double' + VOLATILE `volatile' `' + SIGNED `signed' `' + PTRCONST `void *const' `char *' + ANSI_PROTOTYPES 1 not defined + + CONST is also defined, but is obsolete. Just use const. + + DEFUN (name, arglist, args) + + Defines function NAME. + + ARGLIST lists the arguments, separated by commas and enclosed in + parentheses. ARGLIST becomes the argument list in traditional C. + + ARGS list the arguments with their types. It becomes a prototype in + ANSI C, and the type declarations in traditional C. Arguments should + be separated with `AND'. For functions with a variable number of + arguments, the last thing listed should be `DOTS'. + + DEFUN_VOID (name) + + Defines a function NAME, which takes no arguments. + + obsolete -- EXFUN (name, (prototype)) -- obsolete. + + Replaced by PARAMS. Do not use; will disappear someday soon. + Was used in external function declarations. + In ANSI C it is `NAME PROTOTYPE' (so PROTOTYPE should be enclosed in + parentheses). In traditional C it is `NAME()'. + For a function that takes no arguments, PROTOTYPE should be `(void)'. + + PARAMS ((args)) + + We could use the EXFUN macro to handle prototype declarations, but + the name is misleading and the result is ugly. So we just define a + simple macro to handle the parameter lists, as in: + + static int foo PARAMS ((int, char)); + + This produces: `static int foo();' or `static int foo (int, char);' + + EXFUN would have done it like this: + + static int EXFUN (foo, (int, char)); + + but the function is not external...and it's hard to visually parse + the function name out of the mess. EXFUN should be considered + obsolete; new code should be written to use PARAMS. + + For example: + extern int printf PARAMS ((CONST char *format DOTS)); + int DEFUN(fprintf, (stream, format), + FILE *stream AND CONST char *format DOTS) { ... } + void DEFUN_VOID(abort) { ... } +*/ + +#ifndef _ANSIDECL_H + +#define _ANSIDECL_H 1 + + +/* Every source file includes this file, + so they will all get the switch for lint. */ +/* LINTLIBRARY */ + + +#if defined (__STDC__) || defined (_AIX) || (defined (__mips) && defined (_SYSTYPE_SVR4)) || defined(WIN32) +/* All known AIX compilers implement these things (but don't always + define __STDC__). The RISC/OS MIPS compiler defines these things + in SVR4 mode, but does not define __STDC__. */ + +#define PTR void * +#define PTRCONST void *CONST +#define LONG_DOUBLE long double + +#define AND , +#define NOARGS void +#define CONST const +#define VOLATILE volatile +#define SIGNED signed +#define DOTS , ... + +#define EXFUN(name, proto) name proto +#define DEFUN(name, arglist, args) name(args) +#define DEFUN_VOID(name) name(void) + +#define PROTO(type, name, arglist) type name arglist +#define PARAMS(paramlist) paramlist +#define ANSI_PROTOTYPES 1 + +#else /* Not ANSI C. */ + +#define PTR char * +#define PTRCONST PTR +#define LONG_DOUBLE double + +#define AND ; +#define NOARGS +#define CONST +#ifndef const /* some systems define it in header files for non-ansi mode */ +#define const +#endif +#define VOLATILE +#define SIGNED +#define DOTS + +#define EXFUN(name, proto) name() +#define DEFUN(name, arglist, args) name arglist args; +#define DEFUN_VOID(name) name() +#define PROTO(type, name, arglist) type name () +#define PARAMS(paramlist) () + +#endif /* ANSI C. */ + +#endif /* ansidecl.h */ diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/xmon/nonstdio.h linuxppc64_2_4/arch/ppc64/xmon/nonstdio.h --- linux-2.4.5-ac18/arch/ppc64/xmon/nonstdio.h Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/xmon/nonstdio.h Fri May 4 17:13:59 2001 @@ -0,0 +1,22 @@ +typedef int FILE; +extern FILE *xmon_stdin, *xmon_stdout; +#define EOF (-1) +#define stdin xmon_stdin +#define stdout xmon_stdout +#define printf xmon_printf +#define fprintf xmon_fprintf +#define fputs xmon_fputs +#define fgets xmon_fgets +#define putchar xmon_putchar +#define getchar xmon_getchar +#define putc xmon_putc +#define getc xmon_getc +#define fopen(n, m) NULL +#define fflush(f) do {} while (0) +#define fclose(f) do {} while (0) +extern char *fgets(char *, int, void *); +extern void xmon_printf(const char *, ...); +extern void xmon_fprintf(void *, const char *, ...); +extern void xmon_sprintf(char *, const char *, ...); + +#define perror(s) printf("%s: no files!\n", (s)) diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/xmon/ppc-dis.c linuxppc64_2_4/arch/ppc64/xmon/ppc-dis.c --- linux-2.4.5-ac18/arch/ppc64/xmon/ppc-dis.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/xmon/ppc-dis.c Fri May 4 17:13:59 2001 @@ -0,0 +1,190 @@ +/* ppc-dis.c -- Disassemble PowerPC instructions + Copyright 1994 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +2, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +will be useful, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this file; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "nonstdio.h" +#include "ansidecl.h" +#include "ppc.h" + +static int print_insn_powerpc PARAMS ((FILE *, unsigned long insn, + unsigned long memaddr, int dialect)); + +extern void print_address PARAMS((unsigned long memaddr)); + +/* Print a big endian PowerPC instruction. For convenience, also + disassemble instructions supported by the Motorola PowerPC 601. */ + +int +print_insn_big_powerpc (FILE *out, unsigned long insn, unsigned long memaddr) +{ + return print_insn_powerpc (out, insn, memaddr, + PPC_OPCODE_PPC | PPC_OPCODE_601); +} + +/* Print a PowerPC or POWER instruction. */ + +static int +print_insn_powerpc (FILE *out, unsigned long insn, unsigned long memaddr, + int dialect) +{ + const struct powerpc_opcode *opcode; + const struct powerpc_opcode *opcode_end; + unsigned long op; + + /* Get the major opcode of the instruction. */ + op = PPC_OP (insn); + + /* Find the first match in the opcode table. We could speed this up + a bit by doing a binary search on the major opcode. */ + opcode_end = powerpc_opcodes + powerpc_num_opcodes; + for (opcode = powerpc_opcodes; opcode < opcode_end; opcode++) + { + unsigned long table_op; + const unsigned char *opindex; + const struct powerpc_operand *operand; + int invalid; + int need_comma; + int need_paren; + + table_op = PPC_OP (opcode->opcode); + if (op < table_op) + break; + if (op > table_op) + continue; + + if ((insn & opcode->mask) != opcode->opcode + || (opcode->flags & dialect) == 0) + continue; + + /* Make two passes over the operands. First see if any of them + have extraction functions, and, if they do, make sure the + instruction is valid. */ + invalid = 0; + for (opindex = opcode->operands; *opindex != 0; opindex++) + { + operand = powerpc_operands + *opindex; + if (operand->extract) + (*operand->extract) (insn, &invalid); + } + if (invalid) + continue; + + /* The instruction is valid. */ + fprintf(out, "%s", opcode->name); + if (opcode->operands[0] != 0) + fprintf(out, "\t"); + + /* Now extract and print the operands. */ + need_comma = 0; + need_paren = 0; + for (opindex = opcode->operands; *opindex != 0; opindex++) + { + long value; + + operand = powerpc_operands + *opindex; + + /* Operands that are marked FAKE are simply ignored. We + already made sure that the extract function considered + the instruction to be valid. */ + if ((operand->flags & PPC_OPERAND_FAKE) != 0) + continue; + + /* Extract the value from the instruction. */ + if (operand->extract) + value = (*operand->extract) (insn, (int *) 0); + else + { + value = (insn >> operand->shift) & ((1 << operand->bits) - 1); + if ((operand->flags & PPC_OPERAND_SIGNED) != 0 + && (value & (1 << (operand->bits - 1))) != 0) + value -= 1 << operand->bits; + } + + /* If the operand is optional, and the value is zero, don't + print anything. */ + if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0 + && (operand->flags & PPC_OPERAND_NEXT) == 0 + && value == 0) + continue; + + if (need_comma) + { + fprintf(out, ","); + need_comma = 0; + } + + /* Print the operand as directed by the flags. */ + if ((operand->flags & PPC_OPERAND_GPR) != 0) + fprintf(out, "r%ld", value); + else if ((operand->flags & PPC_OPERAND_FPR) != 0) + fprintf(out, "f%ld", value); + else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0) + print_address (memaddr + value); + else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0) + print_address (value & 0xffffffff); + else if ((operand->flags & PPC_OPERAND_CR) == 0 + || (dialect & PPC_OPCODE_PPC) == 0) + fprintf(out, "%ld", value); + else + { + if (operand->bits == 3) + fprintf(out, "cr%d", value); + else + { + static const char *cbnames[4] = { "lt", "gt", "eq", "so" }; + int cr; + int cc; + + cr = value >> 2; + if (cr != 0) + fprintf(out, "4*cr%d", cr); + cc = value & 3; + if (cc != 0) + { + if (cr != 0) + fprintf(out, "+"); + fprintf(out, "%s", cbnames[cc]); + } + } + } + + if (need_paren) + { + fprintf(out, ")"); + need_paren = 0; + } + + if ((operand->flags & PPC_OPERAND_PARENS) == 0) + need_comma = 1; + else + { + fprintf(out, "("); + need_paren = 1; + } + } + + /* We have found and printed an instruction; return. */ + return 4; + } + + /* We could not find a match. */ + fprintf(out, ".long 0x%lx", insn); + + return 4; +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/xmon/ppc-opc.c linuxppc64_2_4/arch/ppc64/xmon/ppc-opc.c --- linux-2.4.5-ac18/arch/ppc64/xmon/ppc-opc.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/xmon/ppc-opc.c Fri May 4 17:13:59 2001 @@ -0,0 +1,2816 @@ +/* ppc-opc.c -- PowerPC opcode list + Copyright 1994 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +2, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +will be useful, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this file; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include +#include "ansidecl.h" +#include "ppc.h" + +/* This file holds the PowerPC opcode table. The opcode table + includes almost all of the extended instruction mnemonics. This + permits the disassembler to use them, and simplifies the assembler + logic, at the cost of increasing the table size. The table is + strictly constant data, so the compiler should be able to put it in + the .text section. + + This file also holds the operand table. All knowledge about + inserting operands into instructions and vice-versa is kept in this + file. */ + +/* Local insertion and extraction functions. */ + +static unsigned long insert_bat PARAMS ((unsigned long, long, const char **)); +static long extract_bat PARAMS ((unsigned long, int *)); +static unsigned long insert_bba PARAMS ((unsigned long, long, const char **)); +static long extract_bba PARAMS ((unsigned long, int *)); +static unsigned long insert_bd PARAMS ((unsigned long, long, const char **)); +static long extract_bd PARAMS ((unsigned long, int *)); +static unsigned long insert_bdm PARAMS ((unsigned long, long, const char **)); +static long extract_bdm PARAMS ((unsigned long, int *)); +static unsigned long insert_bdp PARAMS ((unsigned long, long, const char **)); +static long extract_bdp PARAMS ((unsigned long, int *)); +static unsigned long insert_bo PARAMS ((unsigned long, long, const char **)); +static long extract_bo PARAMS ((unsigned long, int *)); +static unsigned long insert_boe PARAMS ((unsigned long, long, const char **)); +static long extract_boe PARAMS ((unsigned long, int *)); +static unsigned long insert_ds PARAMS ((unsigned long, long, const char **)); +static long extract_ds PARAMS ((unsigned long, int *)); +static unsigned long insert_li PARAMS ((unsigned long, long, const char **)); +static long extract_li PARAMS ((unsigned long, int *)); +static unsigned long insert_mbe PARAMS ((unsigned long, long, const char **)); +static long extract_mbe PARAMS ((unsigned long, int *)); +static unsigned long insert_mb6 PARAMS ((unsigned long, long, const char **)); +static long extract_mb6 PARAMS ((unsigned long, int *)); +static unsigned long insert_nb PARAMS ((unsigned long, long, const char **)); +static long extract_nb PARAMS ((unsigned long, int *)); +static unsigned long insert_nsi PARAMS ((unsigned long, long, const char **)); +static long extract_nsi PARAMS ((unsigned long, int *)); +static unsigned long insert_ral PARAMS ((unsigned long, long, const char **)); +static unsigned long insert_ram PARAMS ((unsigned long, long, const char **)); +static unsigned long insert_ras PARAMS ((unsigned long, long, const char **)); +static unsigned long insert_rbs PARAMS ((unsigned long, long, const char **)); +static long extract_rbs PARAMS ((unsigned long, int *)); +static unsigned long insert_sh6 PARAMS ((unsigned long, long, const char **)); +static long extract_sh6 PARAMS ((unsigned long, int *)); +static unsigned long insert_spr PARAMS ((unsigned long, long, const char **)); +static long extract_spr PARAMS ((unsigned long, int *)); +static unsigned long insert_tbr PARAMS ((unsigned long, long, const char **)); +static long extract_tbr PARAMS ((unsigned long, int *)); + +/* The operands table. + + The fields are bits, shift, signed, insert, extract, flags. */ + +const struct powerpc_operand powerpc_operands[] = +{ + /* The zero index is used to indicate the end of the list of + operands. */ +#define UNUSED (0) + { 0, 0, 0, 0, 0 }, + + /* The BA field in an XL form instruction. */ +#define BA (1) +#define BA_MASK (0x1f << 16) + { 5, 16, 0, 0, PPC_OPERAND_CR }, + + /* The BA field in an XL form instruction when it must be the same + as the BT field in the same instruction. */ +#define BAT (2) + { 5, 16, insert_bat, extract_bat, PPC_OPERAND_FAKE }, + + /* The BB field in an XL form instruction. */ +#define BB (3) +#define BB_MASK (0x1f << 11) + { 5, 11, 0, 0, PPC_OPERAND_CR }, + + /* The BB field in an XL form instruction when it must be the same + as the BA field in the same instruction. */ +#define BBA (4) + { 5, 11, insert_bba, extract_bba, PPC_OPERAND_FAKE }, + + /* The BD field in a B form instruction. The lower two bits are + forced to zero. */ +#define BD (5) + { 16, 0, insert_bd, extract_bd, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when absolute addressing is + used. */ +#define BDA (6) + { 16, 0, insert_bd, extract_bd, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the - modifier is used. + This sets the y bit of the BO field appropriately. */ +#define BDM (7) + { 16, 0, insert_bdm, extract_bdm, + PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the - modifier is used + and absolute address is used. */ +#define BDMA (8) + { 16, 0, insert_bdm, extract_bdm, + PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the + modifier is used. + This sets the y bit of the BO field appropriately. */ +#define BDP (9) + { 16, 0, insert_bdp, extract_bdp, + PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The BD field in a B form instruction when the + modifier is used + and absolute addressing is used. */ +#define BDPA (10) + { 16, 0, insert_bdp, extract_bdp, + PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The BF field in an X or XL form instruction. */ +#define BF (11) + { 3, 23, 0, 0, PPC_OPERAND_CR }, + + /* An optional BF field. This is used for comparison instructions, + in which an omitted BF field is taken as zero. */ +#define OBF (12) + { 3, 23, 0, 0, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL }, + + /* The BFA field in an X or XL form instruction. */ +#define BFA (13) + { 3, 18, 0, 0, PPC_OPERAND_CR }, + + /* The BI field in a B form or XL form instruction. */ +#define BI (14) +#define BI_MASK (0x1f << 16) + { 5, 16, 0, 0, PPC_OPERAND_CR }, + + /* The BO field in a B form instruction. Certain values are + illegal. */ +#define BO (15) +#define BO_MASK (0x1f << 21) + { 5, 21, insert_bo, extract_bo, 0 }, + + /* The BO field in a B form instruction when the + or - modifier is + used. This is like the BO field, but it must be even. */ +#define BOE (16) + { 5, 21, insert_boe, extract_boe, 0 }, + + /* The BT field in an X or XL form instruction. */ +#define BT (17) + { 5, 21, 0, 0, PPC_OPERAND_CR }, + + /* The condition register number portion of the BI field in a B form + or XL form instruction. This is used for the extended + conditional branch mnemonics, which set the lower two bits of the + BI field. This field is optional. */ +#define CR (18) + { 3, 18, 0, 0, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL }, + + /* The D field in a D form instruction. This is a displacement off + a register, and implies that the next operand is a register in + parentheses. */ +#define D (19) + { 16, 0, 0, 0, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED }, + + /* The DS field in a DS form instruction. This is like D, but the + lower two bits are forced to zero. */ +#define DS (20) + { 16, 0, insert_ds, extract_ds, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED }, + + /* The FL1 field in a POWER SC form instruction. */ +#define FL1 (21) + { 4, 12, 0, 0, 0 }, + + /* The FL2 field in a POWER SC form instruction. */ +#define FL2 (22) + { 3, 2, 0, 0, 0 }, + + /* The FLM field in an XFL form instruction. */ +#define FLM (23) + { 8, 17, 0, 0, 0 }, + + /* The FRA field in an X or A form instruction. */ +#define FRA (24) +#define FRA_MASK (0x1f << 16) + { 5, 16, 0, 0, PPC_OPERAND_FPR }, + + /* The FRB field in an X or A form instruction. */ +#define FRB (25) +#define FRB_MASK (0x1f << 11) + { 5, 11, 0, 0, PPC_OPERAND_FPR }, + + /* The FRC field in an A form instruction. */ +#define FRC (26) +#define FRC_MASK (0x1f << 6) + { 5, 6, 0, 0, PPC_OPERAND_FPR }, + + /* The FRS field in an X form instruction or the FRT field in a D, X + or A form instruction. */ +#define FRS (27) +#define FRT (FRS) + { 5, 21, 0, 0, PPC_OPERAND_FPR }, + + /* The FXM field in an XFX instruction. */ +#define FXM (28) +#define FXM_MASK (0xff << 12) + { 8, 12, 0, 0, 0 }, + + /* The L field in a D or X form instruction. */ +#define L (29) + { 1, 21, 0, 0, PPC_OPERAND_OPTIONAL }, + + /* The LEV field in a POWER SC form instruction. */ +#define LEV (30) + { 7, 5, 0, 0, 0 }, + + /* The LI field in an I form instruction. The lower two bits are + forced to zero. */ +#define LI (31) + { 26, 0, insert_li, extract_li, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED }, + + /* The LI field in an I form instruction when used as an absolute + address. */ +#define LIA (32) + { 26, 0, insert_li, extract_li, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED }, + + /* The MB field in an M form instruction. */ +#define MB (33) +#define MB_MASK (0x1f << 6) + { 5, 6, 0, 0, 0 }, + + /* The ME field in an M form instruction. */ +#define ME (34) +#define ME_MASK (0x1f << 1) + { 5, 1, 0, 0, 0 }, + + /* The MB and ME fields in an M form instruction expressed a single + operand which is a bitmask indicating which bits to select. This + is a two operand form using PPC_OPERAND_NEXT. See the + description in opcode/ppc.h for what this means. */ +#define MBE (35) + { 5, 6, 0, 0, PPC_OPERAND_OPTIONAL | PPC_OPERAND_NEXT }, + { 32, 0, insert_mbe, extract_mbe, 0 }, + + /* The MB or ME field in an MD or MDS form instruction. The high + bit is wrapped to the low end. */ +#define MB6 (37) +#define ME6 (MB6) +#define MB6_MASK (0x3f << 5) + { 6, 5, insert_mb6, extract_mb6, 0 }, + + /* The NB field in an X form instruction. The value 32 is stored as + 0. */ +#define NB (38) + { 6, 11, insert_nb, extract_nb, 0 }, + + /* The NSI field in a D form instruction. This is the same as the + SI field, only negated. */ +#define NSI (39) + { 16, 0, insert_nsi, extract_nsi, + PPC_OPERAND_NEGATIVE | PPC_OPERAND_SIGNED }, + + /* The RA field in an D, DS, X, XO, M, or MDS form instruction. */ +#define RA (40) +#define RA_MASK (0x1f << 16) + { 5, 16, 0, 0, PPC_OPERAND_GPR }, + + /* The RA field in a D or X form instruction which is an updating + load, which means that the RA field may not be zero and may not + equal the RT field. */ +#define RAL (41) + { 5, 16, insert_ral, 0, PPC_OPERAND_GPR }, + + /* The RA field in an lmw instruction, which has special value + restrictions. */ +#define RAM (42) + { 5, 16, insert_ram, 0, PPC_OPERAND_GPR }, + + /* The RA field in a D or X form instruction which is an updating + store or an updating floating point load, which means that the RA + field may not be zero. */ +#define RAS (43) + { 5, 16, insert_ras, 0, PPC_OPERAND_GPR }, + + /* The RB field in an X, XO, M, or MDS form instruction. */ +#define RB (44) +#define RB_MASK (0x1f << 11) + { 5, 11, 0, 0, PPC_OPERAND_GPR }, + + /* The RB field in an X form instruction when it must be the same as + the RS field in the instruction. This is used for extended + mnemonics like mr. */ +#define RBS (45) + { 5, 1, insert_rbs, extract_rbs, PPC_OPERAND_FAKE }, + + /* The RS field in a D, DS, X, XFX, XS, M, MD or MDS form + instruction or the RT field in a D, DS, X, XFX or XO form + instruction. */ +#define RS (46) +#define RT (RS) +#define RT_MASK (0x1f << 21) + { 5, 21, 0, 0, PPC_OPERAND_GPR }, + + /* The SH field in an X or M form instruction. */ +#define SH (47) +#define SH_MASK (0x1f << 11) + { 5, 11, 0, 0, 0 }, + + /* The SH field in an MD form instruction. This is split. */ +#define SH6 (48) +#define SH6_MASK ((0x1f << 11) | (1 << 1)) + { 6, 1, insert_sh6, extract_sh6, 0 }, + + /* The SI field in a D form instruction. */ +#define SI (49) + { 16, 0, 0, 0, PPC_OPERAND_SIGNED }, + + /* The SI field in a D form instruction when we accept a wide range + of positive values. */ +#define SISIGNOPT (50) + { 16, 0, 0, 0, PPC_OPERAND_SIGNED | PPC_OPERAND_SIGNOPT }, + + /* The SPR field in an XFX form instruction. This is flipped--the + lower 5 bits are stored in the upper 5 and vice- versa. */ +#define SPR (51) +#define SPR_MASK (0x3ff << 11) + { 10, 11, insert_spr, extract_spr, 0 }, + + /* The BAT index number in an XFX form m[ft]ibat[lu] instruction. */ +#define SPRBAT (52) +#define SPRBAT_MASK (0x3 << 17) + { 2, 17, 0, 0, 0 }, + + /* The SPRG register number in an XFX form m[ft]sprg instruction. */ +#define SPRG (53) +#define SPRG_MASK (0x3 << 16) + { 2, 16, 0, 0, 0 }, + + /* The SR field in an X form instruction. */ +#define SR (54) + { 4, 16, 0, 0, 0 }, + + /* The SV field in a POWER SC form instruction. */ +#define SV (55) + { 14, 2, 0, 0, 0 }, + + /* The TBR field in an XFX form instruction. This is like the SPR + field, but it is optional. */ +#define TBR (56) + { 10, 11, insert_tbr, extract_tbr, PPC_OPERAND_OPTIONAL }, + + /* The TO field in a D or X form instruction. */ +#define TO (57) +#define TO_MASK (0x1f << 21) + { 5, 21, 0, 0, 0 }, + + /* The U field in an X form instruction. */ +#define U (58) + { 4, 12, 0, 0, 0 }, + + /* The UI field in a D form instruction. */ +#define UI (59) + { 16, 0, 0, 0, 0 }, +}; + +/* The functions used to insert and extract complicated operands. */ + +/* The BA field in an XL form instruction when it must be the same as + the BT field in the same instruction. This operand is marked FAKE. + The insertion function just copies the BT field into the BA field, + and the extraction function just checks that the fields are the + same. */ + +/*ARGSUSED*/ +static unsigned long +insert_bat (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | (((insn >> 21) & 0x1f) << 16); +} + +static long +extract_bat (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn >> 21) & 0x1f) != ((insn >> 16) & 0x1f)) + *invalid = 1; + return 0; +} + +/* The BB field in an XL form instruction when it must be the same as + the BA field in the same instruction. This operand is marked FAKE. + The insertion function just copies the BA field into the BB field, + and the extraction function just checks that the fields are the + same. */ + +/*ARGSUSED*/ +static unsigned long +insert_bba (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | (((insn >> 16) & 0x1f) << 11); +} + +static long +extract_bba (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn >> 16) & 0x1f) != ((insn >> 11) & 0x1f)) + *invalid = 1; + return 0; +} + +/* The BD field in a B form instruction. The lower two bits are + forced to zero. */ + +/*ARGSUSED*/ +static unsigned long +insert_bd (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | (value & 0xfffc); +} + +/*ARGSUSED*/ +static long +extract_bd (insn, invalid) + unsigned long insn; + int *invalid; +{ + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* The BD field in a B form instruction when the - modifier is used. + This modifier means that the branch is not expected to be taken. + We must set the y bit of the BO field to 1 if the offset is + negative. When extracting, we require that the y bit be 1 and that + the offset be positive, since if the y bit is 0 we just want to + print the normal form of the instruction. */ + +/*ARGSUSED*/ +static unsigned long +insert_bdm (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if ((value & 0x8000) != 0) + insn |= 1 << 21; + return insn | (value & 0xfffc); +} + +static long +extract_bdm (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn & (1 << 21)) == 0 + || (insn & (1 << 15)) == 0)) + *invalid = 1; + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* The BD field in a B form instruction when the + modifier is used. + This is like BDM, above, except that the branch is expected to be + taken. */ + +/*ARGSUSED*/ +static unsigned long +insert_bdp (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if ((value & 0x8000) == 0) + insn |= 1 << 21; + return insn | (value & 0xfffc); +} + +static long +extract_bdp (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn & (1 << 21)) == 0 + || (insn & (1 << 15)) != 0)) + *invalid = 1; + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* Check for legal values of a BO field. */ + +static int +valid_bo (long value) +{ + /* Certain encodings have bits that are required to be zero. These + are (z must be zero, y may be anything): + 001zy + 011zy + 1z00y + 1z01y + 1z1zz + */ + switch (value & 0x14) + { + default: + case 0: + return 1; + case 0x4: + return (value & 0x2) == 0; + case 0x10: + return (value & 0x8) == 0; + case 0x14: + return value == 0x14; + } +} + +/* The BO field in a B form instruction. Warn about attempts to set + the field to an illegal value. */ + +static unsigned long +insert_bo (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (errmsg != (const char **) NULL + && ! valid_bo (value)) + *errmsg = "invalid conditional option"; + return insn | ((value & 0x1f) << 21); +} + +static long +extract_bo (insn, invalid) + unsigned long insn; + int *invalid; +{ + long value; + + value = (insn >> 21) & 0x1f; + if (invalid != (int *) NULL + && ! valid_bo (value)) + *invalid = 1; + return value; +} + +/* The BO field in a B form instruction when the + or - modifier is + used. This is like the BO field, but it must be even. When + extracting it, we force it to be even. */ + +static unsigned long +insert_boe (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (errmsg != (const char **) NULL) + { + if (! valid_bo (value)) + *errmsg = "invalid conditional option"; + else if ((value & 1) != 0) + *errmsg = "attempt to set y bit when using + or - modifier"; + } + return insn | ((value & 0x1f) << 21); +} + +static long +extract_boe (insn, invalid) + unsigned long insn; + int *invalid; +{ + long value; + + value = (insn >> 21) & 0x1f; + if (invalid != (int *) NULL + && ! valid_bo (value)) + *invalid = 1; + return value & 0x1e; +} + +/* The DS field in a DS form instruction. This is like D, but the + lower two bits are forced to zero. */ + +/*ARGSUSED*/ +static unsigned long +insert_ds (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | (value & 0xfffc); +} + +/*ARGSUSED*/ +static long +extract_ds (insn, invalid) + unsigned long insn; + int *invalid; +{ + if ((insn & 0x8000) != 0) + return (insn & 0xfffc) - 0x10000; + else + return insn & 0xfffc; +} + +/* The LI field in an I form instruction. The lower two bits are + forced to zero. */ + +/*ARGSUSED*/ +static unsigned long +insert_li (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | (value & 0x3fffffc); +} + +/*ARGSUSED*/ +static long +extract_li (insn, invalid) + unsigned long insn; + int *invalid; +{ + if ((insn & 0x2000000) != 0) + return (insn & 0x3fffffc) - 0x4000000; + else + return insn & 0x3fffffc; +} + +/* The MB and ME fields in an M form instruction expressed as a single + operand which is itself a bitmask. The extraction function always + marks it as invalid, since we never want to recognize an + instruction which uses a field of this type. */ + +static unsigned long +insert_mbe (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + unsigned long uval; + int mb, me; + + uval = value; + + if (uval == 0) + { + if (errmsg != (const char **) NULL) + *errmsg = "illegal bitmask"; + return insn; + } + + me = 31; + while ((uval & 1) == 0) + { + uval >>= 1; + --me; + } + + mb = me; + uval >>= 1; + while ((uval & 1) != 0) + { + uval >>= 1; + --mb; + } + + if (uval != 0) + { + if (errmsg != (const char **) NULL) + *errmsg = "illegal bitmask"; + } + + return insn | (mb << 6) | (me << 1); +} + +static long +extract_mbe (insn, invalid) + unsigned long insn; + int *invalid; +{ + long ret; + int mb, me; + int i; + + if (invalid != (int *) NULL) + *invalid = 1; + + ret = 0; + mb = (insn >> 6) & 0x1f; + me = (insn >> 1) & 0x1f; + for (i = mb; i < me; i++) + ret |= 1 << (31 - i); + return ret; +} + +/* The MB or ME field in an MD or MDS form instruction. The high bit + is wrapped to the low end. */ + +/*ARGSUSED*/ +static unsigned long +insert_mb6 (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | ((value & 0x1f) << 6) | (value & 0x20); +} + +/*ARGSUSED*/ +static long +extract_mb6 (insn, invalid) + unsigned long insn; + int *invalid; +{ + return ((insn >> 6) & 0x1f) | (insn & 0x20); +} + +/* The NB field in an X form instruction. The value 32 is stored as + 0. */ + +static unsigned long +insert_nb (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (value < 0 || value > 32) + *errmsg = "value out of range"; + if (value == 32) + value = 0; + return insn | ((value & 0x1f) << 11); +} + +/*ARGSUSED*/ +static long +extract_nb (insn, invalid) + unsigned long insn; + int *invalid; +{ + long ret; + + ret = (insn >> 11) & 0x1f; + if (ret == 0) + ret = 32; + return ret; +} + +/* The NSI field in a D form instruction. This is the same as the SI + field, only negated. The extraction function always marks it as + invalid, since we never want to recognize an instruction which uses + a field of this type. */ + +/*ARGSUSED*/ +static unsigned long +insert_nsi (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | ((- value) & 0xffff); +} + +static long +extract_nsi (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL) + *invalid = 1; + if ((insn & 0x8000) != 0) + return - ((insn & 0xffff) - 0x10000); + else + return - (insn & 0xffff); +} + +/* The RA field in a D or X form instruction which is an updating + load, which means that the RA field may not be zero and may not + equal the RT field. */ + +static unsigned long +insert_ral (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (value == 0 + || value == ((insn >> 21) & 0x1f)) + *errmsg = "invalid register operand when updating"; + return insn | ((value & 0x1f) << 16); +} + +/* The RA field in an lmw instruction, which has special value + restrictions. */ + +static unsigned long +insert_ram (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (value >= ((insn >> 21) & 0x1f)) + *errmsg = "index register in load range"; + return insn | ((value & 0x1f) << 16); +} + +/* The RA field in a D or X form instruction which is an updating + store or an updating floating point load, which means that the RA + field may not be zero. */ + +static unsigned long +insert_ras (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (value == 0) + *errmsg = "invalid register operand when updating"; + return insn | ((value & 0x1f) << 16); +} + +/* The RB field in an X form instruction when it must be the same as + the RS field in the instruction. This is used for extended + mnemonics like mr. This operand is marked FAKE. The insertion + function just copies the BT field into the BA field, and the + extraction function just checks that the fields are the same. */ + +/*ARGSUSED*/ +static unsigned long +insert_rbs (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | (((insn >> 21) & 0x1f) << 11); +} + +static long +extract_rbs (insn, invalid) + unsigned long insn; + int *invalid; +{ + if (invalid != (int *) NULL + && ((insn >> 21) & 0x1f) != ((insn >> 11) & 0x1f)) + *invalid = 1; + return 0; +} + +/* The SH field in an MD form instruction. This is split. */ + +/*ARGSUSED*/ +static unsigned long +insert_sh6 (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | ((value & 0x1f) << 11) | ((value & 0x20) >> 4); +} + +/*ARGSUSED*/ +static long +extract_sh6 (insn, invalid) + unsigned long insn; + int *invalid; +{ + return ((insn >> 11) & 0x1f) | ((insn << 4) & 0x20); +} + +/* The SPR field in an XFX form instruction. This is flipped--the + lower 5 bits are stored in the upper 5 and vice- versa. */ + +static unsigned long +insert_spr (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6); +} + +static long +extract_spr (insn, invalid) + unsigned long insn; + int *invalid; +{ + return ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0); +} + +/* The TBR field in an XFX instruction. This is just like SPR, but it + is optional. When TBR is omitted, it must be inserted as 268 (the + magic number of the TB register). These functions treat 0 + (indicating an omitted optional operand) as 268. This means that + ``mftb 4,0'' is not handled correctly. This does not matter very + much, since the architecture manual does not define mftb as + accepting any values other than 268 or 269. */ + +#define TB (268) + +static unsigned long +insert_tbr (insn, value, errmsg) + unsigned long insn; + long value; + const char **errmsg; +{ + if (value == 0) + value = TB; + return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6); +} + +static long +extract_tbr (insn, invalid) + unsigned long insn; + int *invalid; +{ + long ret; + + ret = ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0); + if (ret == TB) + ret = 0; + return ret; +} + +/* Macros used to form opcodes. */ + +/* The main opcode. */ +#define OP(x) (((x) & 0x3f) << 26) +#define OP_MASK OP (0x3f) + +/* The main opcode combined with a trap code in the TO field of a D + form instruction. Used for extended mnemonics for the trap + instructions. */ +#define OPTO(x,to) (OP (x) | (((to) & 0x1f) << 21)) +#define OPTO_MASK (OP_MASK | TO_MASK) + +/* The main opcode combined with a comparison size bit in the L field + of a D form or X form instruction. Used for extended mnemonics for + the comparison instructions. */ +#define OPL(x,l) (OP (x) | (((l) & 1) << 21)) +#define OPL_MASK OPL (0x3f,1) + +/* An A form instruction. */ +#define A(op, xop, rc) (OP (op) | (((xop) & 0x1f) << 1) | ((rc) & 1)) +#define A_MASK A (0x3f, 0x1f, 1) + +/* An A_MASK with the FRB field fixed. */ +#define AFRB_MASK (A_MASK | FRB_MASK) + +/* An A_MASK with the FRC field fixed. */ +#define AFRC_MASK (A_MASK | FRC_MASK) + +/* An A_MASK with the FRA and FRC fields fixed. */ +#define AFRAFRC_MASK (A_MASK | FRA_MASK | FRC_MASK) + +/* A B form instruction. */ +#define B(op, aa, lk) (OP (op) | (((aa) & 1) << 1) | ((lk) & 1)) +#define B_MASK B (0x3f, 1, 1) + +/* A B form instruction setting the BO field. */ +#define BBO(op, bo, aa, lk) (B ((op), (aa), (lk)) | (((bo) & 0x1f) << 21)) +#define BBO_MASK BBO (0x3f, 0x1f, 1, 1) + +/* A BBO_MASK with the y bit of the BO field removed. This permits + matching a conditional branch regardless of the setting of the y + bit. */ +#define Y_MASK (1 << 21) +#define BBOY_MASK (BBO_MASK &~ Y_MASK) + +/* A B form instruction setting the BO field and the condition bits of + the BI field. */ +#define BBOCB(op, bo, cb, aa, lk) \ + (BBO ((op), (bo), (aa), (lk)) | (((cb) & 0x3) << 16)) +#define BBOCB_MASK BBOCB (0x3f, 0x1f, 0x3, 1, 1) + +/* A BBOCB_MASK with the y bit of the BO field removed. */ +#define BBOYCB_MASK (BBOCB_MASK &~ Y_MASK) + +/* A BBOYCB_MASK in which the BI field is fixed. */ +#define BBOYBI_MASK (BBOYCB_MASK | BI_MASK) + +/* The main opcode mask with the RA field clear. */ +#define DRA_MASK (OP_MASK | RA_MASK) + +/* A DS form instruction. */ +#define DSO(op, xop) (OP (op) | ((xop) & 0x3)) +#define DS_MASK DSO (0x3f, 3) + +/* An M form instruction. */ +#define M(op, rc) (OP (op) | ((rc) & 1)) +#define M_MASK M (0x3f, 1) + +/* An M form instruction with the ME field specified. */ +#define MME(op, me, rc) (M ((op), (rc)) | (((me) & 0x1f) << 1)) + +/* An M_MASK with the MB and ME fields fixed. */ +#define MMBME_MASK (M_MASK | MB_MASK | ME_MASK) + +/* An M_MASK with the SH and ME fields fixed. */ +#define MSHME_MASK (M_MASK | SH_MASK | ME_MASK) + +/* An MD form instruction. */ +#define MD(op, xop, rc) (OP (op) | (((xop) & 0x7) << 2) | ((rc) & 1)) +#define MD_MASK MD (0x3f, 0x7, 1) + +/* An MD_MASK with the MB field fixed. */ +#define MDMB_MASK (MD_MASK | MB6_MASK) + +/* An MD_MASK with the SH field fixed. */ +#define MDSH_MASK (MD_MASK | SH6_MASK) + +/* An MDS form instruction. */ +#define MDS(op, xop, rc) (OP (op) | (((xop) & 0xf) << 1) | ((rc) & 1)) +#define MDS_MASK MDS (0x3f, 0xf, 1) + +/* An MDS_MASK with the MB field fixed. */ +#define MDSMB_MASK (MDS_MASK | MB6_MASK) + +/* An SC form instruction. */ +#define SC(op, sa, lk) (OP (op) | (((sa) & 1) << 1) | ((lk) & 1)) +#define SC_MASK (OP_MASK | (0x3ff << 16) | (1 << 1) | 1) + +/* An X form instruction. */ +#define X(op, xop) (OP (op) | (((xop) & 0x3ff) << 1)) + +/* An X form instruction with the RC bit specified. */ +#define XRC(op, xop, rc) (X ((op), (xop)) | ((rc) & 1)) + +/* The mask for an X form instruction. */ +#define X_MASK XRC (0x3f, 0x3ff, 1) + +/* An X_MASK with the RA field fixed. */ +#define XRA_MASK (X_MASK | RA_MASK) + +/* An X_MASK with the RB field fixed. */ +#define XRB_MASK (X_MASK | RB_MASK) + +/* An X_MASK with the RT field fixed. */ +#define XRT_MASK (X_MASK | RT_MASK) + +/* An X_MASK with the RA and RB fields fixed. */ +#define XRARB_MASK (X_MASK | RA_MASK | RB_MASK) + +/* An X_MASK with the RT and RA fields fixed. */ +#define XRTRA_MASK (X_MASK | RT_MASK | RA_MASK) + +/* An X form comparison instruction. */ +#define XCMPL(op, xop, l) (X ((op), (xop)) | (((l) & 1) << 21)) + +/* The mask for an X form comparison instruction. */ +#define XCMP_MASK (X_MASK | (1 << 22)) + +/* The mask for an X form comparison instruction with the L field + fixed. */ +#define XCMPL_MASK (XCMP_MASK | (1 << 21)) + +/* An X form trap instruction with the TO field specified. */ +#define XTO(op, xop, to) (X ((op), (xop)) | (((to) & 0x1f) << 21)) +#define XTO_MASK (X_MASK | TO_MASK) + +/* An XFL form instruction. */ +#define XFL(op, xop, rc) (OP (op) | (((xop) & 0x3ff) << 1) | ((rc) & 1)) +#define XFL_MASK (XFL (0x3f, 0x3ff, 1) | (1 << 25) | (1 << 16)) + +/* An XL form instruction with the LK field set to 0. */ +#define XL(op, xop) (OP (op) | (((xop) & 0x3ff) << 1)) + +/* An XL form instruction which uses the LK field. */ +#define XLLK(op, xop, lk) (XL ((op), (xop)) | ((lk) & 1)) + +/* The mask for an XL form instruction. */ +#define XL_MASK XLLK (0x3f, 0x3ff, 1) + +/* An XL form instruction which explicitly sets the BO field. */ +#define XLO(op, bo, xop, lk) \ + (XLLK ((op), (xop), (lk)) | (((bo) & 0x1f) << 21)) +#define XLO_MASK (XL_MASK | BO_MASK) + +/* An XL form instruction which explicitly sets the y bit of the BO + field. */ +#define XLYLK(op, xop, y, lk) (XLLK ((op), (xop), (lk)) | (((y) & 1) << 21)) +#define XLYLK_MASK (XL_MASK | Y_MASK) + +/* An XL form instruction which sets the BO field and the condition + bits of the BI field. */ +#define XLOCB(op, bo, cb, xop, lk) \ + (XLO ((op), (bo), (xop), (lk)) | (((cb) & 3) << 16)) +#define XLOCB_MASK XLOCB (0x3f, 0x1f, 0x3, 0x3ff, 1) + +/* An XL_MASK or XLYLK_MASK or XLOCB_MASK with the BB field fixed. */ +#define XLBB_MASK (XL_MASK | BB_MASK) +#define XLYBB_MASK (XLYLK_MASK | BB_MASK) +#define XLBOCBBB_MASK (XLOCB_MASK | BB_MASK) + +/* An XL_MASK with the BO and BB fields fixed. */ +#define XLBOBB_MASK (XL_MASK | BO_MASK | BB_MASK) + +/* An XL_MASK with the BO, BI and BB fields fixed. */ +#define XLBOBIBB_MASK (XL_MASK | BO_MASK | BI_MASK | BB_MASK) + +/* An XO form instruction. */ +#define XO(op, xop, oe, rc) \ + (OP (op) | (((xop) & 0x1ff) << 1) | (((oe) & 1) << 10) | ((rc) & 1)) +#define XO_MASK XO (0x3f, 0x1ff, 1, 1) + +/* An XO_MASK with the RB field fixed. */ +#define XORB_MASK (XO_MASK | RB_MASK) + +/* An XS form instruction. */ +#define XS(op, xop, rc) (OP (op) | (((xop) & 0x1ff) << 2) | ((rc) & 1)) +#define XS_MASK XS (0x3f, 0x1ff, 1) + +/* A mask for the FXM version of an XFX form instruction. */ +#define XFXFXM_MASK (X_MASK | (1 << 20) | (1 << 11)) + +/* An XFX form instruction with the FXM field filled in. */ +#define XFXM(op, xop, fxm) \ + (X ((op), (xop)) | (((fxm) & 0xff) << 12)) + +/* An XFX form instruction with the SPR field filled in. */ +#define XSPR(op, xop, spr) \ + (X ((op), (xop)) | (((spr) & 0x1f) << 16) | (((spr) & 0x3e0) << 6)) +#define XSPR_MASK (X_MASK | SPR_MASK) + +/* An XFX form instruction with the SPR field filled in except for the + SPRBAT field. */ +#define XSPRBAT_MASK (XSPR_MASK &~ SPRBAT_MASK) + +/* An XFX form instruction with the SPR field filled in except for the + SPRG field. */ +#define XSPRG_MASK (XSPR_MASK &~ SPRG_MASK) + +/* The BO encodings used in extended conditional branch mnemonics. */ +#define BODNZF (0x0) +#define BODNZFP (0x1) +#define BODZF (0x2) +#define BODZFP (0x3) +#define BOF (0x4) +#define BOFP (0x5) +#define BODNZT (0x8) +#define BODNZTP (0x9) +#define BODZT (0xa) +#define BODZTP (0xb) +#define BOT (0xc) +#define BOTP (0xd) +#define BODNZ (0x10) +#define BODNZP (0x11) +#define BODZ (0x12) +#define BODZP (0x13) +#define BOU (0x14) + +/* The BI condition bit encodings used in extended conditional branch + mnemonics. */ +#define CBLT (0) +#define CBGT (1) +#define CBEQ (2) +#define CBSO (3) + +/* The TO encodings used in extended trap mnemonics. */ +#define TOLGT (0x1) +#define TOLLT (0x2) +#define TOEQ (0x4) +#define TOLGE (0x5) +#define TOLNL (0x5) +#define TOLLE (0x6) +#define TOLNG (0x6) +#define TOGT (0x8) +#define TOGE (0xc) +#define TONL (0xc) +#define TOLT (0x10) +#define TOLE (0x14) +#define TONG (0x14) +#define TONE (0x18) +#define TOU (0x1f) + +/* Smaller names for the flags so each entry in the opcodes table will + fit on a single line. */ +#undef PPC +#define PPC PPC_OPCODE_PPC +#define POWER PPC_OPCODE_POWER +#define POWER2 PPC_OPCODE_POWER2 +#define B32 PPC_OPCODE_32 +#define B64 PPC_OPCODE_64 +#define M601 PPC_OPCODE_601 + +/* The opcode table. + + The format of the opcode table is: + + NAME OPCODE MASK FLAGS { OPERANDS } + + NAME is the name of the instruction. + OPCODE is the instruction opcode. + MASK is the opcode mask; this is used to tell the disassembler + which bits in the actual opcode must match OPCODE. + FLAGS are flags indicated what processors support the instruction. + OPERANDS is the list of operands. + + The disassembler reads the table in order and prints the first + instruction which matches, so this table is sorted to put more + specific instructions before more general instructions. It is also + sorted by major opcode. */ + +const struct powerpc_opcode powerpc_opcodes[] = { +{ "tdlgti", OPTO(2,TOLGT), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdllti", OPTO(2,TOLLT), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdeqi", OPTO(2,TOEQ), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdlgei", OPTO(2,TOLGE), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdlnli", OPTO(2,TOLNL), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdllei", OPTO(2,TOLLE), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdlngi", OPTO(2,TOLNG), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdgti", OPTO(2,TOGT), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdgei", OPTO(2,TOGE), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdnli", OPTO(2,TONL), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdlti", OPTO(2,TOLT), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdlei", OPTO(2,TOLE), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdngi", OPTO(2,TONG), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdnei", OPTO(2,TONE), OPTO_MASK, PPC|B64, { RA, SI } }, +{ "tdi", OP(2), OP_MASK, PPC|B64, { TO, RA, SI } }, + +{ "twlgti", OPTO(3,TOLGT), OPTO_MASK, PPC, { RA, SI } }, +{ "tlgti", OPTO(3,TOLGT), OPTO_MASK, POWER, { RA, SI } }, +{ "twllti", OPTO(3,TOLLT), OPTO_MASK, PPC, { RA, SI } }, +{ "tllti", OPTO(3,TOLLT), OPTO_MASK, POWER, { RA, SI } }, +{ "tweqi", OPTO(3,TOEQ), OPTO_MASK, PPC, { RA, SI } }, +{ "teqi", OPTO(3,TOEQ), OPTO_MASK, POWER, { RA, SI } }, +{ "twlgei", OPTO(3,TOLGE), OPTO_MASK, PPC, { RA, SI } }, +{ "tlgei", OPTO(3,TOLGE), OPTO_MASK, POWER, { RA, SI } }, +{ "twlnli", OPTO(3,TOLNL), OPTO_MASK, PPC, { RA, SI } }, +{ "tlnli", OPTO(3,TOLNL), OPTO_MASK, POWER, { RA, SI } }, +{ "twllei", OPTO(3,TOLLE), OPTO_MASK, PPC, { RA, SI } }, +{ "tllei", OPTO(3,TOLLE), OPTO_MASK, POWER, { RA, SI } }, +{ "twlngi", OPTO(3,TOLNG), OPTO_MASK, PPC, { RA, SI } }, +{ "tlngi", OPTO(3,TOLNG), OPTO_MASK, POWER, { RA, SI } }, +{ "twgti", OPTO(3,TOGT), OPTO_MASK, PPC, { RA, SI } }, +{ "tgti", OPTO(3,TOGT), OPTO_MASK, POWER, { RA, SI } }, +{ "twgei", OPTO(3,TOGE), OPTO_MASK, PPC, { RA, SI } }, +{ "tgei", OPTO(3,TOGE), OPTO_MASK, POWER, { RA, SI } }, +{ "twnli", OPTO(3,TONL), OPTO_MASK, PPC, { RA, SI } }, +{ "tnli", OPTO(3,TONL), OPTO_MASK, POWER, { RA, SI } }, +{ "twlti", OPTO(3,TOLT), OPTO_MASK, PPC, { RA, SI } }, +{ "tlti", OPTO(3,TOLT), OPTO_MASK, POWER, { RA, SI } }, +{ "twlei", OPTO(3,TOLE), OPTO_MASK, PPC, { RA, SI } }, +{ "tlei", OPTO(3,TOLE), OPTO_MASK, POWER, { RA, SI } }, +{ "twngi", OPTO(3,TONG), OPTO_MASK, PPC, { RA, SI } }, +{ "tngi", OPTO(3,TONG), OPTO_MASK, POWER, { RA, SI } }, +{ "twnei", OPTO(3,TONE), OPTO_MASK, PPC, { RA, SI } }, +{ "tnei", OPTO(3,TONE), OPTO_MASK, POWER, { RA, SI } }, +{ "twi", OP(3), OP_MASK, PPC, { TO, RA, SI } }, +{ "ti", OP(3), OP_MASK, POWER, { TO, RA, SI } }, + +{ "mulli", OP(7), OP_MASK, PPC, { RT, RA, SI } }, +{ "muli", OP(7), OP_MASK, POWER, { RT, RA, SI } }, + +{ "subfic", OP(8), OP_MASK, PPC, { RT, RA, SI } }, +{ "sfi", OP(8), OP_MASK, POWER, { RT, RA, SI } }, + +{ "dozi", OP(9), OP_MASK, POWER|M601, { RT, RA, SI } }, + +{ "cmplwi", OPL(10,0), OPL_MASK, PPC, { OBF, RA, UI } }, +{ "cmpldi", OPL(10,1), OPL_MASK, PPC|B64, { OBF, RA, UI } }, +{ "cmpli", OP(10), OP_MASK, PPC, { BF, L, RA, UI } }, +{ "cmpli", OP(10), OP_MASK, POWER, { BF, RA, UI } }, + +{ "cmpwi", OPL(11,0), OPL_MASK, PPC, { OBF, RA, SI } }, +{ "cmpdi", OPL(11,1), OPL_MASK, PPC|B64, { OBF, RA, SI } }, +{ "cmpi", OP(11), OP_MASK, PPC, { BF, L, RA, SI } }, +{ "cmpi", OP(11), OP_MASK, POWER, { BF, RA, SI } }, + +{ "addic", OP(12), OP_MASK, PPC, { RT, RA, SI } }, +{ "ai", OP(12), OP_MASK, POWER, { RT, RA, SI } }, +{ "subic", OP(12), OP_MASK, PPC, { RT, RA, NSI } }, + +{ "addic.", OP(13), OP_MASK, PPC, { RT, RA, SI } }, +{ "ai.", OP(13), OP_MASK, POWER, { RT, RA, SI } }, +{ "subic.", OP(13), OP_MASK, PPC, { RT, RA, NSI } }, + +{ "li", OP(14), DRA_MASK, PPC, { RT, SI } }, +{ "lil", OP(14), DRA_MASK, POWER, { RT, SI } }, +{ "addi", OP(14), OP_MASK, PPC, { RT, RA, SI } }, +{ "cal", OP(14), OP_MASK, POWER, { RT, D, RA } }, +{ "subi", OP(14), OP_MASK, PPC, { RT, RA, NSI } }, +{ "la", OP(14), OP_MASK, PPC, { RT, D, RA } }, + +{ "lis", OP(15), DRA_MASK, PPC, { RT, SISIGNOPT } }, +{ "liu", OP(15), DRA_MASK, POWER, { RT, SISIGNOPT } }, +{ "addis", OP(15), OP_MASK, PPC, { RT,RA,SISIGNOPT } }, +{ "cau", OP(15), OP_MASK, POWER, { RT,RA,SISIGNOPT } }, +{ "subis", OP(15), OP_MASK, PPC, { RT, RA, NSI } }, + +{ "bdnz-", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC, { BDM } }, +{ "bdnz+", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC, { BDP } }, +{ "bdnz", BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC, { BD } }, +{ "bdn", BBO(16,BODNZ,0,0), BBOYBI_MASK, POWER, { BD } }, +{ "bdnzl-", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC, { BDM } }, +{ "bdnzl+", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC, { BDP } }, +{ "bdnzl", BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC, { BD } }, +{ "bdnl", BBO(16,BODNZ,0,1), BBOYBI_MASK, POWER, { BD } }, +{ "bdnza-", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC, { BDMA } }, +{ "bdnza+", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC, { BDPA } }, +{ "bdnza", BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC, { BDA } }, +{ "bdna", BBO(16,BODNZ,1,0), BBOYBI_MASK, POWER, { BDA } }, +{ "bdnzla-", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC, { BDMA } }, +{ "bdnzla+", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC, { BDPA } }, +{ "bdnzla", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC, { BDA } }, +{ "bdnla", BBO(16,BODNZ,1,1), BBOYBI_MASK, POWER, { BDA } }, +{ "bdz-", BBO(16,BODZ,0,0), BBOYBI_MASK, PPC, { BDM } }, +{ "bdz+", BBO(16,BODZ,0,0), BBOYBI_MASK, PPC, { BDP } }, +{ "bdz", BBO(16,BODZ,0,0), BBOYBI_MASK, PPC|POWER, { BD } }, +{ "bdzl-", BBO(16,BODZ,0,1), BBOYBI_MASK, PPC, { BDM } }, +{ "bdzl+", BBO(16,BODZ,0,1), BBOYBI_MASK, PPC, { BDP } }, +{ "bdzl", BBO(16,BODZ,0,1), BBOYBI_MASK, PPC|POWER, { BD } }, +{ "bdza-", BBO(16,BODZ,1,0), BBOYBI_MASK, PPC, { BDMA } }, +{ "bdza+", BBO(16,BODZ,1,0), BBOYBI_MASK, PPC, { BDPA } }, +{ "bdza", BBO(16,BODZ,1,0), BBOYBI_MASK, PPC|POWER, { BDA } }, +{ "bdzla-", BBO(16,BODZ,1,1), BBOYBI_MASK, PPC, { BDMA } }, +{ "bdzla+", BBO(16,BODZ,1,1), BBOYBI_MASK, PPC, { BDPA } }, +{ "bdzla", BBO(16,BODZ,1,1), BBOYBI_MASK, PPC|POWER, { BDA } }, +{ "blt-", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "blt+", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "blt", BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bltl-", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bltl+", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bltl", BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "blta-", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "blta+", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "blta", BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bltla-", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bltla+", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bltla", BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bgt-", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bgt+", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bgt", BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bgtl-", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bgtl+", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bgtl", BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bgta-", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bgta+", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bgta", BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bgtla-", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bgtla+", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bgtla", BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "beq-", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "beq+", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "beq", BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "beql-", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "beql+", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "beql", BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "beqa-", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "beqa+", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "beqa", BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "beqla-", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "beqla+", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "beqla", BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bso-", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bso+", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bso", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bsol-", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bsol+", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bsol", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bsoa-", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bsoa+", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bsoa", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bsola-", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bsola+", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bsola", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bun-", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bun+", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bun", BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BD } }, +{ "bunl-", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bunl+", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bunl", BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BD } }, +{ "buna-", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "buna+", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "buna", BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDA } }, +{ "bunla-", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bunla+", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bunla", BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDA } }, +{ "bge-", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bge+", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bge", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bgel-", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bgel+", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bgel", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bgea-", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bgea+", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bgea", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bgela-", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bgela+", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bgela", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bnl-", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnl+", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnl", BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnll-", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnll+", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnll", BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnla-", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnla+", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnla", BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bnlla-", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnlla+", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnlla", BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "ble-", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "ble+", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "ble", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "blel-", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "blel+", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "blel", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "blea-", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "blea+", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "blea", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "blela-", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "blela+", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "blela", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bng-", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bng+", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bng", BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bngl-", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bngl+", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bngl", BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnga-", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnga+", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnga", BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bngla-", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bngla+", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bngla", BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bne-", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bne+", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bne", BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnel-", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnel+", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnel", BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnea-", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnea+", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnea", BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bnela-", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnela+", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnela", BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bns-", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bns+", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bns", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnsl-", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnsl+", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnsl", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } }, +{ "bnsa-", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnsa+", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnsa", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bnsla-", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnsla+", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnsla", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } }, +{ "bnu-", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnu+", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnu", BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC, { CR, BD } }, +{ "bnul-", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDM } }, +{ "bnul+", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BDP } }, +{ "bnul", BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC, { CR, BD } }, +{ "bnua-", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnua+", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnua", BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC, { CR, BDA } }, +{ "bnula-", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDMA } }, +{ "bnula+", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDPA } }, +{ "bnula", BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC, { CR, BDA } }, +{ "bdnzt-", BBO(16,BODNZT,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdnzt+", BBO(16,BODNZT,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdnzt", BBO(16,BODNZT,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bdnztl-", BBO(16,BODNZT,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdnztl+", BBO(16,BODNZT,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdnztl", BBO(16,BODNZT,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bdnzta-", BBO(16,BODNZT,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdnzta+", BBO(16,BODNZT,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdnzta", BBO(16,BODNZT,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdnztla-",BBO(16,BODNZT,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdnztla+",BBO(16,BODNZT,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdnztla", BBO(16,BODNZT,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdnzf-", BBO(16,BODNZF,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdnzf+", BBO(16,BODNZF,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdnzf", BBO(16,BODNZF,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bdnzfl-", BBO(16,BODNZF,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdnzfl+", BBO(16,BODNZF,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdnzfl", BBO(16,BODNZF,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bdnzfa-", BBO(16,BODNZF,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdnzfa+", BBO(16,BODNZF,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdnzfa", BBO(16,BODNZF,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdnzfla-",BBO(16,BODNZF,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdnzfla+",BBO(16,BODNZF,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdnzfla", BBO(16,BODNZF,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bt-", BBO(16,BOT,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bt+", BBO(16,BOT,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bt", BBO(16,BOT,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bbt", BBO(16,BOT,0,0), BBOY_MASK, POWER, { BI, BD } }, +{ "btl-", BBO(16,BOT,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "btl+", BBO(16,BOT,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "btl", BBO(16,BOT,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bbtl", BBO(16,BOT,0,1), BBOY_MASK, POWER, { BI, BD } }, +{ "bta-", BBO(16,BOT,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bta+", BBO(16,BOT,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bta", BBO(16,BOT,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bbta", BBO(16,BOT,1,0), BBOY_MASK, POWER, { BI, BDA } }, +{ "btla-", BBO(16,BOT,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "btla+", BBO(16,BOT,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "btla", BBO(16,BOT,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bbtla", BBO(16,BOT,1,1), BBOY_MASK, POWER, { BI, BDA } }, +{ "bf-", BBO(16,BOF,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bf+", BBO(16,BOF,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bf", BBO(16,BOF,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bbf", BBO(16,BOF,0,0), BBOY_MASK, POWER, { BI, BD } }, +{ "bfl-", BBO(16,BOF,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "bfl+", BBO(16,BOF,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "bfl", BBO(16,BOF,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bbfl", BBO(16,BOF,0,1), BBOY_MASK, POWER, { BI, BD } }, +{ "bfa-", BBO(16,BOF,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bfa+", BBO(16,BOF,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bfa", BBO(16,BOF,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bbfa", BBO(16,BOF,1,0), BBOY_MASK, POWER, { BI, BDA } }, +{ "bfla-", BBO(16,BOF,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bfla+", BBO(16,BOF,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bfla", BBO(16,BOF,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bbfla", BBO(16,BOF,1,1), BBOY_MASK, POWER, { BI, BDA } }, +{ "bdzt-", BBO(16,BODZT,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdzt+", BBO(16,BODZT,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdzt", BBO(16,BODZT,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bdztl-", BBO(16,BODZT,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdztl+", BBO(16,BODZT,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdztl", BBO(16,BODZT,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bdzta-", BBO(16,BODZT,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdzta+", BBO(16,BODZT,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdzta", BBO(16,BODZT,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdztla-", BBO(16,BODZT,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdztla+", BBO(16,BODZT,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdztla", BBO(16,BODZT,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdzf-", BBO(16,BODZF,0,0), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdzf+", BBO(16,BODZF,0,0), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdzf", BBO(16,BODZF,0,0), BBOY_MASK, PPC, { BI, BD } }, +{ "bdzfl-", BBO(16,BODZF,0,1), BBOY_MASK, PPC, { BI, BDM } }, +{ "bdzfl+", BBO(16,BODZF,0,1), BBOY_MASK, PPC, { BI, BDP } }, +{ "bdzfl", BBO(16,BODZF,0,1), BBOY_MASK, PPC, { BI, BD } }, +{ "bdzfa-", BBO(16,BODZF,1,0), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdzfa+", BBO(16,BODZF,1,0), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdzfa", BBO(16,BODZF,1,0), BBOY_MASK, PPC, { BI, BDA } }, +{ "bdzfla-", BBO(16,BODZF,1,1), BBOY_MASK, PPC, { BI, BDMA } }, +{ "bdzfla+", BBO(16,BODZF,1,1), BBOY_MASK, PPC, { BI, BDPA } }, +{ "bdzfla", BBO(16,BODZF,1,1), BBOY_MASK, PPC, { BI, BDA } }, +{ "bc-", B(16,0,0), B_MASK, PPC, { BOE, BI, BDM } }, +{ "bc+", B(16,0,0), B_MASK, PPC, { BOE, BI, BDP } }, +{ "bc", B(16,0,0), B_MASK, PPC|POWER, { BO, BI, BD } }, +{ "bcl-", B(16,0,1), B_MASK, PPC, { BOE, BI, BDM } }, +{ "bcl+", B(16,0,1), B_MASK, PPC, { BOE, BI, BDP } }, +{ "bcl", B(16,0,1), B_MASK, PPC|POWER, { BO, BI, BD } }, +{ "bca-", B(16,1,0), B_MASK, PPC, { BOE, BI, BDMA } }, +{ "bca+", B(16,1,0), B_MASK, PPC, { BOE, BI, BDPA } }, +{ "bca", B(16,1,0), B_MASK, PPC|POWER, { BO, BI, BDA } }, +{ "bcla-", B(16,1,1), B_MASK, PPC, { BOE, BI, BDMA } }, +{ "bcla+", B(16,1,1), B_MASK, PPC, { BOE, BI, BDPA } }, +{ "bcla", B(16,1,1), B_MASK, PPC|POWER, { BO, BI, BDA } }, + +{ "sc", SC(17,1,0), 0xffffffff, PPC, { 0 } }, +{ "svc", SC(17,0,0), SC_MASK, POWER, { LEV, FL1, FL2 } }, +{ "svcl", SC(17,0,1), SC_MASK, POWER, { LEV, FL1, FL2 } }, +{ "svca", SC(17,1,0), SC_MASK, POWER, { SV } }, +{ "svcla", SC(17,1,1), SC_MASK, POWER, { SV } }, + +{ "b", B(18,0,0), B_MASK, PPC|POWER, { LI } }, +{ "bl", B(18,0,1), B_MASK, PPC|POWER, { LI } }, +{ "ba", B(18,1,0), B_MASK, PPC|POWER, { LIA } }, +{ "bla", B(18,1,1), B_MASK, PPC|POWER, { LIA } }, + +{ "mcrf", XL(19,0), XLBB_MASK|(3<<21)|(3<<16), PPC|POWER, { BF, BFA } }, + +{ "blr", XLO(19,BOU,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "br", XLO(19,BOU,16,0), XLBOBIBB_MASK, POWER, { 0 } }, +{ "blrl", XLO(19,BOU,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "brl", XLO(19,BOU,16,1), XLBOBIBB_MASK, POWER, { 0 } }, +{ "bdnzlr", XLO(19,BODNZ,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdnzlr-", XLO(19,BODNZ,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdnzlr+", XLO(19,BODNZP,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdnzlrl", XLO(19,BODNZ,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdnzlrl-",XLO(19,BODNZ,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdnzlrl+",XLO(19,BODNZP,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlr", XLO(19,BODZ,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlr-", XLO(19,BODZ,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlr+", XLO(19,BODZP,16,0), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlrl", XLO(19,BODZ,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlrl-", XLO(19,BODZ,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bdzlrl+", XLO(19,BODZP,16,1), XLBOBIBB_MASK, PPC, { 0 } }, +{ "bltlr", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltlr-", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltlr+", XLOCB(19,BOTP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltr", XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bltlrl", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltlrl-", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltlrl+", XLOCB(19,BOTP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltrl", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bgtlr", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtlr-", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtlr+", XLOCB(19,BOTP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtr", XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bgtlrl", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtlrl-", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtlrl+", XLOCB(19,BOTP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtrl", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "beqlr", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqlr-", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqlr+", XLOCB(19,BOTP,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqr", XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "beqlrl", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqlrl-", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqlrl+", XLOCB(19,BOTP,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqrl", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bsolr", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsolr-", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsolr+", XLOCB(19,BOTP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsor", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bsolrl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsolrl-", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsolrl+", XLOCB(19,BOTP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsorl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bunlr", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunlr-", XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunlr+", XLOCB(19,BOTP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunlrl", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunlrl-", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunlrl+", XLOCB(19,BOTP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgelr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgelr-", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgelr+", XLOCB(19,BOFP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bger", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bgelrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgelrl-", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgelrl+", XLOCB(19,BOFP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgerl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnllr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnllr-", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnllr+", XLOCB(19,BOFP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlr", XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnllrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnllrl-", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnllrl+", XLOCB(19,BOFP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlrl", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "blelr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "blelr-", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "blelr+", XLOCB(19,BOFP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bler", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "blelrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blelrl-", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blelrl+", XLOCB(19,BOFP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blerl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnglr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnglr-", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnglr+", XLOCB(19,BOFP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngr", XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnglrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnglrl-", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnglrl+", XLOCB(19,BOFP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngrl", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnelr", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnelr-", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnelr+", XLOCB(19,BOFP,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bner", XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnelrl", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnelrl-", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnelrl+", XLOCB(19,BOFP,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnerl", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnslr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnslr-", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnslr+", XLOCB(19,BOFP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnslrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnslrl-", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnslrl+", XLOCB(19,BOFP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, POWER, { CR } }, +{ "bnulr", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnulr-", XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnulr+", XLOCB(19,BOFP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnulrl", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnulrl-", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnulrl+", XLOCB(19,BOFP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "btlr", XLO(19,BOT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "btlr-", XLO(19,BOT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "btlr+", XLO(19,BOTP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bbtr", XLO(19,BOT,16,0), XLBOBB_MASK, POWER, { BI } }, +{ "btlrl", XLO(19,BOT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "btlrl-", XLO(19,BOT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "btlrl+", XLO(19,BOTP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bbtrl", XLO(19,BOT,16,1), XLBOBB_MASK, POWER, { BI } }, +{ "bflr", XLO(19,BOF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bflr-", XLO(19,BOF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bflr+", XLO(19,BOFP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bbfr", XLO(19,BOF,16,0), XLBOBB_MASK, POWER, { BI } }, +{ "bflrl", XLO(19,BOF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bflrl-", XLO(19,BOF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bflrl+", XLO(19,BOFP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bbfrl", XLO(19,BOF,16,1), XLBOBB_MASK, POWER, { BI } }, +{ "bdnztlr", XLO(19,BODNZT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnztlr-",XLO(19,BODNZT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnztlr+",XLO(19,BODNZTP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnztlrl",XLO(19,BODNZT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdnztlrl-",XLO(19,BODNZT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdnztlrl+",XLO(19,BODNZTP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflr", XLO(19,BODNZF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflr-",XLO(19,BODNZF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflr+",XLO(19,BODNZFP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflrl",XLO(19,BODNZF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflrl-",XLO(19,BODNZF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdnzflrl+",XLO(19,BODNZFP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlr", XLO(19,BODZT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlr-", XLO(19,BODZT,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlr+", XLO(19,BODZTP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlrl", XLO(19,BODZT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlrl-",XLO(19,BODZT,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdztlrl+",XLO(19,BODZTP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflr", XLO(19,BODZF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflr-", XLO(19,BODZF,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflr+", XLO(19,BODZFP,16,0), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflrl", XLO(19,BODZF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflrl-",XLO(19,BODZF,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bdzflrl+",XLO(19,BODZFP,16,1), XLBOBB_MASK, PPC, { BI } }, +{ "bclr", XLLK(19,16,0), XLYBB_MASK, PPC, { BO, BI } }, +{ "bclrl", XLLK(19,16,1), XLYBB_MASK, PPC, { BO, BI } }, +{ "bclr+", XLYLK(19,16,1,0), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bclrl+", XLYLK(19,16,1,1), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bclr-", XLYLK(19,16,0,0), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bclrl-", XLYLK(19,16,0,1), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bcr", XLLK(19,16,0), XLBB_MASK, POWER, { BO, BI } }, +{ "bcrl", XLLK(19,16,1), XLBB_MASK, POWER, { BO, BI } }, + +{ "crnot", XL(19,33), XL_MASK, PPC, { BT, BA, BBA } }, +{ "crnor", XL(19,33), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "rfi", XL(19,50), 0xffffffff, PPC|POWER, { 0 } }, +{ "rfci", XL(19,51), 0xffffffff, PPC, { 0 } }, + +{ "rfsvc", XL(19,82), 0xffffffff, POWER, { 0 } }, + +{ "crandc", XL(19,129), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "isync", XL(19,150), 0xffffffff, PPC, { 0 } }, +{ "ics", XL(19,150), 0xffffffff, POWER, { 0 } }, + +{ "crclr", XL(19,193), XL_MASK, PPC, { BT, BAT, BBA } }, +{ "crxor", XL(19,193), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "crnand", XL(19,225), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "crand", XL(19,257), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "crset", XL(19,289), XL_MASK, PPC, { BT, BAT, BBA } }, +{ "creqv", XL(19,289), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "crorc", XL(19,417), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "crmove", XL(19,449), XL_MASK, PPC, { BT, BA, BBA } }, +{ "cror", XL(19,449), XL_MASK, PPC|POWER, { BT, BA, BB } }, + +{ "bctr", XLO(19,BOU,528,0), XLBOBIBB_MASK, PPC|POWER, { 0 } }, +{ "bctrl", XLO(19,BOU,528,1), XLBOBIBB_MASK, PPC|POWER, { 0 } }, +{ "bltctr", XLOCB(19,BOT,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltctr-", XLOCB(19,BOT,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltctr+", XLOCB(19,BOTP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltctrl", XLOCB(19,BOT,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltctrl-",XLOCB(19,BOT,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bltctrl+",XLOCB(19,BOTP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctr", XLOCB(19,BOT,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctr-", XLOCB(19,BOT,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctr+", XLOCB(19,BOTP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctrl", XLOCB(19,BOT,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctrl-",XLOCB(19,BOT,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgtctrl+",XLOCB(19,BOTP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctr", XLOCB(19,BOT,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctr-", XLOCB(19,BOT,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctr+", XLOCB(19,BOTP,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctrl", XLOCB(19,BOT,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctrl-",XLOCB(19,BOT,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "beqctrl+",XLOCB(19,BOTP,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctr", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctr-", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctr+", XLOCB(19,BOTP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctrl", XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctrl-",XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bsoctrl+",XLOCB(19,BOTP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctr", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctr-", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctr+", XLOCB(19,BOTP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctrl", XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctrl-",XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bunctrl+",XLOCB(19,BOTP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectr", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectr-", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectr+", XLOCB(19,BOFP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectrl", XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectrl-",XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bgectrl+",XLOCB(19,BOFP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctr", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctr-", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctr+", XLOCB(19,BOFP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctrl", XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctrl-",XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnlctrl+",XLOCB(19,BOFP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectr", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectr-", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectr+", XLOCB(19,BOFP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectrl", XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectrl-",XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "blectrl+",XLOCB(19,BOFP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctr", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctr-", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctr+", XLOCB(19,BOFP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctrl", XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctrl-",XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bngctrl+",XLOCB(19,BOFP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectr", XLOCB(19,BOF,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectr-", XLOCB(19,BOF,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectr+", XLOCB(19,BOFP,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectrl", XLOCB(19,BOF,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectrl-",XLOCB(19,BOF,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnectrl+",XLOCB(19,BOFP,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctr", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctr-", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctr+", XLOCB(19,BOFP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctrl", XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctrl-",XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnsctrl+",XLOCB(19,BOFP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctr", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctr-", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctr+", XLOCB(19,BOFP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctrl", XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctrl-",XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "bnuctrl+",XLOCB(19,BOFP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } }, +{ "btctr", XLO(19,BOT,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "btctr-", XLO(19,BOT,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "btctr+", XLO(19,BOTP,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "btctrl", XLO(19,BOT,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "btctrl-", XLO(19,BOT,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "btctrl+", XLO(19,BOTP,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "bfctr", XLO(19,BOF,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "bfctr-", XLO(19,BOF,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "bfctr+", XLO(19,BOFP,528,0), XLBOBB_MASK, PPC, { BI } }, +{ "bfctrl", XLO(19,BOF,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "bfctrl-", XLO(19,BOF,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "bfctrl+", XLO(19,BOFP,528,1), XLBOBB_MASK, PPC, { BI } }, +{ "bcctr", XLLK(19,528,0), XLYBB_MASK, PPC, { BO, BI } }, +{ "bcctr-", XLYLK(19,528,0,0), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bcctr+", XLYLK(19,528,1,0), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bcctrl", XLLK(19,528,1), XLYBB_MASK, PPC, { BO, BI } }, +{ "bcctrl-", XLYLK(19,528,0,1), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bcctrl+", XLYLK(19,528,1,1), XLYBB_MASK, PPC, { BOE, BI } }, +{ "bcc", XLLK(19,528,0), XLBB_MASK, POWER, { BO, BI } }, +{ "bccl", XLLK(19,528,1), XLBB_MASK, POWER, { BO, BI } }, + +{ "rlwimi", M(20,0), M_MASK, PPC, { RA,RS,SH,MBE,ME } }, +{ "rlimi", M(20,0), M_MASK, POWER, { RA,RS,SH,MBE,ME } }, + +{ "rlwimi.", M(20,1), M_MASK, PPC, { RA,RS,SH,MBE,ME } }, +{ "rlimi.", M(20,1), M_MASK, POWER, { RA,RS,SH,MBE,ME } }, + +{ "rotlwi", MME(21,31,0), MMBME_MASK, PPC, { RA, RS, SH } }, +{ "clrlwi", MME(21,31,0), MSHME_MASK, PPC, { RA, RS, MB } }, +{ "rlwinm", M(21,0), M_MASK, PPC, { RA,RS,SH,MBE,ME } }, +{ "rlinm", M(21,0), M_MASK, POWER, { RA,RS,SH,MBE,ME } }, +{ "rotlwi.", MME(21,31,1), MMBME_MASK, PPC, { RA,RS,SH } }, +{ "clrlwi.", MME(21,31,1), MSHME_MASK, PPC, { RA, RS, MB } }, +{ "rlwinm.", M(21,1), M_MASK, PPC, { RA,RS,SH,MBE,ME } }, +{ "rlinm.", M(21,1), M_MASK, POWER, { RA,RS,SH,MBE,ME } }, + +{ "rlmi", M(22,0), M_MASK, POWER|M601, { RA,RS,RB,MBE,ME } }, +{ "rlmi.", M(22,1), M_MASK, POWER|M601, { RA,RS,RB,MBE,ME } }, + +{ "rotlw", MME(23,31,0), MMBME_MASK, PPC, { RA, RS, RB } }, +{ "rlwnm", M(23,0), M_MASK, PPC, { RA,RS,RB,MBE,ME } }, +{ "rlnm", M(23,0), M_MASK, POWER, { RA,RS,RB,MBE,ME } }, +{ "rotlw.", MME(23,31,1), MMBME_MASK, PPC, { RA, RS, RB } }, +{ "rlwnm.", M(23,1), M_MASK, PPC, { RA,RS,RB,MBE,ME } }, +{ "rlnm.", M(23,1), M_MASK, POWER, { RA,RS,RB,MBE,ME } }, + +{ "nop", OP(24), 0xffffffff, PPC, { 0 } }, +{ "ori", OP(24), OP_MASK, PPC, { RA, RS, UI } }, +{ "oril", OP(24), OP_MASK, POWER, { RA, RS, UI } }, + +{ "oris", OP(25), OP_MASK, PPC, { RA, RS, UI } }, +{ "oriu", OP(25), OP_MASK, POWER, { RA, RS, UI } }, + +{ "xori", OP(26), OP_MASK, PPC, { RA, RS, UI } }, +{ "xoril", OP(26), OP_MASK, POWER, { RA, RS, UI } }, + +{ "xoris", OP(27), OP_MASK, PPC, { RA, RS, UI } }, +{ "xoriu", OP(27), OP_MASK, POWER, { RA, RS, UI } }, + +{ "andi.", OP(28), OP_MASK, PPC, { RA, RS, UI } }, +{ "andil.", OP(28), OP_MASK, POWER, { RA, RS, UI } }, + +{ "andis.", OP(29), OP_MASK, PPC, { RA, RS, UI } }, +{ "andiu.", OP(29), OP_MASK, POWER, { RA, RS, UI } }, + +{ "rotldi", MD(30,0,0), MDMB_MASK, PPC|B64, { RA, RS, SH6 } }, +{ "clrldi", MD(30,0,0), MDSH_MASK, PPC|B64, { RA, RS, MB6 } }, +{ "rldicl", MD(30,0,0), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, +{ "rotldi.", MD(30,0,1), MDMB_MASK, PPC|B64, { RA, RS, SH6 } }, +{ "clrldi.", MD(30,0,1), MDSH_MASK, PPC|B64, { RA, RS, MB6 } }, +{ "rldicl.", MD(30,0,1), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, + +{ "rldicr", MD(30,1,0), MD_MASK, PPC|B64, { RA, RS, SH6, ME6 } }, +{ "rldicr.", MD(30,1,1), MD_MASK, PPC|B64, { RA, RS, SH6, ME6 } }, + +{ "rldic", MD(30,2,0), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, +{ "rldic.", MD(30,2,1), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, + +{ "rldimi", MD(30,3,0), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, +{ "rldimi.", MD(30,3,1), MD_MASK, PPC|B64, { RA, RS, SH6, MB6 } }, + +{ "rotld", MDS(30,8,0), MDSMB_MASK, PPC|B64, { RA, RS, RB } }, +{ "rldcl", MDS(30,8,0), MDS_MASK, PPC|B64, { RA, RS, RB, MB6 } }, +{ "rotld.", MDS(30,8,1), MDSMB_MASK, PPC|B64, { RA, RS, RB } }, +{ "rldcl.", MDS(30,8,1), MDS_MASK, PPC|B64, { RA, RS, RB, MB6 } }, + +{ "rldcr", MDS(30,9,0), MDS_MASK, PPC|B64, { RA, RS, RB, ME6 } }, +{ "rldcr.", MDS(30,9,1), MDS_MASK, PPC|B64, { RA, RS, RB, ME6 } }, + +{ "cmpw", XCMPL(31,0,0), XCMPL_MASK, PPC, { OBF, RA, RB } }, +{ "cmpd", XCMPL(31,0,1), XCMPL_MASK, PPC|B64, { OBF, RA, RB } }, +{ "cmp", X(31,0), XCMP_MASK, PPC, { BF, L, RA, RB } }, +{ "cmp", X(31,0), XCMPL_MASK, POWER, { BF, RA, RB } }, + +{ "twlgt", XTO(31,4,TOLGT), XTO_MASK, PPC, { RA, RB } }, +{ "tlgt", XTO(31,4,TOLGT), XTO_MASK, POWER, { RA, RB } }, +{ "twllt", XTO(31,4,TOLLT), XTO_MASK, PPC, { RA, RB } }, +{ "tllt", XTO(31,4,TOLLT), XTO_MASK, POWER, { RA, RB } }, +{ "tweq", XTO(31,4,TOEQ), XTO_MASK, PPC, { RA, RB } }, +{ "teq", XTO(31,4,TOEQ), XTO_MASK, POWER, { RA, RB } }, +{ "twlge", XTO(31,4,TOLGE), XTO_MASK, PPC, { RA, RB } }, +{ "tlge", XTO(31,4,TOLGE), XTO_MASK, POWER, { RA, RB } }, +{ "twlnl", XTO(31,4,TOLNL), XTO_MASK, PPC, { RA, RB } }, +{ "tlnl", XTO(31,4,TOLNL), XTO_MASK, POWER, { RA, RB } }, +{ "twlle", XTO(31,4,TOLLE), XTO_MASK, PPC, { RA, RB } }, +{ "tlle", XTO(31,4,TOLLE), XTO_MASK, POWER, { RA, RB } }, +{ "twlng", XTO(31,4,TOLNG), XTO_MASK, PPC, { RA, RB } }, +{ "tlng", XTO(31,4,TOLNG), XTO_MASK, POWER, { RA, RB } }, +{ "twgt", XTO(31,4,TOGT), XTO_MASK, PPC, { RA, RB } }, +{ "tgt", XTO(31,4,TOGT), XTO_MASK, POWER, { RA, RB } }, +{ "twge", XTO(31,4,TOGE), XTO_MASK, PPC, { RA, RB } }, +{ "tge", XTO(31,4,TOGE), XTO_MASK, POWER, { RA, RB } }, +{ "twnl", XTO(31,4,TONL), XTO_MASK, PPC, { RA, RB } }, +{ "tnl", XTO(31,4,TONL), XTO_MASK, POWER, { RA, RB } }, +{ "twlt", XTO(31,4,TOLT), XTO_MASK, PPC, { RA, RB } }, +{ "tlt", XTO(31,4,TOLT), XTO_MASK, POWER, { RA, RB } }, +{ "twle", XTO(31,4,TOLE), XTO_MASK, PPC, { RA, RB } }, +{ "tle", XTO(31,4,TOLE), XTO_MASK, POWER, { RA, RB } }, +{ "twng", XTO(31,4,TONG), XTO_MASK, PPC, { RA, RB } }, +{ "tng", XTO(31,4,TONG), XTO_MASK, POWER, { RA, RB } }, +{ "twne", XTO(31,4,TONE), XTO_MASK, PPC, { RA, RB } }, +{ "tne", XTO(31,4,TONE), XTO_MASK, POWER, { RA, RB } }, +{ "trap", XTO(31,4,TOU), 0xffffffff, PPC, { 0 } }, +{ "tw", X(31,4), X_MASK, PPC, { TO, RA, RB } }, +{ "t", X(31,4), X_MASK, POWER, { TO, RA, RB } }, + +{ "subfc", XO(31,8,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sf", XO(31,8,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "subc", XO(31,8,0,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfc.", XO(31,8,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sf.", XO(31,8,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "subc.", XO(31,8,0,1), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfco", XO(31,8,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfo", XO(31,8,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "subco", XO(31,8,1,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfco.", XO(31,8,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfo.", XO(31,8,1,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "subco.", XO(31,8,1,1), XO_MASK, PPC, { RT, RB, RA } }, + +{ "mulhdu", XO(31,9,0,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "mulhdu.", XO(31,9,0,1), XO_MASK, PPC|B64, { RT, RA, RB } }, + +{ "addc", XO(31,10,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "a", XO(31,10,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "addc.", XO(31,10,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "a.", XO(31,10,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "addco", XO(31,10,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "ao", XO(31,10,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "addco.", XO(31,10,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "ao.", XO(31,10,1,1), XO_MASK, POWER, { RT, RA, RB } }, + +{ "mulhwu", XO(31,11,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "mulhwu.", XO(31,11,0,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "mfcr", X(31,19), XRARB_MASK, POWER|PPC, { RT } }, + +{ "lwarx", X(31,20), X_MASK, PPC, { RT, RA, RB } }, + +{ "ldx", X(31,21), X_MASK, PPC|B64, { RT, RA, RB } }, + +{ "lwzx", X(31,23), X_MASK, PPC, { RT, RA, RB } }, +{ "lx", X(31,23), X_MASK, POWER, { RT, RA, RB } }, + +{ "slw", XRC(31,24,0), X_MASK, PPC, { RA, RS, RB } }, +{ "sl", XRC(31,24,0), X_MASK, POWER, { RA, RS, RB } }, +{ "slw.", XRC(31,24,1), X_MASK, PPC, { RA, RS, RB } }, +{ "sl.", XRC(31,24,1), X_MASK, POWER, { RA, RS, RB } }, + +{ "cntlzw", XRC(31,26,0), XRB_MASK, PPC, { RA, RS } }, +{ "cntlz", XRC(31,26,0), XRB_MASK, POWER, { RA, RS } }, +{ "cntlzw.", XRC(31,26,1), XRB_MASK, PPC, { RA, RS } }, +{ "cntlz.", XRC(31,26,1), XRB_MASK, POWER, { RA, RS } }, + +{ "sld", XRC(31,27,0), X_MASK, PPC|B64, { RA, RS, RB } }, +{ "sld.", XRC(31,27,1), X_MASK, PPC|B64, { RA, RS, RB } }, + +{ "and", XRC(31,28,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "and.", XRC(31,28,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "maskg", XRC(31,29,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "maskg.", XRC(31,29,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "cmplw", XCMPL(31,32,0), XCMPL_MASK, PPC, { OBF, RA, RB } }, +{ "cmpld", XCMPL(31,32,1), XCMPL_MASK, PPC|B64, { OBF, RA, RB } }, +{ "cmpl", X(31,32), XCMP_MASK, PPC, { BF, L, RA, RB } }, +{ "cmpl", X(31,32), XCMPL_MASK, POWER, { BF, RA, RB } }, + +{ "subf", XO(31,40,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sub", XO(31,40,0,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subf.", XO(31,40,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sub.", XO(31,40,0,1), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfo", XO(31,40,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "subo", XO(31,40,1,0), XO_MASK, PPC, { RT, RB, RA } }, +{ "subfo.", XO(31,40,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "subo.", XO(31,40,1,1), XO_MASK, PPC, { RT, RB, RA } }, + +{ "ldux", X(31,53), X_MASK, PPC|B64, { RT, RAL, RB } }, + +{ "dcbst", X(31,54), XRT_MASK, PPC, { RA, RB } }, + +{ "lwzux", X(31,55), X_MASK, PPC, { RT, RAL, RB } }, +{ "lux", X(31,55), X_MASK, POWER, { RT, RA, RB } }, + +{ "cntlzd", XRC(31,58,0), XRB_MASK, PPC|B64, { RA, RS } }, +{ "cntlzd.", XRC(31,58,1), XRB_MASK, PPC|B64, { RA, RS } }, + +{ "andc", XRC(31,60,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "andc.", XRC(31,60,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "tdlgt", XTO(31,68,TOLGT), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdllt", XTO(31,68,TOLLT), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdeq", XTO(31,68,TOEQ), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdlge", XTO(31,68,TOLGE), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdlnl", XTO(31,68,TOLNL), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdlle", XTO(31,68,TOLLE), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdlng", XTO(31,68,TOLNG), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdgt", XTO(31,68,TOGT), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdge", XTO(31,68,TOGE), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdnl", XTO(31,68,TONL), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdlt", XTO(31,68,TOLT), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdle", XTO(31,68,TOLE), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdng", XTO(31,68,TONG), XTO_MASK, PPC|B64, { RA, RB } }, +{ "tdne", XTO(31,68,TONE), XTO_MASK, PPC|B64, { RA, RB } }, +{ "td", X(31,68), X_MASK, PPC|B64, { TO, RA, RB } }, + +{ "mulhd", XO(31,73,0,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "mulhd.", XO(31,73,0,1), XO_MASK, PPC|B64, { RT, RA, RB } }, + +{ "mulhw", XO(31,75,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "mulhw.", XO(31,75,0,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "mfmsr", X(31,83), XRARB_MASK, PPC|POWER, { RT } }, + +{ "ldarx", X(31,84), X_MASK, PPC|B64, { RT, RA, RB } }, + +{ "dcbf", X(31,86), XRT_MASK, PPC, { RA, RB } }, + +{ "lbzx", X(31,87), X_MASK, PPC|POWER, { RT, RA, RB } }, + +{ "neg", XO(31,104,0,0), XORB_MASK, PPC|POWER, { RT, RA } }, +{ "neg.", XO(31,104,0,1), XORB_MASK, PPC|POWER, { RT, RA } }, +{ "nego", XO(31,104,1,0), XORB_MASK, PPC|POWER, { RT, RA } }, +{ "nego.", XO(31,104,1,1), XORB_MASK, PPC|POWER, { RT, RA } }, + +{ "mul", XO(31,107,0,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "mul.", XO(31,107,0,1), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "mulo", XO(31,107,1,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "mulo.", XO(31,107,1,1), XO_MASK, POWER|M601, { RT, RA, RB } }, + +{ "clf", X(31,118), XRB_MASK, POWER, { RT, RA } }, + +{ "lbzux", X(31,119), X_MASK, PPC|POWER, { RT, RAL, RB } }, + +{ "not", XRC(31,124,0), X_MASK, PPC|POWER, { RA, RS, RBS } }, +{ "nor", XRC(31,124,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "not.", XRC(31,124,1), X_MASK, PPC|POWER, { RA, RS, RBS } }, +{ "nor.", XRC(31,124,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "subfe", XO(31,136,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfe", XO(31,136,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "subfe.", XO(31,136,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfe.", XO(31,136,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "subfeo", XO(31,136,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfeo", XO(31,136,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "subfeo.", XO(31,136,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "sfeo.", XO(31,136,1,1), XO_MASK, POWER, { RT, RA, RB } }, + +{ "adde", XO(31,138,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "ae", XO(31,138,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "adde.", XO(31,138,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "ae.", XO(31,138,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "addeo", XO(31,138,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "aeo", XO(31,138,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "addeo.", XO(31,138,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "aeo.", XO(31,138,1,1), XO_MASK, POWER, { RT, RA, RB } }, + +{ "mtcr", XFXM(31,144,0xff), XFXFXM_MASK|FXM_MASK, PPC|POWER, { RS }}, +{ "mtcrf", X(31,144), XFXFXM_MASK, PPC|POWER, { FXM, RS } }, + +{ "mtmsr", X(31,146), XRARB_MASK, PPC|POWER, { RS } }, +{ "mtmsrd", X(31,178), XRARB_MASK, PPC|POWER, { RS } }, + +{ "stdx", X(31,149), X_MASK, PPC|B64, { RS, RA, RB } }, + +{ "stwcx.", XRC(31,150,1), X_MASK, PPC, { RS, RA, RB } }, + +{ "stwx", X(31,151), X_MASK, PPC, { RS, RA, RB } }, +{ "stx", X(31,151), X_MASK, POWER, { RS, RA, RB } }, + +{ "slq", XRC(31,152,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "slq.", XRC(31,152,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "sle", XRC(31,153,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sle.", XRC(31,153,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "stdux", X(31,181), X_MASK, PPC|B64, { RS, RAS, RB } }, + +{ "stwux", X(31,183), X_MASK, PPC, { RS, RAS, RB } }, +{ "stux", X(31,183), X_MASK, POWER, { RS, RA, RB } }, + +{ "sliq", XRC(31,184,0), X_MASK, POWER|M601, { RA, RS, SH } }, +{ "sliq.", XRC(31,184,1), X_MASK, POWER|M601, { RA, RS, SH } }, + +{ "subfze", XO(31,200,0,0), XORB_MASK, PPC, { RT, RA } }, +{ "sfze", XO(31,200,0,0), XORB_MASK, POWER, { RT, RA } }, +{ "subfze.", XO(31,200,0,1), XORB_MASK, PPC, { RT, RA } }, +{ "sfze.", XO(31,200,0,1), XORB_MASK, POWER, { RT, RA } }, +{ "subfzeo", XO(31,200,1,0), XORB_MASK, PPC, { RT, RA } }, +{ "sfzeo", XO(31,200,1,0), XORB_MASK, POWER, { RT, RA } }, +{ "subfzeo.",XO(31,200,1,1), XORB_MASK, PPC, { RT, RA } }, +{ "sfzeo.", XO(31,200,1,1), XORB_MASK, POWER, { RT, RA } }, + +{ "addze", XO(31,202,0,0), XORB_MASK, PPC, { RT, RA } }, +{ "aze", XO(31,202,0,0), XORB_MASK, POWER, { RT, RA } }, +{ "addze.", XO(31,202,0,1), XORB_MASK, PPC, { RT, RA } }, +{ "aze.", XO(31,202,0,1), XORB_MASK, POWER, { RT, RA } }, +{ "addzeo", XO(31,202,1,0), XORB_MASK, PPC, { RT, RA } }, +{ "azeo", XO(31,202,1,0), XORB_MASK, POWER, { RT, RA } }, +{ "addzeo.", XO(31,202,1,1), XORB_MASK, PPC, { RT, RA } }, +{ "azeo.", XO(31,202,1,1), XORB_MASK, POWER, { RT, RA } }, + +{ "mtsr", X(31,210), XRB_MASK|(1<<20), PPC|POWER|B32, { SR, RS } }, + +{ "stdcx.", XRC(31,214,1), X_MASK, PPC|B64, { RS, RA, RB } }, + +{ "stbx", X(31,215), X_MASK, PPC|POWER, { RS, RA, RB } }, + +{ "sllq", XRC(31,216,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sllq.", XRC(31,216,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "sleq", XRC(31,217,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sleq.", XRC(31,217,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "subfme", XO(31,232,0,0), XORB_MASK, PPC, { RT, RA } }, +{ "sfme", XO(31,232,0,0), XORB_MASK, POWER, { RT, RA } }, +{ "subfme.", XO(31,232,0,1), XORB_MASK, PPC, { RT, RA } }, +{ "sfme.", XO(31,232,0,1), XORB_MASK, POWER, { RT, RA } }, +{ "subfmeo", XO(31,232,1,0), XORB_MASK, PPC, { RT, RA } }, +{ "sfmeo", XO(31,232,1,0), XORB_MASK, POWER, { RT, RA } }, +{ "subfmeo.",XO(31,232,1,1), XORB_MASK, PPC, { RT, RA } }, +{ "sfmeo.", XO(31,232,1,1), XORB_MASK, POWER, { RT, RA } }, + +{ "mulld", XO(31,233,0,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "mulld.", XO(31,233,0,1), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "mulldo", XO(31,233,1,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "mulldo.", XO(31,233,1,1), XO_MASK, PPC|B64, { RT, RA, RB } }, + +{ "addme", XO(31,234,0,0), XORB_MASK, PPC, { RT, RA } }, +{ "ame", XO(31,234,0,0), XORB_MASK, POWER, { RT, RA } }, +{ "addme.", XO(31,234,0,1), XORB_MASK, PPC, { RT, RA } }, +{ "ame.", XO(31,234,0,1), XORB_MASK, POWER, { RT, RA } }, +{ "addmeo", XO(31,234,1,0), XORB_MASK, PPC, { RT, RA } }, +{ "ameo", XO(31,234,1,0), XORB_MASK, POWER, { RT, RA } }, +{ "addmeo.", XO(31,234,1,1), XORB_MASK, PPC, { RT, RA } }, +{ "ameo.", XO(31,234,1,1), XORB_MASK, POWER, { RT, RA } }, + +{ "mullw", XO(31,235,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "muls", XO(31,235,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "mullw.", XO(31,235,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "muls.", XO(31,235,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "mullwo", XO(31,235,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "mulso", XO(31,235,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "mullwo.", XO(31,235,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "mulso.", XO(31,235,1,1), XO_MASK, POWER, { RT, RA, RB } }, + +{ "mtsrin", X(31,242), XRA_MASK, PPC|B32, { RS, RB } }, +{ "mtsri", X(31,242), XRA_MASK, POWER|B32, { RS, RB } }, + +{ "dcbtst", X(31,246), XRT_MASK, PPC, { RA, RB } }, + +{ "stbux", X(31,247), X_MASK, PPC|POWER, { RS, RAS, RB } }, + +{ "slliq", XRC(31,248,0), X_MASK, POWER|M601, { RA, RS, SH } }, +{ "slliq.", XRC(31,248,1), X_MASK, POWER|M601, { RA, RS, SH } }, + +{ "doz", XO(31,264,0,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "doz.", XO(31,264,0,1), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "dozo", XO(31,264,1,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "dozo.", XO(31,264,1,1), XO_MASK, POWER|M601, { RT, RA, RB } }, + +{ "add", XO(31,266,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "cax", XO(31,266,0,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "add.", XO(31,266,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "cax.", XO(31,266,0,1), XO_MASK, POWER, { RT, RA, RB } }, +{ "addo", XO(31,266,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "caxo", XO(31,266,1,0), XO_MASK, POWER, { RT, RA, RB } }, +{ "addo.", XO(31,266,1,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "caxo.", XO(31,266,1,1), XO_MASK, POWER, { RT, RA, RB } }, + +{ "lscbx", XRC(31,277,0), X_MASK, POWER|M601, { RT, RA, RB } }, +{ "lscbx.", XRC(31,277,1), X_MASK, POWER|M601, { RT, RA, RB } }, + +{ "dcbt", X(31,278), XRT_MASK, PPC, { RA, RB } }, + +{ "lhzx", X(31,279), X_MASK, PPC|POWER, { RT, RA, RB } }, + +{ "icbt", X(31,262), XRT_MASK, PPC, { RA, RB } }, + +{ "eqv", XRC(31,284,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "eqv.", XRC(31,284,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "tlbie", X(31,306), XRTRA_MASK, PPC, { RB } }, +{ "tlbi", X(31,306), XRTRA_MASK, POWER, { RB } }, + +{ "eciwx", X(31,310), X_MASK, PPC, { RT, RA, RB } }, + +{ "lhzux", X(31,311), X_MASK, PPC|POWER, { RT, RAL, RB } }, + +{ "xor", XRC(31,316,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "xor.", XRC(31,316,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "mfdcr", X(31,323), X_MASK, PPC, { RT, SPR } }, + +{ "div", XO(31,331,0,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "div.", XO(31,331,0,1), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "divo", XO(31,331,1,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "divo.", XO(31,331,1,1), XO_MASK, POWER|M601, { RT, RA, RB } }, + +{ "mfmq", XSPR(31,339,0), XSPR_MASK, POWER|M601, { RT } }, +{ "mfxer", XSPR(31,339,1), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfrtcu", XSPR(31,339,4), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfrtcl", XSPR(31,339,5), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfdec", XSPR(31,339,6), XSPR_MASK, POWER|M601, { RT } }, +{ "mflr", XSPR(31,339,8), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfctr", XSPR(31,339,9), XSPR_MASK, PPC|POWER, { RT } }, +{ "mftid", XSPR(31,339,17), XSPR_MASK, POWER, { RT } }, +{ "mfdsisr", XSPR(31,339,18), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfdar", XSPR(31,339,19), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfdec", XSPR(31,339,22), XSPR_MASK, PPC, { RT } }, +{ "mfsdr0", XSPR(31,339,24), XSPR_MASK, POWER, { RT } }, +{ "mfsdr1", XSPR(31,339,25), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfsrr0", XSPR(31,339,26), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfsrr1", XSPR(31,339,27), XSPR_MASK, PPC|POWER, { RT } }, +{ "mfsprg", XSPR(31,339,272), XSPRG_MASK, PPC, { RT, SPRG } }, +{ "mfasr", XSPR(31,339,280), XSPR_MASK, PPC|B64, { RT } }, +{ "mfear", XSPR(31,339,282), XSPR_MASK, PPC, { RT } }, +{ "mfpvr", XSPR(31,339,287), XSPR_MASK, PPC, { RT } }, +{ "mfibatu", XSPR(31,339,528), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfibatl", XSPR(31,339,529), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfdbatu", XSPR(31,339,536), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfdbatl", XSPR(31,339,537), XSPRBAT_MASK, PPC, { RT, SPRBAT } }, +{ "mfspr", X(31,339), X_MASK, PPC|POWER, { RT, SPR } }, + +{ "lwax", X(31,341), X_MASK, PPC|B64, { RT, RA, RB } }, + +{ "lhax", X(31,343), X_MASK, PPC|POWER, { RT, RA, RB } }, + +{ "dccci", X(31,454), XRT_MASK, PPC, { RA, RB } }, + +{ "abs", XO(31,360,0,0), XORB_MASK, POWER|M601, { RT, RA } }, +{ "abs.", XO(31,360,0,1), XORB_MASK, POWER|M601, { RT, RA } }, +{ "abso", XO(31,360,1,0), XORB_MASK, POWER|M601, { RT, RA } }, +{ "abso.", XO(31,360,1,1), XORB_MASK, POWER|M601, { RT, RA } }, + +{ "divs", XO(31,363,0,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "divs.", XO(31,363,0,1), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "divso", XO(31,363,1,0), XO_MASK, POWER|M601, { RT, RA, RB } }, +{ "divso.", XO(31,363,1,1), XO_MASK, POWER|M601, { RT, RA, RB } }, + +{ "tlbia", X(31,370), 0xffffffff, PPC, { 0 } }, + +{ "mftbu", XSPR(31,371,269), XSPR_MASK, PPC, { RT } }, +{ "mftb", X(31,371), X_MASK, PPC, { RT, TBR } }, + +{ "lwaux", X(31,373), X_MASK, PPC|B64, { RT, RAL, RB } }, + +{ "lhaux", X(31,375), X_MASK, PPC|POWER, { RT, RAL, RB } }, + +{ "sthx", X(31,407), X_MASK, PPC|POWER, { RS, RA, RB } }, + +{ "lfqx", X(31,791), X_MASK, POWER2, { FRT, RA, RB } }, + +{ "lfqux", X(31,823), X_MASK, POWER2, { FRT, RA, RB } }, + +{ "stfqx", X(31,919), X_MASK, POWER2, { FRS, RA, RB } }, + +{ "stfqux", X(31,951), X_MASK, POWER2, { FRS, RA, RB } }, + +{ "orc", XRC(31,412,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "orc.", XRC(31,412,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "sradi", XS(31,413,0), XS_MASK, PPC|B64, { RA, RS, SH6 } }, +{ "sradi.", XS(31,413,1), XS_MASK, PPC|B64, { RA, RS, SH6 } }, + +{ "slbie", X(31,434), XRTRA_MASK, PPC|B64, { RB } }, + +{ "ecowx", X(31,438), X_MASK, PPC, { RT, RA, RB } }, + +{ "sthux", X(31,439), X_MASK, PPC|POWER, { RS, RAS, RB } }, + +{ "mr", XRC(31,444,0), X_MASK, PPC|POWER, { RA, RS, RBS } }, +{ "or", XRC(31,444,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "mr.", XRC(31,444,1), X_MASK, PPC|POWER, { RA, RS, RBS } }, +{ "or.", XRC(31,444,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "mtdcr", X(31,451), X_MASK, PPC, { SPR, RS } }, + +{ "divdu", XO(31,457,0,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divdu.", XO(31,457,0,1), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divduo", XO(31,457,1,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divduo.", XO(31,457,1,1), XO_MASK, PPC|B64, { RT, RA, RB } }, + +{ "divwu", XO(31,459,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwu.", XO(31,459,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwuo", XO(31,459,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwuo.", XO(31,459,1,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "mtmq", XSPR(31,467,0), XSPR_MASK, POWER|M601, { RS } }, +{ "mtxer", XSPR(31,467,1), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtlr", XSPR(31,467,8), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtctr", XSPR(31,467,9), XSPR_MASK, PPC|POWER, { RS } }, +{ "mttid", XSPR(31,467,17), XSPR_MASK, POWER, { RS } }, +{ "mtdsisr", XSPR(31,467,18), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtdar", XSPR(31,467,19), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtrtcu", XSPR(31,467,20), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtrtcl", XSPR(31,467,21), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtdec", XSPR(31,467,22), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtsdr0", XSPR(31,467,24), XSPR_MASK, POWER, { RS } }, +{ "mtsdr1", XSPR(31,467,25), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtsrr0", XSPR(31,467,26), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtsrr1", XSPR(31,467,27), XSPR_MASK, PPC|POWER, { RS } }, +{ "mtsprg", XSPR(31,467,272), XSPRG_MASK, PPC, { SPRG, RS } }, +{ "mtasr", XSPR(31,467,280), XSPR_MASK, PPC|B64, { RS } }, +{ "mtear", XSPR(31,467,282), XSPR_MASK, PPC, { RS } }, +{ "mttbl", XSPR(31,467,284), XSPR_MASK, PPC, { RS } }, +{ "mttbu", XSPR(31,467,285), XSPR_MASK, PPC, { RS } }, +{ "mtibatu", XSPR(31,467,528), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtibatl", XSPR(31,467,529), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtdbatu", XSPR(31,467,536), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtdbatl", XSPR(31,467,537), XSPRBAT_MASK, PPC, { SPRBAT, RS } }, +{ "mtspr", X(31,467), X_MASK, PPC|POWER, { SPR, RS } }, + +{ "dcbi", X(31,470), XRT_MASK, PPC, { RA, RB } }, + +{ "nand", XRC(31,476,0), X_MASK, PPC|POWER, { RA, RS, RB } }, +{ "nand.", XRC(31,476,1), X_MASK, PPC|POWER, { RA, RS, RB } }, + +{ "nabs", XO(31,488,0,0), XORB_MASK, POWER|M601, { RT, RA } }, +{ "nabs.", XO(31,488,0,1), XORB_MASK, POWER|M601, { RT, RA } }, +{ "nabso", XO(31,488,1,0), XORB_MASK, POWER|M601, { RT, RA } }, +{ "nabso.", XO(31,488,1,1), XORB_MASK, POWER|M601, { RT, RA } }, + +{ "divd", XO(31,489,0,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divd.", XO(31,489,0,1), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divdo", XO(31,489,1,0), XO_MASK, PPC|B64, { RT, RA, RB } }, +{ "divdo.", XO(31,489,1,1), XO_MASK, PPC|B64, { RT, RA, RB } }, + +{ "divw", XO(31,491,0,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divw.", XO(31,491,0,1), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwo", XO(31,491,1,0), XO_MASK, PPC, { RT, RA, RB } }, +{ "divwo.", XO(31,491,1,1), XO_MASK, PPC, { RT, RA, RB } }, + +{ "slbia", X(31,498), 0xffffffff, PPC|B64, { 0 } }, + +{ "cli", X(31,502), XRB_MASK, POWER, { RT, RA } }, + +{ "mcrxr", X(31,512), XRARB_MASK|(3<<21), PPC|POWER, { BF } }, + +{ "clcs", X(31,531), XRB_MASK, POWER|M601, { RT, RA } }, + +{ "lswx", X(31,533), X_MASK, PPC, { RT, RA, RB } }, +{ "lsx", X(31,533), X_MASK, POWER, { RT, RA, RB } }, + +{ "lwbrx", X(31,534), X_MASK, PPC, { RT, RA, RB } }, +{ "lbrx", X(31,534), X_MASK, POWER, { RT, RA, RB } }, + +{ "lfsx", X(31,535), X_MASK, PPC|POWER, { FRT, RA, RB } }, + +{ "srw", XRC(31,536,0), X_MASK, PPC, { RA, RS, RB } }, +{ "sr", XRC(31,536,0), X_MASK, POWER, { RA, RS, RB } }, +{ "srw.", XRC(31,536,1), X_MASK, PPC, { RA, RS, RB } }, +{ "sr.", XRC(31,536,1), X_MASK, POWER, { RA, RS, RB } }, + +{ "rrib", XRC(31,537,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "rrib.", XRC(31,537,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "srd", XRC(31,539,0), X_MASK, PPC|B64, { RA, RS, RB } }, +{ "srd.", XRC(31,539,1), X_MASK, PPC|B64, { RA, RS, RB } }, + +{ "maskir", XRC(31,541,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "maskir.", XRC(31,541,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "tlbsync", X(31,566), 0xffffffff, PPC, { 0 } }, + +{ "lfsux", X(31,567), X_MASK, PPC|POWER, { FRT, RAS, RB } }, + +{ "mfsr", X(31,595), XRB_MASK|(1<<20), PPC|POWER|B32, { RT, SR } }, + +{ "lswi", X(31,597), X_MASK, PPC, { RT, RA, NB } }, +{ "lsi", X(31,597), X_MASK, POWER, { RT, RA, NB } }, + +{ "sync", X(31,598), 0xffffffff, PPC, { 0 } }, +{ "dcs", X(31,598), 0xffffffff, POWER, { 0 } }, + +{ "lfdx", X(31,599), X_MASK, PPC|POWER, { FRT, RA, RB } }, + +{ "mfsri", X(31,627), X_MASK, POWER, { RT, RA, RB } }, + +{ "dclst", X(31,630), XRB_MASK, POWER, { RS, RA } }, + +{ "lfdux", X(31,631), X_MASK, PPC|POWER, { FRT, RAS, RB } }, + +{ "mfsrin", X(31,659), XRA_MASK, PPC|B32, { RT, RB } }, + +{ "stswx", X(31,661), X_MASK, PPC, { RS, RA, RB } }, +{ "stsx", X(31,661), X_MASK, POWER, { RS, RA, RB } }, + +{ "stwbrx", X(31,662), X_MASK, PPC, { RS, RA, RB } }, +{ "stbrx", X(31,662), X_MASK, POWER, { RS, RA, RB } }, + +{ "stfsx", X(31,663), X_MASK, PPC|POWER, { FRS, RA, RB } }, + +{ "srq", XRC(31,664,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "srq.", XRC(31,664,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "sre", XRC(31,665,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sre.", XRC(31,665,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "stfsux", X(31,695), X_MASK, PPC|POWER, { FRS, RAS, RB } }, + +{ "sriq", XRC(31,696,0), X_MASK, POWER|M601, { RA, RS, SH } }, +{ "sriq.", XRC(31,696,1), X_MASK, POWER|M601, { RA, RS, SH } }, + +{ "stswi", X(31,725), X_MASK, PPC, { RS, RA, NB } }, +{ "stsi", X(31,725), X_MASK, POWER, { RS, RA, NB } }, + +{ "stfdx", X(31,727), X_MASK, PPC|POWER, { FRS, RA, RB } }, + +{ "srlq", XRC(31,728,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "srlq.", XRC(31,728,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "sreq", XRC(31,729,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sreq.", XRC(31,729,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "stfdux", X(31,759), X_MASK, PPC|POWER, { FRS, RAS, RB } }, + +{ "srliq", XRC(31,760,0), X_MASK, POWER|M601, { RA, RS, SH } }, +{ "srliq.", XRC(31,760,1), X_MASK, POWER|M601, { RA, RS, SH } }, + +{ "lhbrx", X(31,790), X_MASK, PPC|POWER, { RT, RA, RB } }, + +{ "sraw", XRC(31,792,0), X_MASK, PPC, { RA, RS, RB } }, +{ "sra", XRC(31,792,0), X_MASK, POWER, { RA, RS, RB } }, +{ "sraw.", XRC(31,792,1), X_MASK, PPC, { RA, RS, RB } }, +{ "sra.", XRC(31,792,1), X_MASK, POWER, { RA, RS, RB } }, + +{ "srad", XRC(31,794,0), X_MASK, PPC|B64, { RA, RS, RB } }, +{ "srad.", XRC(31,794,1), X_MASK, PPC|B64, { RA, RS, RB } }, + +{ "rac", X(31,818), X_MASK, POWER, { RT, RA, RB } }, + +{ "srawi", XRC(31,824,0), X_MASK, PPC, { RA, RS, SH } }, +{ "srai", XRC(31,824,0), X_MASK, POWER, { RA, RS, SH } }, +{ "srawi.", XRC(31,824,1), X_MASK, PPC, { RA, RS, SH } }, +{ "srai.", XRC(31,824,1), X_MASK, POWER, { RA, RS, SH } }, + +{ "eieio", X(31,854), 0xffffffff, PPC, { 0 } }, + +{ "sthbrx", X(31,918), X_MASK, PPC|POWER, { RS, RA, RB } }, + +{ "sraq", XRC(31,920,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "sraq.", XRC(31,920,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "srea", XRC(31,921,0), X_MASK, POWER|M601, { RA, RS, RB } }, +{ "srea.", XRC(31,921,1), X_MASK, POWER|M601, { RA, RS, RB } }, + +{ "extsh", XRC(31,922,0), XRB_MASK, PPC, { RA, RS } }, +{ "exts", XRC(31,922,0), XRB_MASK, POWER, { RA, RS } }, +{ "extsh.", XRC(31,922,1), XRB_MASK, PPC, { RA, RS } }, +{ "exts.", XRC(31,922,1), XRB_MASK, POWER, { RA, RS } }, + +{ "sraiq", XRC(31,952,0), X_MASK, POWER|M601, { RA, RS, SH } }, +{ "sraiq.", XRC(31,952,1), X_MASK, POWER|M601, { RA, RS, SH } }, + +{ "extsb", XRC(31,954,0), XRB_MASK, PPC, { RA, RS} }, +{ "extsb.", XRC(31,954,1), XRB_MASK, PPC, { RA, RS} }, + +{ "iccci", X(31,966), XRT_MASK, PPC, { RA, RB } }, + +{ "icbi", X(31,982), XRT_MASK, PPC, { RA, RB } }, + +{ "stfiwx", X(31,983), X_MASK, PPC, { FRS, RA, RB } }, + +{ "extsw", XRC(31,986,0), XRB_MASK, PPC, { RA, RS } }, +{ "extsw.", XRC(31,986,1), XRB_MASK, PPC, { RA, RS } }, + +{ "dcbz", X(31,1014), XRT_MASK, PPC, { RA, RB } }, +{ "dclz", X(31,1014), XRT_MASK, PPC, { RA, RB } }, + +{ "lwz", OP(32), OP_MASK, PPC, { RT, D, RA } }, +{ "l", OP(32), OP_MASK, POWER, { RT, D, RA } }, + +{ "lwzu", OP(33), OP_MASK, PPC, { RT, D, RAL } }, +{ "lu", OP(33), OP_MASK, POWER, { RT, D, RA } }, + +{ "lbz", OP(34), OP_MASK, PPC|POWER, { RT, D, RA } }, + +{ "lbzu", OP(35), OP_MASK, PPC|POWER, { RT, D, RAL } }, + +{ "stw", OP(36), OP_MASK, PPC, { RS, D, RA } }, +{ "st", OP(36), OP_MASK, POWER, { RS, D, RA } }, + +{ "stwu", OP(37), OP_MASK, PPC, { RS, D, RAS } }, +{ "stu", OP(37), OP_MASK, POWER, { RS, D, RA } }, + +{ "stb", OP(38), OP_MASK, PPC|POWER, { RS, D, RA } }, + +{ "stbu", OP(39), OP_MASK, PPC|POWER, { RS, D, RAS } }, + +{ "lhz", OP(40), OP_MASK, PPC|POWER, { RT, D, RA } }, + +{ "lhzu", OP(41), OP_MASK, PPC|POWER, { RT, D, RAL } }, + +{ "lha", OP(42), OP_MASK, PPC|POWER, { RT, D, RA } }, + +{ "lhau", OP(43), OP_MASK, PPC|POWER, { RT, D, RAL } }, + +{ "sth", OP(44), OP_MASK, PPC|POWER, { RS, D, RA } }, + +{ "sthu", OP(45), OP_MASK, PPC|POWER, { RS, D, RAS } }, + +{ "lmw", OP(46), OP_MASK, PPC, { RT, D, RAM } }, +{ "lm", OP(46), OP_MASK, POWER, { RT, D, RA } }, + +{ "stmw", OP(47), OP_MASK, PPC, { RS, D, RA } }, +{ "stm", OP(47), OP_MASK, POWER, { RS, D, RA } }, + +{ "lfs", OP(48), OP_MASK, PPC|POWER, { FRT, D, RA } }, + +{ "lfsu", OP(49), OP_MASK, PPC|POWER, { FRT, D, RAS } }, + +{ "lfd", OP(50), OP_MASK, PPC|POWER, { FRT, D, RA } }, + +{ "lfdu", OP(51), OP_MASK, PPC|POWER, { FRT, D, RAS } }, + +{ "stfs", OP(52), OP_MASK, PPC|POWER, { FRS, D, RA } }, + +{ "stfsu", OP(53), OP_MASK, PPC|POWER, { FRS, D, RAS } }, + +{ "stfd", OP(54), OP_MASK, PPC|POWER, { FRS, D, RA } }, + +{ "stfdu", OP(55), OP_MASK, PPC|POWER, { FRS, D, RAS } }, + +{ "lfq", OP(56), OP_MASK, POWER2, { FRT, D, RA } }, + +{ "lfqu", OP(57), OP_MASK, POWER2, { FRT, D, RA } }, + +{ "ld", DSO(58,0), DS_MASK, PPC|B64, { RT, DS, RA } }, + +{ "ldu", DSO(58,1), DS_MASK, PPC|B64, { RT, DS, RAL } }, + +{ "lwa", DSO(58,2), DS_MASK, PPC|B64, { RT, DS, RA } }, + +{ "fdivs", A(59,18,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fdivs.", A(59,18,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, + +{ "fsubs", A(59,20,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fsubs.", A(59,20,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, + +{ "fadds", A(59,21,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fadds.", A(59,21,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, + +{ "fsqrts", A(59,22,0), AFRAFRC_MASK, PPC, { FRT, FRB } }, +{ "fsqrts.", A(59,22,1), AFRAFRC_MASK, PPC, { FRT, FRB } }, + +{ "fres", A(59,24,0), AFRAFRC_MASK, PPC, { FRT, FRB } }, +{ "fres.", A(59,24,1), AFRAFRC_MASK, PPC, { FRT, FRB } }, + +{ "fmuls", A(59,25,0), AFRB_MASK, PPC, { FRT, FRA, FRC } }, +{ "fmuls.", A(59,25,1), AFRB_MASK, PPC, { FRT, FRA, FRC } }, + +{ "fmsubs", A(59,28,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fmsubs.", A(59,28,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fmadds", A(59,29,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fmadds.", A(59,29,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fnmsubs", A(59,30,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnmsubs.",A(59,30,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fnmadds", A(59,31,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnmadds.",A(59,31,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "stfq", OP(60), OP_MASK, POWER2, { FRS, D, RA } }, + +{ "stfqu", OP(61), OP_MASK, POWER2, { FRS, D, RA } }, + +{ "std", DSO(62,0), DS_MASK, PPC|B64, { RS, DS, RA } }, + +{ "stdu", DSO(62,1), DS_MASK, PPC|B64, { RS, DS, RAS } }, + +{ "fcmpu", X(63,0), X_MASK|(3<<21), PPC|POWER, { BF, FRA, FRB } }, + +{ "frsp", XRC(63,12,0), XRA_MASK, PPC|POWER, { FRT, FRB } }, +{ "frsp.", XRC(63,12,1), XRA_MASK, PPC|POWER, { FRT, FRB } }, + +{ "fctiw", XRC(63,14,0), XRA_MASK, PPC, { FRT, FRB } }, +{ "fcir", XRC(63,14,0), XRA_MASK, POWER2, { FRT, FRB } }, +{ "fctiw.", XRC(63,14,1), XRA_MASK, PPC, { FRT, FRB } }, +{ "fcir.", XRC(63,14,1), XRA_MASK, POWER2, { FRT, FRB } }, + +{ "fctiwz", XRC(63,15,0), XRA_MASK, PPC, { FRT, FRB } }, +{ "fcirz", XRC(63,15,0), XRA_MASK, POWER2, { FRT, FRB } }, +{ "fctiwz.", XRC(63,15,1), XRA_MASK, PPC, { FRT, FRB } }, +{ "fcirz.", XRC(63,15,1), XRA_MASK, POWER2, { FRT, FRB } }, + +{ "fdiv", A(63,18,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fd", A(63,18,0), AFRC_MASK, POWER, { FRT, FRA, FRB } }, +{ "fdiv.", A(63,18,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fd.", A(63,18,1), AFRC_MASK, POWER, { FRT, FRA, FRB } }, + +{ "fsub", A(63,20,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fs", A(63,20,0), AFRC_MASK, POWER, { FRT, FRA, FRB } }, +{ "fsub.", A(63,20,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fs.", A(63,20,1), AFRC_MASK, POWER, { FRT, FRA, FRB } }, + +{ "fadd", A(63,21,0), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fa", A(63,21,0), AFRC_MASK, POWER, { FRT, FRA, FRB } }, +{ "fadd.", A(63,21,1), AFRC_MASK, PPC, { FRT, FRA, FRB } }, +{ "fa.", A(63,21,1), AFRC_MASK, POWER, { FRT, FRA, FRB } }, + +{ "fsqrt", A(63,22,0), AFRAFRC_MASK, PPC|POWER2, { FRT, FRB } }, +{ "fsqrt.", A(63,22,1), AFRAFRC_MASK, PPC|POWER2, { FRT, FRB } }, + +{ "fsel", A(63,23,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fsel.", A(63,23,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, + +{ "fmul", A(63,25,0), AFRB_MASK, PPC, { FRT, FRA, FRC } }, +{ "fm", A(63,25,0), AFRB_MASK, POWER, { FRT, FRA, FRC } }, +{ "fmul.", A(63,25,1), AFRB_MASK, PPC, { FRT, FRA, FRC } }, +{ "fm.", A(63,25,1), AFRB_MASK, POWER, { FRT, FRA, FRC } }, + +{ "frsqrte", A(63,26,0), AFRAFRC_MASK, PPC, { FRT, FRB } }, +{ "frsqrte.",A(63,26,1), AFRAFRC_MASK, PPC, { FRT, FRB } }, + +{ "fmsub", A(63,28,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fms", A(63,28,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, +{ "fmsub.", A(63,28,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fms.", A(63,28,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, + +{ "fmadd", A(63,29,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fma", A(63,29,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, +{ "fmadd.", A(63,29,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fma.", A(63,29,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, + +{ "fnmsub", A(63,30,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnms", A(63,30,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, +{ "fnmsub.", A(63,30,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnms.", A(63,30,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, + +{ "fnmadd", A(63,31,0), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnma", A(63,31,0), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, +{ "fnmadd.", A(63,31,1), A_MASK, PPC, { FRT,FRA,FRC,FRB } }, +{ "fnma.", A(63,31,1), A_MASK, POWER, { FRT,FRA,FRC,FRB } }, + +{ "fcmpo", X(63,30), X_MASK|(3<<21), PPC|POWER, { BF, FRA, FRB } }, + +{ "mtfsb1", XRC(63,38,0), XRARB_MASK, PPC|POWER, { BT } }, +{ "mtfsb1.", XRC(63,38,1), XRARB_MASK, PPC|POWER, { BT } }, + +{ "fneg", XRC(63,40,0), XRA_MASK, PPC|POWER, { FRT, FRB } }, +{ "fneg.", XRC(63,40,1), XRA_MASK, PPC|POWER, { FRT, FRB } }, + +{ "mcrfs", X(63,64), XRB_MASK|(3<<21)|(3<<16), PPC|POWER, { BF, BFA } }, + +{ "mtfsb0", XRC(63,70,0), XRARB_MASK, PPC|POWER, { BT } }, +{ "mtfsb0.", XRC(63,70,1), XRARB_MASK, PPC|POWER, { BT } }, + +{ "fmr", XRC(63,72,0), XRA_MASK, PPC|POWER, { FRT, FRB } }, +{ "fmr.", XRC(63,72,1), XRA_MASK, PPC|POWER, { FRT, FRB } }, + +{ "mtfsfi", XRC(63,134,0), XRA_MASK|(3<<21)|(1<<11), PPC|POWER, { BF, U } }, +{ "mtfsfi.", XRC(63,134,1), XRA_MASK|(3<<21)|(1<<11), PPC|POWER, { BF, U } }, + +{ "fnabs", XRC(63,136,0), XRA_MASK, PPC|POWER, { FRT, FRB } }, +{ "fnabs.", XRC(63,136,1), XRA_MASK, PPC|POWER, { FRT, FRB } }, + +{ "fabs", XRC(63,264,0), XRA_MASK, PPC|POWER, { FRT, FRB } }, +{ "fabs.", XRC(63,264,1), XRA_MASK, PPC|POWER, { FRT, FRB } }, + +{ "mffs", XRC(63,583,0), XRARB_MASK, PPC|POWER, { FRT } }, +{ "mffs.", XRC(63,583,1), XRARB_MASK, PPC|POWER, { FRT } }, + +{ "mtfsf", XFL(63,711,0), XFL_MASK, PPC|POWER, { FLM, FRB } }, +{ "mtfsf.", XFL(63,711,1), XFL_MASK, PPC|POWER, { FLM, FRB } }, + +{ "fctid", XRC(63,814,0), XRA_MASK, PPC|B64, { FRT, FRB } }, +{ "fctid.", XRC(63,814,1), XRA_MASK, PPC|B64, { FRT, FRB } }, + +{ "fctidz", XRC(63,815,0), XRA_MASK, PPC|B64, { FRT, FRB } }, +{ "fctidz.", XRC(63,815,1), XRA_MASK, PPC|B64, { FRT, FRB } }, + +{ "fcfid", XRC(63,846,0), XRA_MASK, PPC|B64, { FRT, FRB } }, +{ "fcfid.", XRC(63,846,1), XRA_MASK, PPC|B64, { FRT, FRB } }, + +}; + +const int powerpc_num_opcodes = + sizeof (powerpc_opcodes) / sizeof (powerpc_opcodes[0]); + +/* The macro table. This is only used by the assembler. */ + +const struct powerpc_macro powerpc_macros[] = { +{ "extldi", 4, PPC|B64, "rldicr %0,%1,%3,(%2)-1" }, +{ "extldi.", 4, PPC|B64, "rldicr. %0,%1,%3,(%2)-1" }, +{ "extrdi", 4, PPC|B64, "rldicl %0,%1,(%2)+(%3),64-(%2)" }, +{ "extrdi.", 4, PPC|B64, "rldicl. %0,%1,(%2)+(%3),64-(%2)" }, +{ "insrdi", 4, PPC|B64, "rldimi %0,%1,64-((%2)+(%3)),%3" }, +{ "insrdi.", 4, PPC|B64, "rldimi. %0,%1,64-((%2)+(%3)),%3" }, +{ "rotrdi", 3, PPC|B64, "rldicl %0,%1,64-(%2),0" }, +{ "rotrdi.", 3, PPC|B64, "rldicl. %0,%1,64-(%2),0" }, +{ "sldi", 3, PPC|B64, "rldicr %0,%1,%2,63-(%2)" }, +{ "sldi.", 3, PPC|B64, "rldicr. %0,%1,%2,63-(%2)" }, +{ "srdi", 3, PPC|B64, "rldicl %0,%1,64-(%2),%2" }, +{ "srdi.", 3, PPC|B64, "rldicl. %0,%1,64-(%2),%2" }, +{ "clrrdi", 3, PPC|B64, "rldicr %0,%1,0,63-(%2)" }, +{ "clrrdi.", 3, PPC|B64, "rldicr. %0,%1,0,63-(%2)" }, +{ "clrlsldi",4, PPC|B64, "rldic %0,%1,%3,(%2)-(%3)" }, +{ "clrlsldi.",4, PPC|B64, "rldic. %0,%1,%3,(%2)-(%3)" }, + +{ "extlwi", 4, PPC, "rlwinm %0,%1,%3,0,(%2)-1" }, +{ "extlwi.", 4, PPC, "rlwinm. %0,%1,%3,0,(%2)-1" }, +{ "extrwi", 4, PPC, "rlwinm %0,%1,(%2)+(%3),32-(%2),31" }, +{ "extrwi.", 4, PPC, "rlwinm. %0,%1,(%2)+(%3),32-(%2),31" }, +{ "inslwi", 4, PPC, "rlwimi %0,%1,32-(%3),%3,(%2)+(%3)-1" }, +{ "inslwi.", 4, PPC, "rlwimi. %0,%1,32-(%3),%3,(%2)+(%3)-1" }, +{ "insrwi", 4, PPC, "rlwimi %0,%1,32-((%2)+(%3)),%3,(%2)+(%3)-1" }, +{ "insrwi.", 4, PPC, "rlwimi. %0,%1,32-((%2)+(%3)),%3,(%2)+(%3)-1"}, +{ "rotrwi", 3, PPC, "rlwinm %0,%1,32-(%2),0,31" }, +{ "rotrwi.", 3, PPC, "rlwinm. %0,%1,32-(%2),0,31" }, +{ "slwi", 3, PPC, "rlwinm %0,%1,%2,0,31-(%2)" }, +{ "sli", 3, POWER, "rlinm %0,%1,%2,0,31-(%2)" }, +{ "slwi.", 3, PPC, "rlwinm. %0,%1,%2,0,31-(%2)" }, +{ "sli.", 3, POWER, "rlinm. %0,%1,%2,0,31-(%2)" }, +{ "srwi", 3, PPC, "rlwinm %0,%1,32-(%2),%2,31" }, +{ "sri", 3, POWER, "rlinm %0,%1,32-(%2),%2,31" }, +{ "srwi.", 3, PPC, "rlwinm. %0,%1,32-(%2),%2,31" }, +{ "sri.", 3, POWER, "rlinm. %0,%1,32-(%2),%2,31" }, +{ "clrrwi", 3, PPC, "rlwinm %0,%1,0,0,31-(%2)" }, +{ "clrrwi.", 3, PPC, "rlwinm. %0,%1,0,0,31-(%2)" }, +{ "clrlslwi",4, PPC, "rlwinm %0,%1,%3,(%2)-(%3),31-(%3)" }, +{ "clrlslwi.",4, PPC, "rlwinm. %0,%1,%3,(%2)-(%3),31-(%3)" }, + +}; + +const int powerpc_num_macros = + sizeof (powerpc_macros) / sizeof (powerpc_macros[0]); diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/xmon/ppc.h linuxppc64_2_4/arch/ppc64/xmon/ppc.h --- linux-2.4.5-ac18/arch/ppc64/xmon/ppc.h Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/xmon/ppc.h Fri May 4 17:13:59 2001 @@ -0,0 +1,240 @@ +/* ppc.h -- Header file for PowerPC opcode table + Copyright 1994 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Support + +This file is part of GDB, GAS, and the GNU binutils. + +GDB, GAS, and the GNU binutils are free software; you can redistribute +them and/or modify them under the terms of the GNU General Public +License as published by the Free Software Foundation; either version +1, or (at your option) any later version. + +GDB, GAS, and the GNU binutils are distributed in the hope that they +will be useful, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this file; see the file COPYING. If not, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifndef PPC_H +#define PPC_H + +/* The opcode table is an array of struct powerpc_opcode. */ + +struct powerpc_opcode +{ + /* The opcode name. */ + const char *name; + + /* The opcode itself. Those bits which will be filled in with + operands are zeroes. */ + unsigned long opcode; + + /* The opcode mask. This is used by the disassembler. This is a + mask containing ones indicating those bits which must match the + opcode field, and zeroes indicating those bits which need not + match (and are presumably filled in by operands). */ + unsigned long mask; + + /* One bit flags for the opcode. These are used to indicate which + specific processors support the instructions. The defined values + are listed below. */ + unsigned long flags; + + /* An array of operand codes. Each code is an index into the + operand table. They appear in the order which the operands must + appear in assembly code, and are terminated by a zero. */ + unsigned char operands[8]; +}; + +/* The table itself is sorted by major opcode number, and is otherwise + in the order in which the disassembler should consider + instructions. */ +extern const struct powerpc_opcode powerpc_opcodes[]; +extern const int powerpc_num_opcodes; + +/* Values defined for the flags field of a struct powerpc_opcode. */ + +/* Opcode is defined for the PowerPC architecture. */ +#define PPC_OPCODE_PPC (01) + +/* Opcode is defined for the POWER (RS/6000) architecture. */ +#define PPC_OPCODE_POWER (02) + +/* Opcode is defined for the POWER2 (Rios 2) architecture. */ +#define PPC_OPCODE_POWER2 (04) + +/* Opcode is only defined on 32 bit architectures. */ +#define PPC_OPCODE_32 (010) + +/* Opcode is only defined on 64 bit architectures. */ +#define PPC_OPCODE_64 (020) + +/* Opcode is supported by the Motorola PowerPC 601 processor. The 601 + is assumed to support all PowerPC (PPC_OPCODE_PPC) instructions, + but it also supports many additional POWER instructions. */ +#define PPC_OPCODE_601 (040) + +/* A macro to extract the major opcode from an instruction. */ +#define PPC_OP(i) (((i) >> 26) & 0x3f) + +/* The operands table is an array of struct powerpc_operand. */ + +struct powerpc_operand +{ + /* The number of bits in the operand. */ + int bits; + + /* How far the operand is left shifted in the instruction. */ + int shift; + + /* Insertion function. This is used by the assembler. To insert an + operand value into an instruction, check this field. + + If it is NULL, execute + i |= (op & ((1 << o->bits) - 1)) << o->shift; + (i is the instruction which we are filling in, o is a pointer to + this structure, and op is the opcode value; this assumes twos + complement arithmetic). + + If this field is not NULL, then simply call it with the + instruction and the operand value. It will return the new value + of the instruction. If the ERRMSG argument is not NULL, then if + the operand value is illegal, *ERRMSG will be set to a warning + string (the operand will be inserted in any case). If the + operand value is legal, *ERRMSG will be unchanged (most operands + can accept any value). */ + unsigned long (*insert) PARAMS ((unsigned long instruction, long op, + const char **errmsg)); + + /* Extraction function. This is used by the disassembler. To + extract this operand type from an instruction, check this field. + + If it is NULL, compute + op = ((i) >> o->shift) & ((1 << o->bits) - 1); + if ((o->flags & PPC_OPERAND_SIGNED) != 0 + && (op & (1 << (o->bits - 1))) != 0) + op -= 1 << o->bits; + (i is the instruction, o is a pointer to this structure, and op + is the result; this assumes twos complement arithmetic). + + If this field is not NULL, then simply call it with the + instruction value. It will return the value of the operand. If + the INVALID argument is not NULL, *INVALID will be set to + non-zero if this operand type can not actually be extracted from + this operand (i.e., the instruction does not match). If the + operand is valid, *INVALID will not be changed. */ + long (*extract) PARAMS ((unsigned long instruction, int *invalid)); + + /* One bit syntax flags. */ + unsigned long flags; +}; + +/* Elements in the table are retrieved by indexing with values from + the operands field of the powerpc_opcodes table. */ + +extern const struct powerpc_operand powerpc_operands[]; + +/* Values defined for the flags field of a struct powerpc_operand. */ + +/* This operand takes signed values. */ +#define PPC_OPERAND_SIGNED (01) + +/* This operand takes signed values, but also accepts a full positive + range of values when running in 32 bit mode. That is, if bits is + 16, it takes any value from -0x8000 to 0xffff. In 64 bit mode, + this flag is ignored. */ +#define PPC_OPERAND_SIGNOPT (02) + +/* This operand does not actually exist in the assembler input. This + is used to support extended mnemonics such as mr, for which two + operands fields are identical. The assembler should call the + insert function with any op value. The disassembler should call + the extract function, ignore the return value, and check the value + placed in the valid argument. */ +#define PPC_OPERAND_FAKE (04) + +/* The next operand should be wrapped in parentheses rather than + separated from this one by a comma. This is used for the load and + store instructions which want their operands to look like + reg,displacement(reg) + */ +#define PPC_OPERAND_PARENS (010) + +/* This operand may use the symbolic names for the CR fields, which + are + lt 0 gt 1 eq 2 so 3 un 3 + cr0 0 cr1 1 cr2 2 cr3 3 + cr4 4 cr5 5 cr6 6 cr7 7 + These may be combined arithmetically, as in cr2*4+gt. These are + only supported on the PowerPC, not the POWER. */ +#define PPC_OPERAND_CR (020) + +/* This operand names a register. The disassembler uses this to print + register names with a leading 'r'. */ +#define PPC_OPERAND_GPR (040) + +/* This operand names a floating point register. The disassembler + prints these with a leading 'f'. */ +#define PPC_OPERAND_FPR (0100) + +/* This operand is a relative branch displacement. The disassembler + prints these symbolically if possible. */ +#define PPC_OPERAND_RELATIVE (0200) + +/* This operand is an absolute branch address. The disassembler + prints these symbolically if possible. */ +#define PPC_OPERAND_ABSOLUTE (0400) + +/* This operand is optional, and is zero if omitted. This is used for + the optional BF and L fields in the comparison instructions. The + assembler must count the number of operands remaining on the line, + and the number of operands remaining for the opcode, and decide + whether this operand is present or not. The disassembler should + print this operand out only if it is not zero. */ +#define PPC_OPERAND_OPTIONAL (01000) + +/* This flag is only used with PPC_OPERAND_OPTIONAL. If this operand + is omitted, then for the next operand use this operand value plus + 1, ignoring the next operand field for the opcode. This wretched + hack is needed because the Power rotate instructions can take + either 4 or 5 operands. The disassembler should print this operand + out regardless of the PPC_OPERAND_OPTIONAL field. */ +#define PPC_OPERAND_NEXT (02000) + +/* This operand should be regarded as a negative number for the + purposes of overflow checking (i.e., the normal most negative + number is disallowed and one more than the normal most positive + number is allowed). This flag will only be set for a signed + operand. */ +#define PPC_OPERAND_NEGATIVE (04000) + +/* The POWER and PowerPC assemblers use a few macros. We keep them + with the operands table for simplicity. The macro table is an + array of struct powerpc_macro. */ + +struct powerpc_macro +{ + /* The macro name. */ + const char *name; + + /* The number of operands the macro takes. */ + unsigned int operands; + + /* One bit flags for the opcode. These are used to indicate which + specific processors support the instructions. The values are the + same as those for the struct powerpc_opcode flags field. */ + unsigned long flags; + + /* A format string to turn the macro into a normal instruction. + Each %N in the string is replaced with operand number N (zero + based). */ + const char *format; +}; + +extern const struct powerpc_macro powerpc_macros[]; +extern const int powerpc_num_macros; + +#endif /* PPC_H */ diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/xmon/privinst.h linuxppc64_2_4/arch/ppc64/xmon/privinst.h --- linux-2.4.5-ac18/arch/ppc64/xmon/privinst.h Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/xmon/privinst.h Mon May 7 12:48:56 2001 @@ -0,0 +1,102 @@ +/* + * 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 + +#define GETREG(reg) \ + static inline unsigned long get_ ## reg (void) \ + { unsigned long ret; asm volatile ("mf" #reg " %0" : "=r" (ret) :); return ret; } + +#define SETREG(reg) \ + static inline void set_ ## reg (unsigned long val) \ + { asm volatile ("mt" #reg " %0" : : "r" (val)); } + +GETREG(msr) +SETREG(msrd) +GETREG(cr) + +#define GSETSPR(n, name) \ + static inline long get_ ## name (void) \ + { long ret; asm volatile ("mfspr %0," #n : "=r" (ret) : ); return ret; } \ + static inline void set_ ## name (long val) \ + { asm volatile ("mtspr " #n ",%0" : : "r" (val)); } + +GSETSPR(0, mq) +GSETSPR(1, xer) +GSETSPR(4, rtcu) +GSETSPR(5, rtcl) +GSETSPR(8, lr) +GSETSPR(9, ctr) +GSETSPR(18, dsisr) +GSETSPR(19, dar) +GSETSPR(22, dec) +GSETSPR(25, sdr1) +GSETSPR(26, srr0) +GSETSPR(27, srr1) +GSETSPR(272, sprg0) +GSETSPR(273, sprg1) +GSETSPR(274, sprg2) +GSETSPR(275, sprg3) +GSETSPR(282, ear) +GSETSPR(287, pvr) +#ifndef CONFIG_8xx +GSETSPR(528, bat0u) +GSETSPR(529, bat0l) +GSETSPR(530, bat1u) +GSETSPR(531, bat1l) +GSETSPR(532, bat2u) +GSETSPR(533, bat2l) +GSETSPR(534, bat3u) +GSETSPR(535, bat3l) +GSETSPR(1008, hid0) +GSETSPR(1009, hid1) +GSETSPR(1010, iabr) +GSETSPR(1013, dabr) +GSETSPR(1023, pir) +#else +GSETSPR(144, cmpa) +GSETSPR(145, cmpb) +GSETSPR(146, cmpc) +GSETSPR(147, cmpd) +GSETSPR(158, ictrl) +#endif + +static inline int get_sr(int n) +{ + int ret; + +#if 0 +// DRENG does not assemble + asm (" mfsrin %0,%1" : "=r" (ret) : "r" (n << 28)); +#endif + return ret; +} + +static inline void set_sr(int n, int val) +{ +#if 0 +// DRENG does not assemble + asm ("mtsrin %0,%1" : : "r" (val), "r" (n << 28)); +#endif +} + +static inline void store_inst(void *p) +{ + asm volatile ("dcbst 0,%0; sync; icbi 0,%0; isync" : : "r" (p)); +} + +static inline void cflush(void *p) +{ + asm volatile ("dcbf 0,%0; icbi 0,%0" : : "r" (p)); +} + +static inline void cinval(void *p) +{ + asm volatile ("dcbi 0,%0; icbi 0,%0" : : "r" (p)); +} + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/xmon/setjmp.c linuxppc64_2_4/arch/ppc64/xmon/setjmp.c --- linux-2.4.5-ac18/arch/ppc64/xmon/setjmp.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/xmon/setjmp.c Mon May 7 12:48:56 2001 @@ -0,0 +1,77 @@ +/* + * 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. + * + * NB this file must be compiled with -O2. + */ + +int +xmon_setjmp(long *buf) /* NOTE: assert(sizeof(buf) > 184) */ +{ + /* XXX should save fp regs as well */ + asm volatile ( + "mflr 0; std 0,0(%0)\n\ + std 1,8(%0)\n\ + std 2,16(%0)\n\ + mfcr 0; std 0,24(%0)\n\ + std 13,32(%0)\n\ + std 14,40(%0)\n\ + std 15,48(%0)\n\ + std 16,56(%0)\n\ + std 17,64(%0)\n\ + std 18,72(%0)\n\ + std 19,80(%0)\n\ + std 20,88(%0)\n\ + std 21,96(%0)\n\ + std 22,104(%0)\n\ + std 23,112(%0)\n\ + std 24,120(%0)\n\ + std 25,128(%0)\n\ + std 26,136(%0)\n\ + std 27,144(%0)\n\ + std 28,152(%0)\n\ + std 29,160(%0)\n\ + std 30,168(%0)\n\ + std 31,176(%0)\n\ + " : : "r" (buf)); + return 0; +} + +void +xmon_longjmp(long *buf, int val) +{ + if (val == 0) + val = 1; + asm volatile ( + "ld 13,32(%0)\n\ + ld 14,40(%0)\n\ + ld 15,48(%0)\n\ + ld 16,56(%0)\n\ + ld 17,64(%0)\n\ + ld 18,72(%0)\n\ + ld 19,80(%0)\n\ + ld 20,88(%0)\n\ + ld 21,96(%0)\n\ + ld 22,104(%0)\n\ + ld 23,112(%0)\n\ + ld 24,120(%0)\n\ + ld 25,128(%0)\n\ + ld 26,136(%0)\n\ + ld 27,144(%0)\n\ + ld 28,152(%0)\n\ + ld 29,160(%0)\n\ + ld 30,168(%0)\n\ + ld 31,176(%0)\n\ + ld 0,24(%0)\n\ + mtcrf 0x38,0\n\ + ld 0,0(%0)\n\ + ld 1,8(%0)\n\ + ld 2,16(%0)\n\ + mtlr 0\n\ + mr 3,%1\n\ + " : : "r" (buf), "r" (val)); +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/xmon/start.c linuxppc64_2_4/arch/ppc64/xmon/start.c --- linux-2.4.5-ac18/arch/ppc64/xmon/start.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/xmon/start.c Wed Aug 1 15:46:18 2001 @@ -0,0 +1,320 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static volatile unsigned char *sccc, *sccd; +unsigned long TXRDY, RXRDY; +extern void xmon_printf(const char *fmt, ...); +static int xmon_expect(const char *str, unsigned int timeout); + +static int console = 0; +static int via_modem = 0; +/* static int xmon_use_sccb = 0; --Unused */ + +#define TB_SPEED 25000000 + +extern void *comport1; +static inline unsigned int readtb(void) +{ + unsigned int ret; + + asm volatile("mftb %0" : "=r" (ret) :); + return ret; +} + +void buf_access(void) +{ + if ( _machine == _MACH_chrp ) + sccd[3] &= ~0x80; /* reset DLAB */ +} + +extern int adb_init(void); + +static void sysrq_handle_xmon(int key, struct pt_regs *pt_regs, struct kbd_struct *kbd, struct tty_struct *tty) +{ + xmon(pt_regs); +} +static struct sysrq_key_op sysrq_xmon_op = +{ + handler: sysrq_handle_xmon, + help_msg: "xmon", + action_msg: "Entering xmon\n", +}; + +void +xmon_map_scc(void) +{ + /* should already be mapped by the kernel boot */ + sccd = (volatile unsigned char *) (((unsigned long)comport1)); + sccc = (volatile unsigned char *) (((unsigned long)comport1)+5); + TXRDY = 0x20; + RXRDY = 1; + /* This maybe isn't the best place to register sysrq 'x' */ + __sysrq_put_key_op('x', &sysrq_xmon_op); +} + +static int scc_initialized = 0; + +void xmon_init_scc(void); +extern void pmu_poll(void); + +int +xmon_write(void *handle, void *ptr, int nb) +{ + char *p = ptr; + int i, c, ct; + + if (!scc_initialized) + xmon_init_scc(); + ct = 0; + for (i = 0; i < nb; ++i) { + while ((*sccc & TXRDY) == 0) { + } + c = p[i]; + if (c == '\n' && !ct) { + c = '\r'; + ct = 1; + --i; + } else { + if (console) + printk("%c", c); + ct = 0; + } + buf_access(); + *sccd = c; + } + return i; +} + +int xmon_wants_key; +int xmon_adb_keycode; + +int +xmon_read(void *handle, void *ptr, int nb) +{ + char *p = ptr; + int i, c; + + if (!scc_initialized) + xmon_init_scc(); + for (i = 0; i < nb; ++i) { + do { + while ((*sccc & RXRDY) == 0) + ; + buf_access(); + c = *sccd; + } while (c == 0x11 || c == 0x13); + *p++ = c; + } + return i; +} + +int +xmon_read_poll(void) +{ + if ((*sccc & RXRDY) == 0) { +#ifdef CONFIG_ADB_PMU + if (sys_ctrler == SYS_CTRLER_PMU) + pmu_poll(); +#endif /* CONFIG_ADB_PMU */ + return -1; + } + buf_access(); + return *sccd; +} + +void +xmon_init_scc() +{ + if ( _machine == _MACH_chrp ) + { + sccd[3] = 0x83; eieio(); /* LCR = 8N1 + DLAB */ + sccd[0] = 12; eieio(); /* DLL = 9600 baud */ + sccd[1] = 0; eieio(); + sccd[2] = 0; eieio(); /* FCR = 0 */ + sccd[3] = 3; eieio(); /* LCR = 8N1 */ + sccd[1] = 0; eieio(); /* IER = 0 */ + } + + scc_initialized = 1; + if (via_modem) { + for (;;) { + xmon_write(0, "ATE1V1\r", 7); + if (xmon_expect("OK", 5)) { + xmon_write(0, "ATA\r", 4); + if (xmon_expect("CONNECT", 40)) + break; + } + xmon_write(0, "+++", 3); + xmon_expect("OK", 3); + } + } +} + +void *xmon_stdin; +void *xmon_stdout; +void *xmon_stderr; + +void +xmon_init(void) +{ +} + +int +xmon_putc(int c, void *f) +{ + char ch = c; + + if (c == '\n') + xmon_putc('\r', f); + return xmon_write(f, &ch, 1) == 1? c: -1; +} + +int +xmon_putchar(int c) +{ + return xmon_putc(c, xmon_stdout); +} + +int +xmon_fputs(char *str, void *f) +{ + int n = strlen(str); + + return xmon_write(f, str, n) == n? 0: -1; +} + +int +xmon_readchar(void) +{ + char ch; + + for (;;) { + switch (xmon_read(xmon_stdin, &ch, 1)) { + case 1: + return ch; + case -1: + xmon_printf("read(stdin) returned -1\r\n", 0, 0); + return -1; + } + } +} + +static char line[256]; +static char *lineptr; +static int lineleft; + +int xmon_expect(const char *str, unsigned int timeout) +{ + int c; + unsigned int t0; + + timeout *= TB_SPEED; + t0 = readtb(); + do { + lineptr = line; + for (;;) { + c = xmon_read_poll(); + if (c == -1) { + if (readtb() - t0 > timeout) + return 0; + continue; + } + if (c == '\n') + break; + if (c != '\r' && lineptr < &line[sizeof(line) - 1]) + *lineptr++ = c; + } + *lineptr = 0; + } while (strstr(line, str) == NULL); + return 1; +} + +int +xmon_getchar(void) +{ + int c; + + if (lineleft == 0) { + lineptr = line; + for (;;) { + c = xmon_readchar(); + if (c == -1 || c == 4) + break; + if (c == '\r' || c == '\n') { + *lineptr++ = '\n'; + xmon_putchar('\n'); + break; + } + switch (c) { + case 0177: + case '\b': + if (lineptr > line) { + xmon_putchar('\b'); + xmon_putchar(' '); + xmon_putchar('\b'); + --lineptr; + } + break; + case 'U' & 0x1F: + while (lineptr > line) { + xmon_putchar('\b'); + xmon_putchar(' '); + xmon_putchar('\b'); + --lineptr; + } + break; + default: + if (lineptr >= &line[sizeof(line) - 1]) + xmon_putchar('\a'); + else { + xmon_putchar(c); + *lineptr++ = c; + } + } + } + lineleft = lineptr - line; + lineptr = line; + } + if (lineleft == 0) + return -1; + --lineleft; + return *lineptr++; +} + +char * +xmon_fgets(char *str, int nb, void *f) +{ + char *p; + int c; + + for (p = str; p < str + nb - 1; ) { + c = xmon_getchar(); + if (c == -1) { + if (p == str) + return 0; + break; + } + *p++ = c; + if (c == '\n') + break; + } + *p = 0; + return str; +} diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/xmon/subr_prf.c linuxppc64_2_4/arch/ppc64/xmon/subr_prf.c --- linux-2.4.5-ac18/arch/ppc64/xmon/subr_prf.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/xmon/subr_prf.c Mon May 7 12:48:56 2001 @@ -0,0 +1,55 @@ +/* + * Written by Cort Dougan to replace the version originally used + * by Paul Mackerras, which came from NetBSD and thus had copyright + * conflicts with Linux. + * + * This file makes liberal use of the standard linux utility + * routines to reduce the size of the binary. We assume we can + * trust some parts of Linux inside the debugger. + * -- Cort (cort@cs.nmt.edu) + * + * Copyright (C) 1999 Cort Dougan. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include "nonstdio.h" + +extern int xmon_write(void *, void *, int); + +void +xmon_vfprintf(void *f, const char *fmt, va_list ap) +{ + static char xmon_buf[2048]; + int n; + + n = vsprintf(xmon_buf, fmt, ap); + xmon_write(f, xmon_buf, n); +} + +void +xmon_printf(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + xmon_vfprintf(stdout, fmt, ap); + va_end(ap); +} + +void +xmon_fprintf(void *f, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + xmon_vfprintf(f, fmt, ap); + va_end(ap); +} + diff -uNr --exclude=CVS linux-2.4.5-ac18/arch/ppc64/xmon/xmon.c linuxppc64_2_4/arch/ppc64/xmon/xmon.c --- linux-2.4.5-ac18/arch/ppc64/xmon/xmon.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/arch/ppc64/xmon/xmon.c Thu Jul 12 00:55:37 2001 @@ -0,0 +1,3190 @@ +/* + * Routines providing a simple monitor for use on the PowerMac. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nonstdio.h" +#include "privinst.h" +#include + +#include + +#ifndef _PACA_H +#include +#endif + +#define scanhex xmon_scanhex +#define skipbl xmon_skipbl + +static unsigned long adrs; +static int size = 1; +static unsigned long ndump = 64; +static unsigned long nidump = 16; +static unsigned long ncsum = 4096; +static int termch; + +static unsigned long xmon_running = 0; + +static u_int bus_error_jmp[100]; +#define setjmp xmon_setjmp +#define longjmp xmon_longjmp + +#define memlist_entry list_entry +#define memlist_next(x) ((x)->next) +#define memlist_prev(x) ((x)->prev) + + +/* Max number of stack frames we are willing to produce on a backtrace. */ +#define MAXFRAMECOUNT 50 + +/* Breakpoint stuff */ +struct bpt { + unsigned long address; + unsigned instr; + unsigned long count; + unsigned char enabled; + char funcname[64]; /* function name for humans */ +}; + +#define NBPTS 16 +static struct bpt bpts[NBPTS]; +static struct bpt dabr; +static struct bpt iabr; +static unsigned bpinstr = 0x7fe00008; /* trap */ + +/* Prototypes */ +extern void (*debugger_fault_handler)(struct pt_regs *); +static int cmds(struct pt_regs *); +static int mread(unsigned long, void *, int); +static int mwrite(unsigned long, void *, int); +static void handle_fault(struct pt_regs *); +static void byterev(unsigned char *, int); +static void memex(void); +static int bsesc(void); +static void dump(void); +static void prdump(unsigned long, long); +#ifdef __MWERKS__ +static void prndump(unsigned, int); +static int nvreadb(unsigned); +#endif +static int ppc_inst_dump(unsigned long, long); +void print_address(unsigned long); +static int getsp(void); +static void dump_hash_table(void); +static void backtrace(struct pt_regs *); +static void excprint(struct pt_regs *); +static void prregs(struct pt_regs *); +static void memops(int); +static void memlocate(void); +static void memzcan(void); +static void memdiffs(unsigned char *, unsigned char *, unsigned, unsigned); +int skipbl(void); +int scanhex(unsigned long *valp); +static void scannl(void); +static int hexdigit(int); +void getstring(char *, int); +static void flush_input(void); +static int inchar(void); +static void take_input(char *); +/* static void openforth(void); */ +static unsigned long read_spr(int); +static void write_spr(int, unsigned long); +static void super_regs(void); +static void print_sysmap(void); +static void remove_bpts(void); +static void insert_bpts(void); +static struct bpt *at_breakpoint(unsigned long pc); +static void bpt_cmds(void); +static void cacheflush(void); +/* static char *pretty_print_addr(unsigned long addr); -Unused -aglitke */ +static char *lookup_name(unsigned long addr); +static void csum(void); +static void mem_translate(void); +static void mem_check(void); +static void mem_find_real(void); +static void mem_find_vsid(void); +static void mem_check_full_group(void); +static void mem_check_zones(void); +static void mem_check_pagetable_vsids (void); +static void mem_check_pagetable_extension_valid (void); + +static void mem_map_check_slab(void); +static void mem_map_lock_pages(void); +static void mem_map_check_hash(void); +static void mem_check_dup_rpn (void); +static void show_task(struct task_struct * p); +static void show_state(void); +static void debug_trace(void); + +extern int print_insn_big_powerpc(FILE *, unsigned long, unsigned long); +extern void printf(const char *fmt, ...); +extern int putchar(int ch); +extern int setjmp(u_int *); +extern void longjmp(u_int *, int); +extern unsigned long _ASR; +extern struct Naca *naca; + +pte_t *find_linux_pte(pgd_t *pgdir, unsigned long va); /* from htab.c */ + +#define GETWORD(v) (((v)[0] << 24) + ((v)[1] << 16) + ((v)[2] << 8) + (v)[3]) + +static char *help_string = "\ +Commands:\n\ + b show breakpoints\n\ + bd set data breakpoint\n\ + bi set instruction breakpoint\n\ + bc clear breakpoint\n\ + d dump bytes\n\ + dd dump double values\n\ + df dump float values\n\ + di dump instructions\n\ + e print exception information\n\ + f flush cache\n\ + h dump hash table\n\ + m examine/change memory\n\ + mm move a block of memory\n\ + ms set a block of memory\n\ + md compare two blocks of memory\n\ + ml locate a block of memory\n\ + mz zero a block of memory\n\ + mx translation information for an effective address\n\ + mi show information about memory allocation\n\ + M print System.map\n\ + p show the task list\n\ + r print registers\n\ + s single step\n\ + S print special registers\n\ + t print backtrace\n\ + T Enable/Disable PPCDBG flags\n\ + x exit monitor\n\ +"; + + +static int xmon_trace; +#define SSTEP 1 /* stepping because of 's' command */ +#define BRSTEP 2 /* stepping over breakpoint */ + + +/* (Ref: 64-bit PowerPC ELF ABI Spplement; Ian Lance Taylor, Zembu Labs). + A PPC stack frame looks like this: + + High Address + Back Chain + FP reg save area + GP reg save area + Local var space + Parameter save area (SP+48) + TOC save area (SP+40) + link editor doubleword (SP+32) + compiler doubleword (SP+24) + LR save (SP+16) + CR save (SP+8) + Back Chain (SP+0) + + Note that the LR (ret addr) may not be saved in the current frame if + no functions have been called from the current function. + */ + + +/* + A traceback table typically follows each function. + The find_tb_table() func will fill in this struct. Note that the struct + is not an exact match with the encoded table defined by the ABI. It is + defined here more for programming convenience. + */ +struct tbtable { + unsigned long flags; /* flags: */ +#define TBTAB_FLAGSGLOBALLINK (1L<<47) +#define TBTAB_FLAGSISEPROL (1L<<46) +#define TBTAB_FLAGSHASTBOFF (1L<<45) +#define TBTAB_FLAGSINTPROC (1L<<44) +#define TBTAB_FLAGSHASCTL (1L<<43) +#define TBTAB_FLAGSTOCLESS (1L<<42) +#define TBTAB_FLAGSFPPRESENT (1L<<41) +#define TBTAB_FLAGSNAMEPRESENT (1L<<38) +#define TBTAB_FLAGSUSESALLOCA (1L<<37) +#define TBTAB_FLAGSSAVESCR (1L<<33) +#define TBTAB_FLAGSSAVESLR (1L<<32) +#define TBTAB_FLAGSSTORESBC (1L<<31) +#define TBTAB_FLAGSFIXUP (1L<<30) +#define TBTAB_FLAGSPARMSONSTK (1L<<0) + unsigned char fp_saved; /* num fp regs saved f(32-n)..f31 */ + unsigned char gpr_saved; /* num gpr's saved */ + unsigned char fixedparms; /* num fixed point parms */ + unsigned char floatparms; /* num float parms */ + unsigned char parminfo[32]; /* types of args. null terminated */ +#define TBTAB_PARMFIXED 1 +#define TBTAB_PARMSFLOAT 2 +#define TBTAB_PARMDFLOAT 3 + unsigned int tb_offset; /* offset from start of func */ + unsigned long funcstart; /* addr of start of function */ + char name[64]; /* name of function (null terminated)*/ +}; +static int find_tb_table(unsigned long codeaddr, struct tbtable *tab); + +void +xmon(struct pt_regs *excp) +{ + struct pt_regs regs; + int cmd; + unsigned long msr; + + if (xmon_running) + /* If we're already in xmon, just return */ + return; + + xmon_running = 1; + + if (excp == NULL) { + /* Ok, grab regs as they are now. + This won't do a particularily good job because the + prologue has already been executed. + ToDo: We could reach back into the callers save + area to do a better job of representing the + caller's state. + */ + asm volatile ("std 0,0(%0)\n\ + std 1,8(%0)\n\ + std 2,16(%0)\n\ + std 3,24(%0)\n\ + std 4,32(%0)\n\ + std 5,40(%0)\n\ + std 6,48(%0)\n\ + std 7,56(%0)\n\ + std 8,64(%0)\n\ + std 9,72(%0)\n\ + std 10,80(%0)\n\ + std 11,88(%0)\n\ + std 12,96(%0)\n\ + std 13,104(%0)\n\ + std 14,112(%0)\n\ + std 15,120(%0)\n\ + std 16,128(%0)\n\ + std 17,136(%0)\n\ + std 18,144(%0)\n\ + std 19,152(%0)\n\ + std 20,160(%0)\n\ + std 21,168(%0)\n\ + std 22,176(%0)\n\ + std 23,184(%0)\n\ + std 24,192(%0)\n\ + std 25,200(%0)\n\ + std 26,208(%0)\n\ + std 27,216(%0)\n\ + std 28,224(%0)\n\ + std 29,232(%0)\n\ + std 30,240(%0)\n\ + std 31,248(%0)" : : "b" (®s)); + printf("xmon called\n"); + /* Fetch the link reg for this stack frame. + NOTE: the prev printf fills in the lr. */ + regs.nip = regs.link = ((unsigned long *)(regs.gpr[1]))[2]; + regs.msr = get_msr(); + regs.ctr = get_ctr(); + regs.xer = get_xer(); + regs.ccr = get_cr(); + regs.trap = 0; + excp = ®s; + } + msr = get_msr(); + set_msrd(msr & ~MSR_EE); /* disable interrupts */ + remove_bpts(); + excprint(excp); + cmd = cmds(excp); + if (cmd == 's') { + xmon_trace = SSTEP; + excp->msr |= 0x400; + } else if (at_breakpoint(excp->nip)) { + xmon_trace = BRSTEP; + excp->msr |= 0x400; + } else { + xmon_trace = 0; + insert_bpts(); + } + set_msrd(msr); /* restore interrupt enable */ + xmon_running = 0; +} + +/* Code can call this to get a backtrace and continue. */ +void +xmon_backtrace(const char *fmt, ...) +{ + va_list ap; + struct pt_regs regs; + + + /* Ok, grab regs as they are now. + This won't do a particularily good job because the + prologue has already been executed. + ToDo: We could reach back into the callers save + area to do a better job of representing the + caller's state. + */ + asm volatile ("std 0,0(%0)\n\ + std 1,8(%0)\n\ + std 2,16(%0)\n\ + std 3,24(%0)\n\ + std 4,32(%0)\n\ + std 5,40(%0)\n\ + std 6,48(%0)\n\ + std 7,56(%0)\n\ + std 8,64(%0)\n\ + std 9,72(%0)\n\ + std 10,80(%0)\n\ + std 11,88(%0)\n\ + std 12,96(%0)\n\ + std 13,104(%0)\n\ + std 14,112(%0)\n\ + std 15,120(%0)\n\ + std 16,128(%0)\n\ + std 17,136(%0)\n\ + std 18,144(%0)\n\ + std 19,152(%0)\n\ + std 20,160(%0)\n\ + std 21,168(%0)\n\ + std 22,176(%0)\n\ + std 23,184(%0)\n\ + std 24,192(%0)\n\ + std 25,200(%0)\n\ + std 26,208(%0)\n\ + std 27,216(%0)\n\ + std 28,224(%0)\n\ + std 29,232(%0)\n\ + std 30,240(%0)\n\ + std 31,248(%0)" : : "b" (®s)); + /* Fetch the link reg for this stack frame. + NOTE: the prev printf fills in the lr. */ + regs.nip = regs.link = ((unsigned long *)(regs.gpr[1]))[2]; + regs.msr = get_msr(); + regs.ctr = get_ctr(); + regs.xer = get_xer(); + regs.ccr = get_cr(); + regs.trap = 0; + + va_start(ap, fmt); + xmon_vfprintf(stdout, fmt, ap); + xmon_putc('\n', stdout); + va_end(ap); + take_input("\n"); + backtrace(®s); +} + +/* Call this to poll for ^C during busy operations. + * Returns true if the user has hit ^C. + */ +int +xmon_interrupted(void) +{ + int ret = xmon_read_poll(); + if (ret == 3) { + printf("\n^C interrupted.\n"); + return 1; + } + return 0; +} + + +void +xmon_irq(int irq, void *d, struct pt_regs *regs) +{ + printf("Keyboard interrupt\n"); + xmon(regs); +} + +int +xmon_bpt(struct pt_regs *regs) +{ + struct bpt *bp; + + bp = at_breakpoint(regs->nip); + if (!bp) + return 0; + if (bp->count) { + --bp->count; + remove_bpts(); + excprint(regs); + xmon_trace = BRSTEP; + regs->msr |= 0x400; + } else { + printf("Stopped at breakpoint %x (%lx %s)\n", (bp - bpts)+1, bp->address, bp->funcname); + xmon(regs); + } + return 1; +} + +int +xmon_sstep(struct pt_regs *regs) +{ + if (!xmon_trace) + return 0; + if (xmon_trace == BRSTEP) { + xmon_trace = 0; + insert_bpts(); + } else { + xmon(regs); + } + return 1; +} + +int +xmon_dabr_match(struct pt_regs *regs) +{ + if (dabr.enabled && dabr.count) { + --dabr.count; + remove_bpts(); + excprint(regs); + xmon_trace = BRSTEP; + regs->msr |= 0x400; + } else { + dabr.instr = regs->nip; + xmon(regs); + } + return 1; +} + +int +xmon_iabr_match(struct pt_regs *regs) +{ + if (iabr.enabled && iabr.count) { + --iabr.count; + remove_bpts(); + excprint(regs); + xmon_trace = BRSTEP; + regs->msr |= 0x400; + } else { + xmon(regs); + } + return 1; +} + +static struct bpt * +at_breakpoint(unsigned long pc) +{ + int i; + struct bpt *bp; + + if (dabr.enabled && pc == dabr.instr) + return &dabr; + if (iabr.enabled && pc == iabr.address) + return &iabr; + bp = bpts; + for (i = 0; i < NBPTS; ++i, ++bp) + if (bp->enabled && pc == bp->address) + return bp; + return 0; +} + +static void +insert_bpts() +{ + int i; + struct bpt *bp; + + bp = bpts; + for (i = 0; i < NBPTS; ++i, ++bp) { + if (!bp->enabled) + continue; + if (mread(bp->address, &bp->instr, 4) != 4 + || mwrite(bp->address, &bpinstr, 4) != 4) { + printf("Couldn't insert breakpoint at %x, disabling\n", + bp->address); + bp->enabled = 0; + } else { + store_inst((void *)bp->address); + } + } +#if !defined(CONFIG_8xx) && !defined(CONFIG_POWER4) + if (dabr.enabled) + set_dabr(dabr.address); + if (iabr.enabled) + set_iabr(iabr.address); +#endif +} + +static void +remove_bpts() +{ + int i; + struct bpt *bp; + unsigned instr; + +#if !defined(CONFIG_8xx) && !defined(CONFIG_POWER4) + set_dabr(0); + set_iabr(0); +#endif + bp = bpts; + for (i = 0; i < NBPTS; ++i, ++bp) { + if (!bp->enabled) + continue; + if (mread(bp->address, &instr, 4) == 4 + && instr == bpinstr + && mwrite(bp->address, &bp->instr, 4) != 4) + printf("Couldn't remove breakpoint at %x\n", + bp->address); + else + store_inst((void *)bp->address); + } +} + +static char *last_cmd; + +/* Command interpreting routine */ +static int +cmds(struct pt_regs *excp) +{ + int cmd; + + last_cmd = NULL; + for(;;) { + printf("mon> "); + fflush(stdout); + flush_input(); + termch = 0; + cmd = skipbl(); + if( cmd == '\n' ) { + if (last_cmd == NULL) + continue; + take_input(last_cmd); + last_cmd = NULL; + cmd = inchar(); + } + switch (cmd) { + case 'm': + cmd = inchar(); + switch (cmd) { + case 'm': + case 's': + case 'd': + memops(cmd); + break; + case 'l': + memlocate(); + break; + case 'z': + memzcan(); + break; + case 'x': + mem_translate(); + break; + case 'c': + mem_check(); + break; + case 'g': + mem_check_full_group(); + break; + case 'j': + mem_map_check_slab(); + break; + case 'h': + mem_map_check_hash(); + break; + case 'f': + mem_find_real(); + break; + case 'e': + mem_find_vsid(); + break; + case 'r': + mem_check_dup_rpn(); + break; + case 'i': + show_mem(); + break; + case 'k': + mem_check_zones(); + break; + case 'o': + mem_check_pagetable_vsids (); + break; + case 'p': + mem_check_pagetable_extension_valid (); + break; + case 'q': + mem_map_lock_pages() ; + break; + + + default: + termch = cmd; + memex(); + } + break; + case 'd': + dump(); + break; + case 'r': + if (excp != NULL) + prregs(excp); /* print regs */ + break; + case 'e': + if (excp == NULL) + printf("No exception information\n"); + else + excprint(excp); + break; + case 'M': + print_sysmap(); + case 'S': + super_regs(); + break; + case 't': + backtrace(excp); + break; + case 'f': + cacheflush(); + break; + case 'h': + dump_hash_table(); + break; + case 's': + case 'x': + case EOF: + return cmd; + case '?': + printf(help_string); + break; + case 'p': + show_state(); + break; + case 'b': + bpt_cmds(); + break; + case 'c': + csum(); + break; + case 'T': + debug_trace(); + break; + default: + printf("Unrecognized command: "); + do { + if( ' ' < cmd && cmd <= '~' ) + putchar(cmd); + else + printf("\\x%x", cmd); + cmd = inchar(); + } while (cmd != '\n'); + printf(" (type ? for help)\n"); + break; + } + } +} + +static unsigned short fcstab[256] = { + 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf, + 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7, + 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e, + 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876, + 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd, + 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5, + 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c, + 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974, + 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb, + 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3, + 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a, + 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72, + 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9, + 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1, + 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738, + 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70, + 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7, + 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff, + 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036, + 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e, + 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5, + 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd, + 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134, + 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c, + 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3, + 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb, + 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232, + 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a, + 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1, + 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9, + 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330, + 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78 +}; + +#define FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff]) + +static void +csum(void) +{ + unsigned int i; + unsigned short fcs; + unsigned char v; + + scanhex((void *)&adrs); + scanhex((void *)&ncsum); + fcs = 0xffff; + for (i = 0; i < ncsum; ++i) { + if (mread(adrs+i, &v, 1) == 0) { + printf("csum stopped at %x\n", adrs+i); + break; + } + fcs = FCS(fcs, v); + } + printf("%x\n", fcs); +} + +static char *breakpoint_help_string = + "Breakpoint command usage:\n" + "b show breakpoints\n" + "b [cnt] set breakpoint at given instr addr\n" + "bc clear all breakpoints\n" + "bc clear breakpoint number n or at addr\n" +#if !defined(CONFIG_POWER4) + "bi [cnt] set hardware instr breakpoint (broken?)\n" + "bd [cnt] set hardware data breakpoint (broken?)\n" +#endif + ""; + +static void +bpt_cmds(void) +{ + int cmd; + unsigned long a; + int mode, i; + struct bpt *bp; + struct tbtable tab; + + cmd = inchar(); + switch (cmd) { +#if !defined(CONFIG_POWER4) + case 'd': /* bd - hardware data breakpoint */ + mode = 7; + cmd = inchar(); + if (cmd == 'r') + mode = 5; + else if (cmd == 'w') + mode = 6; + else + termch = cmd; + dabr.address = 0; + dabr.count = 0; + dabr.enabled = scanhex(&dabr.address); + scanhex(&dabr.count); + if (dabr.enabled) + dabr.address = (dabr.address & ~7) | mode; + break; + case 'i': /* bi - hardware instr breakpoint */ + iabr.address = 0; + iabr.count = 0; + iabr.enabled = scanhex(&iabr.address); + if (iabr.enabled) + iabr.address |= 3; + scanhex(&iabr.count); + break; +#endif + case 'c': + if (!scanhex(&a)) { + /* clear all breakpoints */ + for (i = 0; i < NBPTS; ++i) + bpts[i].enabled = 0; + iabr.enabled = 0; + dabr.enabled = 0; + printf("All breakpoints cleared\n"); + } else { + if (a <= NBPTS && a >= 1) { + /* assume a breakpoint number */ + --a; /* bp nums are 1 based */ + bp = &bpts[a]; + } else { + /* assume a breakpoint address */ + bp = at_breakpoint(a); + } + if (bp == 0) { + printf("No breakpoint at %x\n", a); + } else { + printf("Cleared breakpoint %x (%lx %s)\n", (bp - bpts)+1, bp->address, bp->funcname); + bp->enabled = 0; + } + } + break; + case '?': + printf(breakpoint_help_string); + break; + default: + termch = cmd; + cmd = skipbl(); + if (cmd == '?') { + printf(breakpoint_help_string); + break; + } + termch = cmd; + if (!scanhex(&a)) { + /* print all breakpoints */ + int bpnum; + + printf(" type address count\n"); + if (dabr.enabled) { + printf(" data %.16lx %8x [", dabr.address & ~7, + dabr.count); + if (dabr.address & 1) + printf("r"); + if (dabr.address & 2) + printf("w"); + printf("]\n"); + } + if (iabr.enabled) + printf(" inst %.16lx %8x\n", iabr.address & ~3, + iabr.count); + for (bp = bpts, bpnum = 1; bp < &bpts[NBPTS]; ++bp, ++bpnum) + if (bp->enabled) + printf("%2x trap %.16lx %8x %s\n", bpnum, bp->address, bp->count, bp->funcname); + break; + } + bp = at_breakpoint(a); + if (bp == 0) { + for (bp = bpts; bp < &bpts[NBPTS]; ++bp) + if (!bp->enabled) + break; + if (bp >= &bpts[NBPTS]) { + printf("Sorry, no free breakpoints. Please clear one first.\n"); + break; + } + } + bp->enabled = 1; + bp->address = a; + bp->count = 0; + scanhex(&bp->count); + /* Find the function name just once. */ + bp->funcname[0] = '\0'; + if (find_tb_table(bp->address, &tab) && tab.name[0]) { + /* Got a nice name for it. */ + int delta = bp->address - tab.funcstart; + sprintf(bp->funcname, "%s+0x%x", tab.name, delta); + } + printf("Set breakpoint %2x trap %.16lx %8x %s\n", (bp-bpts)+1, bp->address, bp->count, bp->funcname); + break; + } +} + +/* Very cheap human name for vector lookup. */ +static +const char *getvecname(unsigned long vec) +{ + char *ret; + switch (vec) { + case 0x100: ret = "(System Reset)"; break; + case 0x200: ret = "(Machine Check)"; break; + case 0x300: ret = "(Data Access)"; break; + case 0x400: ret = "(Instruction Access)"; break; + case 0x500: ret = "(Hardware Interrupt)"; break; + case 0x600: ret = "(Alignment)"; break; + case 0x700: ret = "(Program Check)"; break; + case 0x800: ret = "(FPU Unavailable)"; break; + case 0x900: ret = "(Decrementer)"; break; + case 0xc00: ret = "(System Call)"; break; + case 0xd00: ret = "(Single Step)"; break; + case 0xf00: ret = "(Performance Monitor)"; break; + default: ret = ""; + } + return ret; +} + +static void +backtrace(struct pt_regs *excp) +{ + unsigned long sp; + unsigned long lr; + unsigned long stack[3]; + struct pt_regs regs; + struct tbtable tab; + int framecount; + char *funcname; + /* declare these as raw ptrs so we don't get func descriptors */ + extern void *ret_from_except, *ret_from_syscall_1; + + if (excp != NULL) { + lr = excp->link; + sp = excp->gpr[1]; + } else { + /* Use care not to call any function before this point + so the saved lr has a chance of being good. */ + asm volatile ("mflr %0" : "=r" (lr) :); + sp = getsp(); + } + scanhex(&sp); + scannl(); + for (framecount = 0; + sp != 0 && framecount < MAXFRAMECOUNT; + sp = stack[0], framecount++) { + if (mread(sp, stack, sizeof(stack)) != sizeof(stack)) + break; +#if 0 + if (lr != 0) { + stack[2] = lr; /* fake out the first saved lr. It may not be saved yet. */ + lr = 0; + } +#endif + printf("%.16lx %.16lx", sp, stack[2]); + /* TAI -- for now only the ones cast to unsigned long will match. + * Need to test the rest... + */ + if ((stack[2] == (unsigned long)ret_from_except && + (funcname = "ret_from_except")) + || (stack[2] == (unsigned long)ret_from_syscall_1 && + (funcname = "ret_from_syscall_1")) +#if 0 + || stack[2] == (unsigned) &ret_from_intercept + || stack[2] == (unsigned) &ret_from_syscall_2 + || stack[2] == (unsigned) &lost_irq_ret + || stack[2] == (unsigned) &do_bottom_half_ret + || stack[2] == (unsigned) &do_signal_ret +#endif + ) { + printf(" %s\n", funcname); + if (mread(sp+112, ®s, sizeof(regs)) != sizeof(regs)) + break; + printf("exception: %lx %s regs %lx\n", regs.trap, getvecname(regs.trap), sp+112); + printf(" %.16lx", regs.nip); + if ((regs.nip & 0xffffffff00000000UL) && + find_tb_table(regs.nip, &tab)) { + int delta = regs.nip-tab.funcstart; + if (delta < 0) + printf(" "); + else + printf(" %s+0x%x", tab.name, delta); + } + printf("\n"); + sp = regs.gpr[1]; + if (mread(sp, stack, sizeof(stack)) != sizeof(stack)) + break; + } else { + if (stack[2] && find_tb_table(stack[2], &tab)) { + int delta = stack[2]-tab.funcstart; + if (delta < 0) + printf(" "); + else + printf(" %s+0x%x", tab.name, delta); + } + printf("\n"); + } + if (stack[0] && stack[0] <= sp) { + if ((stack[0] & 0xffffffff00000000UL) == 0) + printf("\n", stack[0]); + else + printf("\n", stack[0]); + break; + } + } + if (framecount >= MAXFRAMECOUNT) + printf("\n"); +} + +int +getsp() +{ + int x; + + asm("mr %0,1" : "=r" (x) :); + return x; +} + + +void +excprint(struct pt_regs *fp) +{ + struct task_struct *c; + unsigned long p; + struct tbtable tab; + + printf("Vector: %lx %s at [%lx]\n", fp->trap, getvecname(fp->trap), fp); + printf(" pc: %lx", fp->nip); + if (find_tb_table(fp->nip, &tab) && tab.name[0]) { + /* Got a nice name for it */ + int delta = fp->nip - tab.funcstart; + printf(" (%s+0x%x)", tab.name, delta); + } + printf("\n"); + printf(" lr: %lx", fp->link); + if (find_tb_table(fp->link, &tab) && tab.name[0]) { + /* Got a nice name for it */ + int delta = fp->link - tab.funcstart; + printf(" (%s+0x%x)", tab.name, delta); + } + printf("\n"); + printf(" sp: %lx\n", fp->gpr[1]); + printf(" msr: %lx\n", fp->msr); + + if (fp->trap == 0x300 || fp->trap == 0x600) { + printf(" dar: %lx\n", fp->dar); + printf(" dsisr: %lx\n", fp->dsisr); + } + + /* XXX: need to copy current or we die. Why? */ + c = current; + p = current_paca; + printf(" current = 0x%lx\n", c); + printf(" paca = 0x%lx\n", p); + if (c) { + printf(" current = %lx, pid = %ld, comm = %s\n", + c, c->pid, c->comm); + } +} + +void +prregs(struct pt_regs *fp) +{ + int n; + unsigned long base; + + if (scanhex((void *)&base)) + fp = (struct pt_regs *) base; + for (n = 0; n < 16; ++n) + printf("R%.2ld = %.16lx R%.2ld = %.16lx\n", n, fp->gpr[n], + n+16, fp->gpr[n+16]); + printf("pc = %.16lx msr = %.16lx\nlr = %.16lx cr = %.16lx\n", + fp->nip, fp->msr, fp->link, fp->ccr); + printf("ctr = %.16lx xer = %.16lx trap = %8lx\n", + fp->ctr, fp->xer, fp->trap); +} + +void +cacheflush(void) +{ + int cmd; + unsigned long nflush; + + cmd = inchar(); + if (cmd != 'i') + termch = cmd; + scanhex((void *)&adrs); + if (termch != '\n') + termch = 0; + nflush = 1; + scanhex(&nflush); + nflush = (nflush + 31) / 32; + if (cmd != 'i') { + for (; nflush > 0; --nflush, adrs += 0x20) + cflush((void *) adrs); + } else { + for (; nflush > 0; --nflush, adrs += 0x20) + cinval((void *) adrs); + } +} + +unsigned long +read_spr(int n) +{ + unsigned int instrs[2]; + unsigned long (*code)(void); + + instrs[0] = 0x7c6002a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6); + instrs[1] = 0x4e800020; + store_inst(instrs); + store_inst(instrs+1); + code = (unsigned long (*)(void)) instrs; + return code(); +} + +void +write_spr(int n, unsigned long val) +{ + unsigned int instrs[2]; + unsigned long (*code)(unsigned long); + + instrs[0] = 0x7c6003a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6); + instrs[1] = 0x4e800020; + store_inst(instrs); + store_inst(instrs+1); + code = (unsigned long (*)(unsigned long)) instrs; + code(val); +} + +static unsigned long regno; +extern char exc_prolog; +extern char dec_exc; + +void +print_sysmap(void) +{ + extern char *sysmap; + if ( sysmap ) + printf("System.map: \n%s", sysmap); +} + +void +super_regs() +{ + int i, cmd; + unsigned long val; + struct Paca* ptrPaca = NULL; + struct ItLpPaca* ptrLpPaca = NULL; + struct ItLpRegSave* ptrLpRegSave = NULL; + + cmd = skipbl(); + if (cmd == '\n') { + unsigned long sp, toc; + asm("mr %0,1" : "=r" (sp) :); + asm("mr %0,2" : "=r" (toc) :); + + printf("msr = %.16lx sprg0= %.16lx\n", get_msr(), get_sprg0()); + printf("pvr = %.16lx sprg1= %.16lx\n", get_pvr(), get_sprg1()); + printf("dec = %.16lx sprg2= %.16lx\n", get_dec(), get_sprg2()); + printf("sp = %.16lx sprg3= %.16lx\n", sp, get_sprg3()); + printf("toc = %.16lx dar = %.16lx\n", toc, get_dar()); + printf("srr0 = %.16lx srr1 = %.16lx\n", get_srr0(), get_srr1()); + printf("asr = %.16lx\n", mfasr()); + for (i = 0; i < 8; ++i) + printf("sr%.2ld = %.16lx sr%.2ld = %.16lx\n", i, get_sr(i), i+8, get_sr(i+8)); + + // Dump out relevant Paca data areas. + printf("Paca: \n"); + ptrPaca = (struct Paca*)get_sprg3(); + printf(" Saved Gpr21=%.16lx Saved Gpr22 =%.16lx \n", ptrPaca->xR21, ptrPaca->xR22); + + printf(" Local Processor Control Area (LpPaca): \n"); + ptrLpPaca = ptrPaca->xLpPacaPtr; + printf(" Saved Srr0=%.16lx Saved Srr1=%.16lx \n", ptrLpPaca->xSavedSrr0, ptrLpPaca->xSavedSrr1); + printf(" Saved Gpr3=%.16lx Saved Gpr4=%.16lx \n", ptrLpPaca->xSavedGpr3, ptrLpPaca->xSavedGpr4); + printf(" Saved Gpr5=%.16lx \n", ptrLpPaca->xSavedGpr5); + + printf(" Local Processor Register Save Area (LpRegSave): \n"); + ptrLpRegSave = ptrPaca->xLpRegSavePtr; + printf(" Saved Sprg0=%.16lx Saved Sprg1=%.16lx \n", ptrLpRegSave->xSPRG0, ptrLpRegSave->xSPRG0); + printf(" Saved Sprg2=%.16lx Saved Sprg3=%.16lx \n", ptrLpRegSave->xSPRG2, ptrLpRegSave->xSPRG3); + printf(" Saved Msr =%.16lx Saved Nia =%.16lx \n", ptrLpRegSave->xMSR, ptrLpRegSave->xNIA); + + return; + } + + scanhex(®no); + switch (cmd) { + case 'w': + val = read_spr(regno); + scanhex(&val); + write_spr(regno, val); + /* fall through */ + case 'r': + printf("spr %lx = %lx\n", regno, read_spr(regno)); + break; + case 's': + val = get_sr(regno); + scanhex(&val); + set_sr(regno, val); + break; + case 'm': + val = get_msr(); + scanhex(&val); + set_msrd(val); + break; + } + scannl(); +} + +#if 0 +static void +openforth() +{ + int c; + char *p; + char cmd[1024]; + int args[5]; + extern int (*prom_entry)(int *); + + p = cmd; + c = skipbl(); + while (c != '\n') { + *p++ = c; + c = inchar(); + } + *p = 0; + args[0] = (int) "interpret"; + args[1] = 1; + args[2] = 1; + args[3] = (int) cmd; + (*prom_entry)(args); + printf("\n"); + if (args[4] != 0) + printf("error %x\n", args[4]); +} +#endif + +#ifndef CONFIG_PPC64BRIDGE +static void +dump_hash_table_seg(unsigned seg, unsigned start, unsigned end) +{ + extern void *Hash; + extern unsigned long Hash_size; + unsigned *htab = Hash; + unsigned hsize = Hash_size; + unsigned v, hmask, va, last_va; + int found, last_found, i; + unsigned *hg, w1, last_w2, last_va0; + + last_found = 0; + hmask = hsize / 64 - 1; + va = start; + start = (start >> 12) & 0xffff; + end = (end >> 12) & 0xffff; + for (v = start; v < end; ++v) { + found = 0; + hg = htab + (((v ^ seg) & hmask) * 16); + w1 = 0x80000000 | (seg << 7) | (v >> 10); + for (i = 0; i < 8; ++i, hg += 2) { + if (*hg == w1) { + found = 1; + break; + } + } + if (!found) { + w1 ^= 0x40; + hg = htab + ((~(v ^ seg) & hmask) * 16); + for (i = 0; i < 8; ++i, hg += 2) { + if (*hg == w1) { + found = 1; + break; + } + } + } + if (!(last_found && found && (hg[1] & ~0x180) == last_w2 + 4096)) { + if (last_found) { + if (last_va != last_va0) + printf(" ... %x", last_va); + printf("\n"); + } + if (found) { + printf("%x to %x", va, hg[1]); + last_va0 = va; + } + last_found = found; + } + if (found) { + last_w2 = hg[1] & ~0x180; + last_va = va; + } + va += 4096; + } + if (last_found) + printf(" ... %x\n", last_va); +} + +#else /* CONFIG_PPC64BRIDGE */ +static void +dump_hash_table_seg(unsigned seg, unsigned start, unsigned end) +{ + extern void *Hash; + extern unsigned long Hash_size; + unsigned *htab = Hash; + unsigned hsize = Hash_size; + unsigned v, hmask, va, last_va; + int found, last_found, i; + unsigned *hg, w1, last_w2, last_va0; + + last_found = 0; + hmask = hsize / 128 - 1; + va = start; + start = (start >> 12) & 0xffff; + end = (end >> 12) & 0xffff; + for (v = start; v < end; ++v) { + found = 0; + hg = htab + (((v ^ seg) & hmask) * 32); + w1 = 1 | (seg << 12) | ((v & 0xf800) >> 4); + for (i = 0; i < 8; ++i, hg += 4) { + if (hg[1] == w1) { + found = 1; + break; + } + } + if (!found) { + w1 ^= 2; + hg = htab + ((~(v ^ seg) & hmask) * 32); + for (i = 0; i < 8; ++i, hg += 4) { + if (hg[1] == w1) { + found = 1; + break; + } + } + } + if (!(last_found && found && (hg[3] & ~0x180) == last_w2 + 4096)) { + if (last_found) { + if (last_va != last_va0) + printf(" ... %x", last_va); + printf("\n"); + } + if (found) { + printf("%x to %x", va, hg[3]); + last_va0 = va; + } + last_found = found; + } + if (found) { + last_w2 = hg[3] & ~0x180; + last_va = va; + } + va += 4096; + } + if (last_found) + printf(" ... %x\n", last_va); +} +#endif /* CONFIG_PPC64BRIDGE */ + +static unsigned long hash_ctx; +static unsigned long hash_start; +static unsigned long hash_end; + +static void +dump_hash_table() +{ + int seg; + unsigned seg_start, seg_end; + + hash_ctx = 0; + hash_start = 0; + hash_end = 0xfffff000; + scanhex(&hash_ctx); + scanhex(&hash_start); + scanhex(&hash_end); + printf("Mappings for context %x\n", hash_ctx); + seg_start = hash_start; + for (seg = hash_start >> 28; seg <= hash_end >> 28; ++seg) { + seg_end = (seg << 28) | 0x0ffff000; + if (seg_end > hash_end) + seg_end = hash_end; + dump_hash_table_seg((hash_ctx << 4) + seg, seg_start, seg_end); + seg_start = seg_end + 0x1000; + } +} + +/* + * Stuff for reading and writing memory safely + */ +extern inline void sync(void) +{ + asm volatile("sync; isync"); +} + +extern inline void __delay(unsigned int loops) +{ + if (loops != 0) + __asm__ __volatile__("mtctr %0; 1: bdnz 1b" : : + "r" (loops) : "ctr"); +} + +int +mread(unsigned long adrs, void *buf, int size) +{ + volatile int n; + char *p, *q; + + n = 0; + if( setjmp(bus_error_jmp) == 0 ){ + debugger_fault_handler = handle_fault; + sync(); + p = (char *) adrs; + q = (char *) buf; + switch (size) { + case 2: *(short *)q = *(short *)p; break; + case 4: *(int *)q = *(int *)p; break; + default: + for( ; n < size; ++n ) { + *q++ = *p++; + sync(); + } + } + sync(); + /* wait a little while to see if we get a machine check */ + __delay(200); + n = size; + } + debugger_fault_handler = 0; + return n; +} + +int +mwrite(unsigned long adrs, void *buf, int size) +{ + volatile int n; + char *p, *q; + + n = 0; + if( setjmp(bus_error_jmp) == 0 ){ + debugger_fault_handler = handle_fault; + sync(); + p = (char *) adrs; + q = (char *) buf; + switch (size) { + case 2: *(short *)p = *(short *)q; break; + case 4: *(int *)p = *(int *)q; break; + default: + for( ; n < size; ++n ) { + *p++ = *q++; + sync(); + } + } + sync(); + /* wait a little while to see if we get a machine check */ + __delay(200); + n = size; + } else { + printf("*** Error writing address %x\n", adrs + n); + } + debugger_fault_handler = 0; + return n; +} + +static int fault_type; +static char *fault_chars[] = { "--", "**", "##" }; + +static void +handle_fault(struct pt_regs *regs) +{ + fault_type = regs->trap == 0x200? 0: regs->trap == 0x300? 1: 2; + longjmp(bus_error_jmp, 1); +} + +#define SWAP(a, b, t) ((t) = (a), (a) = (b), (b) = (t)) + +void +byterev(unsigned char *val, int size) +{ + int t; + + switch (size) { + case 2: + SWAP(val[0], val[1], t); + break; + case 4: + SWAP(val[0], val[3], t); + SWAP(val[1], val[2], t); + break; + case 8: /* is there really any use for this? */ + SWAP(val[0], val[7], t); + SWAP(val[1], val[6], t); + SWAP(val[2], val[5], t); + SWAP(val[3], val[4], t); + break; + } +} + +static int brev; +static int mnoread; + +static char *memex_help_string = + "Memory examine command usage:\n" + "m [addr] [flags] examine/change memory\n" + " addr is optional. will start where left off.\n" + " flags may include chars from this set:\n" + " b modify by bytes (default)\n" + " w modify by words (2 byte)\n" + " l modify by longs (4 byte)\n" + " d modify by doubleword (8 byte)\n" + " r toggle reverse byte order mode\n" + " n do not read memory (for i/o spaces)\n" + " . ok to read (default)\n" + "NOTE: flags are saved as defaults\n" + ""; + +static char *memex_subcmd_help_string = + "Memory examine subcommands:\n" + " hexval write this val to current location\n" + " 'string' write chars from string to this location\n" + " ' increment address\n" + " ^ decrement address\n" + " / increment addr by 0x10. //=0x100, ///=0x1000, etc\n" + " \\ decrement addr by 0x10. \\\\=0x100, \\\\\\=0x1000, etc\n" + " ` clear no-read flag\n" + " ; stay at this addr\n" + " v change to byte mode\n" + " w change to word (2 byte) mode\n" + " l change to long (4 byte) mode\n" + " u change to doubleword (8 byte) mode\n" + " m addr change current addr\n" + " n toggle no-read flag\n" + " r toggle byte reverse flag\n" + " < count back up count bytes\n" + " > count skip forward count bytes\n" + " x exit this mode\n" + ""; + +void +memex() +{ + int cmd, inc, i, nslash; + unsigned long n; + unsigned char val[16]; + + scanhex((void *)&adrs); + cmd = skipbl(); + if (cmd == '?') { + printf(memex_help_string); + return; + } else { + termch = cmd; + } + last_cmd = "m\n"; + while ((cmd = skipbl()) != '\n') { + switch( cmd ){ + case 'b': size = 1; break; + case 'w': size = 2; break; + case 'l': size = 4; break; + case 'd': size = 8; break; + case 'r': brev = !brev; break; + case 'n': mnoread = 1; break; + case '.': mnoread = 0; break; + } + } + if( size <= 0 ) + size = 1; + else if( size > 8 ) + size = 8; + for(;;){ + if (!mnoread) + n = mread(adrs, val, size); + printf("%.16x%c", adrs, brev? 'r': ' '); + if (!mnoread) { + if (brev) + byterev(val, size); + putchar(' '); + for (i = 0; i < n; ++i) + printf("%.2x", val[i]); + for (; i < size; ++i) + printf("%s", fault_chars[fault_type]); + } + putchar(' '); + inc = size; + nslash = 0; + for(;;){ + if( scanhex(&n) ){ + for (i = 0; i < size; ++i) + val[i] = n >> (i * 8); + if (!brev) + byterev(val, size); + mwrite(adrs, val, size); + inc = size; + } + cmd = skipbl(); + if (cmd == '\n') + break; + inc = 0; + switch (cmd) { + case '\'': + for(;;){ + n = inchar(); + if( n == '\\' ) + n = bsesc(); + else if( n == '\'' ) + break; + for (i = 0; i < size; ++i) + val[i] = n >> (i * 8); + if (!brev) + byterev(val, size); + mwrite(adrs, val, size); + adrs += size; + } + adrs -= size; + inc = size; + break; + case ',': + adrs += size; + break; + case '.': + mnoread = 0; + break; + case ';': + break; + case 'x': + case EOF: + scannl(); + return; + case 'b': + case 'v': + size = 1; + break; + case 'w': + size = 2; + break; + case 'l': + size = 4; + break; + case 'u': + size = 8; + break; + case '^': + adrs -= size; + break; + break; + case '/': + if (nslash > 0) + adrs -= 1 << nslash; + else + nslash = 0; + nslash += 4; + adrs += 1 << nslash; + break; + case '\\': + if (nslash < 0) + adrs += 1 << -nslash; + else + nslash = 0; + nslash -= 4; + adrs -= 1 << -nslash; + break; + case 'm': + scanhex((void *)&adrs); + break; + case 'n': + mnoread = 1; + break; + case 'r': + brev = !brev; + break; + case '<': + n = size; + scanhex(&n); + adrs -= n; + break; + case '>': + n = size; + scanhex(&n); + adrs += n; + break; + case '?': + printf(memex_subcmd_help_string); + break; + } + } + adrs += inc; + } +} + +int +bsesc() +{ + int c; + + c = inchar(); + switch( c ){ + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 'b': c = '\b'; break; + case 't': c = '\t'; break; + } + return c; +} + +#define isxdigit(c) (('0' <= (c) && (c) <= '9') \ + || ('a' <= (c) && (c) <= 'f') \ + || ('A' <= (c) && (c) <= 'F')) +void +dump() +{ + int c; + + c = inchar(); + if ((isxdigit(c) && c != 'f' && c != 'd') || c == '\n') + termch = c; + scanhex((void *)&adrs); + if( termch != '\n') + termch = 0; + if( c == 'i' ){ + scanhex(&nidump); + if( nidump == 0 ) + nidump = 16; + adrs += ppc_inst_dump(adrs, nidump); + last_cmd = "di\n"; + } else { + scanhex(&ndump); + if( ndump == 0 ) + ndump = 64; + prdump(adrs, ndump); + adrs += ndump; + last_cmd = "d\n"; + } +} + +void +prdump(unsigned long adrs, long ndump) +{ + long n, m, c, r, nr; + unsigned char temp[16]; + + for( n = ndump; n > 0; ){ + printf("%.16lx", adrs); + putchar(' '); + r = n < 16? n: 16; + nr = mread(adrs, temp, r); + adrs += nr; + for( m = 0; m < r; ++m ){ + if ((m & 7) == 0 && m > 0) + putchar(' '); + if( m < nr ) + printf("%.2x", temp[m]); + else + printf("%s", fault_chars[fault_type]); + } + for(; m < 16; ++m ) + printf(" "); + printf(" |"); + for( m = 0; m < r; ++m ){ + if( m < nr ){ + c = temp[m]; + putchar(' ' <= c && c <= '~'? c: '.'); + } else + putchar(' '); + } + n -= r; + for(; m < 16; ++m ) + putchar(' '); + printf("|\n"); + if( nr < r ) + break; + } +} + +int +ppc_inst_dump(unsigned long adr, long count) +{ + int nr, dotted; + unsigned long first_adr; + unsigned long inst, last_inst; + unsigned char val[4]; + + dotted = 0; + for (first_adr = adr; count > 0; --count, adr += 4){ + nr = mread(adr, val, 4); + if( nr == 0 ){ + const char *x = fault_chars[fault_type]; + printf("%.16lx %s%s%s%s\n", adr, x, x, x, x); + break; + } + inst = GETWORD(val); + if (adr > first_adr && inst == last_inst) { + if (!dotted) { + printf(" ...\n"); + dotted = 1; + } + continue; + } + dotted = 0; + last_inst = inst; + printf("%.16lx ", adr); + printf("%.8x\t", inst); + print_insn_big_powerpc(stdout, inst, adr); /* always returns 4 */ + printf("\n"); + } + return adr - first_adr; +} + +void +print_address(unsigned long addr) +{ + printf("0x%lx", addr); +} + +/* + * Memory operations - move, set, print differences + */ +static unsigned long mdest; /* destination address */ +static unsigned long msrc; /* source address */ +static unsigned long mval; /* byte value to set memory to */ +static unsigned long mcount; /* # bytes to affect */ +static unsigned long mdiffs; /* max # differences to print */ + +void +memops(int cmd) +{ + scanhex((void *)&mdest); + if( termch != '\n' ) + termch = 0; + scanhex((void *)(cmd == 's'? &mval: &msrc)); + if( termch != '\n' ) + termch = 0; + scanhex((void *)&mcount); + switch( cmd ){ + case 'm': + memmove((void *)mdest, (void *)msrc, mcount); + break; + case 's': + memset((void *)mdest, mval, mcount); + break; + case 'd': + if( termch != '\n' ) + termch = 0; + scanhex((void *)&mdiffs); + memdiffs((unsigned char *)mdest, (unsigned char *)msrc, mcount, mdiffs); + break; + } +} + +void +memdiffs(unsigned char *p1, unsigned char *p2, unsigned nb, unsigned maxpr) +{ + unsigned n, prt; + + prt = 0; + for( n = nb; n > 0; --n ) + if( *p1++ != *p2++ ) + if( ++prt <= maxpr ) + printf("%.16x %.2x # %.16x %.2x\n", p1 - 1, + p1[-1], p2 - 1, p2[-1]); + if( prt > maxpr ) + printf("Total of %d differences\n", prt); +} + +static unsigned mend; +static unsigned mask; + +void +memlocate() +{ + unsigned a, n; + unsigned char val[4]; + + last_cmd = "ml"; + scanhex((void *)&mdest); + if (termch != '\n') { + termch = 0; + scanhex((void *)&mend); + if (termch != '\n') { + termch = 0; + scanhex((void *)&mval); + mask = ~0; + if (termch != '\n') termch = 0; + scanhex((void *)&mask); + } + } + n = 0; + for (a = mdest; a < mend; a += 4) { + if (mread(a, val, 4) == 4 + && ((GETWORD(val) ^ mval) & mask) == 0) { + printf("%.16x: %.16x\n", a, GETWORD(val)); + if (++n >= 10) + break; + } + } +} + +static unsigned long mskip = 0x1000; +static unsigned long mlim = 0xffffffff; + +void +memzcan() +{ + unsigned char v; + unsigned a; + int ok, ook; + + scanhex(&mdest); + if (termch != '\n') termch = 0; + scanhex(&mskip); + if (termch != '\n') termch = 0; + scanhex(&mlim); + ook = 0; + for (a = mdest; a < mlim; a += mskip) { + ok = mread(a, &v, 1); + if (ok && !ook) { + printf("%.8x .. ", a); + fflush(stdout); + } else if (!ok && ook) + printf("%.8x\n", a - mskip); + ook = ok; + if (a + mskip < a) + break; + } + if (ook) + printf("%.8x\n", a - mskip); +} + +/* Input scanning routines */ +int +skipbl() +{ + int c; + + if( termch != 0 ){ + c = termch; + termch = 0; + } else + c = inchar(); + while( c == ' ' || c == '\t' ) + c = inchar(); + return c; +} + +int +scanhex(vp) +unsigned long *vp; +{ + int c, d; + unsigned long v; + + c = skipbl(); + d = hexdigit(c); + if( d == EOF ){ + termch = c; + return 0; + } + v = 0; + do { + v = (v << 4) + d; + c = inchar(); + d = hexdigit(c); + } while( d != EOF ); + termch = c; + *vp = v; + return 1; +} + +void +scannl() +{ + int c; + + c = termch; + termch = 0; + while( c != '\n' ) + c = inchar(); +} + +int +hexdigit(c) +{ + if( '0' <= c && c <= '9' ) + return c - '0'; + if( 'A' <= c && c <= 'F' ) + return c - ('A' - 10); + if( 'a' <= c && c <= 'f' ) + return c - ('a' - 10); + return EOF; +} + +void +getstring(char *s, int size) +{ + int c; + + c = skipbl(); + do { + if( size > 1 ){ + *s++ = c; + --size; + } + c = inchar(); + } while( c != ' ' && c != '\t' && c != '\n' ); + termch = c; + *s = 0; +} + +static char line[256]; +static char *lineptr; + +void +flush_input() +{ + lineptr = NULL; +} + +int +inchar() +{ + if (lineptr == NULL || *lineptr == 0) { + if (fgets(line, sizeof(line), stdin) == NULL) { + lineptr = NULL; + return EOF; + } + lineptr = line; + } + return *lineptr++; +} + +void +take_input(str) +char *str; +{ + lineptr = str; +} + +/*static char *pretty_print_addr(unsigned long addr) --Unused + *{ + * printf("%08x", addr); + * if ( lookup_name(addr) ) + * printf(" %s", lookup_name(addr) ); + * return NULL; + *} + */ +static char *lookup_name(unsigned long addr) +{ +return NULL; +#if 0 + /* XXX crap, we don't want the whole of the rest of the map - paulus */ + extern char *sysmap; + extern unsigned long sysmap_size; + char *c = sysmap; + unsigned long cmp; + + if ( !sysmap || !sysmap_size ) + return NULL; + cmp = simple_strtoul(c, &c, 8); + strcpy( last, strsep( &c, "\n")); + while ( c < (sysmap+sysmap_size) ) + { + cmp = simple_strtoul(c, &c, 8); + if ( cmp < addr ) + break; + strcpy( last, strsep( &c, "\n")); + } + return last; +#endif +} + +/* Starting at codeaddr scan forward for a tbtable and fill in the + given table. Return non-zero if successful at doing something. + */ +static int +find_tb_table(unsigned long codeaddr, struct tbtable *tab) +{ + unsigned long codeaddr_max; + unsigned long tbtab_start; + int nr; + int instr; + int num_parms; + + if (tab == NULL) + return 0; + memset(tab, 0, sizeof(tab)); + + /* Scan instructions starting at codeaddr for 128k max */ + for (codeaddr_max = codeaddr + 128*1024*4; + codeaddr < codeaddr_max; + codeaddr += 4) { + nr = mread(codeaddr, &instr, 4); + if (nr != 4) + return 0; /* Bad read. Give up promptly. */ + if (instr == 0) { + /* table should follow. */ + int version; + unsigned long flags; + tbtab_start = codeaddr; /* save it to compute func start addr */ + codeaddr += 4; + nr = mread(codeaddr, &flags, 8); + if (nr != 8) + return 0; /* Bad read or no tb table. */ + tab->flags = flags; + version = (flags >> 56) & 0xff; + if (version != 0) + continue; /* No tb table here. */ + /* Now, like the version, some of the flags are values + that are more conveniently extracted... */ + tab->fp_saved = (flags >> 24) & 0x3f; + tab->gpr_saved = (flags >> 16) & 0x3f; + tab->fixedparms = (flags >> 8) & 0xff; + tab->floatparms = (flags >> 1) & 0x7f; + codeaddr += 8; + num_parms = tab->fixedparms + tab->floatparms; + if (num_parms) { + unsigned int parminfo; + int parm; + if (num_parms > 32) + return 1; /* incomplete */ + nr = mread(codeaddr, &parminfo, 4); + if (nr != 4) + return 1; /* incomplete */ + /* decode parminfo...32 bits. + A zero means fixed. A one means float and the + following bit determines single (0) or double (1). + */ + for (parm = 0; parm < num_parms; parm++) { + if (parminfo & 0x80000000) { + parminfo <<= 1; + if (parminfo & 0x80000000) + tab->parminfo[parm] = TBTAB_PARMDFLOAT; + else + tab->parminfo[parm] = TBTAB_PARMSFLOAT; + } else { + tab->parminfo[parm] = TBTAB_PARMFIXED; + } + parminfo <<= 1; + } + codeaddr += 4; + } + if (flags & TBTAB_FLAGSHASTBOFF) { + nr = mread(codeaddr, &tab->tb_offset, 4); + if (nr != 4) + return 1; /* incomplete */ + if (tab->tb_offset > 0) { + tab->funcstart = tbtab_start - tab->tb_offset; + } + codeaddr += 4; + } + /* hand_mask appears to be always be omitted. */ + if (flags & TBTAB_FLAGSHASCTL) { + /* Assume this will never happen for C or asm */ + return 1; /* incomplete */ + } + if (flags & TBTAB_FLAGSNAMEPRESENT) { + short namlen; + nr = mread(codeaddr, &namlen, 2); + if (nr != 2) + return 1; /* incomplete */ + if (namlen >= sizeof(tab->name)) + namlen = sizeof(tab->name)-1; + codeaddr += 2; + nr = mread(codeaddr, tab->name, namlen); + tab->name[namlen] = '\0'; + codeaddr += namlen; + } + return 1; + } + } + return 0; /* hit max...sorry. */ +} + +void +mem_translate() { + int c; + unsigned long ea, va, vsid, vpn, page, hpteg_slot_primary, hpteg_slot_secondary, primary_hash, i, *steg, esid, stabl; + HPTE * hpte; + HPTEX * hptex; + struct mm_struct * mm; + pte_t *ptep = NULL; + void * pgdir; + + c = inchar(); + if ((isxdigit(c) && c != 'f' && c != 'd') || c == '\n') + termch = c; + scanhex((void *)&ea); + + if ( (( ea >= KRANGE_START ) && ( ea <= (KRANGE_START + (1UL<<60) ))) || + (( ea >= BTMALLOC_START) && ( ea <= BTMALLOC_END)) ) { + ptep = 0; + vsid = get_kernel_vsid(ea); + va = ( vsid << 28 ) | ( ea & 0x0fffffff ); + } else { + // if in vmalloc range, use the vmalloc page directory + if ( ( ea >= VMALLOC_START ) && ( ea <= VMALLOC_END ) ) { + mm = &init_mm; + vsid = get_kernel_vsid( ea ); + } + // if in ioremap range, use the ioremap page directory + else if ( ( ea >= IMALLOC_START ) && ( ea <= IMALLOC_END ) ) { + mm = &ioremap_mm; + vsid = get_kernel_vsid( ea ); + } + // if in user range, use the current task's page directory + else if ( ( ea >= USER_START ) && ( ea <= USER_END ) ) { + mm = current->mm; + vsid = get_vsid(mm->context, ea ); + } + pgdir = mm->pgd; + va = ( vsid << 28 ) | ( ea & 0x0fffffff ); + ptep = find_linux_pte( pgdir, ea ); + } + + vpn = ((vsid << 28) | (((ea) & 0xFFFF000))) >> 12; + page = vpn & 0xffff; + esid = (ea >> 28) & 0xFFFFFFFFF; + + // Search the primary group for an available slot + primary_hash = ( vsid & 0x7fffffffff ) ^ page; + hpteg_slot_primary = ( primary_hash & htab_data.htab_hash_mask ) * HPTES_PER_GROUP; + hpteg_slot_secondary = ( ~primary_hash & htab_data.htab_hash_mask ) * HPTES_PER_GROUP; + + printf("ea : %.16lx\n", ea); + printf("esid : %.16lx\n", esid); + printf("vsid : %.16lx\n", vsid); + + printf("\nSoftware Page Table\n-------------------\n"); + printf("ptep : %.16lx\n", ((unsigned long *)ptep)); + if(ptep) { + printf("*ptep : %.16lx\n", *((unsigned long *)ptep)); + } + + hpte = htab_data.htab + hpteg_slot_primary; + hptex = htab_data.htabx + hpteg_slot_primary; + printf("\nHardware Page Table\n-------------------\n"); + printf("htab base : %.16lx\n", htab_data.htab); + printf("htabx base : %.16lx\n", htab_data.htabx); + printf("slot primary : %.16lx\n", hpteg_slot_primary); + printf("slot secondary : %.16lx\n", hpteg_slot_secondary); + printf("\nPrimary Group\n"); + for (i=0; i<8; ++i) { + if ( hptex->dw1.dw1.flags.valid != 0 ) { + printf("%d: (hpte)%.16lx %.16lx\n", i, hpte->dw0.dword0, hpte->dw1.dword1); + printf(" vsid: %.13lx api: %.2lx hash: %.1lx\n", + (hpte->dw0.dw0.avpn)>>5, + (hpte->dw0.dw0.avpn) & 0x1f, + (hpte->dw0.dw0.h)); + printf(" rpn: %.13lx \n", (hpte->dw1.dw1.rpn)); + printf(" pp: %.1lx \n", + ((hpte->dw1.dw1.pp0)<<2)|(hpte->dw1.dw1.pp)); + printf(" wimgn: %.2lx reference: %.1lx change: %.1lx\n", + ((hpte->dw1.dw1.w)<<4)| + ((hpte->dw1.dw1.i)<<3)| + ((hpte->dw1.dw1.m)<<2)| + ((hpte->dw1.dw1.g)<<1)| + ((hpte->dw1.dw1.n)<<0), + hpte->dw1.dw1.r, hpte->dw1.dw1.c); + printf(" (hptex)%.16lx %.16lx\n", hptex->linux_pte, hptex->dw1.dword1); + } + hptex++; + hpte++; + } + + printf("\nSecondary Group\n"); + // Search the secondary group + hpte = htab_data.htab + hpteg_slot_secondary; + hptex = htab_data.htabx + hpteg_slot_secondary; + for (i=0; i<8; ++i) { + if(hptex->dw1.dw1.flags.valid) { + printf("%d: (hpte)%.16lx %.16lx\n", i, hpte->dw0.dword0, hpte->dw1.dword1); + printf(" vsid: %.13lx api: %.2lx hash: %.1lx\n", + (hpte->dw0.dw0.avpn)>>5, + (hpte->dw0.dw0.avpn) & 0x1f, + (hpte->dw0.dw0.h)); + printf(" rpn: %.13lx \n", (hpte->dw1.dw1.rpn)); + printf(" pp: %.1lx \n", + ((hpte->dw1.dw1.pp0)<<2)|(hpte->dw1.dw1.pp)); + printf(" wimgn: %.2lx reference: %.1lx change: %.1lx\n", + ((hpte->dw1.dw1.w)<<4)| + ((hpte->dw1.dw1.i)<<3)| + ((hpte->dw1.dw1.m)<<2)| + ((hpte->dw1.dw1.g)<<1)| + ((hpte->dw1.dw1.n)<<0), + hpte->dw1.dw1.r, hpte->dw1.dw1.c); + printf(" (hptex)%.16lx %.16lx\n", hptex->linux_pte, hptex->dw1.dword1); + } + hptex++; + hpte++; + } + + printf("\nHardware Segment Table\n-----------------------\n"); + stabl = (unsigned long)(KERNELBASE+(_ASR&0xFFFFFFFFFFFFFFFE)); + steg = (unsigned long *)((stabl) | ((esid & 0x1f) << 7)); + + printf("stab base : %.16lx\n", stabl); + printf("slot : %.16lx\n", steg); + + for (i=0; i<8; ++i) { + printf("%d: (ste) %.16lx %.16lx\n", i, + *((unsigned long *)(steg+i*2)),*((unsigned long *)(steg+i*2+1)) ); + } +} + +void mem_check() { + unsigned long htab_size_bytes; + unsigned long htab_end, htabx_end; + unsigned long last_rpn; + HPTE *hpte1, *hpte2; + HPTEX *hptex1; /* *hptex2; --Unused */ + unsigned long linux_pte; + + htab_size_bytes = htab_data.htab_num_ptegs * 128; // 128B / PTEG + htab_end = (unsigned long)htab_data.htab + htab_size_bytes; + // last_rpn = (naca->physicalMemorySize-1) >> PAGE_SHIFT; + last_rpn = 0xfffff; + + printf("\nHardware Page Table Check\n-------------------\n"); + printf("htab base : %.16lx\n", htab_data.htab); + printf("htab size : %.16lx\n", htab_size_bytes); + + htabx_end = (unsigned long)htab_data.htabx + htab_size_bytes; + for(hptex1 = htab_data.htabx; hptex1 < htabx_end; hptex1++) { + if (hptex1->dw1.dw1.flags.valid) { + if( hptex1->linux_pte ) { + linux_pte = *((unsigned long *)(hptex1->linux_pte)); + if((linux_pte & _PAGE_HPTE) && + ((linux_pte >> PAGE_SHIFT) == (hptex1 - htab_data.htabx))) { + // Everything looks ok - + } else { + printf("linux pte should have a hptex entry!\n"); + printf(" linux pte = %.16lx\n", linux_pte); + printf(" htabx = %.16lx\n", hptex1); + printf(" htabx->lpte = %.16lx\n", hptex1->linux_pte); + printf("__pa(htabx->lpte) = %.16lx\n", __pa(hptex1->linux_pte)); + printf(" htabx->flags = %.16lx\n", hptex1->dw1.dword1); + printf(" htpe = %16.16lx %16.16lx\n", + (hptex1 - htab_data.htabx + htab_data.htab)->dw0.dword0, + (hptex1 - htab_data.htabx + htab_data.htab)->dw1.dword1); + } + } + } + } +#if 1 + for(hpte1 = htab_data.htab; hpte1 < htab_end; hpte1++) { + if ( hpte1->dw0.dw0.v != 0 ) { + if ( hpte1->dw1.dw1.rpn <= last_rpn ) { + for(hpte2 = hpte1+1; hpte2 < htab_end; hpte2++) { + if ( hpte2->dw0.dw0.v != 0 ) { + if(hpte1->dw1.dw1.rpn == hpte2->dw1.dw1.rpn) { + printf(" Duplicate rpn: %.13lx \n", (hpte1->dw1.dw1.rpn)); + printf(" hpte1: %16.16lx *hpte1: %16.16lx %16.16lx\n", + hpte1, hpte1->dw0.dword0, hpte1->dw1.dword1); + printf(" hpte2: %16.16lx *hpte2: %16.16lx %16.16lx\n", + hpte2, hpte2->dw0.dword0, hpte2->dw1.dword1); + } + } + } + } else { + printf(" Bogus rpn: %.13lx \n", (hpte1->dw1.dw1.rpn)); + printf(" hpte: %16.16lx *hpte: %16.16lx %16.16lx\n", + hpte1, hpte1->dw0.dword0, hpte1->dw1.dword1); + } + } + } +#endif + printf("\nDone -------------------\n"); +} + +void mem_find_real() { + unsigned long htab_size_bytes; + unsigned long htab_end; + unsigned long last_rpn; + HPTE *hpte1; + unsigned long pa, rpn; + int c; + + c = inchar(); + if ((isxdigit(c) && c != 'f' && c != 'd') || c == '\n') + termch = c; + scanhex((void *)&pa); + rpn = pa >> 12; + + htab_size_bytes = htab_data.htab_num_ptegs * 128; // 128B / PTEG + htab_end = (unsigned long)htab_data.htab + htab_size_bytes; + // last_rpn = (naca->physicalMemorySize-1) >> PAGE_SHIFT; + last_rpn = 0xfffff; + + printf("\nMem Find RPN\n-------------------\n"); + printf("htab base : %.16lx\n", htab_data.htab); + printf("htab size : %.16lx\n", htab_size_bytes); + + for(hpte1 = htab_data.htab; hpte1 < htab_end; hpte1++) { + if ( hpte1->dw0.dw0.v != 0 ) { + if ( hpte1->dw1.dw1.rpn == rpn ) { + printf(" Found rpn: %.13lx \n", (hpte1->dw1.dw1.rpn)); + printf(" hpte: %16.16lx *hpte1: %16.16lx %16.16lx\n", + hpte1, hpte1->dw0.dword0, hpte1->dw1.dword1); + } + } + } + printf("\nDone -------------------\n"); +} + +void mem_find_vsid() { + unsigned long htab_size_bytes; + unsigned long htab_end; + HPTE *hpte1; + unsigned long vsid; + int c; + + c = inchar(); + if ((isxdigit(c) && c != 'f' && c != 'd') || c == '\n') + termch = c; + scanhex((void *)&vsid); + + htab_size_bytes = htab_data.htab_num_ptegs * 128; // 128B / PTEG + htab_end = (unsigned long)htab_data.htab + htab_size_bytes; + + printf("\nMem Find VSID\n-------------------\n"); + printf("htab base : %.16lx\n", htab_data.htab); + printf("htab size : %.16lx\n", htab_size_bytes); + + for(hpte1 = htab_data.htab; hpte1 < htab_end; hpte1++) { + if ( hpte1->dw0.dw0.v != 0 ) { + if ( ((hpte1->dw0.dw0.avpn)>>5) == vsid ) { + printf(" Found vsid: %.16lx \n", ((hpte1->dw0.dw0.avpn) >> 5)); + printf(" hpte: %16.16lx *hpte1: %16.16lx %16.16lx\n", + hpte1, hpte1->dw0.dword0, hpte1->dw1.dword1); + } + } + } + printf("\nDone -------------------\n"); +} + +void mem_map_check_slab() { + int i, slab_count; + + i = max_mapnr; + slab_count = 0; + + while (i-- > 0) { + if (PageSlab(mem_map+i)){ + printf(" slab entry - mem_map entry =%p \n", mem_map+i); + slab_count ++; + } + + } + + printf(" count of pages for slab = %d \n", slab_count); + + +} + +void mem_map_lock_pages() { + int i, lock_count; + + i = max_mapnr; + lock_count = 0; + + while (i-- > 0) { + if (PageLocked(mem_map+i)){ + printf(" locked entry - mem_map entry =%p \n", mem_map+i); + lock_count ++; + } + + } + + printf(" count of locked pages = %d \n", lock_count); + + +} + + + +void mem_map_check_hash() { + int i; + + i = max_mapnr; + + + while (i-- > 0) { + // skip the reserved + if (!PageReserved(mem_map+i)) + { + + if (((mem_map+i)->next_hash) != NULL){ + if (((unsigned long)((mem_map+i)->next_hash) & 0xf000000000000000) != 0xC000000000000000){ + printf(" mem_map check hash - non c0 entry - address/value = %p %lx \n", + mem_map+i,(mem_map+i)->next_hash); + } + if (((mem_map+i)->next_hash) == 0xC000000000000000){ + printf(" mem_map check hash - 0xC000000000000000 entry = %p \n", mem_map+i); + } + } + } + else + { + if (page_count(mem_map+i) < 0) + { + printf(" reserved page with negative count- entry = %lx \n", mem_map + i); + } + } + + } + + printf(" mem_map check hash completed \n"); + +} + + +void mem_check_zones() +{ + pg_data_t *pgdat; + unsigned long order; + unsigned type; + int i, mem_map_zone_count, mem_map_zone_flag_zero_count, mem_map_reserved_count; + int mem_map_zone_count_reserved, mem_map_zone_flag_nonzero_count; + + pgdat = pgdat_list; + + + printf(" nr_inactive_clean_pages = %lx, nr_free_pages = %lx \n", + nr_inactive_clean_pages(), + nr_free_pages() + ); + + for (type = 0; type < MAX_NR_ZONES; type++) { + struct list_head *head, *curr; + zone_t *zone = pgdat->node_zones + type; + unsigned long nr, total, flags; + + + printf("zone info zone = %p free pages = %lx \n", + zone, zone->free_pages); + printf(" inactive_clean_pages = %lx \n", zone -> inactive_clean_pages); + printf(" inactive_dirty_pages = %lx \n", zone -> inactive_dirty_pages); + printf(" pages_min = %lx \n", zone -> pages_min); + printf(" pages_low = %lx \n" , zone -> pages_low); + printf(" pages_high = %lx \n" , zone -> pages_high); + + total = 0; + if (zone->size) { + spin_lock_irqsave(&zone->lock, flags); + for (order = 0; order < MAX_ORDER; order++) { + head = &(zone->free_area + order)->free_list; + curr = head; + nr = 0; + printf(" pages on free list for order = %d \n", order); + for (;;) { + curr = memlist_next(curr); + + if (curr == head) + break; + printf(" page = %lx flags= %lx \n", curr, ((struct page *)curr)->flags); + nr++; + // add code to check zone map to assure pages are free + } + total += nr * (1 << order); + printf(" entries of order = %d order size in pages = %lx \n", order, 1< 0) { + // skip the reserved + if (!PageReserved(mem_map+i)) + { + if (((mem_map+i)->zone) == zone){ + mem_map_zone_count ++; + if (((mem_map+i)->flags) == 0){ + mem_map_zone_flag_zero_count ++; + + } + else + { + mem_map_zone_flag_nonzero_count ++; + } + + } + // all non reserved pages should be in zone 0 + if (((mem_map+i)->zone) != pgdat->node_zones){ + printf( " page in wrong zone - entry = %p zone = %lx \n", mem_map+i,(mem_map+i)->zone); + } + + } + else + { + mem_map_reserved_count ++; + if (((mem_map+i)->zone) == zone){ + mem_map_zone_count_reserved ++; + } + + } + } + + + printf(" mem_map zone= %p mem_map count for zone = %lx",zone, mem_map_zone_count); + printf(" mem_map_flag_zero_count = %lx\n",mem_map_zone_flag_zero_count); + printf(" mem_map_flag_nonzero_count = %lx\n",mem_map_zone_flag_nonzero_count); + printf(" mem_map_reserved_count = %lx\n",mem_map_reserved_count); + printf(" mem_map_zone_count_reserved = %lx\n",mem_map_zone_count_reserved); + + + spin_unlock_irqrestore(&zone->lock, flags); + } + + } + + + +} + + +void mem_check_dup_rpn () { + unsigned long htab_size_bytes; + unsigned long htab_end, htabx_end; + unsigned long last_rpn; + HPTE *hpte1, *hpte2; + HPTEX *hptex1; + unsigned long linux_pte; + int dup_count; + struct task_struct *p; + unsigned long kernel_vsid_c0,kernel_vsid_c1,kernel_vsid_c2,kernel_vsid_c3; + unsigned long kernel_vsid_c4,kernel_vsid_c5,kernel_vsid_d,kernel_vsid_e; + unsigned long kernel_vsid_f; + unsigned long vsid0,vsid1,vsidB,vsid2; + + htab_size_bytes = htab_data.htab_num_ptegs * 128; // 128B / PTEG + htab_end = (unsigned long)htab_data.htab + htab_size_bytes; + // last_rpn = (naca->physicalMemorySize-1) >> PAGE_SHIFT; + last_rpn = 0xfffff; + + printf("\nHardware Page Table Check\n-------------------\n"); + printf("htab base : %.16lx\n", htab_data.htab); + printf("htab size : %.16lx\n", htab_size_bytes); + + htabx_end = (unsigned long)htab_data.htabx + htab_size_bytes; + for(hptex1 = htab_data.htabx; hptex1 < htabx_end; hptex1++) { + if (hptex1->dw1.dw1.flags.valid) { + if( hptex1->linux_pte ) { + linux_pte = *((unsigned long *)(hptex1->linux_pte)); + if((linux_pte & _PAGE_HPTE) && + ((linux_pte >> PAGE_SHIFT) == (hptex1 - htab_data.htabx))) { + // Everything looks ok - + } else { + printf("linux pte should have a hptex entry!\n"); + printf(" linux pte = %.16lx\n", linux_pte); + printf(" htabx = %.16lx\n", hptex1); + printf(" htabx->lpte = %.16lx\n", hptex1->linux_pte); + printf("__pa(htabx->lpte) = %.16lx\n", __pa(hptex1->linux_pte)); + printf(" htabx->flags = %.16lx\n", hptex1->dw1.dword1); + printf(" htpe = %16.16lx %16.16lx\n", + (hptex1 - htab_data.htabx + htab_data.htab)->dw0.dword0, + (hptex1 - htab_data.htabx + htab_data.htab)->dw1.dword1); + } + } + } + } + + for(hpte1 = htab_data.htab; hpte1 < htab_end; hpte1++) { + if ( hpte1->dw0.dw0.v != 0 ) { + if ( hpte1->dw1.dw1.rpn <= last_rpn ) { + dup_count = 0; + for(hpte2 = hpte1+1; hpte2 < htab_end; hpte2++) { + if ( hpte2->dw0.dw0.v != 0 ) { + if(hpte1->dw1.dw1.rpn == hpte2->dw1.dw1.rpn) { + dup_count++; + } + } + } + if(dup_count > 5) { + printf(" Duplicate rpn: %.13lx \n", (hpte1->dw1.dw1.rpn)); + printf(" mem map array entry %p count = %d \n", + (mem_map+(hpte1->dw1.dw1.rpn)), (mem_map+(hpte1->dw1.dw1.rpn))->count); + for(hpte2 = hpte1+1; hpte2 < htab_end; hpte2++) { + if ( hpte2->dw0.dw0.v != 0 ) { + if(hpte1->dw1.dw1.rpn == hpte2->dw1.dw1.rpn) { + printf(" hpte2: %16.16lx *hpte2: %16.16lx %16.16lx\n", + hpte2, hpte2->dw0.dword0, hpte2->dw1.dword1); + } + } + } + } + } else { + printf(" Bogus rpn: %.13lx \n", (hpte1->dw1.dw1.rpn)); + printf(" hpte: %16.16lx *hpte: %16.16lx %16.16lx\n", + hpte1, hpte1->dw0.dword0, hpte1->dw1.dword1); + } + } + if (xmon_interrupted()) + return; + } + + + + // print the kernel vsids + kernel_vsid_c0 = get_kernel_vsid(0xC000000000000000); + kernel_vsid_c1 = get_kernel_vsid(0xC000000010000000); + kernel_vsid_c2 = get_kernel_vsid(0xC000000020000000); + kernel_vsid_c3 = get_kernel_vsid(0xC000000030000000); + kernel_vsid_c4 = get_kernel_vsid(0xC000000040000000); + kernel_vsid_c5 = get_kernel_vsid(0xC000000050000000); + kernel_vsid_d = get_kernel_vsid(0xD000000000000000); + kernel_vsid_e = get_kernel_vsid(0xE000000000000000); + kernel_vsid_f = get_kernel_vsid(0xF000000000000000); + + printf(" kernel vsid - seg c0 = %lx\n", kernel_vsid_c0 ); + printf(" kernel vsid - seg c1 = %lx\n", kernel_vsid_c1 ); + printf(" kernel vsid - seg c2 = %lx\n", kernel_vsid_c2 ); + printf(" kernel vsid - seg c3 = %lx\n", kernel_vsid_c3 ); + printf(" kernel vsid - seg c4 = %lx\n", kernel_vsid_c4 ); + printf(" kernel vsid - seg c5 = %lx\n", kernel_vsid_c5 ); + printf(" kernel vsid - seg d = %lx\n", kernel_vsid_d ); + printf(" kernel vsid - seg e = %lx\n", kernel_vsid_e ); + printf(" kernel vsid - seg f = %lx\n", kernel_vsid_f ); + + + // print a list of valid vsids for the tasks + read_lock(&tasklist_lock); + for_each_task(p) + if(p->mm) { + struct mm_struct *mm = p->mm; + printf(" task = %p mm = %lx pgd %lx\n", + p, mm, mm->pgd); + vsid0 = get_vsid( mm->context, 0 ); + vsid1 = get_vsid( mm->context, 0x10000000 ); + vsid2 = get_vsid( mm->context, 0x20000000 ); + vsidB = get_vsid( mm->context, 0xB0000000 ); + printf(" context = %lx vsid seg 0 = %lx\n", mm->context, vsid0 ); + printf(" vsid seg 1 = %lx\n", vsid1 ); + printf(" vsid seg 2 = %lx\n", vsid2 ); + printf(" vsid seg 2 = %lx\n", vsidB ); + + printf("\n"); + }; + read_unlock(&tasklist_lock); + + + printf("\nDone -------------------\n"); + +} + + + +void mem_check_pagetable_vsids () { + unsigned long htab_size_bytes; + unsigned long htab_end, htabx_end; + HPTEX *hptex1; + unsigned long last_rpn; + struct task_struct *p; + unsigned long valid_table_count,invalid_table_count,bogus_rpn_count; + int found; + unsigned long user_address_table_count,kernel_page_table_count; + unsigned long pt_vsid; + unsigned long linux_pte; + HPTE *hpte1; + + + htab_size_bytes = htab_data.htab_num_ptegs * 128; // 128B / PTEG + htab_end = (unsigned long)htab_data.htab + htab_size_bytes; + // last_rpn = (naca->physicalMemorySize-1) >> PAGE_SHIFT; + last_rpn = 0xfffff; + + printf("\nHardware Page Table Check\n-------------------\n"); + printf("htab base : %.16lx\n", htab_data.htab); + printf("htab size : %.16lx\n", htab_size_bytes); + + htabx_end = (unsigned long)htab_data.htabx + htab_size_bytes; + + valid_table_count = 0; + invalid_table_count = 0; + bogus_rpn_count = 0; + user_address_table_count = 0; + kernel_page_table_count = 0; + hptex1 = htab_data.htabx; + + + for(hpte1 = htab_data.htab; hpte1 < htab_end; hpte1++,hptex1++) { + if ( hpte1->dw0.dw0.v != 0 ) { + valid_table_count++; + if ( hpte1->dw1.dw1.rpn <= last_rpn ) { + pt_vsid = (hpte1->dw0.dw0.avpn) >> 5; + if ((pt_vsid == get_kernel_vsid(0xC000000000000000)) | + (pt_vsid == get_kernel_vsid(0xC000000010000000)) | + (pt_vsid == get_kernel_vsid(0xC000000020000000)) | + (pt_vsid == get_kernel_vsid(0xC000000030000000)) | + (pt_vsid == get_kernel_vsid(0xC000000040000000)) | + (pt_vsid == get_kernel_vsid(0xC000000050000000)) | + (pt_vsid == get_kernel_vsid(0xD000000000000000)) | + (pt_vsid == get_kernel_vsid(0xE000000000000000)) | + (pt_vsid == get_kernel_vsid(0xF000000000000000)) ) + + { + kernel_page_table_count ++; + } + else + { + read_lock(&tasklist_lock); + found = 0; + for_each_task(p) { + + if(p->mm && (found == 0)) { + struct mm_struct *mm = p->mm; + + if ((pt_vsid == get_vsid( mm->context, 0 )) | + (pt_vsid == get_vsid( mm->context, 0x10000000 )) | + (pt_vsid == get_vsid( mm->context, 0x20000000 )) | + (pt_vsid == get_vsid( mm->context, 0x30000000 )) | + (pt_vsid == get_vsid( mm->context, 0x40000000 )) | + (pt_vsid == get_vsid( mm->context, 0x50000000 )) | + (pt_vsid == get_vsid( mm->context, 0x60000000 )) | + (pt_vsid == get_vsid( mm->context, 0x70000000 )) | + (pt_vsid == get_vsid( mm->context, 0x80000000 )) | + (pt_vsid == get_vsid( mm->context, 0x90000000 )) | + (pt_vsid == get_vsid( mm->context, 0xA0000000 )) | + (pt_vsid == get_vsid( mm->context, 0xB0000000 ))) + { + user_address_table_count ++; + found = 1; + } + } + } + read_unlock(&tasklist_lock); + if (found == 0) + { + printf(" vsid not found vsid = %lx, hpte = %p \n", + pt_vsid,hpte1); + printf(" rpn in entry = %lx \n", hpte1->dw1.dw1.rpn); + printf(" mem map address = %lx \n", mem_map + (hpte1->dw1.dw1.rpn)); + + printf(" page table extension address = %lx \n",hptex1); + if (hptex1->dw1.dw1.flags.valid) + { + if( hptex1->linux_pte ) + { + linux_pte = *((unsigned long *)(hptex1->linux_pte)); + if((linux_pte & _PAGE_HPTE) && + ((linux_pte >> PAGE_SHIFT) == (hptex1 - htab_data.htabx))) + { + printf("linux pte matches and valid \n"); + printf(" linux pte = %lx \n",linux_pte); + + } + else // invalid linux pte + { + printf("linux pte should have a hptex entry!\n"); + printf(" linux pte = %lx\n", linux_pte); + printf(" htabx = %lx\n", hptex1); + printf(" htabx->lpte = %lx\n", hptex1->linux_pte); + + printf(" htabx->flags = %lx\n", hptex1->dw1.dword1); + + } + } + else // no linux pte + { + printf(" no linux pte in the extension \n"); + } + } + else // extension not valid + { + printf(" extension not valid \n"); + } + + + } + else // found + { + } + + } // good rpn + + } + else + { + bogus_rpn_count ++; + + } + } + else + { + invalid_table_count++; + } + } + + + printf(" page table valid counts - valid entries = %lx invalid entries = %lx \n", + valid_table_count, invalid_table_count); + + printf(" bogus rpn entries ( probably io) = %lx \n", bogus_rpn_count); + + + + printf(" page table counts - kernel entries = %lx user entries = %lx \n", + kernel_page_table_count, user_address_table_count); + + printf("\nDone -------------------\n"); + +} + +void mem_check_pagetable_extension_valid () { + unsigned long htab_size_bytes; + unsigned long htab_end, htabx_end; + HPTEX *hptex1; + unsigned long last_rpn; + + unsigned long entry_miscompare,both_entries_valid,both_entries_invalid; + + + HPTE *hpte1; + + + htab_size_bytes = htab_data.htab_num_ptegs * 128; // 128B / PTEG + htab_end = (unsigned long)htab_data.htab + htab_size_bytes; + // last_rpn = (naca->physicalMemorySize-1) >> PAGE_SHIFT; + last_rpn = 0xfffff; + + printf("\nHardware Page Table Check\n-------------------\n"); + printf("htab base : %.16lx\n", htab_data.htab); + printf("htab size : %.16lx\n", htab_size_bytes); + + htabx_end = (unsigned long)htab_data.htabx + htab_size_bytes; + + entry_miscompare = 0; + both_entries_invalid = 0; + both_entries_valid = 0; + + + hptex1 = htab_data.htabx; + for(hpte1 = htab_data.htab; hpte1 < htab_end; hpte1++,hptex1++) { + if ( hpte1->dw0.dw0.v != 0 ) + { + if (hptex1 -> dw1.dw1.flags.valid) + { + both_entries_valid ++; + } + else + { + // + printf(" hardware page table valid but extension not valid %lx %lx \n", + hpte1,hptex1); + entry_miscompare++; + + } + } + else // hpte invalid + { + if (hptex1 -> dw1.dw1.flags.valid) + { + // + printf(" hardware page table not valid but extension valid %lx %lx \n", + hpte1,hptex1); + entry_miscompare ++; + + } + else + { + both_entries_invalid++; + + } + + } + + + } + + printf(" both entries valid = %lx \n", both_entries_valid); + printf(" both entries invalid = %lx \n", both_entries_invalid); + printf(" entry micompares = %lx \n", entry_miscompare); +} + + + + + + + + + + +void mem_check_full_group() { + unsigned long htab_size_bytes; + unsigned count; + unsigned count_array[] = {0,0,0,0,0,0,0,0,0}; + unsigned i; + unsigned long htab_end; + HPTE *hpte1, *hpte2, *hpte3; + u64 rpn = 0; + + htab_size_bytes = htab_data.htab_num_ptegs * 128; // 128B / PTEG + htab_end = (unsigned long)htab_data.htab + htab_size_bytes; + + printf("\nHardware Page Find full groups \n-------------------\n"); + printf("htab base : %.16lx\n", htab_data.htab); + printf("htab size : %.16lx\n", htab_size_bytes); + + for (hpte1 = htab_data.htab; (unsigned long)hpte1 < htab_end; hpte1= hpte1 + 8) + { + count = 0; + hpte2 = hpte1; + for (i=0; i<8; ++i) + { + if ( hpte2->dw0.dw0.v != 0 ) + { + count++; + } + hpte2++; + } + if (count == 8 ) + { + printf(" full group starting with entry %lx \n", hpte1); + hpte3 = hpte1; + for (i=0; i<8; ++i) + { + if ( hpte3->dw0.dw0.v != 0 ) + { + printf(" entry number %d \n",i); + printf(" vsid: %.13lx api: %.2lx hash: %.1lx\n", + (hpte3->dw0.dw0.avpn)>>5, + (hpte3->dw0.dw0.avpn) & 0x1f, + (hpte3->dw0.dw0.h)); + printf(" rpn: %.13lx \n", (hpte3->dw1.dw1.rpn)); + // Dump out the memmap array entry address, corresponding virtual address, and reference count. + rpn = hpte3->dw1.dw1.rpn; + printf(" mem_map+rpn=%p, virtual@=%p, count=%lx \n", mem_map+rpn, (mem_map+rpn)->virtual, (mem_map+rpn)->count); + } + hpte3++; + } + if (xmon_interrupted()) + return; + } + + count_array[count]++; + } + for (i=1; i<9; ++i) + { + printf(" group count for size %i = %lx \n", i, count_array[i]); + } + + printf("\nDone -------------------\n"); +} + + + +static void show_task(struct task_struct * p) +{ + /* unsigned long free = 0; --Unused */ + int state; + static const char * stat_nam[] = { "R", "S", "D", "Z", "T", "W" }; + + printf("--------------------------------------------------------------------------\n"); + printf("%-11.11s pid: %5.5lx ppid: %5.5lx state: ", + p->comm, p->pid, p->p_pptr->pid); + state = p->state ? ffz(~p->state) + 1 : 0; + if (((unsigned) state) < sizeof(stat_nam)/sizeof(char *)) + printf(stat_nam[state]); + else + printf(" "); + if (p == current) + printf(" pc: current task "); + else + printf(" pc: 0x%16.16lx ", thread_saved_pc(&p->thread)); + + if (p->p_cptr) + printf("%5d ", p->p_cptr->pid); + else + printf(" "); + if (!p->mm) + printf(" (L-TLB) "); + else + printf(" (NOTLB) "); + if (p->p_ysptr) + printf("%7d", p->p_ysptr->pid); + else + printf(" "); + if (p->p_osptr) + printf(" %5d\n", p->p_osptr->pid); + else + printf("\n"); + + { + struct sigqueue *q; + char s[sizeof(sigset_t)*2+1], b[sizeof(sigset_t)*2+1]; + + render_sigset_t(&p->pending.signal, s); + render_sigset_t(&p->blocked, b); + printf(" sig: %d %s %s :", signal_pending(p), s, b); + for (q = p->pending.head; q ; q = q->next) + printf(" %d", q->info.si_signo); + printf(" X\n"); + } + + printf(" pers : %lx current : %lx", + p->personality, p); + printf("\n"); + + printf(" thread : 0x%16.16lx ksp : 0x%16.16lx\n", + &(p->thread), (p->thread.ksp)); + printf(" pgdir : 0x%16.16lx\n", (p->thread.pgdir)); + printf(" regs : 0x%16.16lx sysc : 0x%16.16lx\n", + (p->thread.regs), (p->thread.last_syscall)); + if(p->thread.regs) { + printf(" nip : 0x%16.16lx msr : 0x%16.16lx\n", + ((p->thread.regs)->nip), ((p->thread.regs)->msr)); + printf(" ctr : 0x%16.16lx link : 0x%16.16lx\n", + ((p->thread.regs)->ctr), ((p->thread.regs)->link)); + printf(" xer : 0x%16.16lx ccr : 0x%16.16lx\n", + ((p->thread.regs)->xer), ((p->thread.regs)->ccr)); + printf(" mq : 0x%16.16lx trap : 0x%16.16lx\n", + ((p->thread.regs)->mq), ((p->thread.regs)->trap)); + printf(" dar : 0x%16.16lx dsis : 0x%16.16lx\n", + ((p->thread.regs)->dar), ((p->thread.regs)->dsisr)); + printf(" rslt : 0x%16.16lx org3 : 0x%16.16lx\n", + ((p->thread.regs)->result), (p->thread.regs->orig_gpr3)); + } + + if(p->mm) { + struct mm_struct *mm = p->mm; + printf(" mm : 0x%16.16lx pgd : 0x%16.16lx\n", + mm, mm->pgd); + printf(" context: 0x%16.16lx mmap : 0x%16.16lx\n", + mm->context, mm->mmap); + + printf("\n"); + } + +} + +static void show_state(void) +{ + struct task_struct *p; + +#if (BITS_PER_LONG == 32) + printf("\n" + " free sibling\n"); + printf("task name st PC stack pid father child younger older\n"); +#else + printf("\n" + " free sibling\n"); + printf(" task PC stack pid father child younger older\n"); +#endif + read_lock(&tasklist_lock); + for_each_task(p) + show_task(p); + read_unlock(&tasklist_lock); +} + +static void debug_trace(void) { + unsigned long val, cmd, on; + + cmd = skipbl(); + if (cmd == '\n') { + /* show current state */ + unsigned long i; + printf("naca->debug_switch = 0x%lx\n", naca->debug_switch); + for (i = 0; i < PPCDBG_NUM_FLAGS ;i++) { + on = PPCDBG_BITVAL(i) & naca->debug_switch; + printf("%02x %s %12s ", i, on ? "on " : "off", trace_names[i] ? trace_names[i] : ""); + if (((i+1) % 3) == 0) + printf("\n"); + } + printf("\n"); + return; + } + while (cmd != '\n') { + on = 1; /* default if no sign given */ + while (cmd == '+' || cmd == '-') { + on = (cmd == '+'); + cmd = inchar(); + if (cmd == ' ' || cmd == '\n') { /* Turn on or off based on + or - */ + naca->debug_switch = on ? PPCDBG_ALL:PPCDBG_NONE; + printf("Setting all values to %s...\n", on ? "on" : "off"); + if (cmd == '\n') return; + else cmd = skipbl(); + } + else + termch = cmd; + } + termch = cmd; /* not +/- ... let scanhex see it */ + scanhex((void *)&val); + if (val >= 64) { + printf("Value %x out of range:\n", val); + return; + } + if (on) { + naca->debug_switch |= PPCDBG_BITVAL(val); + printf("enable debug %x %s\n", val, trace_names[val] ? trace_names[val] : ""); + } else { + naca->debug_switch &= ~PPCDBG_BITVAL(val); + printf("disable debug %x %s\n", val, trace_names[val] ? trace_names[val] : ""); + } + cmd = skipbl(); + } +} + + + diff -uNr --exclude=CVS linux-2.4.5-ac18/drivers/block/viodasd.c linuxppc64_2_4/drivers/block/viodasd.c --- linux-2.4.5-ac18/drivers/block/viodasd.c Tue Jun 26 21:59:15 2001 +++ linuxppc64_2_4/drivers/block/viodasd.c Fri May 4 17:12:54 2001 @@ -49,17 +49,14 @@ #define do_viodasd_request do_hd_request static int numdsk = 16; static int viodasd_max_disk = 16; -#define VIOD_DEVICE_NAME "hd" #else #define MAJOR_NR VIODASD_MAJOR -#define PARTITION_SHIFT 3 -static int numdsk = 32; -static int viodasd_max_disk = 32; -#define VIOD_DEVICE_NAME "viod" +#define PARTITION_SHIFT 2 +static int numdsk = 64; +static int viodasd_max_disk = 64; #endif - -#define VIODASD_VERS "1.02" +#define VIODASD_VERS "1.01" #define LOCAL_END_REQUEST @@ -250,7 +247,7 @@ ***************************************************************************/ static void viodasd_end_request(struct request *req, int uptodate) { - if (end_that_request_first(req, uptodate, VIOD_DEVICE_NAME)) + if (end_that_request_first(req, uptodate, DEVICE_NAME)) return; end_that_request_last(req); @@ -279,7 +276,12 @@ if (viodasd_partitions[minor].nr_sects != 0) { devp = MKDEV(MAJOR_NR, minor); - invalidate_device(devp, 1); + fsync_dev(devp); + + sb = get_super(devp); + if (sb) invalidate_inodes(sb); + + invalidate_buffers(devp); } viodasd_partitions[minor].start_sect = 0; @@ -322,7 +324,7 @@ HvLpEvent_AckType_ImmediateAck, viopath_sourceinst(viopath_hostLp), viopath_targetinst(viopath_hostLp), - (u64)(u32)&we, + (u64)(unsigned long)&we, VIOVERSION << 16, ((u64)device_no << 48), 0, 0, 0); @@ -549,7 +551,7 @@ HvLpEvent_AckType_ImmediateAck, viopath_sourceinst(viopath_hostLp), viopath_targetinst(viopath_hostLp), - (u64)(u32)&Semaphore, + (u64)(unsigned long)&Semaphore, VIOVERSION << 16, ((u64)device_no << 48), 0, 0, 0); @@ -749,7 +751,7 @@ /* Update stats */ viod_stats[device_no][statindex].ntce[sgindex]++; - +#if 0 /* This optimization handles a single DMA block */ if (sgindex == 1) { @@ -761,7 +763,7 @@ HvLpEvent_AckType_ImmediateAck, viopath_sourceinst(viopath_hostLp), viopath_targetinst(viopath_hostLp), - (u64)(u32)req->buffer, + (u64)(unsigned long)req->buffer, VIOVERSION << 16, ((u64)device_no << 48), start, @@ -769,6 +771,7 @@ sg[0].dma_length); } else +#endif { /* Now build up the actual request. Note that we store the pointer */ /* to the request buffer in the correlation token so we can match */ @@ -782,13 +785,15 @@ bevent->event.xSubtype = viocmd; bevent->event.xSourceLp = HvLpConfig_getLpIndex(); bevent->event.xTargetLp = viopath_hostLp; +#if 0 bevent->event.xSizeMinus1 = offsetof(struct vioblocklpevent, u.rwData.dmaInfo) + (sizeof(bevent->u.rwData.dmaInfo[0])*(sgindex))-1; +#endif bevent->event.xSizeMinus1 = sizeof(struct vioblocklpevent)-1; bevent->event.xSourceInstanceId = viopath_sourceinst(viopath_hostLp); bevent->event.xTargetInstanceId = viopath_targetinst(viopath_hostLp); - bevent->event.xCorrelationToken = (u64)(u32)req->buffer; + bevent->event.xCorrelationToken = (u64)(unsigned long)req->buffer; bevent->mVersion = VIOVERSION; bevent->mDisk = device_no; bevent->u.rwData.mOffset = start; @@ -875,9 +880,11 @@ ***************************************************************************/ static int viodasd_check_change(kdev_t dev) { +#if 0 struct viodasd_waitevent we; HvLpEvent_Rc hvrc; int device_no = DEVICE_NR(dev); +#endif /* This semaphore is raised in the interrupt handler */ DECLARE_MUTEX_LOCKED(Semaphore); @@ -889,6 +896,8 @@ return -EIO; } + return 0; +#if 0 we.sem = &Semaphore; @@ -900,7 +909,7 @@ HvLpEvent_AckType_ImmediateAck, viopath_sourceinst(viopath_hostLp), viopath_targetinst(viopath_hostLp), - (u64)(u32)&we, + (u64)(unsigned long)&we, VIOVERSION << 16, ((u64)device_no << 48), 0, 0, 0); @@ -922,6 +931,7 @@ } return we.changed; +#endif } /*************************************************************************** @@ -991,7 +1001,7 @@ * in the waitevent structure and post the semaphore to wake up the guy who * sent the request */ case vioblockopen: - pwe = (struct viodasd_waitevent *)(u32)event->xCorrelationToken; + pwe = (struct viodasd_waitevent *)(unsigned long)event->xCorrelationToken; pwe->rc = event->xRc; if (event->xRc == HvLpEvent_Rc_Good) { @@ -1054,14 +1064,14 @@ /* Since this is running in interrupt mode, we need to make sure we're not * stepping on any global I/O operations */ - spin_lock_irqsave(&io_request_lock, flags); + spin_lock_irqsave(&io_request_lock, flags); /* Now find the matching request in OUR list (remember we moved the request * from the global list to our list when we got it) */ req = blkdev_entry_to_request(reqlist.next); while ((&req->queue != &reqlist) && - ((u64)(u32)req->buffer != bevent->event.xCorrelationToken)) + ((u64)(unsigned long)req->buffer != bevent->event.xCorrelationToken)) req = blkdev_entry_to_request(req->queue.next); if (&req->queue == &reqlist) @@ -1126,11 +1136,11 @@ break; case vioblockflush: - up((void*)(u32)event->xCorrelationToken); + up((void*)(unsigned long)event->xCorrelationToken); break; case vioblockcheck: - pwe = (struct viodasd_waitevent *)(u32)event->xCorrelationToken; + pwe = (struct viodasd_waitevent *)(unsigned long)event->xCorrelationToken; pwe->rc = event->xRc; pwe->changed = bevent->u.check.changed; up(pwe->sem); @@ -1169,7 +1179,7 @@ blk_size[MAJOR_NR] = NULL; blksize_size[MAJOR_NR] = NULL; - devfs_unregister_blkdev(MAJOR_NR,VIOD_DEVICE_NAME); + devfs_unregister_blkdev(MAJOR_NR,DEVICE_NAME); } /*************************************************************************** @@ -1188,7 +1198,7 @@ */ if (viopath_hostLp == HvLpIndexInvalid) { - err_printk("%s: invalid hosting partition\n", VIOD_DEVICE_NAME); + err_printk("%s: invalid hosting partition\n", DEVICE_NAME); return -1; } @@ -1196,14 +1206,14 @@ * Do the devfs_register. This seems to work even if devfs is not * configured */ - if (devfs_register_blkdev(MAJOR_NR,VIOD_DEVICE_NAME,&viodasd_fops)) { + if (devfs_register_blkdev(MAJOR_NR,DEVICE_NAME,&viodasd_fops)) { err_printk("%s: unable to get major number %d\n", - VIOD_DEVICE_NAME,MAJOR_NR); + DEVICE_NAME,MAJOR_NR); return -1; } printk("%s: iSeries Virtual DASD vers %s, major %d, max disks %d, hosting partition %d\n", - VIOD_DEVICE_NAME,VIODASD_VERS,MAJOR_NR,numdsk,viopath_hostLp); + DEVICE_NAME,VIODASD_VERS,MAJOR_NR,numdsk,viopath_hostLp); /* Do the blk device initialization */ blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST); @@ -1212,7 +1222,7 @@ /* Start filling in gendsk structure */ viodasd_gendsk.major = MAJOR_NR; - viodasd_gendsk.major_name = VIOD_DEVICE_NAME; + viodasd_gendsk.major_name = DEVICE_NAME; viodasd_gendsk.nr_real = numdsk; viodasd_gendsk.next = gendisk_head; gendisk_head = &viodasd_gendsk; @@ -1228,7 +1238,7 @@ else { printk("%s: opened path to hosting partition %d\n", - VIOD_DEVICE_NAME, + DEVICE_NAME, viopath_hostLp); } @@ -1283,7 +1293,7 @@ { if (i == 0) printk("%s: Currently %d disks connected\n", - VIOD_DEVICE_NAME, + DEVICE_NAME, (int)viodasd_max_disk+1); register_disk(&viodasd_gendsk, @@ -1293,7 +1303,7 @@ viodasd_partitions[i<vm_start; i < vma->vm_end; i += PAGE_SIZE) { pgd = pgd_offset(vma->vm_mm, i); pmd = pmd_offset(pgd, i); - pte = pte_offset(pmd, i); - if (pte_present(*pte)) { - address = __pa(pte_page(*pte)) + ptep = pte_offset(pmd, i); + pte = fetch_pte(ptep); + if (pte_present(pte)) { + address = __pa(pte_page(pte)) + (i & (PAGE_SIZE-1)); DRM_PROC_PRINT(" 0x%08lx -> 0x%08lx" " %c%c%c%c%c\n", diff -uNr --exclude=CVS linux-2.4.5-ac18/drivers/char/n_tty.c linuxppc64_2_4/drivers/char/n_tty.c --- linux-2.4.5-ac18/drivers/char/n_tty.c Tue Jun 26 21:55:39 2001 +++ linuxppc64_2_4/drivers/char/n_tty.c Wed Jun 6 18:47:40 2001 @@ -538,7 +538,7 @@ * handle specially, do shortcut processing to speed things * up. */ - if (!test_bit(c, &tty->process_char_map) || tty->lnext) { + if (!test_bit(c, tty->process_char_map) || tty->lnext) { finish_erasing(tty); tty->lnext = 0; if (L_ECHO(tty)) { @@ -659,7 +659,7 @@ handle_newline: spin_lock_irqsave(&tty->read_lock, flags); - set_bit(tty->read_head, &tty->read_flags); + set_bit(tty->read_head, tty->read_flags); put_tty_queue_nolock(c, tty); tty->canon_head = tty->read_head; tty->canon_data++; @@ -811,38 +811,38 @@ memset(tty->process_char_map, 0, 256/8); if (I_IGNCR(tty) || I_ICRNL(tty)) - set_bit('\r', &tty->process_char_map); + set_bit('\r', tty->process_char_map); if (I_INLCR(tty)) - set_bit('\n', &tty->process_char_map); + set_bit('\n', tty->process_char_map); if (L_ICANON(tty)) { - set_bit(ERASE_CHAR(tty), &tty->process_char_map); - set_bit(KILL_CHAR(tty), &tty->process_char_map); - set_bit(EOF_CHAR(tty), &tty->process_char_map); - set_bit('\n', &tty->process_char_map); - set_bit(EOL_CHAR(tty), &tty->process_char_map); + set_bit(ERASE_CHAR(tty), tty->process_char_map); + set_bit(KILL_CHAR(tty), tty->process_char_map); + set_bit(EOF_CHAR(tty), tty->process_char_map); + set_bit('\n', tty->process_char_map); + set_bit(EOL_CHAR(tty), tty->process_char_map); if (L_IEXTEN(tty)) { set_bit(WERASE_CHAR(tty), - &tty->process_char_map); + tty->process_char_map); set_bit(LNEXT_CHAR(tty), - &tty->process_char_map); + tty->process_char_map); set_bit(EOL2_CHAR(tty), - &tty->process_char_map); + tty->process_char_map); if (L_ECHO(tty)) set_bit(REPRINT_CHAR(tty), - &tty->process_char_map); + tty->process_char_map); } } if (I_IXON(tty)) { - set_bit(START_CHAR(tty), &tty->process_char_map); - set_bit(STOP_CHAR(tty), &tty->process_char_map); + set_bit(START_CHAR(tty), tty->process_char_map); + set_bit(STOP_CHAR(tty), tty->process_char_map); } if (L_ISIG(tty)) { - set_bit(INTR_CHAR(tty), &tty->process_char_map); - set_bit(QUIT_CHAR(tty), &tty->process_char_map); - set_bit(SUSP_CHAR(tty), &tty->process_char_map); + set_bit(INTR_CHAR(tty), tty->process_char_map); + set_bit(QUIT_CHAR(tty), tty->process_char_map); + set_bit(SUSP_CHAR(tty), tty->process_char_map); } - clear_bit(__DISABLED_CHAR, &tty->process_char_map); + clear_bit(__DISABLED_CHAR, tty->process_char_map); sti(); tty->raw = 0; tty->real_raw = 0; @@ -1058,7 +1058,7 @@ int eol; eol = test_and_clear_bit(tty->read_tail, - &tty->read_flags); + tty->read_flags); c = tty->read_buf[tty->read_tail]; spin_lock_irqsave(&tty->read_lock, flags); tty->read_tail = ((tty->read_tail+1) & diff -uNr --exclude=CVS linux-2.4.5-ac18/drivers/char/sysrq.c linuxppc64_2_4/drivers/char/sysrq.c --- linux-2.4.5-ac18/drivers/char/sysrq.c Tue Jun 26 21:59:16 2001 +++ linuxppc64_2_4/drivers/char/sysrq.c Tue Jun 26 13:10:03 2001 @@ -51,14 +51,13 @@ console_loglevel = 7; printk("%d\n", i); console_loglevel = i; -} +} static struct sysrq_key_op sysrq_loglevel_op = { - handler: sysrq_handle_loglevel, - help_msg: "loglevel0-8", - action_msg: "Loglevel set to ", + handler: sysrq_handle_loglevel, + help_msg: "loglevel0-8", + action_msg: "Loglevel set to ", }; - /* SAK sysrq handler */ #ifdef CONFIG_VT static void sysrq_handle_SAK(int key, struct pt_regs *pt_regs, @@ -68,13 +67,12 @@ reset_vc(fg_console); } static struct sysrq_key_op sysrq_SAK_op = { - handler: sysrq_handle_SAK, - help_msg: "saK", - action_msg: "SAK\n", + handler: sysrq_handle_SAK, + help_msg: "saK", + action_msg: "SAK\n", }; #endif - /* unraw sysrq handler */ static void sysrq_handle_unraw(int key, struct pt_regs *pt_regs, struct kbd_struct *kbd, struct tty_struct *tty) { @@ -82,9 +80,9 @@ kbd->kbdmode = VC_XLATE; } static struct sysrq_key_op sysrq_unraw_op = { - handler: sysrq_handle_unraw, - help_msg: "unRaw", - action_msg: "Keyboard mode set to XLATE\n", + handler: sysrq_handle_unraw, + help_msg: "unRaw", + action_msg: "Keyboard mode set to XLATE\n", }; @@ -94,12 +92,10 @@ machine_restart(NULL); } static struct sysrq_key_op sysrq_reboot_op = { - handler: sysrq_handle_reboot, - help_msg: "reBoot", - action_msg: "Resetting\n", + handler: sysrq_handle_reboot, + help_msg: "reBoot", + action_msg: "Resetting\n", }; - - /* SYNC SYSRQ HANDLERS BLOCK */ diff -uNr --exclude=CVS linux-2.4.5-ac18/drivers/char/viocons.c linuxppc64_2_4/drivers/char/viocons.c --- linux-2.4.5-ac18/drivers/char/viocons.c Tue Jun 26 21:59:16 2001 +++ linuxppc64_2_4/drivers/char/viocons.c Fri May 4 17:12:58 2001 @@ -29,13 +29,11 @@ #include #include #include -#include #include #include #include #include #include -#include #ifndef _VIO_H #include "asm/iSeries/vio.h" @@ -56,30 +54,19 @@ #include #endif -/*************************************************************************** - * Check that the tty_driver_data actually points to our stuff - ***************************************************************************/ -#define VIOTTY_PARANOIA_CHECK 1 -#define VIOTTY_MAGIC (0x0DCB) - static int debug; static DECLARE_WAIT_QUEUE_HEAD(viocons_wait_queue); -static int viotty_major = 229; - -#define VTTY_PORTS 10 - -static u64 sndMsgSeq[VTTY_PORTS]; -static u64 sndMsgAck[VTTY_PORTS]; +static int viotty_major = 100; +static u64 sndMsgSeq[HVMAXARCHITECTEDLPS]; +static u64 sndMsgAck[HVMAXARCHITECTEDLPS]; +static u64 rcvMsgSeq[HVMAXARCHITECTEDLPS] __attribute__ ((unused)); +static u64 rcvMsgAck[HVMAXARCHITECTEDLPS] __attribute__ ((unused)); +static HvLpIndex consLpTarget; static spinlock_t consolelock; -/*************************************************************************** - * When we get writes faster than we can send it to the partition, - * buffer the data here. There is one set of buffers for each virtual - * port. - ***************************************************************************/ /* Note that bufferUsed is a bit map of used buffers. * It had better have enough bits to hold NUM_BUF * the bitops assume it is a multiple of unsigned long @@ -94,71 +81,29 @@ int curbuf; int bufferOverflow; int overflowMessage; -} overflow[VTTY_PORTS]; +} overflow[HVMAXARCHITECTEDLPS]; -static void initDataEvent(struct viocharlpevent *viochar, HvLpIndex lp); +static void initDataEvent(struct viocharlpevent *viochar); +/* + * The serial driver boot-time initialization code! + */ static struct tty_driver viotty_driver; static int viotty_refcount; +#define VTTY_PORTS 1 + static struct tty_struct *viotty_table[VTTY_PORTS]; static struct termios *viotty_termios[VTTY_PORTS]; static struct termios *viotty_termios_locked[VTTY_PORTS]; -static void hvlog(char *fmt, ...) -{ - int i; - static char buf[256]; - va_list args; - va_start(args, fmt); - i = vsprintf(buf, fmt, args); - va_end(args); - HvCall_writeLogBuffer(buf, i); - HvCall_writeLogBuffer("\r",1); - -} - -/*************************************************************************** - * Our port information. We store a pointer to one entry in the - * tty_driver_data - ***************************************************************************/ -static struct port_info_tag { - int magic; - int count; - struct tty_struct *tty; - HvLpIndex lp; - u8 vcons; - u8 port; -} port_info[VTTY_PORTS]; - -/*************************************************************************** - * Make sure we're pointing to a valid port_info structure. Shamelessly - * plagerized from serial.c - ***************************************************************************/ -static inline int viotty_paranoia_check(struct port_info_tag *pi, - kdev_t device, const char *routine) +static void bufferInit(void) { -#ifdef VIOTTY_PARANOIA_CHECK - static const char *badmagic = - "Warning: bad magic number for port_info struct (%s) in %s\n"; - static const char *badinfo = - "Warning: null port_info for (%s) in %s\n"; - - if (!pi) { - printk(badinfo, kdevname(device), routine); - return 1; - } - if (pi->magic != VIOTTY_MAGIC) { - printk(badmagic, kdevname(device), routine); - return 1; - } -#endif - return 0; + memset(overflow,0x00,sizeof(overflow)); } /*************************************************************************** - * Handle reads from the proc file system. Right now we just dump the - * state of the first TTY + * Handle reads from the proc file system ***************************************************************************/ static int proc_read(char *buf, char **start, off_t offset, int blen, int *eof, void *data) @@ -264,12 +209,12 @@ } /*************************************************************************** - * Handle writes to our proc file system. Right now just turns on and off - * our debug flag + * Handle writes to our proc file system ***************************************************************************/ static int proc_write(struct file *file, const char *buffer, unsigned long count, void *data) { + struct tty_struct *tty = viotty_table[0]; if (count) { if (buffer[0] == '1') @@ -277,6 +222,12 @@ printk("viocons: debugging on\n"); debug = 1; } + else if (buffer[0] == '2') + { + printk("viocons: setting some tty stuff\n"); + tty->termios->c_cc[VLNEXT] = 0x16; + tty->termios->c_oflag &= ~ONLCR; + } else { printk("viocons: debugging off\n"); @@ -308,10 +259,7 @@ remove_proc_entry("viocons", iSeries_proc); } -/*************************************************************************** - * Add data to our pending-send buffers - ***************************************************************************/ -static int bufferAdd(u8 port, const char *buf, size_t len, int userFlag) +static int bufferAdd(HvLpIndex lp, const char *buf, size_t len, int userFlag) { size_t bleft = len; size_t curlen; @@ -322,117 +270,87 @@ { /* * If there is no space left in the current buffer, we have - * filled everything up, so return. If we filled the previous - * buffer we would already have moved to the next one. + * filled everything up, so return */ - if (overflow[port].bufferBytes[overflow[port].curbuf] == OVERFLOW_SIZE) + if (overflow[lp].bufferBytes[overflow[lp].curbuf] == OVERFLOW_SIZE) { - hvlog("buffer %d full. no more space\n",overflow[port].curbuf); - overflow[port].bufferOverflow++; - overflow[port].overflowMessage = 1; + if (debug) printk("buffer %d full. no more space\n",overflow[lp].curbuf); + overflow[lp].bufferOverflow++; + overflow[lp].overflowMessage = 1; return len - bleft; } - /* - * Turn on the "used" bit for this buffer. If it's already on, that's - * fine. - */ - set_bit(overflow[port].curbuf,&overflow[port].bufferUsed); + set_bit(overflow[lp].curbuf,&overflow[lp].bufferUsed); - /* - * See if this buffer has been allocated. If not, allocate it - */ - if (overflow[port].buffer[overflow[port].curbuf] == NULL) - overflow[port].buffer[overflow[port].curbuf] = + if (overflow[lp].buffer[overflow[lp].curbuf] == NULL) + overflow[lp].buffer[overflow[lp].curbuf] = kmalloc(OVERFLOW_SIZE, GFP_ATOMIC); - /* - * Figure out how much we can copy into this buffer - */ - if (bleft < (OVERFLOW_SIZE - overflow[port].bufferBytes[overflow[port].curbuf])) + if (bleft < (OVERFLOW_SIZE - overflow[lp].bufferBytes[overflow[lp].curbuf])) curlen = bleft; else - curlen = OVERFLOW_SIZE - overflow[port].bufferBytes[overflow[port].curbuf]; + curlen = OVERFLOW_SIZE - overflow[lp].bufferBytes[overflow[lp].curbuf]; - /* - * Copy the data into the buffer - */ if (userFlag) - copy_from_user(overflow[port].buffer[overflow[port].curbuf]+ - overflow[port].bufferBytes[overflow[port].curbuf],cbuf,curlen); + copy_from_user(overflow[lp].buffer[overflow[lp].curbuf]+ + overflow[lp].bufferBytes[overflow[lp].curbuf],cbuf,curlen); else - memcpy(overflow[port].buffer[overflow[port].curbuf]+ - overflow[port].bufferBytes[overflow[port].curbuf],cbuf,curlen); + memcpy(overflow[lp].buffer[overflow[lp].curbuf]+ + overflow[lp].bufferBytes[overflow[lp].curbuf],cbuf,curlen); - overflow[port].bufferBytes[overflow[port].curbuf] += curlen; + overflow[lp].bufferBytes[overflow[lp].curbuf] += curlen; cbuf += curlen; bleft -= curlen; + /* * Now see if we've filled this buffer */ - if (overflow[port].bufferBytes[overflow[port].curbuf] == VIOCHAR_MAX_DATA) + if (overflow[lp].bufferBytes[overflow[lp].curbuf] == VIOCHAR_MAX_DATA) { - nextbuf = (overflow[port].curbuf+1)%NUM_BUF; + nextbuf = (overflow[lp].curbuf+1)%NUM_BUF; /* * Move to the next buffer if it hasn't been used yet */ - if (test_bit(nextbuf,&overflow[port].bufferUsed) == 0) + if (test_bit(nextbuf,&overflow[lp].bufferUsed) == 0) { - overflow[port].curbuf = nextbuf; + overflow[lp].curbuf = nextbuf; } } } return len; } -/*************************************************************************** - * Send pending data - ***************************************************************************/ -void sendBuffers(u8 port, HvLpIndex lp) +void sendBuffers(HvLpIndex lp, struct tty_struct *tty) { HvLpEvent_Rc hvrc; int nextbuf; struct viocharlpevent *viochar = (struct viocharlpevent *)vio_char_event_buffer; int flags; - spin_lock_irqsave(&consolelock, - flags); - - if (overflow[port].bufferUsed == 0) + if (overflow[lp].bufferUsed == 0) { if (debug) printk("in sendbuffers, but no buffers used\n"); - spin_unlock_irqrestore(&consolelock, - flags); - return; } - /* - * curbuf points to the buffer we're filling. We want to start sending AFTER - * this one. - */ - nextbuf = (overflow[port].curbuf + 1) % NUM_BUF; - - /* - * Loop until we find a buffer with the bufferUsed bit on - */ - while (test_bit(nextbuf,&overflow[port].bufferUsed) == 0) + nextbuf = (overflow[lp].curbuf + 1) % NUM_BUF; + while (test_bit(nextbuf,&overflow[lp].bufferUsed) == 0) nextbuf = (nextbuf + 1) % NUM_BUF; - initDataEvent(viochar, lp); + spin_lock_irqsave(&consolelock, + flags); - /* - * While we have buffers with data, and our send window is open, send them - */ - while ((test_bit(nextbuf, &overflow[port].bufferUsed)) && - ((sndMsgSeq[port] - sndMsgAck[port]) immediateDataLen = overflow[port].bufferBytes[nextbuf]; - viochar->event.xCorrelationToken = sndMsgSeq[port]++; + viochar->immediateDataLen = overflow[lp].bufferBytes[nextbuf]; + viochar->event.xCorrelationToken = sndMsgSeq[consLpTarget]++; - memcpy(viochar->immediateData,overflow[port].buffer[nextbuf],viochar->immediateDataLen); + memcpy(viochar->immediateData,overflow[lp].buffer[nextbuf],viochar->immediateDataLen); hvrc = HvCallEvent_signalLpEvent(&viochar->event); if (hvrc) @@ -440,45 +358,41 @@ printk("viocons: error sending event! %d\n",(int)hvrc); } - /* - * clear the bufferUsed bit, zero the number of bytes in this buffer, - * and move to the next buffer - */ - clear_bit(nextbuf, &overflow[port].bufferUsed); - overflow[port].bufferBytes[nextbuf] = 0; + clear_bit(nextbuf, &overflow[lp].bufferUsed); + overflow[lp].bufferBytes[nextbuf] = 0; nextbuf = (nextbuf + 1) % NUM_BUF; } + spin_unlock_irqrestore(&consolelock, + flags); /* * If we have emptied all the buffers, start at 0 again. * this will re-use any allocated buffers */ - if (overflow[port].bufferUsed == 0) + if (overflow[lp].bufferUsed == 0) { - overflow[port].curbuf = 0; + overflow[lp].curbuf = 0; - if (overflow[port].overflowMessage) - overflow[port].overflowMessage = 0; + if ((overflow[lp].overflowMessage) && (debug)) + { + printk("Coming out of overflow!\n"); + } + + if (overflow[lp].overflowMessage) + overflow[lp].overflowMessage = 0; - if (port_info[port].tty) + if (tty) { - if ((port_info[port].tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && - (port_info[port].tty->ldisc.write_wakeup)) - (port_info[port].tty->ldisc.write_wakeup) (port_info[port].tty); - wake_up_interruptible(&port_info[port].tty->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + (tty->ldisc.write_wakeup)) + (tty->ldisc.write_wakeup) (tty); + wake_up_interruptible(&tty->write_wait); } } - spin_unlock_irqrestore(&consolelock, - flags); - } -/*************************************************************************** - * Our internal writer. Gets called both from the console device and - * the tty device. the tty pointer will be NULL if called from the console. - ***************************************************************************/ static int internal_write(struct tty_struct * tty, const char *buf, size_t len, int userFlag) { HvLpEvent_Rc hvrc; @@ -487,57 +401,24 @@ const char *curbuf = buf; struct viocharlpevent *viochar = (struct viocharlpevent *)vio_char_event_buffer; unsigned long flags; - struct port_info_tag * pi = NULL; - HvLpIndex lp; - u8 port; - - if (tty) - { - pi = (struct port_info_tag *)tty->driver_data; - - if (!pi || viotty_paranoia_check(pi, tty->device, "viotty_internal_write")) - return -ENODEV; - - lp = pi->lp; - port = pi->port; - } - else - { - /* - * If this is the console device, use the lp from the first port entry - */ - port = 0; - lp = port_info[0].lp; - } - /* - * Always put console output in the hypervisor console log - */ - if (port == 0) - HvCall_writeLogBuffer(buf, len); + HvCall_writeLogBuffer(buf, len); - /* - * If the path to this LP is closed, don't bother doing anything more. - * just dump the data on the floor - */ - if (!viopath_isactive(lp)) + if (!viopath_isactive(consLpTarget)) return len; - /* - * If there is already data queued for this port, send it - */ - if (overflow[port].bufferUsed) - sendBuffers(port, lp); + if (overflow[consLpTarget].bufferUsed) + sendBuffers(consLpTarget, tty); spin_lock_irqsave(&consolelock, flags); - initDataEvent(viochar, lp); + initDataEvent(viochar); /* Got the lock, don't cause console output */ while ((bleft > 0) && - (overflow[port].bufferUsed == 0) && - ((sndMsgSeq[port] - sndMsgAck[port]) VIOCHAR_MAX_DATA) @@ -546,7 +427,7 @@ curlen = bleft; viochar->immediateDataLen = curlen; - viochar->event.xCorrelationToken = sndMsgSeq[port]++; + viochar->event.xCorrelationToken = sndMsgSeq[consLpTarget]++; if (userFlag) copy_from_user(viochar->immediateData,curbuf,curlen); @@ -566,12 +447,9 @@ bleft -= curlen; } - /* - * If we didn't send it all, buffer it - */ if (bleft > 0) { - bleft -= bufferAdd(lp, curbuf,bleft, userFlag); + bleft -= bufferAdd(consLpTarget, curbuf,bleft, userFlag); } spin_unlock_irqrestore(&consolelock, flags); @@ -582,10 +460,7 @@ return len - bleft; } -/*************************************************************************** - * Initialize the common fields in a charLpEvent - ***************************************************************************/ -static void initDataEvent(struct viocharlpevent *viochar, HvLpIndex lp) +static void initDataEvent(struct viocharlpevent *viochar) { memset(viochar, 0x00, sizeof(struct viocharlpevent)); @@ -596,187 +471,23 @@ viochar->event.xType = HvLpEvent_Type_VirtualIo; viochar->event.xSubtype = viomajorsubtype_chario | viochardata; viochar->event.xSourceLp = HvLpConfig_getLpIndex(); - viochar->event.xTargetLp = lp; + viochar->event.xTargetLp = consLpTarget; viochar->event.xSizeMinus1 = sizeof(struct viocharlpevent); - viochar->event.xSourceInstanceId = viopath_sourceinst(lp); - viochar->event.xTargetInstanceId = viopath_targetinst(lp); + viochar->event.xSourceInstanceId = viopath_sourceinst(consLpTarget); + viochar->event.xTargetInstanceId = viopath_targetinst(consLpTarget); } -/*************************************************************************** - * console device write - ***************************************************************************/ static void viocons_write(struct console *co, const char *s, unsigned count) { - /*** Ryan S. Arnold : ryanarn@us.ibm.com *** 05/03/2001 *********************** - * This parser will ensure that all single instances of either \n or \r are - * matched into carriage return/line feed combinations. It also allows for - * instances where there already exist \n\r combinations as well as the - * reverse, \r\n combinations. - */ - - int index; - int foundcr; - int foundlf; - int match; - int slicelen; - int slicebegin; - char charptr[1]; - char vioconswrite[14]; - - index = 0; - foundcr = 0; - foundlf = 0; - match = 0; - slicelen = 0; - slicebegin = 0; - -// strcpy( vioconswrite, "viocons_write:"); -// internal_write( NULL, vioconswrite, 14, 0 ); - - for( index = 0; index < count; index++ ) - { - if ( foundlf && s[index] != 0x0d ) - { - if ( !match ) - { - internal_write( NULL, &s[slicebegin], slicelen, 0 ); - charptr[0] = '\r'; - internal_write( NULL, charptr, 1, 0 ); - slicebegin = index; - slicelen = 1; - } - else //else match = 1; - { - match = 0; - slicelen++; - } - } - else if ( foundcr && s[index] == 0x0a && !match ) - { - match = 1; - slicelen++; - } - else if ( foundlf && s[index] == 0x0d && !match ) - { - match = 1; - slicelen++; - } - else - { - slicelen++; - } - - if ( s[index] == 0x0d ) - { - foundcr = 1; - foundlf = 0; - } - else if ( s[index] == 0x0a ) - { - foundlf = 1; - foundcr = 0; - } - else - { - foundcr = 0; - foundlf = 0; - } - } - - internal_write( NULL, &s[slicebegin], slicelen, 0 ); - - if ( foundlf && !match ) - { - charptr[0] = '\r'; - internal_write( NULL, charptr, 1, 0 ); - } - else if ( foundcr && !match ) - { - charptr[0] = '\n'; - internal_write( NULL, charptr, 1, 0 ); - } -/* - int index; - char charptr[1]; - int foundcr; - int slicebegin; - int sliceend; - - foundcr = 0; - slicebegin = 0; - sliceend = 0; - - for ( index = 0; index < count; index++ ) - { - if( !foundcr && s[index] == 0x0a ) - { - if ( (slicebegin - sliceend > 0) && sliceend < count ) - { - //internal_write(NULL, &s[slicebegin],sliceend - slicebegin +1, 0 ); - internal_write(NULL, &s[slicebegin],sliceend - slicebegin, 0 ); - //slicebegin = sliceend + 1; - slicebegin = sliceend; - } - charptr[0] = '\r'; - internal_write(NULL, charptr, 1, 0); - } - if( foundcr && s[index] != 0x0a ) - { - if ( ( index - 2 ) >= 0 ) - { - if ( s[index - 2] != 0x0a ) - { - //internal_write(NULL, &s[slicebegin],sliceend - slicebegin + 1, 0 ); - internal_write(NULL, &s[slicebegin],sliceend - slicebegin, 0 ); - //slicebegin = sliceend + 1; - slicebegin = sliceend; - charptr[0] = '\n'; - internal_write(NULL, charptr, 1, 0); - } - } - } - sliceend++; - - if( s[index] == 0x0d ) - foundcr = 1; - else - foundcr = 0; - } - - internal_write(NULL, &s[slicebegin], sliceend - slicebegin + 1, 0 ); - //internal_write(NULL, &s[slicebegin], count - slicebegin, 0 ); - - if ( count > 1) - { - if ( foundcr == 1 && s[count-1] != 0x0a ) - { - charptr[0] = '\n'; - internal_write(NULL, charptr, 1, 0); - } - else if ( s[count-1] == 0x0a && s[count-2] != 0x0d ) - { - - charptr[0] = '\r'; - internal_write(NULL, charptr, 1, 0); - } - } -*/ - + internal_write(NULL, s, count, 0); } - -/*************************************************************************** - * Work out a the device associate with this console - ***************************************************************************/ static kdev_t viocons_device(struct console *c) { - return MKDEV(TTY_MAJOR, c->index + viotty_driver.minor_start); + return MKDEV(viotty_major, 0); } -/*************************************************************************** - * console device read method - ***************************************************************************/ static int viocons_read( struct console *co, const char *s, unsigned count ) { @@ -786,9 +497,6 @@ return 0; } -/*************************************************************************** - * console device wait until a key is pressed - ***************************************************************************/ static int viocons_wait_key( struct console *co ) { printk("In viocons_wait_key\n"); @@ -797,9 +505,6 @@ return 0; } -/*************************************************************************** - * Do console device setup - ***************************************************************************/ static int __init viocons_setup(struct console *co, char *options) { printk("viocons: in viocons_setup\n"); @@ -807,11 +512,8 @@ return 0; } -/*************************************************************************** - * console device I/O methods - ***************************************************************************/ static struct console viocons = { - name: "ttyS", + name: "viocons", write: viocons_write, read: viocons_read, device: viocons_device, @@ -821,110 +523,44 @@ }; -/*************************************************************************** - * TTY Open method - ***************************************************************************/ static int viotty_open(struct tty_struct *tty, struct file * filp) { - int port; - unsigned long flags; - MOD_INC_USE_COUNT; - port = MINOR(tty->device) - tty->driver.minor_start; - if ((port < 0) || (port >= VTTY_PORTS)) { - MOD_DEC_USE_COUNT; - return -ENODEV; - } - spin_lock_irqsave(&consolelock, - flags); - tty->driver_data = &port_info[port]; - port_info[port].tty = tty; - port_info[port].count++; - spin_unlock_irqrestore(&consolelock, - flags); + tty->driver_data = filp; return 0; } -/*************************************************************************** - * TTY Close method - ***************************************************************************/ static void viotty_close(struct tty_struct *tty, struct file * filp) { - unsigned long flags; - int badCountFlag = 0; - struct port_info_tag * pi = (struct port_info_tag *)tty->driver_data; - - if (!pi || viotty_paranoia_check(pi, tty->device, "viotty_close")) - return; - - spin_lock_irqsave(&consolelock, - flags); - if ((tty->count == 1) && (pi->count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. pi->count should always - * be one in these conditions. If it's greater than - * one, we've got real problems, since it means the - * serial port won't be shutdown. - */ - badCountFlag = 1; - pi->count = 1; - } - if (--pi->count < 0) { - badCountFlag = 1; - pi->count = 0; - } - - spin_unlock_irqrestore(&consolelock, - flags); - - if (badCountFlag) - printk("viotty_close: bad port count\n"); - MOD_DEC_USE_COUNT; + tty->driver_data = NULL; } -/*************************************************************************** - * TTY Write method - ***************************************************************************/ static int viotty_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count) { return internal_write(tty, buf, count, from_user); } -/*************************************************************************** - * TTY put_char method - ***************************************************************************/ static void viotty_put_char(struct tty_struct *tty, unsigned char ch) { internal_write(tty, &ch, 1, 0); } -/*************************************************************************** - * TTY flush_chars method - ***************************************************************************/ static void viotty_flush_chars(struct tty_struct *tty) { } -/*************************************************************************** - * TTY write_room method - ***************************************************************************/ static int viotty_write_room(struct tty_struct *tty) { int i; int room = 0; - struct port_info_tag * pi = (struct port_info_tag *)tty->driver_data; - - if (!pi || viotty_paranoia_check(pi, tty->device, "viotty_sendbuffers")) - return 0; // If no buffers are used, return the max size - if (overflow[pi->port].bufferUsed == 0) - return VIOCHAR_MAX_DATA * NUM_BUF; + if (overflow[consLpTarget].bufferUsed == 0) + return VIOCHAR_MAX_DATA; for (i=0; ((iport].bufferBytes[i]); + room += (OVERFLOW_SIZE - overflow[consLpTarget].bufferBytes[i]); } if (room > VIOCHAR_MAX_DATA) @@ -933,9 +569,6 @@ return room; } -/*************************************************************************** - * TTY chars_in_buffer_room method - ***************************************************************************/ static int viotty_chars_in_buffer(struct tty_struct *tty) { return 0; @@ -948,18 +581,6 @@ static int viotty_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) { - switch (cmd) - { - /* the ioctls below read/set the flags usually shown in the leds */ - /* don't use them - they will go away without warning */ - case KDGETLED: - case KDGKBLED: - return put_user(0, (char*)arg); - - case KDSKBLED: - return 0; - } - return n_tty_ioctl(tty,file,cmd,arg); } @@ -999,33 +620,30 @@ { } -/*************************************************************************** - * Handle an open charLpEvent. Could be either interrupt or ack - ***************************************************************************/ static void vioHandleOpenEvent(struct HvLpEvent *event) { unsigned long flags; u8 eventRc; u16 eventSubtypeRc; - struct viocharlpevent *cevent = (struct viocharlpevent *)event; - u8 port = cevent->virtualDevice; if (event->xFlags.xFunction == HvLpEvent_Function_Ack) { - if (port >= VTTY_PORTS) - return; - spin_lock_irqsave(&consolelock, flags); /* Got the lock, don't cause console output */ if (event->xRc == HvLpEvent_Rc_Good) { - sndMsgSeq[port] = sndMsgAck[port] = 0; + if (!viopath_isactive(consLpTarget)) + { + consLpTarget = event->xTargetLp; + /* printk("viocons: console open to partition %d\n", + consLpTarget); */ + sndMsgSeq[consLpTarget] = sndMsgAck[consLpTarget] = 0; + } + } - port_info[port].lp = event->xTargetLp; - spin_unlock_irqrestore(&consolelock, flags); @@ -1051,58 +669,49 @@ flags); /* Got the lock, don't cause console output */ - /* Make sure this is a good virtual tty */ - if (port >= VTTY_PORTS) - { - eventRc = HvLpEvent_Rc_SubtypeError; - eventSubtypeRc = viorc_openRejected; - } - - /* If this is tty is already connected to a different - partition, fail */ - else if ((port_info[port].lp != HvLpIndexInvalid) && - (port_info[port].lp != event->xSourceLp)) + /* If the current console is active, fail the open. Note + that if consLpTarget is HvLpIndexInvalid, the isactive + call just returns false + */ + if ((consLpTarget != HvLpIndexInvalid) && + (consLpTarget != event->xSourceLp)) { eventRc = HvLpEvent_Rc_SubtypeError; eventSubtypeRc = viorc_openRejected; } else { - port_info[port].lp = event->xSourceLp; + consLpTarget = event->xSourceLp; eventRc = HvLpEvent_Rc_Good; eventSubtypeRc = viorc_good; - sndMsgSeq[port] = sndMsgAck[port] = 0; + sndMsgSeq[consLpTarget] = sndMsgAck[consLpTarget] = 0; } spin_unlock_irqrestore(&consolelock, flags); + /* if (eventRc == HvLpEvent_Rc_Good) + printk("viocons: console open from partition %d\n",consLpTarget); + else + printk("viocons: console open rejected\n"); */ + /* Return the acknowledgement */ HvCallEvent_ackLpEvent(event); } } -/*************************************************************************** - * Handle a close open charLpEvent. Could be either interrupt or ack - ***************************************************************************/ static void vioHandleCloseEvent(struct HvLpEvent *event) { unsigned long flags; - struct viocharlpevent *cevent = (struct viocharlpevent *)event; - u8 port = cevent->virtualDevice; - if (event->xFlags.xFunction == HvLpEvent_Function_Int) { - if (port >= VTTY_PORTS) - return; - /* For closes, just mark the console partition invalid */ spin_lock_irqsave(&consolelock, flags); /* Got the lock, don't cause console output */ - if (port_info[port].lp == event->xSourceLp) - port_info[port].lp = HvLpIndexInvalid; + if (consLpTarget == event->xSourceLp) + consLpTarget = HvLpIndexInvalid; spin_unlock_irqrestore(&consolelock, flags); @@ -1114,52 +723,19 @@ } } -/*************************************************************************** - * Handle a config charLpEvent. Could be either interrupt or ack - ***************************************************************************/ -static void vioHandleConfig( struct HvLpEvent *event ) -{ - struct viocharlpevent *cevent = (struct viocharlpevent *)event; - int len; - - printk("inside of vioHandleConfig!\n"); - - len = cevent->immediateDataLen; - HvCall_writeLogBuffer(cevent->immediateData, cevent->immediateDataLen); - - if ( cevent->immediateData[0] == 0x01 ) - { - printk("viocons: window resized to %d: %d: %d: %d\n", - cevent->immediateData[1], - cevent->immediateData[2], - cevent->immediateData[3], - cevent->immediateData[4]); - } - else - { - printk("viocons: unknown config event\n"); - } - return; -} - -/*************************************************************************** - * Handle a data charLpEvent. - ***************************************************************************/ static void vioHandleData(struct HvLpEvent *event) { struct tty_struct *tty; struct viocharlpevent *cevent = (struct viocharlpevent *)event; - struct port_info_tag * pi; int len; - u8 port = cevent->virtualDevice; - if (port >= VTTY_PORTS) + if (cevent->virtualDevice > VTTY_PORTS) { printk("viocons: data on invalid virtual device\n"); return; } - tty = port_info[port].tty; + tty = viotty_table[cevent->virtualDevice]; if (tty == NULL) { @@ -1173,24 +749,9 @@ return; } - /* - * Just to be paranoid, make sure the tty points back to this port - */ - pi = (struct port_info_tag *)tty->driver_data; - - if (!pi || viotty_paranoia_check(pi, tty->device, "vioHandleData")) - return; - len = cevent->immediateDataLen; - /* - * Log port 0 data to the hypervisor log - */ - if (port == 0) - HvCall_writeLogBuffer(cevent->immediateData, cevent->immediateDataLen); - - if (len == 0) - return; + HvCall_writeLogBuffer(cevent->immediateData, cevent->immediateDataLen); /* Don't copy more bytes than there is room for in the buffer */ if (tty->flip.count + len > TTY_FLIPBUF_SIZE) @@ -1199,6 +760,9 @@ printk("viocons: flip buffer overflow!\n"); } + if (len == 0) + return; + memcpy (tty->flip.char_buf_ptr, cevent->immediateData, len); @@ -1207,46 +771,46 @@ /* Update the kernel buffer end */ tty->flip.count += len; tty->flip.char_buf_ptr += len; - tty->flip.flag_buf_ptr += len; tty_flip_buffer_push (tty); } -/*************************************************************************** - * Handle an ack charLpEvent. - ***************************************************************************/ static void vioHandleAck(struct HvLpEvent *event) { + struct tty_struct *tty; struct viocharlpevent *cevent = (struct viocharlpevent *)event; unsigned long flags; - u8 port = cevent->virtualDevice; - if (port >= VTTY_PORTS) + if (cevent->virtualDevice > VTTY_PORTS) { printk("viocons: data on invalid virtual device\n"); return; } + tty = viotty_table[cevent->virtualDevice]; + spin_lock_irqsave(&consolelock, flags); - sndMsgAck[port] = event->xCorrelationToken; + sndMsgAck[event->xSourceLp] = event->xCorrelationToken; + if (overflow[event->xSourceLp].bufferUsed) + sendBuffers(event->xSourceLp, tty); spin_unlock_irqrestore(&consolelock, flags); - - if (overflow[port].bufferUsed) - sendBuffers(port, port_info[port].lp); } -/*************************************************************************** - * Handle charLpEvents and route to the appropriate routine - ***************************************************************************/ + static void vioHandleCharEvent(struct HvLpEvent *event) { int charminor; if (event == NULL) { + /* Notification that a partition went away! */ + if (!viopath_isactive(consLpTarget)) + { + /* TODO! Clean up */ + } return; } charminor = event->xSubtype & VIOMINOR_SUBTYPE_MASK; @@ -1264,17 +828,11 @@ case viocharack: vioHandleAck(event); break; - case viocharconfig: - vioHandleConfig(event); - break; default: } } -/*************************************************************************** - * Send an open event - ***************************************************************************/ -static int viocons_sendOpen(HvLpIndex remoteLp, u8 port, void *sem) +int viocons_sendOpen(HvLpIndex remoteLp, void *sem) { return HvCallEvent_signalLpEventFast(remoteLp, HvLpEvent_Type_VirtualIo, @@ -1284,9 +842,7 @@ viopath_sourceinst(remoteLp), viopath_targetinst(remoteLp), (u64)(unsigned long)sem, - VIOVERSION << 16, - ((u64)port << 48), - 0, 0, 0); + 0, 0, 0, 0, 0); } @@ -1296,6 +852,13 @@ DECLARE_MUTEX_LOCKED(Semaphore); int rc; + int i; + + for (i=0; i 0x2032D && defined(CONFIG_DEVFS_FS)) - viotty_driver.name = "tty%d"; -#else - viotty_driver.name = "tty"; -#endif - viotty_driver.major = TTY_MAJOR; - viotty_driver.minor_start = 1; - viotty_driver.name_base = 1; + viotty_driver.name = "viocons/%d"; + viotty_driver.major = viotty_major; + viotty_driver.minor_start = 0; viotty_driver.num = VTTY_PORTS; - viotty_driver.type = TTY_DRIVER_TYPE_CONSOLE; - viotty_driver.subtype = 1; + viotty_driver.type = TTY_DRIVER_TYPE_SYSTEM; + viotty_driver.subtype = SYSTEM_TYPE_CONSOLE; viotty_driver.init_termios = tty_std_termios; viotty_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS; viotty_driver.refcount = &viotty_refcount; @@ -1400,35 +952,6 @@ printk("Couldn't register viotty driver\n"); } - // Now create the vcs and vcsa devfs entries so mingetty works -#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) - { - struct tty_driver temp_driver = viotty_driver; - int i; - - temp_driver.name = "vcs%d"; - for (i = 0; i < VTTY_PORTS; i++) - tty_register_devfs (&temp_driver, - 0, - i + temp_driver.minor_start); - - temp_driver.name = "vcsa%d"; - for (i = 0; i < VTTY_PORTS; i++) - tty_register_devfs (&temp_driver, - 0, - i + temp_driver.minor_start); - - // For compatibility with some earlier code only! - // This will go away!!! - temp_driver.name = "viocons/%d"; - temp_driver.name_base = 0; - for (i = 0; i < VTTY_PORTS; i++) - tty_register_devfs (&temp_driver, - 0, - i + temp_driver.minor_start); - } -#endif - /* * Create the proc entry */ @@ -1439,25 +962,15 @@ void __init viocons_init(void) { - int i; printk("viocons registering console\n"); - - memset(&port_info, 0x00, sizeof(port_info)); - for (i=0; i * (C) Copyright 2000 IBM Corporation @@ -21,14 +20,6 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - *************************************************************************** - * This routine provides access to tape drives owned and managed by an OS/400 - * partition running on the same box as this Linux partition. - * - * All tape operations are performed by sending messages back and forth to - * the OS/400 partition. The format of the messages is defined in - * include/asm-ppc/iSeries/vio.h - * */ #include #include @@ -61,7 +52,9 @@ #include "asm/iSeries/HvLpConfig.h" #endif -static int viotape_major = 230; +static DECLARE_WAIT_QUEUE_HEAD(viotape_wait_queue); + +static int viotape_major = 101; static int viotape_numdev = 0; static u64 sndMsgSeq; @@ -69,27 +62,8 @@ static u64 rcvMsgSeq; static u64 rcvMsgAck; -/*************************************************************************** - * The minor number follows the conventions of the SCSI tape drives. The - * rewind and mode are encoded in the minor #. We use this struct to break - * them out - ***************************************************************************/ -struct viot_devinfo_struct { - int major; - int minor; - int devno; - int mode; - int rewind; -}; - -/*************************************************************************** - * Maximum # tapes we support - ***************************************************************************/ #define VIOTAPE_MAX_TAPE 8 -/*************************************************************************** - * Our info on the tapes - ***************************************************************************/ static struct { char rsrcname[10]; char type[4]; @@ -98,15 +72,8 @@ static struct mtget viomtget[VIOTAPE_MAX_TAPE]; -/*************************************************************************** - * We single-thread - ***************************************************************************/ static struct semaphore reqSem; -/*************************************************************************** - * When we send a request, we use this struct to get the response back - * from the interrupt handler - ***************************************************************************/ struct opStruct { void *buffer; dma_addr_t dmaaddr; @@ -119,35 +86,6 @@ static spinlock_t opStructListLock; static struct opStruct *opStructList; -/*************************************************************************** - *************************************************************************** - * CODE STARTS HERE - *************************************************************************** - ***************************************************************************/ - -/*************************************************************************** - * Decode the kdev_t into its parts - ***************************************************************************/ -void getDevInfo(kdev_t dev, struct viot_devinfo_struct *devi) -{ - devi->major = MAJOR(dev); - devi->minor = MINOR(dev); - devi->devno = devi->minor & 0x1F; - devi->mode = (devi->minor & 0x60) >> 5; - devi->rewind = devi->minor & 0x80; - - printk("viotape dev: major %d, minor %d, devno %d, mode %d, rewind %d\n", - devi->major, - devi->minor, - devi->devno, - devi->mode, - devi->rewind); -} - - -/*************************************************************************** - * Allocate an op structure from our pool - ***************************************************************************/ static struct opStruct *getOpStruct(void) { struct opStruct *newOpStruct; @@ -172,9 +110,6 @@ return newOpStruct; } -/*************************************************************************** - * Return an op structure to our pool - ***************************************************************************/ static void freeOpStruct(struct opStruct *opStruct) { spin_lock(&opStructListLock); @@ -183,23 +118,15 @@ spin_unlock(&opStructListLock); } -/*************************************************************************** - * Get info on all tapes from OS/400 - ***************************************************************************/ static void get_viotape_info(void) { dma_addr_t dmaaddr; HvLpEvent_Rc hvrc; int i; - struct opStruct *op = getOpStruct(); DECLARE_MUTEX_LOCKED(Semaphore); - if (op == NULL) - return; memset(viotape_unitinfo, 0x00, sizeof(viotape_unitinfo)); - - op->sem = &Semaphore; - + dmaaddr = pci_map_single(NULL, &viotape_unitinfo, sizeof(viotape_unitinfo), PCI_DMA_FROMDEVICE); @@ -216,7 +143,7 @@ HvLpEvent_AckType_ImmediateAck, viopath_sourceinst(viopath_hostLp), viopath_targetinst(viopath_hostLp), - (u64)(u32)op, + (u64)(u32)&Semaphore, VIOVERSION << 16, dmaaddr, sizeof(viotape_unitinfo), @@ -229,29 +156,19 @@ down(&Semaphore); - freeOpStruct(op); - for (i=0; ((i < VIOTAPE_MAX_TAPE) && (viotape_unitinfo[i].rsrcname[0])); i++) { - printk("found a tape %10.10s\n",viotape_unitinfo[i].rsrcname); viotape_numdev++; } } -/*************************************************************************** - * Don't support seek - ***************************************************************************/ static long long viotap_llseek(struct file *file, long long offset, int origin) { - printk("viotap_llseek: not supported\n"); return -EINVAL; /* not supported */ } -/*************************************************************************** - * Write - ***************************************************************************/ static ssize_t viotap_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { HvLpEvent_Rc hvrc; @@ -260,14 +177,11 @@ struct opStruct *op = getOpStruct(); int noblock = ((flags & O_NONBLOCK) != 0); int err; - struct viot_devinfo_struct devi; DECLARE_MUTEX_LOCKED(Semaphore); if (op == NULL) return -ENOMEM; - getDevInfo(dev, &devi); - /* We need to make sure we can send a request. We use * a semaphore to keep track of # requests in use. If * we are non-blocking, make sure we don't block on the @@ -306,7 +220,6 @@ err = copy_from_user( op->buffer, (const void *) buf, count); if (err) { - printk("viotape: error on copy from user\n"); pci_free_consistent(NULL, count, op->buffer, op->dmaaddr); freeOpStruct(op); up(&reqSem); @@ -331,7 +244,7 @@ viopath_targetinst(viopath_hostLp), (u64)(u32)op, VIOVERSION << 16, - ((u64)devi.devno << 48) | + ((u64)dev << 48) | op->dmaaddr, count, 0, @@ -351,12 +264,6 @@ down(&Semaphore); err = op->rc; - - printk("viotape write: rc %d, count %d, result %d\n", - err, count, op->count); - - count = op->count; - /* Free the buffer */ pci_free_consistent(NULL, count, op->buffer, op->dmaaddr); freeOpStruct(op); @@ -364,12 +271,12 @@ if (err) return -EIO; else - return count; + return 0; } -/*************************************************************************** - * read - ***************************************************************************/ +/** + * viotap_read: + */ static ssize_t viotap_read(struct file *file, char *buf, size_t count, loff_t *ptr) { HvLpEvent_Rc hvrc; @@ -378,14 +285,11 @@ struct opStruct *op = getOpStruct(); int noblock = ((flags & O_NONBLOCK) != 0); int err; - struct viot_devinfo_struct devi; DECLARE_MUTEX_LOCKED(Semaphore); if (op == NULL) return -ENOMEM; - getDevInfo(dev, &devi); - /* We need to make sure we can send a request. We use * a semaphore to keep track of # requests in use. If * we are non-blocking, make sure we don't block on the @@ -404,7 +308,6 @@ down(&reqSem); } - /* Allocate a DMA buffer */ op->buffer = pci_alloc_consistent(NULL, count, @@ -412,6 +315,8 @@ if ((op->dmaaddr == 0xFFFFFFFF) || (op->buffer == NULL)) { + printk("viotape error allocating dma buffer for len %d\n", + count); freeOpStruct(op); up(&reqSem); return -EFAULT; @@ -430,7 +335,7 @@ viopath_targetinst(viopath_hostLp), (u64)(u32)op, VIOVERSION << 16, - ((u64)devi.devno << 48) | + ((u64)dev << 48) | op->dmaaddr, count, 0, @@ -448,28 +353,18 @@ if (op->rc == 0) { - /* If we got data back */ - if (op->count) + /* Copy the data into the buffer */ + err = copy_to_user( buf, op->buffer, count); + if (err) { - /* Copy the data into the buffer */ - err = copy_to_user( buf, op->buffer, count); - if (err) - { - printk("error on copy_to_user\n"); - pci_free_consistent(NULL, count, op->buffer, op->dmaaddr); - freeOpStruct(op); - up(&reqSem); - return -EFAULT; - } + pci_free_consistent(NULL, count, op->buffer, op->dmaaddr); + freeOpStruct(op); + up(&reqSem); + return -EFAULT; } } err = op->rc; - - printk("viotape read: rc %d, count %d, result %d\n", - err, count, op->count); - count = op->count; - /* Free the buffer */ pci_free_consistent(NULL, count, op->buffer, op->dmaaddr); freeOpStruct(op); @@ -477,12 +372,12 @@ if (err) return -EIO; else - return count; + return 0; } -/*************************************************************************** - * read - ***************************************************************************/ +/** + * viotap_ioctl: + */ static int viotap_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { @@ -491,12 +386,9 @@ DECLARE_MUTEX_LOCKED(Semaphore); kdev_t dev = file->f_dentry->d_inode->i_rdev; struct opStruct *op = getOpStruct(); - struct viot_devinfo_struct devi; if (op == NULL) return -ENOMEM; - getDevInfo(dev, &devi); - down(&reqSem); switch (cmd) @@ -534,7 +426,6 @@ return -EIO; } - op->sem = &Semaphore; hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, HvLpEvent_Type_VirtualIo, viomajorsubtype_tape | viotapeop, @@ -544,10 +435,9 @@ viopath_targetinst(viopath_hostLp), (u64)(u32)op, VIOVERSION << 16, - ((u64)devi.devno << 48), - 0, - (((u64)myOp) << 32) | mtc.mt_count, - 0); + ((u64)dev << 48) | myOp, + ((u64)mtc.mt_count) << 32, + 0, 0); if (hvrc != HvLpEvent_Rc_Good) { printk("viotape hv error on op %d\n",(int)hvrc); @@ -556,7 +446,6 @@ return -EIO; } down(&Semaphore); - if (op->rc) { freeOpStruct(op); @@ -573,7 +462,6 @@ } case MTIOCGET: - op->sem = &Semaphore; hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, HvLpEvent_Type_VirtualIo, viomajorsubtype_tape | viotapegetstatus , @@ -583,7 +471,7 @@ viopath_targetinst(viopath_hostLp), (u64)(u32)op, VIOVERSION << 16, - ((u64)devi.devno << 48), + ((u64)dev << 48), 0, 0, 0); if (hvrc != HvLpEvent_Rc_Good) { @@ -619,24 +507,20 @@ return 0; } -/*************************************************************************** - * Open - ***************************************************************************/ +/** + * viotap_open: + */ + static int viotap_open(struct inode *inode, struct file *file) { DECLARE_MUTEX_LOCKED(Semaphore); kdev_t dev = file->f_dentry->d_inode->i_rdev; HvLpEvent_Rc hvrc; struct opStruct *op = getOpStruct(); - struct viot_devinfo_struct devi; if (op == NULL) return -ENOMEM; - getDevInfo(dev, &devi); - - // Note: We currently only support one mode! - if ((devi.devno >= viotape_numdev) || - (devi.mode)) + if (dev >= viotape_numdev) { freeOpStruct(op); return -ENODEV; @@ -653,7 +537,7 @@ viopath_targetinst(viopath_hostLp), (u64)(u32)op, VIOVERSION << 16, - ((u64)devi.devno << 48), + ((u64)dev << 48), 0, 0, 0); @@ -668,75 +552,38 @@ if (op->rc) { - printk("viotape: open failed\n"); freeOpStruct(op); return -EIO; } else { - printk("viotape: open worked\n"); freeOpStruct(op); return 0; } } -/*************************************************************************** - * Release - ***************************************************************************/ +/** + * viotap_close: + */ + static int viotap_release(struct inode *inode, struct file *file) { - DECLARE_MUTEX_LOCKED(Semaphore); kdev_t dev = file->f_dentry->d_inode->i_rdev; - HvLpEvent_Rc hvrc; - struct viot_devinfo_struct devi; - struct opStruct *op = getOpStruct(); - - if (op == NULL) - return -ENOMEM; - - getDevInfo(dev, &devi); - - if (devi.devno >= viotape_numdev) - { - freeOpStruct(op); - return -ENODEV; - } + HvLpEvent_Rc hvrc; - op->sem = &Semaphore; - - if (devi.rewind) - { - hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, - HvLpEvent_Type_VirtualIo, - viomajorsubtype_tape | viotapeop, - HvLpEvent_AckInd_DoAck, - HvLpEvent_AckType_ImmediateAck, - viopath_sourceinst(viopath_hostLp), - viopath_targetinst(viopath_hostLp), - (u64)(u32)op, - VIOVERSION << 16, - ((u64)devi.devno << 48), - 0, - ((u64)VIOTAPOP_REW) << 32, - 0); - down(&Semaphore); - - if (op->rc) - { - printk("viotape: rewind failed\n"); - } - } + if (dev >= viotape_numdev) + return -EINVAL; hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, HvLpEvent_Type_VirtualIo, viomajorsubtype_tape | viotapeclose, - HvLpEvent_AckInd_DoAck, + HvLpEvent_AckInd_NoAck, HvLpEvent_AckType_ImmediateAck, viopath_sourceinst(viopath_hostLp), viopath_targetinst(viopath_hostLp), - (u64)(u32)op, + 0, VIOVERSION << 16, - ((u64)devi.devno << 48), + ((u64)dev << 48), 0, 0, 0); @@ -745,13 +592,6 @@ printk("viotape bad rc on signalLpEvent %d\n",(int)hvrc); return -EIO; } - - down(&Semaphore); - - if (op->rc) - { - printk("viotape: close failed\n"); - } return 0; } @@ -765,14 +605,10 @@ release: viotap_release, }; -/*************************************************************************** - * Handle interrupt events for tape - ***************************************************************************/ static void vioHandleTapeEvent(struct HvLpEvent *event) { int tapeminor; struct opStruct *op; - struct viotapelpevent *tevent = (struct viotapelpevent *)event; if (event == NULL) { @@ -788,16 +624,14 @@ { case viotapegetinfo: case viotapeopen: + up((void*)(u32)event->xCorrelationToken); + break; case viotapeclose: - op = (struct opStruct *)(u32)event->xCorrelationToken; - op->rc = event->xRc; - up(op->sem); break; case viotaperead: case viotapewrite: op = (struct opStruct *)(u32)event->xCorrelationToken; op->rc = event->xRc; - op->count = tevent->mLen; if (op->sem) { @@ -809,32 +643,15 @@ freeOpStruct(op); up(&reqSem); } - break; case viotapeop: case viotapegetpos: case viotapesetpos: case viotapegetstatus: - op = (struct opStruct *)(u32)event->xCorrelationToken; - if (op) - { - op->count = tevent->u.tapeOp.mCount; - op->rc = event->xRc; - - if (op->sem) - { - up(op->sem); - } - } - break; default: - printk("viotape: wierd ack\n"); } } -/*************************************************************************** - * Do initialization - ***************************************************************************/ int __init viotap_init(void) { DECLARE_MUTEX_LOCKED(Semaphore); @@ -883,6 +700,7 @@ viotape_major, i*8, S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP, &viotap_fops, NULL)) + if (0) { printk("Cannot register viotape %s!\n", tapename); @@ -896,16 +714,19 @@ viotape_unitinfo[i].model); } } - + return 0; } -/*************************************************************************** - * Cleanup - ***************************************************************************/ +/** + * cleanup_module: + */ + static void __exit viotap_exit(void) { vio_clearTapeHandler(); + + } module_init(viotap_init); diff -uNr --exclude=CVS linux-2.4.5-ac18/drivers/ide/ide-tape.c linuxppc64_2_4/drivers/ide/ide-tape.c --- linux-2.4.5-ac18/drivers/ide/ide-tape.c Tue Jun 26 21:59:17 2001 +++ linuxppc64_2_4/drivers/ide/ide-tape.c Mon Jun 18 18:18:49 2001 @@ -3,7 +3,7 @@ * * Copyright (C) 1995 - 1999 Gadi Oxman * - * $Header$ + * $Header: /cvs/linuxppc64/linuxppc64_2_4/drivers/ide/ide-tape.c,v 1.2 2001/06/18 23:18:49 tgall Exp $ * * This driver was constructed as a student project in the software laboratory * of the faculty of electrical engineering in the Technion - Israel's diff -uNr --exclude=CVS linux-2.4.5-ac18/drivers/ieee1394/video1394.c linuxppc64_2_4/drivers/ieee1394/video1394.c --- linux-2.4.5-ac18/drivers/ieee1394/video1394.c Tue Jun 26 21:59:17 2001 +++ linuxppc64_2_4/drivers/ieee1394/video1394.c Mon Jun 18 18:18:49 2001 @@ -172,7 +172,7 @@ pmd = pmd_offset(pgd, adr); if (!pmd_none(*pmd)) { ptep = pte_offset(pmd, adr); - pte = *ptep; + pte = fetch_pte(ptep); if(pte_present(pte)) { ret = (unsigned long) page_address(pte_page(pte)); ret |= (adr & (PAGE_SIZE - 1)); diff -uNr --exclude=CVS linux-2.4.5-ac18/drivers/macintosh/nvram.c linuxppc64_2_4/drivers/macintosh/nvram.c --- linux-2.4.5-ac18/drivers/macintosh/nvram.c Tue Jun 26 21:56:00 2001 +++ linuxppc64_2_4/drivers/macintosh/nvram.c Wed May 30 00:12:19 2001 @@ -18,7 +18,7 @@ #define NVRAM_SIZE 8192 -static long long nvram_llseek(struct file *file, loff_t offset, int origin) +static loff_t nvram_llseek(struct file *file, loff_t offset, int origin) { switch (origin) { case 1: diff -uNr --exclude=CVS linux-2.4.5-ac18/drivers/media/video/bttv-driver.c linuxppc64_2_4/drivers/media/video/bttv-driver.c --- linux-2.4.5-ac18/drivers/media/video/bttv-driver.c Tue Jun 26 21:56:06 2001 +++ linuxppc64_2_4/drivers/media/video/bttv-driver.c Mon Jun 18 18:18:52 2001 @@ -152,7 +152,7 @@ pmd = pmd_offset(pgd, adr); if (!pmd_none(*pmd)) { ptep = pte_offset(pmd, adr); - pte = *ptep; + pte = fetch_pte(ptep); if(pte_present(pte)) { ret = (unsigned long) page_address(pte_page(pte)); ret |= (adr & (PAGE_SIZE - 1)); diff -uNr --exclude=CVS linux-2.4.5-ac18/drivers/media/video/cpia.c linuxppc64_2_4/drivers/media/video/cpia.c --- linux-2.4.5-ac18/drivers/media/video/cpia.c Tue Jun 26 21:56:06 2001 +++ linuxppc64_2_4/drivers/media/video/cpia.c Mon Jun 18 18:18:52 2001 @@ -201,7 +201,7 @@ pmd = pmd_offset(pgd, adr); if (!pmd_none(*pmd)) { ptep = pte_offset(pmd, adr); - pte = *ptep; + pte = fetch_pte(ptep); if (pte_present(pte)) { ret = (unsigned long) page_address(pte_page(pte)); ret |= (adr & (PAGE_SIZE-1)); diff -uNr --exclude=CVS linux-2.4.5-ac18/drivers/net/3c523.c linuxppc64_2_4/drivers/net/3c523.c --- linux-2.4.5-ac18/drivers/net/3c523.c Tue Jun 26 21:59:17 2001 +++ linuxppc64_2_4/drivers/net/3c523.c Mon Jun 18 18:18:53 2001 @@ -82,7 +82,7 @@ Ganesh Sittampalam Stuart Adamson - $Header: /fsys2/home/chrisb/linux-1.3.59-MCA/drivers/net/RCS/3c523.c,v 1.1 1996/02/05 01:53:46 chrisb Exp chrisb $ + $Header: /cvs/linuxppc64/linuxppc64_2_4/drivers/net/3c523.c,v 1.2 2001/06/18 23:18:53 tgall Exp $ */ #include diff -uNr --exclude=CVS linux-2.4.5-ac18/drivers/net/3c523.h linuxppc64_2_4/drivers/net/3c523.h --- linux-2.4.5-ac18/drivers/net/3c523.h Tue Jun 26 21:55:34 2001 +++ linuxppc64_2_4/drivers/net/3c523.h Fri May 4 17:12:43 2001 @@ -15,7 +15,7 @@ * * See 3c523.c for details. * - * $Header: /home/chrisb/linux-1.2.13-3c523/drivers/net/RCS/3c523.h,v 1.6 1996/01/20 05:09:00 chrisb Exp chrisb $ + * $Header: /cvs/linuxppc64/linuxppc64_2_4/drivers/net/3c523.h,v 1.1.1.1 2001/05/04 22:12:43 tgall Exp $ */ /* diff -uNr --exclude=CVS linux-2.4.5-ac18/drivers/net/Space.c linuxppc64_2_4/drivers/net/Space.c --- linux-2.4.5-ac18/drivers/net/Space.c Tue Jun 26 21:55:31 2001 +++ linuxppc64_2_4/drivers/net/Space.c Mon May 21 18:25:28 2001 @@ -541,9 +541,10 @@ -#ifdef CONFIG_TR +#if 0 /* ifdef CONFIG_TR */ /* Token-ring device probe */ extern int ibmtr_probe(struct net_device *); +extern int olympic_probe(struct net_device *); extern int smctr_probe(struct net_device *); static int @@ -552,6 +553,9 @@ if (1 #ifdef CONFIG_IBMTR && ibmtr_probe(dev) +#endif +#ifdef CONFIG_IBMOL + && olympic_probe(dev) #endif #ifdef CONFIG_SMCTR && smctr_probe(dev) diff -uNr --exclude=CVS linux-2.4.5-ac18/drivers/net/acenic.c linuxppc64_2_4/drivers/net/acenic.c --- linux-2.4.5-ac18/drivers/net/acenic.c Tue Jun 26 21:59:18 2001 +++ linuxppc64_2_4/drivers/net/acenic.c Mon Jun 18 18:18:53 2001 @@ -120,30 +120,6 @@ #define PCI_DEVICE_ID_SGI_ACENIC 0x0009 #endif -#if LINUX_VERSION_CODE >= 0x20400 -static struct pci_device_id acenic_pci_tbl[] __initdata = { - { PCI_VENDOR_ID_ALTEON, PCI_DEVICE_ID_ALTEON_ACENIC_FIBRE, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - { PCI_VENDOR_ID_ALTEON, PCI_DEVICE_ID_ALTEON_ACENIC_COPPER, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3C985, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - { PCI_VENDOR_ID_NETGEAR, PCI_DEVICE_ID_NETGEAR_GA620, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - { PCI_VENDOR_ID_NETGEAR, PCI_DEVICE_ID_NETGEAR_GA620T, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - /* - * Farallon used the DEC vendor ID on their cards incorrectly. - */ - { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_FARALLON_PN9000SX, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_ACENIC, - PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, - { } -}; -MODULE_DEVICE_TABLE(pci, acenic_pci_tbl); -#endif - #ifndef wmb #define wmb() mb() @@ -521,230 +497,188 @@ "acenic.c: v0.81 04/20/2001 Jes Sorensen, linux-acenic@SunSITE.dk\n" " http://home.cern.ch/~jes/gige/acenic.html\n"; -static struct net_device *root_dev; - -static int probed __initdata = 0; - - -int __devinit acenic_probe (ACE_PROBE_ARG) +static int __devinit acenic_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) { -#ifdef NEW_NETINIT struct net_device *dev; -#endif - struct ace_private *ap; - struct pci_dev *pdev = NULL; int boards_found = 0; int version_disp; - if (probed) - return -ENODEV; - probed++; - if (!pci_present()) /* is PCI support present? */ return -ENODEV; version_disp = 0; - while ((pdev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET<<8, pdev))) { + dev = init_etherdev(NULL, sizeof(struct ace_private)); - if (!((pdev->vendor == PCI_VENDOR_ID_ALTEON) && - ((pdev->device == PCI_DEVICE_ID_ALTEON_ACENIC_FIBRE) || - (pdev->device == PCI_DEVICE_ID_ALTEON_ACENIC_COPPER)))&& - !((pdev->vendor == PCI_VENDOR_ID_3COM) && - (pdev->device == PCI_DEVICE_ID_3COM_3C985)) && - !((pdev->vendor == PCI_VENDOR_ID_NETGEAR) && - ((pdev->device == PCI_DEVICE_ID_NETGEAR_GA620) || - (pdev->device == PCI_DEVICE_ID_NETGEAR_GA620T))) && - /* - * Farallon used the DEC vendor ID on their cards by - * mistake for a while - */ - !((pdev->vendor == PCI_VENDOR_ID_DEC) && - (pdev->device == PCI_DEVICE_ID_FARALLON_PN9000SX)) && - !((pdev->vendor == PCI_VENDOR_ID_SGI) && - (pdev->device == PCI_DEVICE_ID_SGI_ACENIC))) - continue; - - dev = init_etherdev(NULL, sizeof(struct ace_private)); - - if (dev == NULL) { - printk(KERN_ERR "acenic: Unable to allocate " - "net_device structure!\n"); - break; - } + if (dev == NULL) { + printk(KERN_ERR "acenic: Unable to allocate " + "net_device structure!\n"); + return -ENODEV; + } - SET_MODULE_OWNER(dev); + SET_MODULE_OWNER(dev); - if (!dev->priv) - dev->priv = kmalloc(sizeof(*ap), GFP_KERNEL); - if (!dev->priv) { - printk(KERN_ERR "acenic: Unable to allocate memory\n"); - return -ENOMEM; - } - - ap = dev->priv; - ap->pdev = pdev; - - dev->irq = pdev->irq; - dev->open = &ace_open; - dev->hard_start_xmit = &ace_start_xmit; - dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_HIGHDMA; - if (1) { - static void ace_watchdog(struct net_device *dev); - dev->tx_timeout = &ace_watchdog; - dev->watchdog_timeo = 5*HZ; - } - dev->stop = &ace_close; - dev->get_stats = &ace_get_stats; - dev->set_multicast_list = &ace_set_multicast_list; - dev->do_ioctl = &ace_ioctl; - dev->set_mac_address = &ace_set_mac_addr; - dev->change_mtu = &ace_change_mtu; + if (!dev->priv) + dev->priv = kmalloc(sizeof(*ap), GFP_KERNEL); + if (!dev->priv) { + printk(KERN_ERR "acenic: Unable to allocate memory\n"); + return -ENOMEM; + } - /* display version info if adapter is found */ - if (!version_disp) - { - /* set display flag to TRUE so that */ - /* we only display this string ONCE */ - version_disp = 1; - printk(version); - } + ap = dev->priv; + ap->pdev = pdev; - /* - * Enable master mode before we start playing with the - * pci_command word since pci_set_master() will modify - * it. - */ - pci_set_master(pdev); + dev->irq = pdev->irq; + dev->open = &ace_open; + dev->hard_start_xmit = &ace_start_xmit; + dev->features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_HIGHDMA; + if (1) { + static void ace_watchdog(struct net_device *dev); + dev->tx_timeout = &ace_watchdog; + dev->watchdog_timeo = 5*HZ; + } + dev->stop = &ace_close; + dev->get_stats = &ace_get_stats; + dev->set_multicast_list = &ace_set_multicast_list; + dev->do_ioctl = &ace_ioctl; + dev->set_mac_address = &ace_set_mac_addr; + dev->change_mtu = &ace_change_mtu; - pci_read_config_word(pdev, PCI_COMMAND, &ap->pci_command); + /* display version info if adapter is found */ + if (!version_disp) + { + /* set display flag to TRUE so that */ + /* we only display this string ONCE */ + version_disp = 1; + printk(version); + } - /* OpenFirmware on Mac's does not set this - DOH.. */ - if (!(ap->pci_command & PCI_COMMAND_MEMORY)) { - printk(KERN_INFO "%s: Enabling PCI Memory Mapped " - "access - was not enabled by BIOS/Firmware\n", - dev->name); - ap->pci_command = ap->pci_command | PCI_COMMAND_MEMORY; - pci_write_config_word(ap->pdev, PCI_COMMAND, - ap->pci_command); - wmb(); - } + /* + * Enable master mode before we start playing with the + * pci_command word since pci_set_master() will modify + * it. + */ + pci_set_master(pdev); - pci_read_config_byte(pdev, PCI_LATENCY_TIMER, - &ap->pci_latency); - if (ap->pci_latency <= 0x40) { - ap->pci_latency = 0x40; - pci_write_config_byte(pdev, PCI_LATENCY_TIMER, - ap->pci_latency); - } + pci_read_config_word(pdev, PCI_COMMAND, &ap->pci_command); - /* - * Remap the regs into kernel space - this is abuse of - * dev->base_addr since it was means for I/O port - * addresses but who gives a damn. - */ - dev->base_addr = pci_resource_start(pdev, 0); - ap->regs = (struct ace_regs *)ioremap(dev->base_addr, 0x4000); - if (!ap->regs) { - printk(KERN_ERR "%s: Unable to map I/O register, " - "AceNIC %i will be disabled.\n", - dev->name, boards_found); - break; - } + /* OpenFirmware on Mac's does not set this - DOH.. */ + if (!(ap->pci_command & PCI_COMMAND_MEMORY)) { + printk(KERN_INFO "%s: Enabling PCI Memory Mapped " + "access - was not enabled by BIOS/Firmware\n", + dev->name); + ap->pci_command = ap->pci_command | PCI_COMMAND_MEMORY; + pci_write_config_word(ap->pdev, PCI_COMMAND, + ap->pci_command); + wmb(); + } - switch(pdev->vendor) { - case PCI_VENDOR_ID_ALTEON: - strncpy(ap->name, "AceNIC Gigabit Ethernet", - sizeof (ap->name)); - printk(KERN_INFO "%s: Alteon AceNIC ", dev->name); - break; - case PCI_VENDOR_ID_3COM: - strncpy(ap->name, "3Com 3C985 Gigabit Ethernet", - sizeof (ap->name)); - printk(KERN_INFO "%s: 3Com 3C985 ", dev->name); - break; - case PCI_VENDOR_ID_NETGEAR: - strncpy(ap->name, "NetGear GA620 Gigabit Ethernet", - sizeof (ap->name)); - printk(KERN_INFO "%s: NetGear GA620 ", dev->name); - break; - case PCI_VENDOR_ID_DEC: - if (pdev->device == PCI_DEVICE_ID_FARALLON_PN9000SX) { - strncpy(ap->name, "Farallon PN9000-SX " - "Gigabit Ethernet", sizeof (ap->name)); - printk(KERN_INFO "%s: Farallon PN9000-SX ", - dev->name); - break; - } - case PCI_VENDOR_ID_SGI: - strncpy(ap->name, "SGI AceNIC Gigabit Ethernet", - sizeof (ap->name)); - printk(KERN_INFO "%s: SGI AceNIC ", dev->name); - break; - default: - strncpy(ap->name, "Unknown AceNIC based Gigabit " - "Ethernet", sizeof (ap->name)); - printk(KERN_INFO "%s: Unknown AceNIC ", dev->name); + pci_read_config_byte(pdev, PCI_LATENCY_TIMER, + &ap->pci_latency); + if (ap->pci_latency <= 0x40) { + ap->pci_latency = 0x40; + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, + ap->pci_latency); + } + + /* + * Remap the regs into kernel space - this is abuse of + * dev->base_addr since it was means for I/O port + * addresses but who gives a damn. + */ + dev->base_addr = pci_resource_start(pdev, 0); + ap->regs = (struct ace_regs *)ioremap(dev->base_addr, 0x4000); + if (!ap->regs) { + printk(KERN_ERR "%s: Unable to map I/O register, " + "AceNIC %i will be disabled.\n", + dev->name, boards_found); + return -ENODEV; + } + + switch(pdev->vendor) { + case PCI_VENDOR_ID_ALTEON: + strncpy(ap->name, "AceNIC Gigabit Ethernet", + sizeof (ap->name)); + printk(KERN_INFO "%s: Alteon AceNIC ", dev->name); + break; + case PCI_VENDOR_ID_3COM: + strncpy(ap->name, "3Com 3C985 Gigabit Ethernet", + sizeof (ap->name)); + printk(KERN_INFO "%s: 3Com 3C985 ", dev->name); + break; + case PCI_VENDOR_ID_NETGEAR: + strncpy(ap->name, "NetGear GA620 Gigabit Ethernet", + sizeof (ap->name)); + printk(KERN_INFO "%s: NetGear GA620 ", dev->name); + break; + case PCI_VENDOR_ID_DEC: + if (pdev->device == PCI_DEVICE_ID_FARALLON_PN9000SX) { + strncpy(ap->name, "Farallon PN9000-SX " + "Gigabit Ethernet", sizeof (ap->name)); + printk(KERN_INFO "%s: Farallon PN9000-SX ", + dev->name); break; } - ap->name [sizeof (ap->name) - 1] = '\0'; - printk("Gigabit Ethernet at 0x%08lx, ", dev->base_addr); + case PCI_VENDOR_ID_SGI: + strncpy(ap->name, "SGI AceNIC Gigabit Ethernet", + sizeof (ap->name)); + printk(KERN_INFO "%s: SGI AceNIC ", dev->name); + break; + default: + strncpy(ap->name, "Unknown AceNIC based Gigabit " + "Ethernet", sizeof (ap->name)); + printk(KERN_INFO "%s: Unknown AceNIC ", dev->name); + break; + } + ap->name [sizeof (ap->name) - 1] = '\0'; + printk("Gigabit Ethernet at 0x%08lx, ", dev->base_addr); #ifdef __sparc__ - printk("irq %s\n", __irq_itoa(dev->irq)); + printk("irq %s\n", __irq_itoa(dev->irq)); #else - printk("irq %i\n", dev->irq); + printk("irq %i\n", dev->irq); #endif #ifdef CONFIG_ACENIC_OMIT_TIGON_I - if ((readl(&ap->regs->HostCtrl) >> 28) == 4) { - printk(KERN_ERR "%s: Driver compiled without Tigon I" - " support - NIC disabled\n", dev->name); - ace_init_cleanup(dev); - kfree(dev); - continue; - } + if ((readl(&ap->regs->HostCtrl) >> 28) == 4) { + printk(KERN_ERR "%s: Driver compiled without Tigon I" + " support - NIC disabled\n", dev->name); + ace_init_cleanup(dev); + kfree(dev); + return -ENODEV; + } #endif - if (ace_allocate_descriptors(dev)) { - /* - * ace_allocate_descriptors() calls - * ace_init_cleanup() on error. - */ - kfree(dev); - continue; - } + if (ace_allocate_descriptors(dev)) { + /* + * ace_allocate_descriptors() calls + * ace_init_cleanup() on error. + */ + kfree(dev); + return -ENODEV; + } #ifdef MODULE - if (boards_found >= ACE_MAX_MOD_PARMS) - ap->board_idx = BOARD_IDX_OVERFLOW; - else - ap->board_idx = boards_found; + if (boards_found >= ACE_MAX_MOD_PARMS) + ap->board_idx = BOARD_IDX_OVERFLOW; + else + ap->board_idx = boards_found; #else - ap->board_idx = BOARD_IDX_STATIC; + ap->board_idx = BOARD_IDX_STATIC; #endif - if (ace_init(dev)) { - /* - * ace_init() calls ace_init_cleanup() on error. - */ - kfree(dev); - continue; - } + pdev->driver_data = dev; - boards_found++; + if (ace_init(dev)) { + /* + * ace_init() calls ace_init_cleanup() on error. + */ + kfree(dev); + return -ENODEV; } - /* - * If we're at this point we're going through ace_probe() for - * the first time. Return success (0) if we've initialized 1 - * or more boards. Otherwise, return failure (-ENODEV). - */ - - if (boards_found > 0) - return 0; - else - return -ENODEV; + return 0; } @@ -759,136 +693,95 @@ MODULE_PARM(max_rx_desc, "1-" __MODULE_STRING(8) "i"); #endif - -static void __exit ace_module_cleanup(void) +static void __devexit acenic_remove_one(struct pci_dev *pdev) { + struct net_device *dev = pdev->driver_data; struct ace_private *ap; struct ace_regs *regs; - struct net_device *next; short i; - while (root_dev) { - ap = root_dev->priv; - next = ap->next; - - regs = ap->regs; + ap = dev->priv; + regs = ap->regs; - writel(readl(®s->CpuCtrl) | CPU_HALT, ®s->CpuCtrl); - if (ap->version >= 2) - writel(readl(®s->CpuBCtrl) | CPU_HALT, - ®s->CpuBCtrl); - /* - * This clears any pending interrupts - */ - writel(1, ®s->Mb0Lo); + writel(readl(®s->CpuCtrl) | CPU_HALT, ®s->CpuCtrl); + if (ap->version >= 2) + writel(readl(®s->CpuBCtrl) | CPU_HALT, + ®s->CpuBCtrl); + /* + * This clears any pending interrupts + */ + writel(1, ®s->Mb0Lo); - /* - * Make sure no other CPUs are processing interrupts - * on the card before the buffers are being released. - * Otherwise one might experience some `interesting' - * effects. - * - * Then release the RX buffers - jumbo buffers were - * already released in ace_close(). - */ - synchronize_irq(); + /* + * Make sure no other CPUs are processing interrupts + * on the card before the buffers are being released. + * Otherwise one might experience some `interesting' + * effects. + * + * Then release the RX buffers - jumbo buffers were + * already released in ace_close(). + */ + synchronize_irq(); - for (i = 0; i < RX_STD_RING_ENTRIES; i++) { - struct sk_buff *skb = ap->skb->rx_std_skbuff[i].skb; + for (i = 0; i < RX_STD_RING_ENTRIES; i++) { + struct sk_buff *skb = ap->skb->rx_std_skbuff[i].skb; - if (skb) { + if (skb) { #ifndef DUMMY_PCI_UNMAP - dma_addr_t mapping; + dma_addr_t mapping; - mapping = ap->skb->rx_std_skbuff[i].mapping; - pci_unmap_single(ap->pdev, mapping, - ACE_STD_BUFSIZE - (2 + 16), - PCI_DMA_FROMDEVICE); + mapping = ap->skb->rx_std_skbuff[i].mapping; + pci_unmap_single(ap->pdev, mapping, + ACE_STD_BUFSIZE - (2 + 16), + PCI_DMA_FROMDEVICE); #endif - ap->rx_std_ring[i].size = 0; - ap->skb->rx_std_skbuff[i].skb = NULL; - dev_kfree_skb(skb); - } + ap->rx_std_ring[i].size = 0; + ap->skb->rx_std_skbuff[i].skb = NULL; + dev_kfree_skb(skb); } - if (ap->version >= 2) { - for (i = 0; i < RX_MINI_RING_ENTRIES; i++) { - struct sk_buff *skb = ap->skb->rx_mini_skbuff[i].skb; - - if (skb) { -#ifndef DUMMY_PCI_UNMAP - dma_addr_t mapping; + } + if (ap->version >= 2) { + for (i = 0; i < RX_MINI_RING_ENTRIES; i++) { + struct sk_buff *skb = ap->skb->rx_mini_skbuff[i].skb; - mapping = ap->skb->rx_mini_skbuff[i].mapping; - pci_unmap_single(ap->pdev, mapping, - ACE_MINI_BUFSIZE - (2 + 16), - PCI_DMA_FROMDEVICE); -#endif - ap->rx_mini_ring[i].size = 0; - ap->skb->rx_mini_skbuff[i].skb = NULL; - dev_kfree_skb(skb); - } - } - } - for (i = 0; i < RX_JUMBO_RING_ENTRIES; i++) { - struct sk_buff *skb = ap->skb->rx_jumbo_skbuff[i].skb; if (skb) { #ifndef DUMMY_PCI_UNMAP dma_addr_t mapping; - mapping = ap->skb->rx_jumbo_skbuff[i].mapping; + mapping = ap->skb->rx_mini_skbuff[i].mapping; pci_unmap_single(ap->pdev, mapping, - ACE_JUMBO_BUFSIZE - (2 + 16), + ACE_MINI_BUFSIZE - (2 + 16), PCI_DMA_FROMDEVICE); #endif - - ap->rx_jumbo_ring[i].size = 0; - ap->skb->rx_jumbo_skbuff[i].skb = NULL; + ap->rx_mini_ring[i].size = 0; + ap->skb->rx_mini_skbuff[i].skb = NULL; dev_kfree_skb(skb); } } - - ace_init_cleanup(root_dev); - kfree(root_dev); - root_dev = next; } -} - - -int __init ace_module_init(void) -{ - int status; - - root_dev = NULL; + for (i = 0; i < RX_JUMBO_RING_ENTRIES; i++) { + struct sk_buff *skb = ap->skb->rx_jumbo_skbuff[i].skb; + if (skb) { +#ifndef DUMMY_PCI_UNMAP + dma_addr_t mapping; -#ifdef NEW_NETINIT - status = acenic_probe(); -#else - status = acenic_probe(NULL); + mapping = ap->skb->rx_jumbo_skbuff[i].mapping; + pci_unmap_single(ap->pdev, mapping, + ACE_JUMBO_BUFSIZE - (2 + 16), + PCI_DMA_FROMDEVICE); #endif - return status; -} + ap->rx_jumbo_ring[i].size = 0; + ap->skb->rx_jumbo_skbuff[i].skb = NULL; + dev_kfree_skb(skb); + } + } -#if (LINUX_VERSION_CODE < 0x02032a) -#ifdef MODULE -int init_module(void) -{ - return ace_module_init(); + ace_init_cleanup(dev); + kfree(dev); } - -void cleanup_module(void) -{ - ace_module_cleanup(); -} -#endif -#else -module_init(ace_module_init); -module_exit(ace_module_cleanup); -#endif - - static void ace_free_descriptors(struct net_device *dev) { struct ace_private *ap = dev->priv; @@ -1324,13 +1217,6 @@ goto init_error; } - /* - * Register the device here to be able to catch allocated - * interrupt handlers in case the firmware doesn't come up. - */ - ap->next = root_dev; - root_dev = dev; - #ifdef INDEX_DEBUG spin_lock_init(&ap->debug_lock); ap->last_tx = TX_RING_ENTRIES - 1; @@ -3338,3 +3224,45 @@ * compile-command: "gcc -D__KERNEL__ -DMODULE -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -pipe -fno-strength-reduce -DMODVERSIONS -include ../../include/linux/modversions.h -c -o acenic.o acenic.c" * End: */ + +static struct pci_device_id acenic_pci_tbl[] __initdata = { + { PCI_VENDOR_ID_ALTEON, PCI_DEVICE_ID_ALTEON_ACENIC_FIBRE, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + { PCI_VENDOR_ID_ALTEON, PCI_DEVICE_ID_ALTEON_ACENIC_COPPER, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3C985, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + { PCI_VENDOR_ID_NETGEAR, PCI_DEVICE_ID_NETGEAR_GA620, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + { PCI_VENDOR_ID_NETGEAR, PCI_DEVICE_ID_NETGEAR_GA620T, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + /* + * Farallon used the DEC vendor ID on their cards incorrectly. + */ + { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_FARALLON_PN9000SX, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + { PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_ACENIC, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_NETWORK_ETHERNET << 8, 0xffff00, }, + { } +}; +MODULE_DEVICE_TABLE(pci, acenic_pci_tbl); + +static struct pci_driver acenic_driver = { + name: "acenic", + id_table: acenic_pci_tbl, + probe: acenic_init_one, + remove: acenic_remove_one, +}; + +static int __init acenic_init_module(void) +{ + return pci_module_init(&acenic_driver); +} + +static void __exit acenic_cleanup_module(void) +{ + pci_unregister_driver(&acenic_driver); +} + +module_init(acenic_init_module); +module_exit(acenic_cleanup_module); diff -uNr --exclude=CVS linux-2.4.5-ac18/drivers/net/pcnet32.c linuxppc64_2_4/drivers/net/pcnet32.c --- linux-2.4.5-ac18/drivers/net/pcnet32.c Tue Jun 26 21:59:18 2001 +++ linuxppc64_2_4/drivers/net/pcnet32.c Fri Jun 22 09:47:42 2001 @@ -293,7 +293,7 @@ static int pcnet32_probe_vlbus(int cards_found); static int pcnet32_probe_pci(struct pci_dev *, const struct pci_device_id *); -static int pcnet32_probe1(unsigned long, unsigned char, int, int, struct pci_dev *); +static int pcnet32_probe1(unsigned long, unsigned int, int, int, struct pci_dev *); static int pcnet32_open(struct net_device *); static int pcnet32_init_ring(struct net_device *); static int pcnet32_start_xmit(struct sk_buff *, struct net_device *); @@ -316,7 +316,7 @@ const char *name; u16 vendor_id, device_id, svid, sdid, flags; int io_size; - int (*probe1) (unsigned long, unsigned char, int, int, struct pci_dev *); + int (*probe1) (unsigned long, unsigned int, int, int, struct pci_dev *); }; @@ -449,7 +449,9 @@ static int __init pcnet32_probe_vlbus(int cards_found) { unsigned long ioaddr = 0; // FIXME dev ? dev->base_addr: 0; +#ifndef __powerpc__ unsigned int irq_line = 0; // FIXME dev ? dev->irq : 0; +#endif int *port; printk(KERN_INFO "pcnet32_probe_vlbus: cards_found=%d\n", cards_found); @@ -517,7 +519,7 @@ * pdev will be NULL when called from pcnet32_probe_vlbus. */ static int __devinit -pcnet32_probe1(unsigned long ioaddr, unsigned char irq_line, int shared, int card_idx, struct pci_dev *pdev) +pcnet32_probe1(unsigned long ioaddr, unsigned int irq_line, int shared, int card_idx, struct pci_dev *pdev) { struct pcnet32_private *lp; dma_addr_t lp_dma_addr; @@ -535,12 +537,13 @@ pcnet32_dwio_reset(ioaddr); pcnet32_wio_reset(ioaddr); - /* NOTE: 16-bit check is first, otherwise some older PCnet chips fail */ - if (pcnet32_wio_read_csr (ioaddr, 0) == 4 && pcnet32_wio_check (ioaddr)) { - a = &pcnet32_wio; + /* Important to do the check for dwio mode first. */ + if (pcnet32_dwio_read_csr(ioaddr, 0) == 4 && pcnet32_dwio_check(ioaddr)) { + a = &pcnet32_dwio; } else { - if (pcnet32_dwio_read_csr (ioaddr, 0) == 4 && pcnet32_dwio_check(ioaddr)) { - a = &pcnet32_dwio; + if (pcnet32_wio_read_csr(ioaddr, 0) == 4 && + pcnet32_wio_check(ioaddr)) { + a = &pcnet32_wio; } else return -ENODEV; } @@ -651,19 +654,31 @@ for (i = 0; i < 6; i++) { promaddr[i] = inb(ioaddr + i); } + printk("csraddr: "); + for (i = 0; i < 6; i++) + printk(" %2.2x", dev->dev_addr[i] ); + printk("\npromaddr: "); + for (i = 0; i < 6; i++) + printk(" %2.2x", promaddr[i] ); + printk("\n"); if( memcmp( promaddr, dev->dev_addr, 6) ) - { - printk(" warning PROM address does not match CSR address\n"); -#if defined(__i386__) - printk(KERN_WARNING "%s: Probably a Compaq, using the PROM address of", dev->name); - memcpy(dev->dev_addr, promaddr, 6); -#endif - } + printk(" warning: PROM address does not match CSR address\n"); + if( !is_valid_ether_addr(dev->dev_addr) ) { + printk("bad csr addr\n"); + if (!is_valid_ether_addr(promaddr)) { + printk("bad promaddr\n"); + /* if neither ethernet address is not valid, force to 00:00:00:00:00:00 */ + for (i = 0; i < 6; i++) + dev->dev_addr[i]=0; + } + else + { + printk(" warning: Using PROM address\n"); + for (i = 0; i < 6; i++) + dev->dev_addr[i]=promaddr[i]; + } + } } - /* if the ethernet address is not valid, force to 00:00:00:00:00:00 */ - if( !is_valid_ether_addr(dev->dev_addr) ) - for (i = 0; i < 6; i++) - dev->dev_addr[i]=0; for (i = 0; i < 6; i++) printk(" %2.2x", dev->dev_addr[i] ); @@ -887,7 +902,7 @@ lp->init_block.filter[1] = 0x00000000; if (pcnet32_init_ring(dev)) return -ENOMEM; - + /* Re-initialize the PCNET32, and start it when done. */ lp->a.write_csr (ioaddr, 1, (lp->dma_addr + offsetof(struct pcnet32_private, init_block)) &0xffff); lp->a.write_csr (ioaddr, 2, (lp->dma_addr + offsetof(struct pcnet32_private, init_block)) >> 16); @@ -969,7 +984,7 @@ } skb_reserve (rx_skbuff, 2); } - lp->rx_dma_addr[i] = pci_map_single(lp->pci_dev, rx_skbuff->tail, rx_skbuff->len, PCI_DMA_FROMDEVICE); + lp->rx_dma_addr[i] = pci_map_single(lp->pci_dev, rx_skbuff->tail, PKT_BUF_SZ-2, PCI_DMA_FROMDEVICE); lp->rx_ring[i].base = (u32)le32_to_cpu(lp->rx_dma_addr[i]); lp->rx_ring[i].buf_length = le16_to_cpu(-PKT_BUF_SZ); lp->rx_ring[i].status = le16_to_cpu(0x8000); @@ -1303,7 +1318,8 @@ skb_put (skb, pkt_len); lp->rx_skbuff[entry] = newskb; newskb->dev = dev; - lp->rx_dma_addr[entry] = pci_map_single(lp->pci_dev, newskb->tail, newskb->len, PCI_DMA_FROMDEVICE); + pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[entry], PKT_BUF_SZ-2, PCI_DMA_FROMDEVICE); + lp->rx_dma_addr[entry] = pci_map_single(lp->pci_dev, newskb->tail, PKT_BUF_SZ-2, PCI_DMA_FROMDEVICE); lp->rx_ring[entry].base = le32_to_cpu(lp->rx_dma_addr[entry]); rx_in_place = 1; } else @@ -1389,7 +1405,7 @@ for (i = 0; i < RX_RING_SIZE; i++) { lp->rx_ring[i].status = 0; if (lp->rx_skbuff[i]) { - pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i], lp->rx_skbuff[i]->len, PCI_DMA_FROMDEVICE); + pci_unmap_single(lp->pci_dev, lp->rx_dma_addr[i], PKT_BUF_SZ-2, PCI_DMA_FROMDEVICE); dev_kfree_skb(lp->rx_skbuff[i]); } lp->rx_skbuff[i] = NULL; diff -uNr --exclude=CVS linux-2.4.5-ac18/drivers/net/veth-proc.c linuxppc64_2_4/drivers/net/veth-proc.c --- linux-2.4.5-ac18/drivers/net/veth-proc.c Wed Dec 31 18:00:00 1969 +++ linuxppc64_2_4/drivers/net/veth-proc.c Fri May 4 17:12:47 2001 @@ -0,0 +1,73 @@ +/* File veth-proc.c created by Kyle A. Lucke on Wed Oct 18 2000. */ + +/* Change Activity: */ +/* End Change Activity */ + +#ifndef _VETH_PROC_H +#include +#endif +#ifndef _HVTYPES_H +#include +#endif +#ifndef _HVLPCONFIG_H +#include +#endif +#ifndef _VETH_H +#include "veth.h" +#endif + + +static struct proc_dir_entry * veth_proc_root = NULL; + +void veth_proc_init(struct proc_dir_entry *iSeries_proc) +{ + long i=0; + HvLpIndex thisLp = HvLpConfig_getLpIndex(); + u16 vlanMap = HvLpConfig_getVirtualLanIndexMap(); + long vlanIndex = 0; + + + veth_proc_root = proc_mkdir("veth", iSeries_proc); + if (!veth_proc_root) return; + + for (i=0; i < HvMaxArchitectedLps; ++i) + { + if (i != thisLp) + { + if (HvLpConfig_doLpsCommunicateOnVirtualLan(thisLp, i)) + { + struct proc_dir_entry *ent; + char name[10] = ""; + sprintf(name, "lp%ld", i); + ent = create_proc_entry(name, S_IFREG|S_IRUSR, veth_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)i; + ent->read_proc = proc_veth_dump_connection; + ent->write_proc = NULL; + } + } + } + + while (vlanMap != 0) + { + int bitOn = vlanMap & 0x8000; + + if (bitOn) + { + struct proc_dir_entry *ent; + char name[10] = ""; + sprintf(name, "veth%ld", vlanIndex); + ent = create_proc_entry(name, S_IFREG|S_IRUSR, veth_proc_root); + if (!ent) return; + ent->nlink = 1; + ent->data = (void *)vlanIndex; + ent->read_proc = proc_veth_dump_port; + ent->write_proc = NULL; + } + + ++vlanIndex; + vlanMap = vlanMap << 1; + } +} + diff -uNr --exclude=CVS linux-2.4.5-ac18/drivers/net/veth.c linuxppc64_2_4/drivers/net/veth.c --- linux-2.4.5-ac18/drivers/net/veth.c Tue Jun 26 21:59:18 2001 +++ linuxppc64_2_4/drivers/net/veth.c Fri May 4 17:12:47 2001 @@ -2,24 +2,6 @@ /**************************************************************************/ /* */ -/* IBM eServer iSeries Virtual Ethernet Device Driver */ -/* Copyright (C) 2001 Kyle A. Lucke (klucke@raleigh.ibm.com), IBM Corp. */ -/* */ -/* This program is free software; you can redistribute it and/or modify */ -/* it under the terms of the GNU General Public License as published by */ -/* the Free Software Foundation; either version 2 of the License, or */ -/* (at your option) any later version. */ -/* */ -/* This program is distributed in the hope that it will be useful, */ -/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ -/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ -/* GNU General Public License for more details. */ -/* */ -/* You should have received a copy of the GNU General Public License */ -/* along with this program; if not, write to the Free Software */ -/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 */ -/* USA */ -/* */ /* This module contains the implementation of a virtual ethernet device */ /* for use with iSeries LPAR Linux. It utilizes low-level message passing*/ /* provided by the hypervisor to enable an ethernet-like network device */ @@ -80,10 +62,10 @@ /* */ /* Tunable parameters: */ /* */ -/* VethBuffersToAllocate: This compile time option defaults to 120. It can*/ +/* VethBuffersToAllocate: This compile time option defaults to 80. It can*/ /* be safely changed to something greater or less than the default. It */ /* controls how much memory Linux will allocate per remote partition it is*/ -/* communicating with. The user can play with this to see how it affects */ +/* communicating with. The use can play with this to see how it affects */ /* performance, packets dropped, etc. Without trying to understand the */ /* complete driver, it can be thought of as the maximum number of packets */ /* outstanding to a remote partition at a time. */ @@ -112,35 +94,14 @@ #ifndef _HVLPCONFIG_H #include #endif -#ifndef _VETH_PROC_H -#include -#endif -#ifndef _HVTYPES_H -#include -#endif -#ifndef _ISERIES_PROC_H -#include -#endif -#include -#include - - -#define veth_printk(fmt, args...) \ -printk(KERN_INFO "%s: " fmt, __FILE__, ## args) - -#define veth_error_printk(fmt, args...) \ -printk(KERN_ERR "(%s:%3.3d) ERROR: " fmt, __FILE__, __LINE__ , ## args) static const char __initdata *version = -"v0.9 02/15/2001 Kyle Lucke, klucke@raleigh.ibm.com, klucke@us.ibm.com\n"; +"veth.c: v0.1 01/9/2001 Kyle Lucke, klucke@us.ibm.com\n"; static int probed __initdata = 0; #define VethBuffersToAllocate 120 static struct VethFabricMgr *mFabricMgr = NULL; -static struct proc_dir_entry * veth_proc_root = NULL; - -DECLARE_MUTEX_LOCKED(VethProcSemaphore); static int veth_open(struct net_device *dev); static int veth_close(struct net_device *dev); @@ -150,11 +111,10 @@ static void veth_handleAck(struct HvLpEvent *); static void veth_handleInt(struct HvLpEvent *); static void veth_openConnections(void); -static void veth_openConnection(u8, int lockMe); -static void veth_closeConnection(u8, int lockMe); +static void veth_openConnection(u8); +static void veth_closeConnection(u8); static void veth_intFinishOpeningConnections(void *, int number); static void veth_finishOpeningConnections(void *); -static void veth_finishOpeningConnectionsLocked(struct VethLpConnection *); static int veth_multicast_wanted(struct VethPort *port, u64 dest); static void veth_set_multicast_list(struct net_device *dev); @@ -175,13 +135,8 @@ static void veth_finishMsgsInit(struct VethLpConnection *connection); static void veth_intFinishCapBh(void *, int); static void veth_finishCapBh(struct VethLpConnection *connection); -static void veth_finishCapBhLocked(struct VethLpConnection *connection); static void veth_finishSendCap(struct VethLpConnection *connection); static void veth_timedAck(unsigned long connectionPtr); -#ifdef MODULE -static void veth_waitForEnd(void); -#endif -static void veth_failMe(struct VethLpConnection *connection); int __init veth_probe(void) { @@ -208,18 +163,19 @@ dev = init_vethdev(NULL, sizeof(struct VethPort), vlanIndex); if (dev == NULL) { - veth_error_printk("Unable to allocate net_device structure!\n"); + printk(KERN_ERR "veth.c: Unable to allocate " + "net_device structure!\n"); break; } if (!dev->priv) dev->priv = kmalloc(sizeof(struct VethPort), GFP_KERNEL); if (!dev->priv) { - veth_error_printk("Unable to allocate memory\n"); + printk(KERN_ERR "veth.c: Unable to allocate memory\n"); return -ENOMEM; } - veth_printk("Found an ethernet device %s (veth=%d) (addr=%p)\n", dev->name, vlanIndex, dev); + printk("veth.c: Found an ethernet device %s (veth=%d) (addr=%p)\n", dev->name, vlanIndex, dev->priv); port = mFabricMgr->mPorts[vlanIndex] = (struct VethPort *)dev->priv; memset(port, 0, sizeof(struct VethPort)); rwlock_init(&(port->mMcastGate)); @@ -230,8 +186,8 @@ dev->dev_addr[2] = 0xFF; dev->dev_addr[3] = vlanIndex; dev->dev_addr[4] = 0xFF; - dev->dev_addr[5] = HvLpConfig_getLpIndex_outline(); - dev->mtu = 9000; + dev->dev_addr[5] = HvLpConfig_getLpIndex(); + dev->mtu = 9000; memcpy(&(port->mMyAddress), dev->dev_addr, 6); @@ -248,7 +204,7 @@ /* set display flag to TRUE so that */ /* we only display this string ONCE */ displayVersion = 1; - veth_printk("%s", version); + printk(version); } } @@ -264,201 +220,57 @@ } #ifdef MODULE -MODULE_AUTHOR("Kyle Lucke , "); -MODULE_DESCRIPTION("iSeries Virtual ethernet driver"); - -DECLARE_MUTEX_LOCKED(VethModuleBhDone); -int VethModuleReopen = 1; - -void veth_proc_delete(struct proc_dir_entry *iSeries_proc) -{ - int i=0; - HvLpIndex thisLp = HvLpConfig_getLpIndex_outline(); - u16 vlanMap = HvLpConfig_getVirtualLanIndexMap(); - int vlanIndex = 0; - - for (i=0; i < HvMaxArchitectedLps; ++i) - { - if (i != thisLp) - { - if (HvLpConfig_doLpsCommunicateOnVirtualLan(thisLp, i)) - { - char name[10] = ""; - sprintf(name, "lp%d", i); - remove_proc_entry(name, veth_proc_root); - } - } - } - - while (vlanMap != 0) - { - int bitOn = vlanMap & 0x8000; - - if (bitOn) - { - char name[10] = ""; - sprintf(name, "veth%d", vlanIndex); - remove_proc_entry(name, veth_proc_root); - } - - ++vlanIndex; - vlanMap = vlanMap << 1; - } - - remove_proc_entry("veth", iSeries_proc); - - up(&VethProcSemaphore); -} - -void veth_waitForEnd(void) -{ - up(&VethModuleBhDone); -} +MODULE_AUTHOR("Kyle Lucke "); +MODULE_DESCRIPTION("Virtual iSeries ethernet driver"); void __exit veth_module_cleanup(void) { int i; - struct VethFabricMgr *myFm = mFabricMgr; - struct tq_struct myBottomHalf; - struct net_device *thisOne = NULL; - - VethModuleReopen = 0; - - for (i = 0; i < HvMaxArchitectedLps; ++i) - { - veth_closeConnection(i, 1); - } - - myBottomHalf.routine = (void *)(void *)veth_waitForEnd; - - queue_task(&myBottomHalf, &tq_immediate); - mark_bh(IMMEDIATE_BH); - - down(&VethModuleBhDone); - - HvLpEvent_unregisterHandler(HvLpEvent_Type_VirtualLan); - - mb(); - mFabricMgr = NULL; - mb(); - - down(&VethProcSemaphore); - - iSeries_proc_callback(&veth_proc_delete); - - down(&VethProcSemaphore); - - for (i = 0; i < HvMaxArchitectedLps; ++i) + for (i = 0; i < HvMaxArchitectedVirtualLans; ++i) { - if (myFm->mConnection[i].mNumberAllocated + myFm->mConnection[i].mNumberRcvMsgs > 0) - { - mf_deallocateLpEvents(myFm->mConnection[i].mRemoteLp, - HvLpEvent_Type_VirtualLan, - myFm->mConnection[i].mNumberAllocated + myFm->mConnection[i].mNumberRcvMsgs, - NULL, - NULL); - } + struct net_device *thisOne = mFabricMgr->mPorts[i]->mDev; + mFabricMgr->mPorts[i] = NULL; - if (myFm->mConnection[i].mMsgs != NULL) + if (thisOne != NULL) { - kfree(myFm->mConnection[i].mMsgs); + printk("veth.c: Unregistering veth %s (veth=%d)\n", thisOne->name, i); + unregister_netdev(thisOne); } } - for (i = 0; i < HvMaxArchitectedVirtualLans; ++i) + for (i = 0; i < HvMaxArchitectedLps; ++i) { - if (myFm->mPorts[i] != NULL) - { - thisOne = myFm->mPorts[i]->mDev; - myFm->mPorts[i] = NULL; + int j = 0; + int numMsgs = mFabricMgr->mConnection[i].mNumMsgs; - mb(); + veth_closeConnection(i); - if (thisOne != NULL) - { - veth_printk("Unregistering %s (veth=%d)\n", thisOne->name, i); - unregister_netdev(thisOne); - } - } + kfree(mFabricMgr->mConnection[i].mMsgs); } - kfree(myFm); + + HvLpEvent_unregisterHandler(HvLpEvent_Type_VirtualLan); } module_exit(veth_module_cleanup); #endif - -void veth_proc_init(struct proc_dir_entry *iSeries_proc) -{ - int i=0; - HvLpIndex thisLp = HvLpConfig_getLpIndex_outline(); - u16 vlanMap = HvLpConfig_getVirtualLanIndexMap(); - int vlanIndex = 0; - - - veth_proc_root = proc_mkdir("veth", iSeries_proc); - if (!veth_proc_root) return; - - for (i=0; i < HvMaxArchitectedLps; ++i) - { - if (i != thisLp) - { - if (HvLpConfig_doLpsCommunicateOnVirtualLan(thisLp, i)) - { - struct proc_dir_entry *ent; - char name[10] = ""; - sprintf(name, "lpar%d", i); - ent = create_proc_entry(name, S_IFREG|S_IRUSR, veth_proc_root); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)i; - ent->read_proc = proc_veth_dump_connection; - ent->write_proc = NULL; - } - } - } - - while (vlanMap != 0) - { - int bitOn = vlanMap & 0x8000; - - if (bitOn) - { - struct proc_dir_entry *ent; - char name[10] = ""; - sprintf(name, "veth%d", vlanIndex); - ent = create_proc_entry(name, S_IFREG|S_IRUSR, veth_proc_root); - if (!ent) return; - ent->nlink = 1; - ent->data = (void *)vlanIndex; - ent->read_proc = proc_veth_dump_port; - ent->write_proc = NULL; - } - - ++vlanIndex; - vlanMap = vlanMap << 1; - } - - up(&VethProcSemaphore); -} - int __init veth_module_init(void) { int status; int i; + printk("veth.c: Initializing veth module\n"); mFabricMgr = kmalloc(sizeof(struct VethFabricMgr), GFP_KERNEL); memset(mFabricMgr, 0, sizeof(struct VethFabricMgr)); - veth_printk("Initializing veth module, fabric mgr (address=%p)\n", mFabricMgr); mFabricMgr->mEyecatcher = 0x56455448464D4752ULL; - mFabricMgr->mThisLp = HvLpConfig_getLpIndex_outline(); + mFabricMgr->mThisLp = HvLpConfig_getLpIndex(); for (i=0; i < HvMaxArchitectedLps; ++i) { mFabricMgr->mConnection[i].mEyecatcher = 0x564554484C50434EULL; - veth_failMe(mFabricMgr->mConnection+i); + mFabricMgr->mConnection[i].mConnectionStatus.mFailed = 1; spin_lock_init(&mFabricMgr->mConnection[i].mAckGate); spin_lock_init(&mFabricMgr->mConnection[i].mStatusGate); } @@ -470,23 +282,11 @@ veth_openConnections(); } - iSeries_proc_callback(&veth_proc_init); - return status; } module_init(veth_module_init); -static void veth_failMe(struct VethLpConnection *connection) -{ - connection->mConnectionStatus.mSentCap = 0; - connection->mConnectionStatus.mCapAcked = 0; - connection->mConnectionStatus.mGotCap = 0; - connection->mConnectionStatus.mGotCapAcked = 0; - connection->mConnectionStatus.mSentMonitor = 0; - connection->mConnectionStatus.mFailed = 1; -} - static int veth_open(struct net_device *dev) { struct VethPort *port = (struct VethPort *)dev->priv; @@ -525,8 +325,7 @@ if (mFabricMgr == NULL) { - veth_error_printk("NULL fabric manager with active ports!\n"); - netif_stop_queue(dev); + netif_stop_queue(dev); return 1; } @@ -544,14 +343,14 @@ { if (i != mFabricMgr->mThisLp) { - if (clone) - skb = skb_clone(skb, GFP_ATOMIC); - else - clone = 1; - + if (clone) + skb = skb_clone(skb, GFP_ATOMIC); + else + clone = 1; + if (HvLpConfig_doLpsCommunicateOnVirtualLan(mFabricMgr->mThisLp, i)) { - /* the ack handles deleting the skb */ + // the ack handles deleting the skb veth_pTransmit(skb, i, dev); } } @@ -576,17 +375,17 @@ if ((skb->len > 14) && (skb->len <= 9018)) { - dma_addr_t dma_addr = pci_map_single(NULL, - skb->data, - skb->len, - PCI_DMA_TODEVICE); - - if (dma_addr != -1) - { + dma_addr_t dma_addr = pci_map_single(NULL, + skb->data, + skb->len, + PCI_DMA_TODEVICE); + + if (dma_addr != -1) + { msg->mSkb = skb; - msg->mEvent.mSendData.mAddress[0] = dma_addr; + msg->mEvent.mSendData.mAddress[0] = dma_addr; msg->mEvent.mSendData.mLength[0] = skb->len; - msg->mEvent.mSendData.mEofMask = 0xFFFFFFFFUL; + msg->mEvent.mSendData.mEofMask = 0xFFFFFFFFUL; test_and_set_bit(0, &(msg->mInUse)); @@ -603,24 +402,24 @@ msg->mEvent.mFpData.mData3, msg->mEvent.mFpData.mData4, msg->mEvent.mFpData.mData5); - } - else - { - returnCode = -1; /* Bad return code */ - } + } + else + { + returnCode = -1; // Bad return code + } if (returnCode != HvLpEvent_Rc_Good) { struct VethPort *port = (struct VethPort *)dev->priv; - - if (msg->mEvent.mSendData.mAddress[0]) - { - pci_unmap_single(NULL, dma_addr, skb->len, PCI_DMA_TODEVICE); - } - - dev_kfree_skb_irq(skb); - - msg->mSkb = NULL; + + if (msg->mEvent.mSendData.mAddress[0]) + { + pci_unmap_single(NULL, dma_addr, skb->len, PCI_DMA_TODEVICE); + } + + dev_kfree_skb_irq(skb); + + msg->mSkb = NULL; memset(&(msg->mEvent.mSendData), 0, sizeof(struct VethFramesData)); VETHSTACKPUSH(&(connection->mMsgStack), msg); port->mStats.tx_dropped++; @@ -723,7 +522,6 @@ } default: { - veth_error_printk("Unknown ack type %d from lpar %d\n", event->xSubtype, connection->mRemoteLp); } }; } @@ -762,7 +560,6 @@ } default: { - veth_error_printk("Unknown interrupt type %d from lpar %d\n", event->xSubtype, connection->mRemoteLp); } }; } @@ -782,11 +579,11 @@ { if (HvLpConfig_doLpsCommunicateOnVirtualLan(mFabricMgr->mThisLp, i)) { - veth_openConnection(i, 1); + veth_openConnection(i); } else { - veth_closeConnection(i, 1); + veth_closeConnection(i); } } } @@ -800,18 +597,13 @@ queue_task(&connection->mAllocBhTq, &tq_immediate); mark_bh(IMMEDIATE_BH); } - static void veth_finishOpeningConnections(void *parm) { - unsigned long flags; struct VethLpConnection *connection = (struct VethLpConnection *)parm; + unsigned long flags; + spin_lock_irqsave(&connection->mStatusGate, flags); - veth_finishOpeningConnectionsLocked(connection); - spin_unlock_irqrestore(&connection->mStatusGate, flags); -} -static void veth_finishOpeningConnectionsLocked(struct VethLpConnection *connection) -{ if (connection->mNumberAllocated >= 2) { connection->mConnectionStatus.mCapMonAlloced = 1; @@ -819,12 +611,13 @@ } else { - veth_error_printk("Couldn't allocate base msgs for lpar %d, only got %d\n", connection->mRemoteLp, connection->mNumberAllocated); - veth_failMe(connection); + connection->mConnectionStatus.mFailed = 1; } + + spin_unlock_irqrestore(&connection->mStatusGate, flags); } -static void veth_openConnection(u8 remoteLp, int lockMe) +static void veth_openConnection(u8 remoteLp) { unsigned long flags; unsigned long flags2; @@ -845,8 +638,7 @@ memset(&connection->mAllocBhTq, 0, sizeof(connection->mAllocBhTq)); connection->mAllocBhTq.routine = (void *)(void *)veth_finishOpeningConnections; - if (lockMe) - spin_lock_irqsave(&connection->mStatusGate, flags); + spin_lock_irqsave(&connection->mStatusGate, flags); connection->mRemoteLp = remoteLp; @@ -856,14 +648,19 @@ connection->mNumAcks = 0; HvCallEvent_openLpEventPath(remoteLp, HvLpEvent_Type_VirtualLan); + connection->mConnectionStatus.mOpen = 1; /* clean up non-acked msgs */ for (i=0; i < connection->mNumMsgs; ++i) { - veth_recycleMsg(connection, i); + if (test_and_clear_bit(0, &(connection->mMsgs[i].mInUse))) + { + struct VethMsg *myMsg = connection->mMsgs + i; + memset(myMsg->mEvent.mSendData.mLength, 0, sizeof(u16)*4); + VETHSTACKPUSH(&connection->mMsgStack, myMsg); + } } - connection->mConnectionStatus.mOpen = 1; source = connection->mSourceInst = HvCallEvent_getSourceLpInstanceId(remoteLp, HvLpEvent_Type_VirtualLan); target = connection->mTargetInst = HvCallEvent_getTargetLpInstanceId(remoteLp, HvLpEvent_Type_VirtualLan); @@ -880,28 +677,25 @@ } else { - veth_finishOpeningConnectionsLocked(connection); + veth_finishOpeningConnections(connection); } spin_unlock_irqrestore(&connection->mAckGate, flags2); - if (lockMe) - spin_unlock_irqrestore(&connection->mStatusGate, flags); + spin_unlock_irqrestore(&connection->mStatusGate, flags); } -static void veth_closeConnection(u8 remoteLp, int lockMe) +static void veth_closeConnection(u8 remoteLp) { struct VethLpConnection *connection = &(mFabricMgr->mConnection[remoteLp]); unsigned long flags; unsigned long flags2; - if (lockMe) - spin_lock_irqsave(&connection->mStatusGate, flags); + spin_lock_irqsave(&connection->mStatusGate, flags); if (connection->mConnectionStatus.mOpen == 1) { HvCallEvent_closeLpEventPath(remoteLp, HvLpEvent_Type_VirtualLan); connection->mConnectionStatus.mOpen = 0; - veth_failMe(connection); /* reset ack data */ spin_lock_irqsave(&connection->mAckGate, flags2); @@ -912,8 +706,7 @@ spin_unlock_irqrestore(&connection->mAckGate, flags2); } - if (lockMe) - spin_unlock_irqrestore(&connection->mStatusGate, flags); + spin_unlock_irqrestore(&connection->mStatusGate, flags); } static void veth_msgsInit(struct VethLpConnection *connection) @@ -994,7 +787,7 @@ connection->mMyCap.mUnionData.mFields.mThreshold = 10; else connection->mMyCap.mUnionData.mFields.mThreshold = 20; - + connection->mMyCap.mUnionData.mFields.mTimer = VethAckTimeoutUsec; veth_finishSendCap(connection); @@ -1038,8 +831,7 @@ } else if (returnCode != HvLpEvent_Rc_Good) { - veth_error_printk("Couldn't send cap to lpar %d, rc %Lx\n", connection->mRemoteLp, returnCode); - veth_failMe(connection); + connection->mConnectionStatus.mFailed = 1; } else { @@ -1058,7 +850,6 @@ } else { - veth_error_printk("Received a capabilities from lpar %d while already processing one\n", connection->mRemoteLp); event->mBaseEvent.xRc = HvLpEvent_Rc_BufferNotAvailable; HvCallEvent_ackLpEvent((struct HvLpEvent *)event); } @@ -1075,7 +866,7 @@ } else { - veth_error_printk("Received a capabilities ack from lpar %d while already processing one\n", connection->mRemoteLp); + /* here? */ } } @@ -1090,7 +881,7 @@ } else { - veth_error_printk("Received a monitor ack from lpar %d while already processing one\n", connection->mRemoteLp); + /* here? */ } } @@ -1101,13 +892,13 @@ struct VethMsg *myMsg = connection->mMsgs + msg; if (test_and_clear_bit(0, &(myMsg->mInUse))) { - pci_unmap_single(NULL, - myMsg->mEvent.mSendData.mAddress[0], - myMsg->mEvent.mSendData.mLength[0], - PCI_DMA_TODEVICE); - dev_kfree_skb_irq(myMsg->mSkb); + pci_unmap_single(NULL, + myMsg->mEvent.mSendData.mAddress[0], + myMsg->mEvent.mSendData.mLength[0], + PCI_DMA_TODEVICE); + dev_kfree_skb_irq(myMsg->mSkb); - myMsg->mSkb = NULL; + myMsg->mSkb = NULL; memset(&(myMsg->mEvent.mSendData), 0, sizeof(struct VethFramesData)); VETHSTACKPUSH(&connection->mMsgStack, myMsg); } @@ -1115,7 +906,6 @@ { if (connection->mConnectionStatus.mOpen) { - veth_error_printk("Received a frames ack for msg %d from lpar %d while not outstanding\n", msg, connection->mRemoteLp); } } } @@ -1151,11 +941,10 @@ connection); } else - veth_finishCapBhLocked(connection); + veth_finishCapBh(connection); } else { - veth_error_printk("Received incompatible capabilities from lpar %d\n", connection->mRemoteLp); event->mBaseEvent.xRc = HvLpEvent_Rc_InvalidSubtypeData; HvCallEvent_ackLpEvent((struct HvLpEvent *)event); } @@ -1176,7 +965,7 @@ connection->mConnectionStatus.mCapAcked = 1; if ((connection->mConnectionStatus.mGotCap == 1) && - (connection->mConnectionStatus.mGotCapAcked == 1)) + (connection->mConnectionStatus.mGotCapAcked == 1)) { if (connection->mConnectionStatus.mSentMonitor != 1) veth_sendMonitor(connection); @@ -1184,8 +973,7 @@ } else { - veth_error_printk("Bad rc(%d) from lpar %d on capabilities\n", event->mBaseEvent.xRc, connection->mRemoteLp); - veth_failMe(connection); + connection->mConnectionStatus.mFailed = 1; } clear_bit(0,&(connection->mCapAckBhPending)); @@ -1198,14 +986,18 @@ spin_lock_irqsave(&connection->mStatusGate, flags); - veth_failMe(connection); + connection->mConnectionStatus.mSentCap = 0; + connection->mConnectionStatus.mCapAcked = 0; + connection->mConnectionStatus.mGotCap = 0; + connection->mConnectionStatus.mGotCapAcked = 0; + connection->mConnectionStatus.mSentMonitor = 0; + connection->mConnectionStatus.mFailed = 1; - veth_printk("Monitor ack returned for lpar %d\n", connection->mRemoteLp); del_timer(&connection->mAckTimer); if (connection->mConnectionStatus.mOpen) { - veth_closeConnection(connection->mRemoteLp, 0); + veth_closeConnection(connection->mRemoteLp); udelay(100); @@ -1214,21 +1006,7 @@ } else { -#ifdef MODULE - if (VethModuleReopen) -#endif - veth_openConnection(connection->mRemoteLp, 0); -#ifdef MODULE - else - { - int i=0; - - for (i=0; i < connection->mNumMsgs; ++i) - { - veth_recycleMsg(connection, i); - } - } -#endif + veth_openConnection(connection->mRemoteLp); clear_bit(0,&(connection->mMonitorAckBhPending)); } @@ -1329,22 +1107,22 @@ if (connection->mNumAcks == connection->mRemoteCap.mUnionData.mFields.mThreshold) { HvLpEvent_Rc rc = HvCallEvent_signalLpEventFast(connection->mRemoteLp, - HvLpEvent_Type_VirtualLan, - VethEventTypeFramesAck, - HvLpEvent_AckInd_NoAck, - HvLpEvent_AckType_ImmediateAck, - connection->mSourceInst, - connection->mTargetInst, - 0, - connection->mEventData.mFpData.mData1, - connection->mEventData.mFpData.mData2, - connection->mEventData.mFpData.mData3, - connection->mEventData.mFpData.mData4, - connection->mEventData.mFpData.mData5); + HvLpEvent_Type_VirtualLan, + VethEventTypeFramesAck, + HvLpEvent_AckInd_NoAck, + HvLpEvent_AckType_ImmediateAck, + connection->mSourceInst, + connection->mTargetInst, + 0, + connection->mEventData.mFpData.mData1, + connection->mEventData.mFpData.mData2, + connection->mEventData.mFpData.mData3, + connection->mEventData.mFpData.mData4, + connection->mEventData.mFpData.mData5); if (rc != HvLpEvent_Rc_Good) { - veth_error_printk("Bad lp event return code(%Lx) acking frames from lpar %d\n", rc, connection->mRemoteLp); + printk(KERN_ERR "veth.c: Bad lp event return code(%Lx) acking frames!\n", rc); } connection->mNumAcks = 0; @@ -1363,38 +1141,38 @@ unsigned long flags; HvLpEvent_Rc rc; struct VethLpConnection *connection = (struct VethLpConnection *) connectionPtr; - /* Ack all the events */ + // Ack all the events spin_lock_irqsave(&connection->mAckGate, flags); if (connection->mNumAcks > 0) { - rc = HvCallEvent_signalLpEventFast(connection->mRemoteLp, - HvLpEvent_Type_VirtualLan, - VethEventTypeFramesAck, - HvLpEvent_AckInd_NoAck, - HvLpEvent_AckType_ImmediateAck, - connection->mSourceInst, - connection->mTargetInst, - 0, - connection->mEventData.mFpData.mData1, - connection->mEventData.mFpData.mData2, - connection->mEventData.mFpData.mData3, - connection->mEventData.mFpData.mData4, - connection->mEventData.mFpData.mData5); - - if (rc != HvLpEvent_Rc_Good) - { - veth_error_printk("Bad lp event return code(%Lx) acking frames from lpar %d!\n", rc, connection->mRemoteLp); - } + rc = HvCallEvent_signalLpEventFast(connection->mRemoteLp, + HvLpEvent_Type_VirtualLan, + VethEventTypeFramesAck, + HvLpEvent_AckInd_NoAck, + HvLpEvent_AckType_ImmediateAck, + connection->mSourceInst, + connection->mTargetInst, + 0, + connection->mEventData.mFpData.mData1, + connection->mEventData.mFpData.mData2, + connection->mEventData.mFpData.mData3, + connection->mEventData.mFpData.mData4, + connection->mEventData.mFpData.mData5); + + if (rc != HvLpEvent_Rc_Good) + { + printk(KERN_ERR "veth.c: Bad lp event return code(%Lx) acking frames!\n", rc); + } - connection->mNumAcks = 0; + connection->mNumAcks = 0; - memset(&connection->mEventData, 0xFF, sizeof(connection->mEventData)); + memset(&connection->mEventData, 0xFF, sizeof(connection->mEventData)); } - + spin_unlock_irqrestore(&connection->mAckGate, flags); - - /* Reschedule the timer */ + + // Reschedule the timer connection->mAckTimer.expires = jiffies + connection->mTimeout; add_timer(&connection->mAckTimer); } @@ -1443,40 +1221,32 @@ connection->mConnectionStatus.mSentMonitor = 1; connection->mConnectionStatus.mFailed = 0; - /* Start the ACK timer */ - init_timer(&connection->mAckTimer); - connection->mAckTimer.function = veth_timedAck; - connection->mAckTimer.data = (unsigned long) connection; - connection->mAckTimer.expires = jiffies + connection->mTimeout; - add_timer(&connection->mAckTimer); + // Start the ACK timer + init_timer(&connection->mAckTimer); + connection->mAckTimer.function = veth_timedAck; + connection->mAckTimer.data = (unsigned long) connection; + connection->mAckTimer.expires = jiffies + connection->mTimeout; + add_timer(&connection->mAckTimer); } else - { - veth_error_printk("Monitor send to lpar %d failed with rc %Lx\n", connection->mRemoteLp, returnCode); - veth_failMe(connection); - } + connection->mConnectionStatus.mFailed = 1; } static void veth_finishCapBh(struct VethLpConnection *connection) { - unsigned long flags; - spin_lock_irqsave(&connection->mStatusGate, flags); - veth_finishCapBhLocked(connection); - spin_unlock_irqrestore(&connection->mStatusGate, flags); -} - -static void veth_finishCapBhLocked(struct VethLpConnection *connection) -{ struct VethLpEvent *event = &connection->mCapEvent; + unsigned long flags; struct VethCapData *remoteCap = &(connection->mRemoteCap); int numAcks = (remoteCap->mUnionData.mFields.mNumberBuffers / remoteCap->mUnionData.mFields.mThreshold) + 1; - /* Convert timer to jiffies */ + spin_lock_irqsave(&connection->mStatusGate, flags); + + // Convert timer to jiffies if (connection->mMyCap.mUnionData.mFields.mTimer) - connection->mTimeout = remoteCap->mUnionData.mFields.mTimer * HZ / 1000000; + connection->mTimeout = remoteCap->mUnionData.mFields.mTimer * HZ / 1000000; else - connection->mTimeout = VethAckTimeoutUsec * HZ / 1000000; + connection->mTimeout = VethAckTimeoutUsec * HZ / 1000000; if (connection->mNumberLpAcksAlloced >= numAcks) { @@ -1500,31 +1270,28 @@ } else { - veth_error_printk("Failed to ack remote cap for lpar %d with rc %Lx\n", connection->mRemoteLp, returnCode); - veth_failMe(connection); + connection->mConnectionStatus.mFailed = 1; } } else { - veth_error_printk("Couldn't allocate all the frames ack events for lpar %d\n", connection->mRemoteLp); event->mBaseEvent.xRc = HvLpEvent_Rc_BufferNotAvailable; HvCallEvent_ackLpEvent((struct HvLpEvent *)event); } + + spin_unlock_irqrestore(&connection->mStatusGate, flags); } int proc_veth_dump_connection (char *page, char **start, off_t off, int count, int *eof, void *data) { char *out = page; - int whichConnection = (int) data; + long whichConnection = (long) data; int len = 0; struct VethLpConnection *connection = NULL; if ((whichConnection < 0) || (whichConnection > HvMaxArchitectedLps) || (mFabricMgr == NULL)) - { - veth_error_printk("Got bad data from /proc file system\n"); len = sprintf(page, "ERROR\n"); - } else { int thereWasStuffBefore = 0; @@ -1534,6 +1301,7 @@ out += sprintf(out, "Source Inst:\t%04X\n", connection->mSourceInst); out += sprintf(out, "Target Inst:\t%04X\n", connection->mTargetInst); out += sprintf(out, "Num Msgs:\t%d\n", connection->mNumMsgs); + out += sprintf(out, "Num Rcv Msgs:\t%d\n", connection->mNumberRcvMsgs); out += sprintf(out, "Num Lp Acks:\t%d\n", connection->mNumberLpAcksAlloced); out += sprintf(out, "Num Acks:\t%d\n", connection->mNumAcks); @@ -1641,16 +1409,24 @@ out += sprintf(out, "Capabilities (System:):\n"); out += sprintf(out, "\tLocal:<"); out += sprintf(out, "%d/%d/%d/%d>\n", - connection->mMyCap.mUnionData.mFields.mVersion, - connection->mMyCap.mUnionData.mFields.mNumberBuffers, - connection->mMyCap.mUnionData.mFields.mThreshold, - connection->mMyCap.mUnionData.mFields.mTimer); + connection->mMyCap.mUnionData.mFields.mVersion, + connection->mMyCap.mUnionData.mFields.mNumberBuffers, + connection->mMyCap.mUnionData.mFields.mThreshold, + connection->mMyCap.mUnionData.mFields.mTimer); out += sprintf(out, "\tRemote:<"); out += sprintf(out, "%d/%d/%d/%d>\n", - connection->mRemoteCap.mUnionData.mFields.mVersion, - connection->mRemoteCap.mUnionData.mFields.mNumberBuffers, - connection->mRemoteCap.mUnionData.mFields.mThreshold, - connection->mRemoteCap.mUnionData.mFields.mTimer); + connection->mRemoteCap.mUnionData.mFields.mVersion, + connection->mRemoteCap.mUnionData.mFields.mNumberBuffers, + connection->mRemoteCap.mUnionData.mFields.mThreshold, + connection->mRemoteCap.mUnionData.mFields.mTimer); + /* + struct VethMsg *mMsgs; + union + { + struct VethFramesAckData mAckData; + struct VethFastPathData mFpData; + } mEventData; + VETHSTACK(VethMsg) mMsgStack;*/ len = out - page; } len -= off; @@ -1668,7 +1444,7 @@ (char *page, char **start, off_t off, int count, int *eof, void *data) { char *out = page; - int whichPort = (int) data; + long whichPort = (long) data; int len = 0; struct VethPort *port = NULL; @@ -1700,7 +1476,7 @@ } else { - out += sprintf(page, "veth%d is not configured.\n", whichPort); + out += sprintf(page, "veth%ld is not configured.\n", whichPort); } len = out - page; @@ -1715,5 +1491,3 @@ *start = page + off; return len; } - - diff -uNr --exclude=CVS linux-2.4.5-ac18/drivers/pci/pci.c linuxppc64_2_4/drivers/pci/pci.c --- linux-2.4.5-ac18/drivers/pci/pci.c Tue Jun 26 21:55:54 2001 +++ linuxppc64_2_4/drivers/pci/pci.c Tue Jun 26 08:09:31 2001 @@ -745,6 +745,14 @@ u32 l, sz; struct resource *res; + /************************************************************ + * Check for architecture dependant call to read the BARs. + ************************************************************/ + if( dev->bus->ops->pci_read_bases != NULL) { + dev->bus->ops->pci_read_bases(dev, howmany, rom); + return; + } + for(pos=0; posresource[pos]; @@ -1026,6 +1034,14 @@ static void pci_read_irq(struct pci_dev *dev) { unsigned char irq; + + /************************************************************ + * Check for architecture dependant call to read and set irg + ************************************************************/ + if(dev->bus->ops->pci_read_irq != NULL) { + dev->bus->ops->pci_read_irq(dev); + return; + } pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq); if (irq) @@ -1047,7 +1063,17 @@ { u32 class; - sprintf(dev->slot_name, "%02x:%02x.%d", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + /* sprintf(dev->slot_name, "%02x:%02x.%d", dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); */ + + + /******************************************************************** + * Prefix the bus number with the phb number for large(>256 bus) systems. + ********************************************************************/ + sprintf(dev->slot_name, "%4x%02x:%02x.%1x", + ( (dev->bus->number>>8) &0xFF0000), + ( dev->bus->number&0x0000FF), + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + sprintf(dev->name, "PCI device %04x:%04x", dev->vendor, dev->device); pci_read_config_dword(dev, PCI_CLASS_REVISION, &class); @@ -1091,6 +1117,14 @@ printk(KERN_ERR "PCI: %s: class %x doesn't match header type %02x. Ignoring class.\n", dev->slot_name, class, dev->hdr_type); dev->class = PCI_CLASS_NOT_DEFINED; + } + + + /********************************************************************* + * Give the architure code a chance to fix up/tweak the devices. + *********************************************************************/ + if(dev->bus->ops->pci_fixup_registers != NULL) { + dev->bus->ops->pci_fixup_registers(dev); } /* We found a fine healthy device, go go go... */ diff -uNr --exclude=CVS linux-2.4.5-ac18/drivers/sbus/char/zs.c linuxppc64_2_4/drivers/sbus/char/zs.c --- linux-2.4.5-ac18/drivers/sbus/char/zs.c Tue Jun 26 21:55:56 2001 +++ linuxppc64_2_4/drivers/sbus/char/zs.c Sat Jun 23 01:38:24 2001 @@ -2072,8 +2072,8 @@ } else { pgd_t *pgd = pgd_offset_k((unsigned long)vaddr[0]); pmd_t *pmd = pmd_offset(pgd, (unsigned long)vaddr[0]); - pte_t *pte = pte_offset(pmd, (unsigned long)vaddr[0]); - unsigned long base = pte_val(*pte) & _PAGE_PADDR; + pte_t *ptep= pte_offset(pmd, (unsigned long)vaddr[0]); + unsigned long base = pte_val(fetch_pte(ptep)) & _PAGE_PADDR; /* Translate PROM's mapping we captured at boot * time into physical address. diff -uNr --exclude=CVS linux-2.4.5-ac18/drivers/scsi/osst.c linuxppc64_2_4/drivers/scsi/osst.c --- linux-2.4.5-ac18/drivers/scsi/osst.c Tue Jun 26 21:59:20 2001 +++ linuxppc64_2_4/drivers/scsi/osst.c Sat Jun 23 01:38:25 2001 @@ -31,6 +31,7 @@ #define OSST_FW_NEED_POLL_MAX 10704 /*(108D)*/ #define OSST_FW_NEED_POLL(x,d) ((x) >= OSST_FW_NEED_POLL_MIN && (x) <= OSST_FW_NEED_POLL_MAX && d->host->this_id != 7) +#include #include #include diff -uNr --exclude=CVS linux-2.4.5-ac18/drivers/scsi/scsi_scan.c linuxppc64_2_4/drivers/scsi/scsi_scan.c --- linux-2.4.5-ac18/drivers/scsi/scsi_scan.c Tue Jun 26 21:59:20 2001 +++ linuxppc64_2_4/drivers/scsi/scsi_scan.c Wed Jun 27 13:43:42 2001 @@ -301,8 +301,18 @@ scsi_initialize_queue(SDpnt, shpnt); SDpnt->request_queue.queuedata = (void *) SDpnt; /* Make sure we have something that is valid for DMA purposes */ + + /* + * Using memory from the stack is horribly ugly and fails for + * ppc64. Better kmalloc the memory. + * -- tibit + */ + /* scsi_result = ((!shpnt->unchecked_isa_dma) ? &scsi_result0[0] : kmalloc(512, GFP_DMA)); + */ + + scsi_result = kmalloc(512, GFP_DMA); } if (scsi_result == NULL) { diff -uNr --exclude=CVS linux-2.4.5-ac18/drivers/scsi/sr_ioctl.c linuxppc64_2_4/drivers/scsi/sr_ioctl.c --- linux-2.4.5-ac18/drivers/scsi/sr_ioctl.c Tue Jun 26 21:59:20 2001 +++ linuxppc64_2_4/drivers/scsi/sr_ioctl.c Wed Jun 27 13:43:42 2001 @@ -107,7 +107,6 @@ if (!scsi_block_when_processing_errors(SDev)) return -ENODEV; - scsi_wait_req(SRpnt, (void *) sr_cmd, (void *) buffer, buflength, IOCTL_TIMEOUT, IOCTL_RETRIES); @@ -334,7 +333,12 @@ { u_char sr_cmd[10]; int result, target = MINOR(cdi->dev); - unsigned char buffer[32]; + unsigned char *buffer = scsi_malloc(512); + + if (buffer == NULL) { + printk("SCSI DMA pool exhausted."); + return -ENOMEM; + } memset(sr_cmd, 0, sizeof(sr_cmd)); @@ -407,6 +411,7 @@ return -EINVAL; } + scsi_free(buffer, 512); #if 0 if (result) printk("DEBUG: sr_audio: result for ioctl %x: %x\n", cmd, result); diff -uNr --exclude=CVS linux-2.4.5-ac18/drivers/usb/ov511.c linuxppc64_2_4/drivers/usb/ov511.c --- linux-2.4.5-ac18/drivers/usb/ov511.c Tue Jun 26 21:59:21 2001 +++ linuxppc64_2_4/drivers/usb/ov511.c Mon Jun 18 18:19:09 2001 @@ -240,7 +240,7 @@ pmd = pmd_offset(pgd, adr); if (!pmd_none(*pmd)) { ptep = pte_offset(pmd, adr); - pte = *ptep; + pte = fetch_pte(ptep); if (pte_present(pte)) { ret = (unsigned long) page_address(pte_page(pte)); ret |= (adr & (PAGE_SIZE - 1)); diff -uNr --exclude=CVS linux-2.4.5-ac18/drivers/video/offb.c linuxppc64_2_4/drivers/video/offb.c --- linux-2.4.5-ac18/drivers/video/offb.c Tue Jun 26 21:59:21 2001 +++ linuxppc64_2_4/drivers/video/offb.c Wed Aug 1 15:46:41 2001 @@ -32,7 +32,9 @@ #endif #include #include +#ifndef CONFIG_PPC64 #include +#endif #include