diff options
Diffstat (limited to 'arch/ppc')
74 files changed, 13172 insertions, 2332 deletions
diff --git a/arch/ppc/Makefile b/arch/ppc/Makefile index ac2719977..6c81701d9 100644 --- a/arch/ppc/Makefile +++ b/arch/ppc/Makefile @@ -1,6 +1,3 @@ -# -# ppc/Makefile -# # This file is included by the global makefile so that you can add your own # architecture-specific flags and dependencies. Remember to do have actions # for "archclean" and "archdep" for cleaning up and making dependencies for @@ -12,29 +9,28 @@ # # Copyright (C) 1994 by Linus Torvalds # Changes for PPC by Gary Thomas -# Modified by Cort Dougan +# Rewritten by Cort Dougan and Paul Mackerras # +ifeq ($(CONFIG_PMAC),y) +KERNELBASE =0xc0000000 +else +KERNELBASE =0x90000000 +endif + # PowerPC (cross) tools -SUFFIX = -AS = as$(SUFFIX) -ASFLAGS = -LD = ld$(SUFFIX) -LINKFLAGS = -T arch/ppc/ld.script -Ttext 0x90000000 -HOSTCC = gcc -CC = gcc$(SUFFIX) +ifneq ($(shell uname -m),ppc) +CROSS_COMPILE = ppc-linux-elf- +else +CHECKS = checks +endif + +ASFLAGS = +LINKFLAGS = -T arch/ppc/vmlinux.lds -Ttext $(KERNELBASE) -Bstatic CFLAGSINC = -D__KERNEL__ -I$(TOPDIR)/include -D__powerpc__ -CFLAGS = $(CFLAGSINC) \ - -Wstrict-prototypes -fomit-frame-pointer \ - -fno-builtin \ - -finhibit-size-directive \ - -O2 -fsigned-char -pipe -ffixed-r2 -mstring -mmultiple -msoft-float -# -fverbose-asm +CFLAGS := $(CFLAGS) -D__powerpc__ -fsigned-char -msoft-float -pipe \ + -fno-builtin -ffixed-r2 -Wno-uninitialized -mmultiple -mstring CPP = $(CC) -E $(CFLAGS) -AR = ar$(SUFFIX) -RANLIB = ranlib$(SUFFIX) -STRIP = strip$(SUFFIX) -NM = nm$(SUFFIX) ifdef CONFIG_601 CFLAGS := $(CFLAGS) -mcpu=601 -DCPU=601 @@ -55,59 +51,35 @@ SUBDIRS := $(SUBDIRS) $(ARCH_SUBDIRS) ARCHIVES := arch/ppc/kernel/kernel.o arch/ppc/mm/mm.o arch/ppc/lib/lib.o $(ARCHIVES) CORE_FILES := arch/ppc/kernel/kernel.o arch/ppc/mm/mm.o arch/ppc/lib/lib.o $(CORE_FILES) +ifdef CONFIG_XMON +SUBDIRS += arch/ppc/xmon +CORE_FILES += arch/ppc/xmon/x.o +endif + +ifdef CONFIG_PMAC +MAKEBOOT = $(MAKE) -C arch/$(ARCH)/coffboot +else +# PReP and CHRP systems MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot +endif checks: @$(MAKE) -C arch/$(ARCH)/kernel checks -netboot: checks vmlinux - @$(MAKEBOOT) netboot - -znetboot: checks vmlinux - @$(MAKEBOOT) znetboot +BOOT_TARGETS = netboot znetboot zImage floppy install \ + vmlinux.coff znetboot.initrd zImage.initrd -#rcpboot: checks vmlinux -# @$(MAKEBOOT) rcpboot +$(BOOT_TARGETS): $(CHECKS) vmlinux + @$(MAKEBOOT) $@ -zImage: checks vmlinux - @$(MAKEBOOT) zImage - -floppy: checks vmlinux - @$(MAKEBOOT) floppy - -install: checks vmlinux - @$(MAKEBOOT) install - -vmlinux.coff : checks vmlinux - $(MAKE) -C arch/ppc/coffboot/ vmlinux.coff - -arch/ppc/kernel: dummy - $(MAKE) linuxsubdirs SUBDIRS=arch/ppc/kernel - -arch/ppc/mm: dummy - $(MAKE) linuxsubdirs SUBDIRS=arch/ppc/mm - -arch/ppc/lib: dummy - $(MAKE) linuxsubdirs SUBDIRS=arch/ppc/lib - -diffs: - arch/ppc/mkdiff - -tar: - arch/ppc/mktar +tags: + etags */*.c include/{asm,linux}/*.h arch/ppc/kernel/*.{c,h} archclean: - rm -f arch/ppc/kernel/mk_defs arch/ppc/kernel/ppc_defs.h arch/ppc/kernel/checks TAGS - rm -f `find arch/ppc/ \( -name '*.[oas]' -o -name '*~' -o -name '#*#' \) -print` - rm -f `find include/asm-ppc/ \( -name '*.[oas]' -o -name '*~' -o -name '#*#' \) -print` + rm -f arch/ppc/kernel/mk_defs arch/ppc/kernel/ppc_defs.h + rm -f arch/ppc/kernel/checks @$(MAKEBOOT) clean archdep: - $(MAKE) -C arch/ppc/boot fastdep - $(MAKE) -C arch/ppc/kernel fastdep - $(MAKE) -C arch/ppc/mm fastdep - $(MAKE) -C arch/ppc/lib fastdep - -tags : - etags arch/ppc/*/*.c arch/ppc/*/*.S include/asm/* */*.c + $(MAKEBOOT) fastdep diff --git a/arch/ppc/boot/Makefile b/arch/ppc/boot/Makefile index 5dcac750e..a731ec2a0 100644 --- a/arch/ppc/boot/Makefile +++ b/arch/ppc/boot/Makefile @@ -22,58 +22,81 @@ $(CC) -D__ASSEMBLY__ -traditional -c -o $*.o $< -ZLINKFLAGS = -T ../ld.script -Ttext 0x00800000 +ZLINKFLAGS = -T ../vmlinux.lds -Ttext 0x00800000 GZIP_FLAGS = -v9 SYSTEM = $(TOPDIR)/vmlinux - -OBJECTS = head.o inflate.o unzip.o misc.o vreset.o - +OBJECTS := head.o inflate.o unzip.o misc.o vreset.o kbd.o CFLAGS = -O2 -DSTDC_HEADERS -I$(TOPDIR)/include +OBJCOPY = $(CROSS_COMPILE)objcopy +OBJCOPY_ARGS = -O elf32-powerpc + all: $(TOPDIR)/zImage mkprep : mkprep.c $(HOSTCC) $(CFLAGSINC) -o mkprep mkprep.c +piggyback : piggyback.c + $(HOSTCC) $(CFLAGSINC) -o piggyback piggyback.c + find_name : find_name.c $(HOSTCC) $(CFLAGSINC) -o find_name find_name.c -mk_type41: mk_type41.c - $(HOSTCC) $(CFLAGSINC) -o mk_type41 mk_type41.c - -piggyback: piggyback.c - $(HOSTCC) $(CFLAGS) -o piggyback piggyback.c - floppy: $(TOPDIR)/vmlinux zImage dd if=$(TOPDIR)/zImage of=/dev/fd0H1440 bs=64b -netboot : $(TOPDIR)/vmlinux mkprep - mkprep $(TOPDIR)/vmlinux $(TOPDIR)/netboot +floppy.initrd: $(TOPDIR)/vmlinux zImage + dd if=$(TOPDIR)/zImage.initrd of=/dev/fd0H1440 bs=64b + +znetboot : zImage mkprep + cp $(TOPDIR)/zImage /usr/local/tftpboot/vmlinux -znetboot : zvmlinux mkprep - mkprep zvmlinux $(TOPDIR)/znetboot - cp $(TOPDIR)/znetboot /usr/local/tftpboot/vmlinux +znetboot.initrd : zImage.initrd mkprep + cp $(TOPDIR)/zImage.initrd /usr/local/tftpboot/vmlinux -rcpboot : znetboot - rcp $(TOPDIR)/znetboot charon:/usr/tftpboot/vmlinux +# +# This really needs to go away. Perhaps a +# zImage.prep and zImage.chrp might be better. +# Once we're able to get a lilo-ish program +# on prep systems this won't be a problem. +# -- Cort +# +ifdef CONFIG_CHRP +zImage: zvmlinux + cp zvmlinux $(TOPDIR)/zImage +zImage.initrd: zvmlinux.initrd + cp zvmlinux.initrd $(TOPDIR)/zImage.initrd + +zvmlinux: $(OBJECTS) $(SYSTEM) find_name vmlinux.gz piggyback + ./piggyback < vmlinux.gz | $(AS) -o piggy.o + $(LD) $(ZLINKFLAGS) -o $@ $(OBJECTS) piggy.o + rm -f piggy.o +else zImage: zvmlinux mkprep mkprep -pbp zvmlinux $(TOPDIR)/zImage -install: zImage - dd if=$(TOPDIR)/zImage of=/dev/sda4 - ln -s /dev/sda4 $(INSTALL_PATH)/vmlinuz - cp $(TOPDIR)/System.map $(INSTALL_PATH)/ +zImage.initrd: zvmlinux.initrd mkprep + mkprep -pbp zvmlinux.initrd $(TOPDIR)/zImage.initrd -zvmlinux: $(OBJECTS) $(SYSTEM) mkprep find_name - mkprep $(TOPDIR)/vmlinux -|gzip ${GZIP_FLAGS}|mkprep -asm - -|$(AS) -o piggy.o - $(LD) $(ZLINKFLAGS) -o zvmlinux $(OBJECTS) piggy.o - rm -f piggy.o +zvmlinux: $(OBJECTS) $(SYSTEM) mkprep find_name vmlinux.gz + $(LD) $(ZLINKFLAGS) -o zvmlinux.tmp $(OBJECTS) + $(OBJCOPY) $(OBJCOPY_ARGS) --add-section=image=vmlinux.gz \ + zvmlinux.tmp $@ + rm zvmlinux.tmp +endif + +vmlinux.gz: $(TOPDIR)/vmlinux + dd bs=64k skip=1 if=$(TOPDIR)/vmlinux | gzip -vf9 - > vmlinux.gz + +zvmlinux.initrd: zvmlinux + $(OBJCOPY) $(OBJCOPY_ARGS) --add-section=initrd=ramdisk.image.gz \ + zvmlinux $@ clean: - rm -f piggyback zvmlinux mk_type41 mkprep mkboot find_name - rm -f $(TOPDIR)/{zImage,znetboot,netboot} + rm -f vmlinux* znetboot* zImage* zvmlinux* mkprep find_name + rm -f $(TOPDIR)/{zImage*,znetboot*,zvmlinux*,vmlinux*} fastdep: $(TOPDIR)/scripts/mkdep *.[Sch] > .depend diff --git a/arch/ppc/boot/find_name.c b/arch/ppc/boot/find_name.c new file mode 100644 index 000000000..23646fb69 --- /dev/null +++ b/arch/ppc/boot/find_name.c @@ -0,0 +1,47 @@ +#include <stdio.h> +#include <asm/page.h> +#include <sys/mman.h> +/* + * Finds a given address in the System.map and prints it out + * with its neighbors. -- Cort + */ + +void main(int argc, char **argv) +{ + unsigned long addr, cmp, i; + FILE *f; + char *ptr; + char s[256], last[256]; + + if ( argc < 2 ) + { + fprintf(stderr, "Usage: %s <address>\n", argv[0]); + exit(-1); + } + + for ( i = 1 ; argv[i] ; i++ ) + { + sscanf( argv[i], "%0x", &addr ); + /* adjust if addr is relative to kernelbase */ + if ( addr < PAGE_OFFSET ) + addr += PAGE_OFFSET; + + if ( (f = fopen( "System.map", "r" )) == NULL ) + { + perror("fopen()\n"); + exit(-1); + } + + while ( !feof(f) ) + { + fgets(s, 255 , f); + sscanf( s, "%0x", &cmp ); + if ( addr < cmp ) + break; + strcpy( last, s); + } + + printf( "%s", last); + } + fclose(f); +} diff --git a/arch/ppc/boot/head.S b/arch/ppc/boot/head.S index 6b25cb157..2cfda5cbe 100644 --- a/arch/ppc/boot/head.S +++ b/arch/ppc/boot/head.S @@ -13,17 +13,6 @@ start: bl start_ start_: -/* TEMP - No residual data on BeBox (yet) */ -#if 0 -#define IS_BE_BOX 0x42654278 /* 'BeBx' */ - lis r2,IS_BE_BOX>>16 - ori r2,r2,IS_BE_BOX&0xFFFF - cmp 0,r30,r2 - bne notBeBox - li r3,0 -#endif -notBeBox: -/* TEMP */ mr r11,r3 /* Save pointer to residual data */ mfmsr r3 /* Turn off interrupts */ li r4,0 @@ -48,7 +37,9 @@ notBeBox: mtlr r21 mtctr r22 bctr /* Jump to code */ -/* Relocate code to final resting spot */ +/* + * no matter where we're loaded, move ourselves to -Ttext address + */ relocate: mflr r3 /* Compute code bias */ subi r3,r3,4 @@ -98,13 +89,26 @@ start_ldr: mr r5,r6 /* Checksum */ mr r6,r11 /* Residual data */ bl decompress_kernel - /*mr r29,r3*/ /* R3 = TotalMemory */ - /*lis r28,hold_residual@h - ori r28,r28,hold_residual@l*/ + /* changed to use r3 (as firmware does) for kernel as ptr to residual -- Cort*/ - li r5,0x100 /* Kernel code starts here */ - mtlr r5 + lis r6,cmd_line@h + ori r6,r6,cmd_line@l + subi r7,r6,1 +00: lbzu r2,1(r7) + cmpi 0,r2,0 + bne 00b + + /* r4,r5 have initrd_start, size */ + lis r2,initrd_start@h + ori r2,r2,initrd_start@l + lwz r4,0(r2) + lis r2,initrd_end@h + ori r2,r2,initrd_end@l + lwz r5,0(r2) + + li r9,0x00c /* Kernel code starts here */ + mtlr r9 blr hang: b hang @@ -142,6 +146,45 @@ _put_HID0: blr /* + * Delay for a number of microseconds + * -- Use the BUS timer (assumes 66MHz) + */ + .globl udelay +udelay: + mfspr r4,PVR + srwi r4,r4,16 + cmpi 0,r4,1 /* 601 ? */ + bne .udelay_not_601 +00: li r0,86 /* Instructions / microsecond? */ + mtctr r0 +10: addi r0,r0,0 /* NOP */ + bdnz 10b + subic. r3,r3,1 + bne 00b + blr + +.udelay_not_601: + mulli r4,r3,1000 /* nanoseconds */ + addi r4,r4,59 + li r5,60 + divw r4,r4,r5 /* BUS ticks */ +1: mftbu r5 + mftb r6 + mftbu r7 + cmp 0,r5,r7 + bne 1b /* Get [synced] base time */ + addc r9,r6,r4 /* Compute end time */ + addze r8,r5 +2: mftbu r5 + cmp 0,r5,r8 + blt 2b + bgt 3f + mftb r6 + cmp 0,r6,r9 + blt 2b +3: blr + +/* * This space [buffer] is used to forceably flush the data cache when * running in copyback mode. This is necessary IFF the data cache could * contain instructions for which the instruction cache has stale data. diff --git a/arch/ppc/boot/kbd.c b/arch/ppc/boot/kbd.c new file mode 100644 index 000000000..6c0065b00 --- /dev/null +++ b/arch/ppc/boot/kbd.c @@ -0,0 +1,166 @@ +/* Keyboard handler */ + +#include <../drivers/char/defkeymap.c> /* yeah I know it's bad */ + +#define L 0x0001 /* locking function */ +#define SHF 0x0002 /* keyboard shift */ +#define ALT 0x0004 /* alternate shift -- alternate chars */ +#define NUM 0x0008 /* numeric shift cursors vs. numeric */ +#define CTL 0x0010 /* control shift -- allows ctl function */ +#define CPS 0x0020 /* caps shift -- swaps case of letter */ +#define ASCII 0x0040 /* ascii code for this key */ +#define STP 0x0080 /* stop output */ +#define FUNC 0x0100 /* function key */ +#define SCROLL 0x0200 /* scroll lock key */ + +unsigned char shfts, ctls, alts, caps, num, stp; + +#define KBDATAP 0x60 /* kbd data port */ +#define KBSTATUSPORT 0x61 /* kbd status */ +#define KBSTATP 0x64 /* kbd status port */ +#define KBINRDY 0x01 +#define KBOUTRDY 0x02 + +#define _x__ 0x00 /* Unknown / unmapped */ + +const unsigned short action[] = { + 0, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, /* scan 0- 7 */ + ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, /* scan 8-15 */ + ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, /* scan 16-23 */ + ASCII, ASCII, ASCII, ASCII, ASCII, CTL, ASCII, ASCII, /* scan 24-31 */ + ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, /* scan 32-39 */ + ASCII, ASCII, SHF, ASCII, ASCII, ASCII, ASCII, ASCII, /* scan 40-47 */ + ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, SHF, ASCII, /* scan 48-55 */ + ALT, ASCII, CPS, FUNC, FUNC, FUNC, FUNC, FUNC, /* scan 56-63 */ + FUNC, FUNC, FUNC, FUNC, FUNC, NUM,SCROLL, ASCII, /* scan 64-71 */ + ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, /* scan 72-79 */ + ASCII, ASCII, ASCII, ASCII, 0, 0, 0, 0, /* scan 80-87 */ + 0,0,0,0,0,0,0,0, /* scan 88-95 */ + 0,0,0,0,0,0,0,0, /* scan 96-103 */ + 0,0,0,0,0,0,0,0, /* scan 104-111 */ + 0,0,0,0,0,0,0,0, /* scan 112-119 */ + 0,0,0,0,0,0,0,0, /* scan 120-127 */ +}; + +static int +kbd(noblock) + int noblock; +{ + unsigned char dt, brk, act; + int first = 1; +loop: + if (noblock) { + if ((inb(KBSTATP) & KBINRDY) == 0) + return (-1); + } else while((inb(KBSTATP) & KBINRDY) == 0) ; + + dt = inb(KBDATAP); + + brk = dt & 0x80; /* brk == 1 on key release */ + dt = dt & 0x7f; /* keycode */ + + act = action[dt]; + if (/*act&SHF*/ dt == 54) + shfts = brk ? 0 : 1; + if (/*act&ALT*/ dt == 48) + alts = brk ? 0 : 1; + if (/*act&NUM*/ dt == 69) + if (act&L) { + /* NUM lock */ + if(!brk) + num = !num; + } else + num = brk ? 0 : 1; + if (/*act&CTL*/ dt == 29) + ctls = brk ? 0 : 1; + if (/*act&CPS*/ dt == 58) + if (act&L) { + /* CAPS lock */ + if(!brk) + caps = !caps; + } else + caps = brk ? 0 : 1; + if (0/*act&STP*/) + if (act&L) { + if(!brk) + stp = !stp; + } else + stp = brk ? 0 : 1; + + if ((act&ASCII) && !brk) { + unsigned char chr; + if (shfts) + chr = shift_map[dt]; + else if (ctls) + chr = ctrl_map[dt]; + else + chr = plain_map[dt]; + if (alts) + chr |= 0x80; + + if (caps && (chr >= 'a' && chr <= 'z')) + chr -= 'a' - 'A' ; + if ( chr == 0x01 ) chr = '\n'; /* hack */ +#define CTRL(s) (s & 0x1F) + if ((chr == '\r') || (chr == '\n') || (chr == CTRL('A')) || (chr == CTRL('S'))) + { + /* Wait for key up */ + while (1) + { + while((inb(KBSTATP) & KBINRDY) == 0) ; + dt = inb(KBDATAP); + if (dt & 0x80) /* key up */ break; + } + } + return (chr); + } + if (first && brk) return (0); /* Ignore initial 'key up' codes */ + goto loop; +} + +static +scankbd(void) { + return (kbd(1) != -1); +} + +static +kbdreset(void) +{ + unsigned char c; + int i; + + /* Send self-test */ + while (inb(KBSTATP) & KBOUTRDY) ; + outb(KBSTATP,0xAA); + while ((inb(KBSTATP) & KBINRDY) == 0) ; /* wait input ready */ + if ((c = inb(KBDATAP)) != 0x55) + { + puts("Keyboard self test failed - result:"); + puthex(c); + puts("\n"); + } + /* Enable interrupts and keyboard controller */ + while (inb(KBSTATP) & KBOUTRDY) ; + outb(KBSTATP,0x60); + while (inb(KBSTATP) & KBOUTRDY) ; + outb(KBDATAP,0x45); + for (i = 0; i < 10000; i++) udelay(1); + while (inb(KBSTATP) & KBOUTRDY) ; + outb(KBSTATP,0xAE); +} + +static int kbd_reset = 0; + +CRT_getc(void) +{ + int c; + if (!kbd_reset) {kbdreset(); kbd_reset++; } + while ((c = kbd(0)) == 0) ; + return(c); +} + +CRT_tstc(void) +{ + if (!kbd_reset) {kbdreset(); kbd_reset++; } + return ((inb(KBSTATP) & KBINRDY) != 0); +} diff --git a/arch/ppc/boot/misc.c b/arch/ppc/boot/misc.c index a983ea106..4d2a723f5 100644 --- a/arch/ppc/boot/misc.c +++ b/arch/ppc/boot/misc.c @@ -8,13 +8,23 @@ * puts by Nick Holloway 1993 * * Adapted for PowerPC by Gary Thomas + * Updated and modified by Cort Dougan (cort@cs.nmt.edu) */ #include "gzip.h" #include "lzw.h" #include "asm/residual.h" +#include <elf.h> RESIDUAL hold_residual; +unsigned long initrd_start = 0, initrd_end = 0; +char *zimage_start; +int zimage_size; +extern char input_data[]; +extern int input_len; +void cksum_text(void); +void verify_data(unsigned long load_addr); + void dump_buf(unsigned char *p, int s); #define EOF -1 @@ -26,8 +36,7 @@ unsigned outcnt; unsigned insize; unsigned inptr; -extern char input_data[]; -extern int input_len; +char cmd_line[256]; int input_ptr; @@ -56,6 +65,9 @@ int lines, cols; int orig_x, orig_y; void puts(const char *); +void putc(const char c); +void puthex(unsigned long val); +void _bcopy(char *src, char *dst, int len); void *malloc(int size) { @@ -77,7 +89,7 @@ void *malloc(int size) */ if (free_mem_ptr < (long)&end) { - if (free_mem_ptr > (long)&input_data[input_ptr]) + if (free_mem_ptr > (long)&zimage_start[input_ptr]) error("\nOut of memory\n"); return p; @@ -87,7 +99,7 @@ void *malloc(int size) #endif return p; puts("large kernel, low 1M tight..."); - free_mem_ptr = (long)input_data; + free_mem_ptr = (long)zimage_start; } } @@ -115,6 +127,53 @@ static void scroll() vidmem[i] = ' '; } +tstc(void) +{ + return (CRT_tstc() ); +} + +getc(void) +{ + while (1) { + if (CRT_tstc()) return (CRT_getc()); + } +} + +void +putc(const char c) +{ + int x,y; + + x = orig_x; + y = orig_y; + + if ( c == '\n' ) { + x = 0; + if ( ++y >= lines ) { + scroll(); + y--; + } + } else if (c == '\b') { + if (x > 0) { + x--; + } + } else { + vidmem [ ( x + cols * y ) * 2 ] = c; + if ( ++x >= cols ) { + x = 0; + if ( ++y >= lines ) { + scroll(); + y--; + } + } + } + + cursor(x, y); + + orig_x = x; + orig_y = y; +} + void puts(const char *s) { int x,y; @@ -229,10 +288,10 @@ puts("*"); insize = 0; do { len = INBUFSIZ-insize; - if (len > (input_len-input_ptr+1)) len=input_len-input_ptr+1; + if (len > (zimage_size-input_ptr+1)) len=zimage_size-input_ptr+1; if (len == 0 || len == EOF) break; - for (i=0;i<len;i++) inbuf[insize+i] = input_data[input_ptr+i]; + for (i=0;i<len;i++) inbuf[insize+i] = zimage_start[input_ptr+i]; insize += len; input_ptr += len; } while (insize < INBUFSIZ); @@ -313,7 +372,15 @@ void error(char *x) unsigned long decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, RESIDUAL *residual) { - unsigned long TotalMemory; + int timer; + char *cp, ch; + Elf32_Ehdr *eh; + Elf32_Shdr *sh, *strtab_shdr; + char *strtab; + unsigned long i; + extern unsigned long start(void); + + output_data = (char *)0x0; /* Points to 0 */ lines = 25; cols = 80; @@ -339,33 +406,135 @@ decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, R clear_bufs(); makecrc(); - puts("Loaded at "); puthex(load_addr); puts(", "); puthex(num_words); puts(" words"); - puts(", cksum = "); puthex(cksum); puts("\n"); + puts("Cksum: "); puthex(cksum); puts("\n"); + puts("Loaded at: "); puthex(load_addr); puts(" "); puthex(num_words+load_addr); + puts("\n"); + puts("Boot code relocated to: "); puthex((unsigned long)start); puts(" "); + puthex((unsigned long)(num_words+start)); + puts("\n"); if (residual) { - _bcopy(residual, &hold_residual, sizeof(hold_residual)); - puts("Residual data at "); puthex(residual); puts("\n"); - show_residual_data(residual); - TotalMemory = residual->TotalMemory; - } else { - TotalMemory = 0x01000000; + _bcopy((char *)residual, (char *)&hold_residual, sizeof(hold_residual)); + puts("Residual data at: "); puthex((unsigned long)residual); puts(" "); + puthex((unsigned long)((unsigned long)(residual->ResidualLength) + residual)); puts("\n"); + puts("Residual data relocated to: "); puthex((unsigned long)&hold_residual); puts("\n"); } - puts("Uncompressing Linux..."); + /* + * Take care of initrd if we have one + */ + /* + * the _actual_ load addr is 64k (elf hdr size) lower but the + * firmware gives us the starting exec point not the load ptr + * -- Cort + */ + eh = (Elf32_Ehdr *)(load_addr - 65536); + sh = (Elf32_Shdr *)((unsigned long)eh + eh->e_shoff ); + /*puts("Entry point: "); puthex(eh->e_entry); puts("\n");*/ + + /* find string table */ + for ( i = 0 ; i <= eh->e_shnum ; i++,sh++) + { + /*puts("Section: "); puthex(i); + puts(" type: "); puthex(sh->sh_type); + puts(" offset: "); puthex(sh->sh_offset); + puts("\n");*/ + + if ( sh->sh_type == SHT_STRTAB ) + { + strtab_shdr = sh; + strtab = (char *)(sh->sh_offset + (unsigned long)eh); + /*puts("Found strtab at: "); puthex((unsigned long)strtab); + puts("\n");*/ + break; + } + } - method = get_method(0); + /* find the initrd and image sections */ + if ( strtab_shdr ) + { + sh = (Elf32_Shdr *)((unsigned long)eh + eh->e_shoff ); + for ( i = 0 ; i <= eh->e_shnum ; i++,sh++) + { + if ( !memcmp("initrd", (char *)(strtab+sh->sh_name), 6 ) ) + { + initrd_start = (unsigned long)eh + sh->sh_offset; + initrd_end = initrd_start + sh->sh_size; + puts("Found initrd at: "); puthex(initrd_start); + puts(" "); puthex(initrd_end); + puts("\n"); + } + if ( !memcmp("image", (char *)(strtab+sh->sh_name), 5 ) ) + { + zimage_start = (char *)((unsigned long)eh + sh->sh_offset); + zimage_size = sh->sh_size; + puts("Found zimage at: "); puthex((unsigned long)zimage_start); + puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); + puts("\n"); + } + } + } + else + { + puts("Couldn't find string table for boot image!\n"); + } + + /* relocate initrd */ + if ( initrd_start ) + { + memcpy ((void *)0x00D00000,(void *)initrd_start, + initrd_end - initrd_start ); + initrd_end = 0x00D00000 + initrd_end - initrd_start; + initrd_start = 0x00D00000; + puts("Moved initrd to: "); puthex(initrd_start); + puts(" "); puthex(initrd_end); puts("\n"); + } - work(0, 0); + CRT_tstc(); /* Forces keyboard to be initialized */ + puts("\nLinux/PPC load: "); + timer = 0; + cp = cmd_line; + while (timer++ < 5*1000) { + if (tstc()) { + while ((ch = getc()) != '\n' && ch != '\r') { + if (ch == '\b') { + if (cp != cmd_line) { + cp--; + puts("\b \b"); + } + } else { + *cp++ = ch; + putc(ch); + } + } + break; /* Exit 'timer' loop */ + } + udelay(1000); /* 1 msec */ + } + *cp = 0; + puts("\n"); + + /* mappings on early boot can only handle 16M */ + if ( (int)(&cmd_line[0]) > (16<<20)) + puts("cmd_line > 16M\n"); + if ( (int)&hold_residual > (16<<20)) + puts("hold_residual > 16M\n"); + if ( initrd_start > (16<<20)) + puts("initrd_start > 16M\n"); + + + puts("Uncompressing Linux..."); + method = get_method(0); + work(0, 0); puts("done.\n"); + puts("Now booting the kernel\n"); - /*return (TotalMemory);*/ /* Later this can be a pointer to saved residual data */ - return &hold_residual; + return (unsigned long)&hold_residual; } show_residual_data(RESIDUAL *res) { puts("Residual data: "); puthex(res->ResidualLength); puts(" bytes\n"); - puts("Total memory: "); puthex(res->TotalMemory); puts("\n"); #if 0 puts("Residual structure = "); puthex(sizeof(*res)); puts(" bytes\n"); dump_buf(&hold_residual, 32); @@ -407,14 +576,14 @@ cksum_data() { unsigned int *ptr, len, cksum, cnt; cksum = cnt = 0; - ptr = input_data; + ptr = (unsigned int *)zimage_start; puts("Checksums: "); - for (len = 0; len < input_len; len += 4) { + for (len = 0; len < zimage_size; len += 4) { cksum ^= *ptr++; if (len && ((len & 0x0FFF) == 0)) { if (cnt == 0) { puts("\n ["); - puthex(ptr-1); + puthex((unsigned long)ptr-1); puts("] "); } puthex(cksum); @@ -429,7 +598,7 @@ cksum_data() puts("Data cksum = "); puthex(cksum); puts("\n"); } -cksum_text() +void cksum_text(void) { extern int start, etext; unsigned int *ptr, len, text_len, cksum, cnt; @@ -442,7 +611,7 @@ cksum_text() if (len && ((len & 0x0FFF) == 0)) { if (cnt == 0) { puts("\n ["); - puthex(ptr-1); + puthex((unsigned long)ptr-1); puts("] "); } puthex(cksum); @@ -457,24 +626,24 @@ cksum_text() puts("TEXT cksum = "); puthex(cksum); puts("\n"); } -verify_data(unsigned long load_addr) +void verify_data(unsigned long load_addr) { extern int start, etext; unsigned int *orig_ptr, *copy_ptr, len, errors; errors = 0; - copy_ptr = input_data; - orig_ptr = (unsigned int *)(load_addr + ((unsigned int)input_data - (unsigned int)&start)); - for (len = 0; len < input_len; len += 4) { + copy_ptr = (unsigned int *)zimage_start; + orig_ptr = (unsigned int *)(load_addr + ((unsigned int)zimage_start - (unsigned int)&start)); + for (len = 0; len < zimage_size; len += 4) { if (*copy_ptr++ != *orig_ptr++) { errors++; } } - copy_ptr = input_data; - orig_ptr = (unsigned int *)(load_addr + ((unsigned int)input_data - (unsigned int)&start)); - for (len = 0; len < input_len; len += 4) { + copy_ptr = (unsigned int *)zimage_start; + orig_ptr = (unsigned int *)(load_addr + ((unsigned int)zimage_start - (unsigned int)&start)); + for (len = 0; len < zimage_size; len += 4) { if (*copy_ptr++ != *orig_ptr++) { - dump_buf(copy_ptr-1, 128); - dump_buf(orig_ptr-1, 128); + dump_buf((unsigned char *) (copy_ptr-1), 128); + dump_buf((unsigned char *) (orig_ptr-1), 128); puts("Total errors = "); puthex(errors*4); puts("\n"); while (1) ; } @@ -486,9 +655,9 @@ test_data(unsigned long load_addr) extern int start, etext; unsigned int *orig_ptr, *copy_ptr, len, errors; errors = 0; - copy_ptr = input_data; - orig_ptr = (unsigned int *)(load_addr + ((unsigned int)input_data - (unsigned int)&start)); - for (len = 0; len < input_len; len += 4) { + copy_ptr = (unsigned int *)zimage_start; + orig_ptr = (unsigned int *)(load_addr + ((unsigned int)zimage_start - (unsigned int)&start)); + for (len = 0; len < zimage_size; len += 4) { if (*copy_ptr++ != *orig_ptr++) { errors++; } @@ -532,7 +701,7 @@ void dump_buf(unsigned char *p, int s) } while (s > 0) { - puthex(p); puts(": "); + puthex((unsigned long)p); puts(": "); for (i = 0; i < 16; i++) { if (i < s) diff --git a/arch/ppc/boot/mkprep.c b/arch/ppc/boot/mkprep.c index 417334670..06b08df98 100644 --- a/arch/ppc/boot/mkprep.c +++ b/arch/ppc/boot/mkprep.c @@ -131,17 +131,18 @@ int main(int argc, char *argv[]) argptr++; /* skip elf header in input file */ - lseek(in_fd, elfhdr_size, SEEK_SET); + if ( !prep ) + lseek(in_fd, elfhdr_size, SEEK_SET); /* write prep partition if necessary */ if ( prep ) - write_prep_partition( in_fd, out_fd ); + write_prep_partition( in_fd, out_fd ); /* write input image to bootimage */ if ( asmoutput ) - write_asm_data( in_fd, out_fd ); + write_asm_data( in_fd, out_fd ); else - copy_image(in_fd, out_fd); + copy_image(in_fd, out_fd); return 0; } @@ -161,28 +162,11 @@ void write_prep_partition(int in, int out) } bzero( block, sizeof block ); - - /* set entry point and boot image size */ - *entry = cpu_to_le32(0x400); - /* need use size - elfheader? */ + /* set entry point and boot image size skipping over elf header */ + *entry = cpu_to_le32(0x400+65536); *length = cpu_to_le32(info.st_size+0x400); - /* - * Writes the "boot record", which contains the partition table, to the - * diskette, followed by the dummy PC boot block and load image descriptor - * block. It returns the number of bytes it has written to the load - * image. - * - * The boot record is the first block of the diskette and identifies the - * "PReP" partition. The "PReP" partition contains the "load image" starting - * at offset zero within the partition. The first block of the load image is - * a dummy PC boot block. The second block is the "load image descriptor" - * which contains the size of the load image and the entry point into the - * image. The actual boot image starts at offset 1024 bytes (third sector) - * in the partition. - */ - /* sets magic number for msdos partition (used by linux) */ block[510] = 0x55; block[511] = 0xAA; @@ -220,7 +204,7 @@ void write_prep_partition(int in, int out) /* This has to be 0 on the PowerStack? */ pe->beginning_sector = cpu_to_le32(0); #endif -/*pe->number_of_sectors = cpu_to_le32(2*18*80-1);*/ + pe->number_of_sectors = cpu_to_le32(2*18*80-1); write( out, block, sizeof(block) ); write( out, entry, sizeof(*entry) ); diff --git a/arch/ppc/coffboot/.cvsignore b/arch/ppc/coffboot/.cvsignore new file mode 100644 index 000000000..45968cd8d --- /dev/null +++ b/arch/ppc/coffboot/.cvsignore @@ -0,0 +1,7 @@ +.depend +vmlinux.coff.initrd +coffboot +elfextract +hack-coff +vmlinux.coff +zImage diff --git a/arch/ppc/coffboot/Makefile b/arch/ppc/coffboot/Makefile new file mode 100644 index 000000000..e145411c6 --- /dev/null +++ b/arch/ppc/coffboot/Makefile @@ -0,0 +1,50 @@ +# Makefile for making XCOFF bootable images for booting on PowerMacs +# using Open Firmware. +# +# Paul Mackerras January 1997 + +# PowerPC (cross) tools +ifneq ($(shell uname -m),ppc) +CROSS_COMPILE =powerpc-eabi- +endif + +HOSTCC = gcc +HOSTCFLAGS = -O -I$(TOPDIR)/include + +CC = $(CROSS_COMPILE)gcc +LD = $(CROSS_COMPILE)ld +CFLAGS = -O -fno-builtin -I$(TOPDIR)/include +OBJCOPY = $(CROSS_COMPILE)objcopy +OBJCOPY_ARGS = -O aixcoff-rs6000 -R .stab -R .stabstr -R .comment \ + --add-section=image=zImage +LD_ARGS = -e _start -T ld.script -Ttext 500000 -Tdata 510000 -Bstatic +GZIP = gzip -9 + +OBJS = crt0.o start.o main.o misc.o string.o zlib.o +LIBS = $(TOPDIR)/lib/lib.a + +vmlinux.coff: coffboot hack-coff zImage + $(OBJCOPY) $(OBJCOPY_ARGS) coffboot $@ + ./hack-coff $@ + +vmlinux.coff.initrd: coffboot hack-coff zImage ramdisk.image.gz + $(OBJCOPY) $(OBJCOPY_ARGS) --add-section=initrd=ramdisk.image.gz \ + coffboot $@ + ./hack-coff $@ + +coffboot: $(OBJS) ld.script + $(LD) -o coffboot $(LD_ARGS) $(OBJS) $(LIBS) + +zImage: $(TOPDIR)/vmlinux elfextract + ./elfextract $(TOPDIR)/vmlinux | $(GZIP) >zImage + +hack-coff: hack-coff.c + $(HOSTCC) $(HOSTCFLAGS) -o hack-coff hack-coff.c + +elfextract: elfextract.c + $(HOSTCC) $(HOSTCFLAGS) -o elfextract elfextract.c + +clean: + rm -f elfextract hack-coff coffboot zImage vmlinux.coff + +fastdep: diff --git a/arch/ppc/coffboot/crt0.S b/arch/ppc/coffboot/crt0.S new file mode 100644 index 000000000..43ae4e0ec --- /dev/null +++ b/arch/ppc/coffboot/crt0.S @@ -0,0 +1,24 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + .text + .globl _start +_start: + .long __start,0,0 + + .globl __start +__start: + lis 9,_start@h + lis 8,_etext@ha + addi 8,8,_etext@l +1: dcbf 0,9 + icbi 0,9 + addi 9,9,0x20 + cmplwi 0,9,8 + blt 1b + b start diff --git a/arch/ppc/coffboot/elfextract.c b/arch/ppc/coffboot/elfextract.c new file mode 100644 index 000000000..1b50ca475 --- /dev/null +++ b/arch/ppc/coffboot/elfextract.c @@ -0,0 +1,98 @@ +/* + * Extract the loadable program segment from an elf file. + * + * Copyright 1996 Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include <stdio.h> +#include <linux/elf.h> + +FILE *fi, *fo; +char *ni, *no; +char buf[65536]; + +void +rd(void *buf, int len) +{ + int nr; + + nr = fread(buf, 1, len, fi); + if (nr == len) + return; + if (ferror(fi)) + fprintf(stderr, "%s: read error\n", ni); + else + fprintf(stderr, "%s: short file\n", ni); + exit(1); +} + +main(int ac, char **av) +{ + unsigned nb, len; + Elf32_Ehdr eh; + Elf32_Phdr ph; + + if (ac > 3 || ac > 1 && av[1][0] == '-') { + fprintf(stderr, "Usage: %s [elf-file [image-file]]\n", av[0]); + exit(0); + } + + fi = stdin; + ni = "(stdin)"; + fo = stdout; + no = "(stdout)"; + + if (ac > 1) { + ni = av[1]; + fi = fopen(ni, "rb"); + if (fi == NULL) { + perror(ni); + exit(1); + } + } + + rd(&eh, sizeof(eh)); + if (eh.e_ident[EI_MAG0] != ELFMAG0 + || eh.e_ident[EI_MAG1] != ELFMAG1 + || eh.e_ident[EI_MAG2] != ELFMAG2 + || eh.e_ident[EI_MAG3] != ELFMAG3) { + fprintf(stderr, "%s: not an ELF file\n", ni); + exit(1); + } + + fseek(fi, eh.e_phoff + (eh.e_phnum - 1) * sizeof(ph), 0); + rd(&ph, sizeof(ph)); + if (ph.p_type != PT_LOAD) { + fprintf(stderr, "%s: doesn't have a loadable segment\n", ni); + exit(1); + } + + if (ac > 2) { + no = av[2]; + fo = fopen(no, "wb"); + if (fo == NULL) { + perror(no); + exit(1); + } + } + + fseek(fi, ph.p_offset, 0); + for (len = ph.p_filesz; len != 0; len -= nb) { + nb = len; + if (nb > sizeof(buf)) + nb = sizeof(buf); + rd(buf, nb); + if (fwrite(buf, 1, nb, fo) != nb) { + fprintf(stderr, "%s: write error\n", no); + exit(1); + } + } + + fclose(fo); + fclose(fi); + exit(0); +} diff --git a/arch/ppc/coffboot/hack-coff.c b/arch/ppc/coffboot/hack-coff.c new file mode 100644 index 000000000..3dfa8d800 --- /dev/null +++ b/arch/ppc/coffboot/hack-coff.c @@ -0,0 +1,79 @@ +/* + * hack-coff.c - hack the header of an xcoff file to fill in + * a few fields needed by the Open Firmware xcoff loader on + * Power Macs but not initialized by objcopy. + * + * Copyright (C) Paul Mackerras 1997. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include <stdio.h> +#include "rs6000.h" + +#define AOUT_MAGIC 0x010b + +#define get_16be(x) ((((unsigned char *)(x))[0] << 8) \ + + ((unsigned char *)(x))[1]) +#define put_16be(x, v) (((unsigned char *)(x))[0] = (v) >> 8, \ + ((unsigned char *)(x))[1] = (v) & 0xff) +#define get_32be(x) ((((unsigned char *)(x))[0] << 24) \ + + (((unsigned char *)(x))[1] << 16) \ + + (((unsigned char *)(x))[2] << 8) \ + + ((unsigned char *)(x))[3]) + +main(int ac, char **av) +{ + int fd; + int i, nsect; + int aoutsz; + struct external_filehdr fhdr; + AOUTHDR aout; + struct external_scnhdr shdr; + + if (ac != 2) { + fprintf(stderr, "Usage: hack-coff coff-file\n"); + exit(1); + } + if ((fd = open(av[1], 2)) == -1) { + perror(av[2]); + exit(1); + } + if (read(fd, &fhdr, sizeof(fhdr)) != sizeof(fhdr)) + goto readerr; + i = get_16be(fhdr.f_magic); + if (i != U802TOCMAGIC && i != U802WRMAGIC && i != U802ROMAGIC) { + fprintf(stderr, "%s: not an xcoff file\n", av[1]); + exit(1); + } + aoutsz = get_16be(fhdr.f_opthdr); + if (read(fd, &aout, aoutsz) != aoutsz) + goto readerr; + nsect = get_16be(fhdr.f_nscns); + for (i = 0; i < nsect; ++i) { + if (read(fd, &shdr, sizeof(shdr)) != sizeof(shdr)) + goto readerr; + if (strcmp(shdr.s_name, ".text") == 0) { + put_16be(aout.o_snentry, i+1); + put_16be(aout.o_sntext, i+1); + } else if (strcmp(shdr.s_name, ".data") == 0) { + put_16be(aout.o_sndata, i+1); + } else if (strcmp(shdr.s_name, ".bss") == 0) { + put_16be(aout.o_snbss, i+1); + } + } + put_16be(aout.magic, AOUT_MAGIC); + if (lseek(fd, (long) sizeof(struct external_filehdr), 0) == -1 + || write(fd, &aout, aoutsz) != aoutsz) { + fprintf(stderr, "%s: write error\n", av[1]); + exit(1); + } + close(fd); + exit(0); + +readerr: + fprintf(stderr, "%s: read error or file too short\n", av[1]); + exit(1); +} diff --git a/arch/ppc/coffboot/ld.script b/arch/ppc/coffboot/ld.script new file mode 100644 index 000000000..2469ed65d --- /dev/null +++ b/arch/ppc/coffboot/ld.script @@ -0,0 +1,68 @@ +OUTPUT_ARCH(powerpc) +SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/local/powerpc-any-elf/lib); +/* Do we need any of these for elf? + __DYNAMIC = 0; */ +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .rel.text : { *(.rel.text) } + .rela.text : { *(.rela.text) } + .rel.data : { *(.rel.data) } + .rela.data : { *(.rela.data) } + .rel.rodata : { *(.rel.rodata) } + .rela.rodata : { *(.rela.rodata) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } + .init : { *(.init) } =0 + .plt : { *(.plt) } + .text : + { + *(.text) + *(.rodata) + *(.rodata1) + *(.got1) + } + .fini : { *(.fini) } =0 + .ctors : { *(.ctors) } + .dtors : { *(.dtors) } + _etext = .; + PROVIDE (etext = .); + /* Read-write section, merged into data segment: */ + . = (. + 0x0FFF) & 0xFFFFF000; + .data : + { + *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.got.plt) *(.got) + *(.dynamic) + CONSTRUCTORS + } + _edata = .; + PROVIDE (edata = .); + __bss_start = .; + .bss : + { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + } + _end = . ; + PROVIDE (end = .); +} + diff --git a/arch/ppc/coffboot/main.c b/arch/ppc/coffboot/main.c new file mode 100644 index 000000000..ab0ee6c4a --- /dev/null +++ b/arch/ppc/coffboot/main.c @@ -0,0 +1,185 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include "nonstdio.h" +#include "rs6000.h" +#include "zlib.h" + +extern void *finddevice(const char *); +extern int getprop(void *, const char *, void *, int); +void gunzip(void *, int, unsigned char *, int *); + +#define get_16be(x) (*(unsigned short *)(x)) +#define get_32be(x) (*(unsigned *)(x)) + +#define RAM_START 0xc0000000 +#define RAM_END 0xc0800000 /* only 8M mapped with BATs */ + +#define RAM_FREE 0xc0540000 /* after image of coffboot */ + +char *avail_ram; +char *end_avail; + +coffboot(int a1, int a2, void *prom) +{ + void *options; + unsigned loadbase; + struct external_filehdr *eh; + struct external_scnhdr *sp; + struct external_scnhdr *isect, *rsect; + int ns, oh, i; + unsigned sa, len; + void *dst; + unsigned char *im; + unsigned initrd_start, initrd_size; + + printf("coffboot starting\n"); + options = finddevice("/options"); + if (options == (void *) -1) + exit(); + if (getprop(options, "load-base", &loadbase, sizeof(loadbase)) + != sizeof(loadbase)) { + printf("error getting load-base\n"); + exit(); + } + setup_bats(); + + eh = (struct external_filehdr *) loadbase; + ns = get_16be(eh->f_nscns); + oh = get_16be(eh->f_opthdr); + + sp = (struct external_scnhdr *) (loadbase + sizeof(struct external_filehdr) + oh); + isect = rsect = NULL; + for (i = 0; i < ns; ++i, ++sp) { + if (strcmp(sp->s_name, "image") == 0) + isect = sp; + else if (strcmp(sp->s_name, "initrd") == 0) + rsect = sp; + } + if (isect == NULL) { + printf("image section not found\n"); + exit(); + } + + if (rsect != NULL && (initrd_size = get_32be(rsect->s_size)) != 0) { + initrd_start = (RAM_END - initrd_size) & ~0xFFF; + a1 = initrd_start; + a2 = initrd_size; + printf("initial ramdisk at %x (%u bytes)\n", + initrd_start, initrd_size); + memcpy((char *) initrd_start, + (char *) (loadbase + get_32be(rsect->s_scnptr)), + initrd_size); + end_avail = (char *) initrd_start; + } else { + end_avail = (char *) RAM_END; + } + + im = (unsigned char *)(loadbase + get_32be(isect->s_scnptr)); + len = get_32be(isect->s_size); + dst = (void *) RAM_START; + + if (im[0] == 0x1f && im[1] == 0x8b) { + void *cp = (void *) RAM_FREE; + avail_ram = (void *) (RAM_FREE + ((len + 7) & -8)); + memcpy(cp, im, len); + printf("gunzipping... "); + gunzip(dst, 0x400000, cp, &len); + printf("done\n"); + + } else { + memmove(dst, im, len); + } + + flush_cache(dst, len); + + sa = *(unsigned *)dst + RAM_START; + printf("start address = 0x%x\n", sa); + +#if 0 + pause(); +#endif + (*(void (*)())sa)(a1, a2, prom); + + printf("returned?\n"); + + pause(); +} + +void *zalloc(void *x, unsigned items, unsigned size) +{ + void *p = avail_ram; + + size *= items; + size = (size + 7) & -8; + avail_ram += size; + if (avail_ram > end_avail) { + printf("oops... out of memory\n"); + pause(); + } + return p; +} + +void zfree(void *x, void *addr, unsigned nb) +{ +} + +#define HEAD_CRC 2 +#define EXTRA_FIELD 4 +#define ORIG_NAME 8 +#define COMMENT 0x10 +#define RESERVED 0xe0 + +#define DEFLATED 8 + +void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp) +{ + z_stream s; + int r, i, flags; + + /* skip header */ + i = 10; + flags = src[3]; + if (src[2] != DEFLATED || (flags & RESERVED) != 0) { + printf("bad gzipped data\n"); + exit(); + } + if ((flags & EXTRA_FIELD) != 0) + i = 12 + src[10] + (src[11] << 8); + if ((flags & ORIG_NAME) != 0) + while (src[i++] != 0) + ; + if ((flags & COMMENT) != 0) + while (src[i++] != 0) + ; + if ((flags & HEAD_CRC) != 0) + i += 2; + if (i >= *lenp) { + printf("gunzip: ran out of data in header\n"); + exit(); + } + + s.zalloc = zalloc; + s.zfree = zfree; + r = inflateInit2(&s, -MAX_WBITS); + if (r != Z_OK) { + printf("inflateInit2 returned %d\n", r); + exit(); + } + s.next_in = src + i; + s.avail_in = *lenp - i; + s.next_out = dst; + s.avail_out = dstlen; + r = inflate(&s, Z_FINISH); + if (r != Z_OK && r != Z_STREAM_END) { + printf("inflate returned %d\n", r); + exit(); + } + *lenp = s.next_out - (unsigned char *) dst; + inflateEnd(&s); +} diff --git a/arch/ppc/coffboot/misc.S b/arch/ppc/coffboot/misc.S new file mode 100644 index 000000000..ae08ee2fa --- /dev/null +++ b/arch/ppc/coffboot/misc.S @@ -0,0 +1,50 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + .text + +/* + * Use the BAT0 registers to map the 1st 8MB of RAM to 0xc0000000. + */ + .globl setup_bats +setup_bats: + mfpvr 3 + rlwinm 3,3,16,16,31 /* r3 = 1 for 601, 4 for 604 */ + cmpi 0,3,1 + lis 4,0xc000 + bne 4f + ori 4,4,4 /* set up BAT registers for 601 */ + li 5,0x7f + b 5f +4: ori 4,4,0xff /* set up BAT registers for 604 */ + li 5,2 + mtdbatu 0,4 + mtdbatl 0,5 +5: mtibatu 0,4 + mtibatl 0,5 + isync + blr + +/* + * Flush the dcache and invalidate the icache for a range of addresses. + * + * flush_cache(addr, len) + */ + .global flush_cache +flush_cache: + addi 4,4,0x1f /* len = (len + 0x1f) / 0x20 */ + rlwinm. 4,4,27,5,31 + mtctr 4 + beqlr +1: dcbf 0,3 + icbi 0,3 + addi 3,3,0x20 + bdnz 1b + sync + isync + blr diff --git a/arch/ppc/coffboot/nonstdio.h b/arch/ppc/coffboot/nonstdio.h new file mode 100644 index 000000000..664b8384a --- /dev/null +++ b/arch/ppc/coffboot/nonstdio.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +typedef int FILE; +extern FILE *stdin, *stdout; +#define NULL ((void *)0) +#define EOF (-1) +#define fopen(n, m) NULL +#define fflush(f) 0 +#define fclose(f) 0 +extern char *fgets(); + +#define perror(s) printf("%s: no files!\n", (s)) diff --git a/arch/ppc/coffboot/rs6000.h b/arch/ppc/coffboot/rs6000.h new file mode 100644 index 000000000..0def1d95d --- /dev/null +++ b/arch/ppc/coffboot/rs6000.h @@ -0,0 +1,243 @@ +/* IBM RS/6000 "XCOFF" file definitions for BFD. + Copyright (C) 1990, 1991 Free Software Foundation, Inc. + FIXME: Can someone provide a transliteration of this name into ASCII? + Using the following chars caused a compiler warning on HIUX (so I replaced + them with octal escapes), and isn't useful without an understanding of what + character set it is. + Written by Mimi Ph\373\364ng-Th\345o V\365 of IBM + and John Gilmore of Cygnus Support. */ + +/********************** FILE HEADER **********************/ + +struct external_filehdr { + char f_magic[2]; /* magic number */ + char f_nscns[2]; /* number of sections */ + char f_timdat[4]; /* time & date stamp */ + char f_symptr[4]; /* file pointer to symtab */ + char f_nsyms[4]; /* number of symtab entries */ + char f_opthdr[2]; /* sizeof(optional hdr) */ + char f_flags[2]; /* flags */ +}; + + /* IBM RS/6000 */ +#define U802WRMAGIC 0730 /* writeable text segments **chh** */ +#define U802ROMAGIC 0735 /* readonly sharable text segments */ +#define U802TOCMAGIC 0737 /* readonly text segments and TOC */ + +#define BADMAG(x) \ + ((x).f_magic != U802ROMAGIC && (x).f_magic != U802WRMAGIC && \ + (x).f_magic != U802TOCMAGIC) + +#define FILHDR struct external_filehdr +#define FILHSZ 20 + + +/********************** AOUT "OPTIONAL HEADER" **********************/ + + +typedef struct +{ + unsigned char magic[2]; /* type of file */ + unsigned char vstamp[2]; /* version stamp */ + unsigned char tsize[4]; /* text size in bytes, padded to FW bdry */ + unsigned char dsize[4]; /* initialized data " " */ + unsigned char bsize[4]; /* uninitialized data " " */ + unsigned char entry[4]; /* entry pt. */ + unsigned char text_start[4]; /* base of text used for this file */ + unsigned char data_start[4]; /* base of data used for this file */ + unsigned char o_toc[4]; /* address of TOC */ + unsigned char o_snentry[2]; /* section number of entry point */ + unsigned char o_sntext[2]; /* section number of .text section */ + unsigned char o_sndata[2]; /* section number of .data section */ + unsigned char o_sntoc[2]; /* section number of TOC */ + unsigned char o_snloader[2]; /* section number of .loader section */ + unsigned char o_snbss[2]; /* section number of .bss section */ + unsigned char o_algntext[2]; /* .text alignment */ + unsigned char o_algndata[2]; /* .data alignment */ + unsigned char o_modtype[2]; /* module type (??) */ + unsigned char o_cputype[2]; /* cpu type */ + unsigned char o_maxstack[4]; /* max stack size (??) */ + unsigned char o_maxdata[4]; /* max data size (??) */ + unsigned char o_resv2[12]; /* reserved */ +} +AOUTHDR; + +#define AOUTSZ 72 +#define SMALL_AOUTSZ (28) +#define AOUTHDRSZ 72 + +#define RS6K_AOUTHDR_OMAGIC 0x0107 /* old: text & data writeable */ +#define RS6K_AOUTHDR_NMAGIC 0x0108 /* new: text r/o, data r/w */ +#define RS6K_AOUTHDR_ZMAGIC 0x010B /* paged: text r/o, both page-aligned */ + + +/********************** SECTION HEADER **********************/ + + +struct external_scnhdr { + char s_name[8]; /* section name */ + char s_paddr[4]; /* physical address, aliased s_nlib */ + char s_vaddr[4]; /* virtual address */ + char s_size[4]; /* section size */ + char s_scnptr[4]; /* file ptr to raw data for section */ + char s_relptr[4]; /* file ptr to relocation */ + char s_lnnoptr[4]; /* file ptr to line numbers */ + char s_nreloc[2]; /* number of relocation entries */ + char s_nlnno[2]; /* number of line number entries*/ + char s_flags[4]; /* flags */ +}; + +/* + * names of "special" sections + */ +#define _TEXT ".text" +#define _DATA ".data" +#define _BSS ".bss" +#define _PAD ".pad" +#define _LOADER ".loader" + +#define SCNHDR struct external_scnhdr +#define SCNHSZ 40 + +/* XCOFF uses a special .loader section with type STYP_LOADER. */ +#define STYP_LOADER 0x1000 + +/* XCOFF uses a special .debug section with type STYP_DEBUG. */ +#define STYP_DEBUG 0x2000 + +/* XCOFF handles line number or relocation overflow by creating + another section header with STYP_OVRFLO set. */ +#define STYP_OVRFLO 0x8000 + +/********************** LINE NUMBERS **********************/ + +/* 1 line number entry for every "breakpointable" source line in a section. + * Line numbers are grouped on a per function basis; first entry in a function + * grouping will have l_lnno = 0 and in place of physical address will be the + * symbol table index of the function name. + */ +struct external_lineno { + union { + char l_symndx[4]; /* function name symbol index, iff l_lnno == 0*/ + char l_paddr[4]; /* (physical) address of line number */ + } l_addr; + char l_lnno[2]; /* line number */ +}; + + +#define LINENO struct external_lineno +#define LINESZ 6 + + +/********************** SYMBOLS **********************/ + +#define E_SYMNMLEN 8 /* # characters in a symbol name */ +#define E_FILNMLEN 14 /* # characters in a file name */ +#define E_DIMNUM 4 /* # array dimensions in auxiliary entry */ + +struct external_syment +{ + union { + char e_name[E_SYMNMLEN]; + struct { + char e_zeroes[4]; + char e_offset[4]; + } e; + } e; + char e_value[4]; + char e_scnum[2]; + char e_type[2]; + char e_sclass[1]; + char e_numaux[1]; +}; + + + +#define N_BTMASK (017) +#define N_TMASK (060) +#define N_BTSHFT (4) +#define N_TSHIFT (2) + + +union external_auxent { + struct { + char x_tagndx[4]; /* str, un, or enum tag indx */ + union { + struct { + char x_lnno[2]; /* declaration line number */ + char x_size[2]; /* str/union/array size */ + } x_lnsz; + char x_fsize[4]; /* size of function */ + } x_misc; + union { + struct { /* if ISFCN, tag, or .bb */ + char x_lnnoptr[4]; /* ptr to fcn line # */ + char x_endndx[4]; /* entry ndx past block end */ + } x_fcn; + struct { /* if ISARY, up to 4 dimen. */ + char x_dimen[E_DIMNUM][2]; + } x_ary; + } x_fcnary; + char x_tvndx[2]; /* tv index */ + } x_sym; + + union { + char x_fname[E_FILNMLEN]; + struct { + char x_zeroes[4]; + char x_offset[4]; + } x_n; + } x_file; + + struct { + char x_scnlen[4]; /* section length */ + char x_nreloc[2]; /* # relocation entries */ + char x_nlinno[2]; /* # line numbers */ + } x_scn; + + struct { + char x_tvfill[4]; /* tv fill value */ + char x_tvlen[2]; /* length of .tv */ + char x_tvran[2][2]; /* tv range */ + } x_tv; /* info about .tv section (in auxent of symbol .tv)) */ + + struct { + unsigned char x_scnlen[4]; + unsigned char x_parmhash[4]; + unsigned char x_snhash[2]; + unsigned char x_smtyp[1]; + unsigned char x_smclas[1]; + unsigned char x_stab[4]; + unsigned char x_snstab[2]; + } x_csect; + +}; + +#define SYMENT struct external_syment +#define SYMESZ 18 +#define AUXENT union external_auxent +#define AUXESZ 18 +#define DBXMASK 0x80 /* for dbx storage mask */ +#define SYMNAME_IN_DEBUG(symptr) ((symptr)->n_sclass & DBXMASK) + + + +/********************** RELOCATION DIRECTIVES **********************/ + + +struct external_reloc { + char r_vaddr[4]; + char r_symndx[4]; + char r_size[1]; + char r_type[1]; +}; + + +#define RELOC struct external_reloc +#define RELSZ 10 + +#define DEFAULT_DATA_SECTION_ALIGNMENT 4 +#define DEFAULT_BSS_SECTION_ALIGNMENT 4 +#define DEFAULT_TEXT_SECTION_ALIGNMENT 4 +/* For new sections we havn't heard of before */ +#define DEFAULT_SECTION_ALIGNMENT 4 diff --git a/arch/ppc/coffboot/start.c b/arch/ppc/coffboot/start.c new file mode 100644 index 000000000..363e65962 --- /dev/null +++ b/arch/ppc/coffboot/start.c @@ -0,0 +1,280 @@ +/* + * Copyright (C) Paul Mackerras 1997. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include <stdarg.h> + +int (*prom)(); + +void *chosen_handle; +void *stdin; +void *stdout; +void *stderr; + +void exit(void); +void *finddevice(const char *name); +int getprop(void *phandle, const char *name, void *buf, int buflen); + +void +start(int a1, int a2, void *promptr) +{ + prom = (int (*)()) promptr; + chosen_handle = finddevice("/chosen"); + if (chosen_handle == (void *) -1) + exit(); + if (getprop(chosen_handle, "stdout", &stdout, sizeof(stdout)) != 4) + exit(); + stderr = stdout; + if (getprop(chosen_handle, "stdin", &stdin, sizeof(stdin)) != 4) + exit(); + + coffboot(a1, a2, promptr); + for (;;) + exit(); +} + +int +write(void *handle, void *ptr, int nb) +{ + struct prom_args { + char *service; + int nargs; + int nret; + void *ihandle; + void *addr; + int len; + int actual; + } args; + + args.service = "write"; + args.nargs = 3; + args.nret = 1; + args.ihandle = handle; + args.addr = ptr; + args.len = nb; + args.actual = -1; + (*prom)(&args); + return args.actual; +} + +int +read(void *handle, void *ptr, int nb) +{ + struct prom_args { + char *service; + int nargs; + int nret; + void *ihandle; + void *addr; + int len; + int actual; + } args; + + args.service = "read"; + args.nargs = 3; + args.nret = 1; + args.ihandle = handle; + args.addr = ptr; + args.len = nb; + args.actual = -1; + (*prom)(&args); + return args.actual; +} + +void +exit() +{ + struct prom_args { + char *service; + } args; + + for (;;) { + args.service = "exit"; + (*prom)(&args); + } +} + +void +pause() +{ + struct prom_args { + char *service; + } args; + + args.service = "enter"; + (*prom)(&args); +} + +void * +finddevice(const char *name) +{ + struct prom_args { + char *service; + int nargs; + int nret; + const char *devspec; + void *phandle; + } args; + + args.service = "finddevice"; + args.nargs = 1; + args.nret = 1; + args.devspec = name; + args.phandle = (void *) -1; + (*prom)(&args); + return args.phandle; +} + +int +getprop(void *phandle, const char *name, void *buf, int buflen) +{ + struct prom_args { + char *service; + int nargs; + int nret; + void *phandle; + const char *name; + void *buf; + int buflen; + int size; + } args; + + args.service = "getprop"; + args.nargs = 4; + args.nret = 1; + args.phandle = phandle; + args.name = name; + args.buf = buf; + args.buflen = buflen; + args.size = -1; + (*prom)(&args); + return args.size; +} + +int +putc(int c, void *f) +{ + char ch = c; + + if (c == '\n') + putc('\r', f); + return write(f, &ch, 1) == 1? c: -1; +} + +int +putchar(int c) +{ + return putc(c, stdout); +} + +int +fputs(char *str, void *f) +{ + int n = strlen(str); + + return write(f, str, n) == n? 0: -1; +} + +int +readchar() +{ + char ch; + + for (;;) { + switch (read(stdin, &ch, 1)) { + case 1: + return ch; + case -1: + printk("read(stdin) returned -1\r\n"); + return -1; + } + } +} + +static char line[256]; +static char *lineptr; +static int lineleft; + +int +getchar() +{ + int c; + + if (lineleft == 0) { + lineptr = line; + for (;;) { + c = readchar(); + if (c == -1 || c == 4) + break; + if (c == '\r' || c == '\n') { + *lineptr++ = '\n'; + putchar('\n'); + break; + } + switch (c) { + case 0177: + case '\b': + if (lineptr > line) { + putchar('\b'); + putchar(' '); + putchar('\b'); + --lineptr; + } + break; + case 'U' & 0x1F: + while (lineptr > line) { + putchar('\b'); + putchar(' '); + putchar('\b'); + --lineptr; + } + break; + default: + if (lineptr >= &line[sizeof(line) - 1]) + putchar('\a'); + else { + putchar(c); + *lineptr++ = c; + } + } + } + lineleft = lineptr - line; + lineptr = line; + } + if (lineleft == 0) + return -1; + --lineleft; + return *lineptr++; +} + +extern int vsprintf(char *buf, const char *fmt, va_list args); +static char sprint_buf[1024]; + +void +printk(char *fmt, ...) +{ + va_list args; + int n; + + va_start(args, fmt); + n = vsprintf(sprint_buf, fmt, args); + va_end(args); + write(stdout, sprint_buf, n); +} + +int +printf(char *fmt, ...) +{ + va_list args; + int n; + + va_start(args, fmt); + n = vsprintf(sprint_buf, fmt, args); + va_end(args); + write(stdout, sprint_buf, n); + return n; +} diff --git a/arch/ppc/coffboot/string.S b/arch/ppc/coffboot/string.S new file mode 100644 index 000000000..ba83591b7 --- /dev/null +++ b/arch/ppc/coffboot/string.S @@ -0,0 +1,206 @@ +/* + * String handling functions for PowerPC. + * + * Copyright (C) 1996 Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#define r0 0 +#define r3 3 +#define r4 4 +#define r5 5 +#define r6 6 +#define r7 7 +#define r8 8 + + .globl strcpy +strcpy: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r5) + bne 1b + blr + + .globl strncpy +strncpy: + cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r6) + bdnzf 2,1b /* dec ctr, branch if ctr != 0 && !cr0.eq */ + blr + + .globl strcat +strcat: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r5) + cmpwi 0,r0,0 + bne 1b + addi r5,r5,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r5) + bne 1b + blr + + .globl strcmp +strcmp: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r3,1(r5) + cmpwi 1,r3,0 + lbzu r0,1(r4) + subf. r3,r0,r3 + beqlr 1 + beq 1b + blr + + .globl strlen +strlen: + addi r4,r3,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + bne 1b + subf r3,r3,r4 + blr + + .globl memset +memset: + rlwimi r4,r4,8,16,23 + rlwimi r4,r4,16,0,15 + addi r6,r3,-4 + cmplwi 0,r5,4 + blt 7f + stwu r4,4(r6) + beqlr + andi. r0,r6,3 + add r5,r0,r5 + subf r6,r0,r6 + rlwinm r0,r5,32-2,2,31 + mtctr r0 + bdz 6f +1: stwu r4,4(r6) + bdnz 1b +6: andi. r5,r5,3 +7: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r6,3 +8: stbu r4,1(r6) + bdnz 8b + blr + + .globl bcopy +bcopy: + mr r6,r3 + mr r3,r4 + mr r4,r6 + b memcpy + + .globl memmove +memmove: + cmplw 0,r3,r4 + bgt backwards_memcpy + /* fall through */ + + .globl memcpy +memcpy: + rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ + addi r6,r3,-4 + addi r4,r4,-4 + beq 2f /* if less than 8 bytes to do */ + andi. r0,r6,3 /* get dest word aligned */ + mtctr r7 + bne 5f +1: lwz r7,4(r4) + lwzu r8,8(r4) + stw r7,4(r6) + stwu r8,8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f + lwzu r0,4(r4) + addi r5,r5,-4 + stwu r0,4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r4,r4,3 + addi r6,r6,3 +4: lbzu r0,1(r4) + stbu r0,1(r6) + bdnz 4b + blr +5: subfic r0,r0,4 + mtctr r0 +6: lbz r7,4(r4) + addi r4,r4,1 + stb r7,4(r6) + addi r6,r6,1 + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b + + .globl backwards_memcpy +backwards_memcpy: + rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ + add r6,r3,r5 + add r4,r4,r5 + beq 2f + andi. r0,r6,3 + mtctr r7 + bne 5f +1: lwz r7,-4(r4) + lwzu r8,-8(r4) + stw r7,-4(r6) + stwu r8,-8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f + lwzu r0,-4(r4) + subi r5,r5,4 + stwu r0,-4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 +4: lbzu r0,-1(r4) + stbu r0,-1(r6) + bdnz 4b + blr +5: mtctr r0 +6: lbzu r7,-1(r4) + stbu r7,-1(r6) + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b + + .globl memcmp +memcmp: + cmpwi 0,r5,0 + blelr + mtctr r5 + addi r6,r3,-1 + addi r4,r4,-1 +1: lbzu r3,1(r6) + lbzu r0,1(r4) + subf. r3,r0,r3 + bdnzt 2,1b + blr diff --git a/arch/ppc/coffboot/zlib.c b/arch/ppc/coffboot/zlib.c new file mode 100644 index 000000000..12d07df41 --- /dev/null +++ b/arch/ppc/coffboot/zlib.c @@ -0,0 +1,2143 @@ +/* + * This file is derived from various .h and .c files from the zlib-0.95 + * distribution by Jean-loup Gailly and Mark Adler, with some additions + * by Paul Mackerras to aid in implementing Deflate compression and + * decompression for PPP packets. See zlib.h for conditions of + * distribution and use. + * + * Changes that have been made include: + * - changed functions not used outside this file to "local" + * - added minCompression parameter to deflateInit2 + * - added Z_PACKET_FLUSH (see zlib.h for details) + * - added inflateIncomp + * + * $Id: zlib.c,v 1.1 1997/08/30 04:51:48 ralf Exp $ + */ + +/*+++++*/ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* From: zutil.h,v 1.9 1995/05/03 17:27:12 jloup Exp */ + +#define _Z_UTIL_H + +#include "zlib.h" + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +#define FAR + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern char *z_errmsg[]; /* indexed by 1-zlib_error */ + +#define ERR_RETURN(strm,err) return (strm->msg=z_errmsg[1-err], err) +/* To be used only when the state is known to be valid */ + +#ifndef NULL +#define NULL ((void *) 0) +#endif + + /* common constants */ + +#define DEFLATED 8 + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + + /* functions */ + +#include <string.h> +#define zmemcpy memcpy +#define zmemzero(dest, len) memset(dest, 0, len) + +/* Diagnostic functions */ +#ifdef DEBUG_ZLIB +# include <stdio.h> +# ifndef verbose +# define verbose 0 +# endif +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x ;} +# define Tracevv(x) {if (verbose>1) fprintf x ;} +# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} +# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +typedef uLong (*check_func) OF((uLong check, Bytef *buf, uInt len)); + +/* voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); */ +/* void zcfree OF((voidpf opaque, voidpf ptr)); */ + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr, size) \ + (*((strm)->zfree))((strm)->opaque, (voidpf)(addr), (size)) +#define TRY_FREE(s, p, n) {if (p) ZFREE(s, p, n);} + +/* deflate.h -- internal compression state + * Copyright (C) 1995 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/*+++++*/ +/* infblock.h -- header to use infblock.c + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +struct inflate_blocks_state; +typedef struct inflate_blocks_state FAR inflate_blocks_statef; + +local inflate_blocks_statef * inflate_blocks_new OF(( + z_stream *z, + check_func c, /* check function */ + uInt w)); /* window size */ + +local int inflate_blocks OF(( + inflate_blocks_statef *, + z_stream *, + int)); /* initial return code */ + +local void inflate_blocks_reset OF(( + inflate_blocks_statef *, + z_stream *, + uLongf *)); /* check value on output */ + +local int inflate_blocks_free OF(( + inflate_blocks_statef *, + z_stream *, + uLongf *)); /* check value on output */ + +local int inflate_addhistory OF(( + inflate_blocks_statef *, + z_stream *)); + +local int inflate_packet_flush OF(( + inflate_blocks_statef *)); + +/*+++++*/ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Huffman code lookup table entry--this entry is four bytes for machines + that have 16-bit pointers (e.g. PC's in the small or medium model). */ + +typedef struct inflate_huft_s FAR inflate_huft; + +struct inflate_huft_s { + union { + struct { + Byte Exop; /* number of extra bits or operation */ + Byte Bits; /* number of bits in this code or subcode */ + } what; + uInt Nalloc; /* number of these allocated here */ + Bytef *pad; /* pad structure to a power of 2 (4 bytes for */ + } word; /* 16-bit, 8 bytes for 32-bit machines) */ + union { + uInt Base; /* literal, length base, or distance base */ + inflate_huft *Next; /* pointer to next level of table */ + } more; +}; + +#ifdef DEBUG_ZLIB + local uInt inflate_hufts; +#endif + +local int inflate_trees_bits OF(( + uIntf *, /* 19 code lengths */ + uIntf *, /* bits tree desired/actual depth */ + inflate_huft * FAR *, /* bits tree result */ + z_stream *)); /* for zalloc, zfree functions */ + +local int inflate_trees_dynamic OF(( + uInt, /* number of literal/length codes */ + uInt, /* number of distance codes */ + uIntf *, /* that many (total) code lengths */ + uIntf *, /* literal desired/actual bit depth */ + uIntf *, /* distance desired/actual bit depth */ + inflate_huft * FAR *, /* literal/length tree result */ + inflate_huft * FAR *, /* distance tree result */ + z_stream *)); /* for zalloc, zfree functions */ + +local int inflate_trees_fixed OF(( + uIntf *, /* literal desired/actual bit depth */ + uIntf *, /* distance desired/actual bit depth */ + inflate_huft * FAR *, /* literal/length tree result */ + inflate_huft * FAR *)); /* distance tree result */ + +local int inflate_trees_free OF(( + inflate_huft *, /* tables to free */ + z_stream *)); /* for zfree function */ + + +/*+++++*/ +/* infcodes.h -- header to use infcodes.c + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +struct inflate_codes_state; +typedef struct inflate_codes_state FAR inflate_codes_statef; + +local inflate_codes_statef *inflate_codes_new OF(( + uInt, uInt, + inflate_huft *, inflate_huft *, + z_stream *)); + +local int inflate_codes OF(( + inflate_blocks_statef *, + z_stream *, + int)); + +local void inflate_codes_free OF(( + inflate_codes_statef *, + z_stream *)); + + +/*+++++*/ +/* inflate.c -- zlib interface to inflate modules + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* inflate private state */ +struct internal_state { + + /* mode */ + enum { + METHOD, /* waiting for method byte */ + FLAG, /* waiting for flag byte */ + BLOCKS, /* decompressing blocks */ + CHECK4, /* four check bytes to go */ + CHECK3, /* three check bytes to go */ + CHECK2, /* two check bytes to go */ + CHECK1, /* one check byte to go */ + DONE, /* finished check, done */ + BAD} /* got an error--stay here */ + mode; /* current inflate mode */ + + /* mode dependent information */ + union { + uInt method; /* if FLAGS, method byte */ + struct { + uLong was; /* computed check value */ + uLong need; /* stream check value */ + } check; /* if CHECK, check values to compare */ + uInt marker; /* if BAD, inflateSync's marker bytes count */ + } sub; /* submode */ + + /* mode independent information */ + int nowrap; /* flag for no wrapper */ + uInt wbits; /* log2(window size) (8..15, defaults to 15) */ + inflate_blocks_statef + *blocks; /* current inflate_blocks state */ + +}; + + +int inflateReset(z) +z_stream *z; +{ + uLong c; + + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + z->total_in = z->total_out = 0; + z->msg = Z_NULL; + z->state->mode = z->state->nowrap ? BLOCKS : METHOD; + inflate_blocks_reset(z->state->blocks, z, &c); + Trace((stderr, "inflate: reset\n")); + return Z_OK; +} + + +int inflateEnd(z) +z_stream *z; +{ + uLong c; + + if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->blocks != Z_NULL) + inflate_blocks_free(z->state->blocks, z, &c); + ZFREE(z, z->state, sizeof(struct internal_state)); + z->state = Z_NULL; + Trace((stderr, "inflate: end\n")); + return Z_OK; +} + + +int inflateInit2(z, w) +z_stream *z; +int w; +{ + /* initialize state */ + if (z == Z_NULL) + return Z_STREAM_ERROR; +/* if (z->zalloc == Z_NULL) z->zalloc = zcalloc; */ +/* if (z->zfree == Z_NULL) z->zfree = zcfree; */ + if ((z->state = (struct internal_state FAR *) + ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL) + return Z_MEM_ERROR; + z->state->blocks = Z_NULL; + + /* handle undocumented nowrap option (no zlib header or check) */ + z->state->nowrap = 0; + if (w < 0) + { + w = - w; + z->state->nowrap = 1; + } + + /* set window size */ + if (w < 8 || w > 15) + { + inflateEnd(z); + return Z_STREAM_ERROR; + } + z->state->wbits = (uInt)w; + + /* create inflate_blocks state */ + if ((z->state->blocks = + inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, 1 << w)) + == Z_NULL) + { + inflateEnd(z); + return Z_MEM_ERROR; + } + Trace((stderr, "inflate: allocated\n")); + + /* reset state */ + inflateReset(z); + return Z_OK; +} + + +int inflateInit(z) +z_stream *z; +{ + return inflateInit2(z, DEF_WBITS); +} + + +#define NEEDBYTE {if(z->avail_in==0)goto empty;r=Z_OK;} +#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++) + +int inflate(z, f) +z_stream *z; +int f; +{ + int r; + uInt b; + + if (z == Z_NULL || z->next_in == Z_NULL) + return Z_STREAM_ERROR; + r = Z_BUF_ERROR; + while (1) switch (z->state->mode) + { + case METHOD: + NEEDBYTE + if (((z->state->sub.method = NEXTBYTE) & 0xf) != DEFLATED) + { + z->state->mode = BAD; + z->msg = "unknown compression method"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + if ((z->state->sub.method >> 4) + 8 > z->state->wbits) + { + z->state->mode = BAD; + z->msg = "invalid window size"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + z->state->mode = FLAG; + case FLAG: + NEEDBYTE + if ((b = NEXTBYTE) & 0x20) + { + z->state->mode = BAD; + z->msg = "invalid reserved bit"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + if (((z->state->sub.method << 8) + b) % 31) + { + z->state->mode = BAD; + z->msg = "incorrect header check"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + Trace((stderr, "inflate: zlib header ok\n")); + z->state->mode = BLOCKS; + case BLOCKS: + r = inflate_blocks(z->state->blocks, z, r); + if (f == Z_PACKET_FLUSH && z->avail_in == 0 && z->avail_out != 0) + r = inflate_packet_flush(z->state->blocks); + if (r == Z_DATA_ERROR) + { + z->state->mode = BAD; + z->state->sub.marker = 0; /* can try inflateSync */ + break; + } + if (r != Z_STREAM_END) + return r; + r = Z_OK; + inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was); + if (z->state->nowrap) + { + z->state->mode = DONE; + break; + } + z->state->mode = CHECK4; + case CHECK4: + NEEDBYTE + z->state->sub.check.need = (uLong)NEXTBYTE << 24; + z->state->mode = CHECK3; + case CHECK3: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 16; + z->state->mode = CHECK2; + case CHECK2: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE << 8; + z->state->mode = CHECK1; + case CHECK1: + NEEDBYTE + z->state->sub.check.need += (uLong)NEXTBYTE; + + if (z->state->sub.check.was != z->state->sub.check.need) + { + z->state->mode = BAD; + z->msg = "incorrect data check"; + z->state->sub.marker = 5; /* can't try inflateSync */ + break; + } + Trace((stderr, "inflate: zlib check ok\n")); + z->state->mode = DONE; + case DONE: + return Z_STREAM_END; + case BAD: + return Z_DATA_ERROR; + default: + return Z_STREAM_ERROR; + } + + empty: + if (f != Z_PACKET_FLUSH) + return r; + z->state->mode = BAD; + z->state->sub.marker = 0; /* can try inflateSync */ + return Z_DATA_ERROR; +} + +/* + * This subroutine adds the data at next_in/avail_in to the output history + * without performing any output. The output buffer must be "caught up"; + * i.e. no pending output (hence s->read equals s->write), and the state must + * be BLOCKS (i.e. we should be willing to see the start of a series of + * BLOCKS). On exit, the output will also be caught up, and the checksum + * will have been updated if need be. + */ + +int inflateIncomp(z) +z_stream *z; +{ + if (z->state->mode != BLOCKS) + return Z_DATA_ERROR; + return inflate_addhistory(z->state->blocks, z); +} + + +int inflateSync(z) +z_stream *z; +{ + uInt n; /* number of bytes to look at */ + Bytef *p; /* pointer to bytes */ + uInt m; /* number of marker bytes found in a row */ + uLong r, w; /* temporaries to save total_in and total_out */ + + /* set up */ + if (z == Z_NULL || z->state == Z_NULL) + return Z_STREAM_ERROR; + if (z->state->mode != BAD) + { + z->state->mode = BAD; + z->state->sub.marker = 0; + } + if ((n = z->avail_in) == 0) + return Z_BUF_ERROR; + p = z->next_in; + m = z->state->sub.marker; + + /* search */ + while (n && m < 4) + { + if (*p == (Byte)(m < 2 ? 0 : 0xff)) + m++; + else if (*p) + m = 0; + else + m = 4 - m; + p++, n--; + } + + /* restore */ + z->total_in += p - z->next_in; + z->next_in = p; + z->avail_in = n; + z->state->sub.marker = m; + + /* return no joy or set up to restart on a new block */ + if (m != 4) + return Z_DATA_ERROR; + r = z->total_in; w = z->total_out; + inflateReset(z); + z->total_in = r; z->total_out = w; + z->state->mode = BLOCKS; + return Z_OK; +} + +#undef NEEDBYTE +#undef NEXTBYTE + +/*+++++*/ +/* infutil.h -- types and macros common to blocks and codes + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* inflate blocks semi-private state */ +struct inflate_blocks_state { + + /* mode */ + enum { + TYPE, /* get type bits (3, including end bit) */ + LENS, /* get lengths for stored */ + STORED, /* processing stored block */ + TABLE, /* get table lengths */ + BTREE, /* get bit lengths tree for a dynamic block */ + DTREE, /* get length, distance trees for a dynamic block */ + CODES, /* processing fixed or dynamic block */ + DRY, /* output remaining window bytes */ + DONEB, /* finished last block, done */ + BADB} /* got a data error--stuck here */ + mode; /* current inflate_block mode */ + + /* mode dependent information */ + union { + uInt left; /* if STORED, bytes left to copy */ + struct { + uInt table; /* table lengths (14 bits) */ + uInt index; /* index into blens (or border) */ + uIntf *blens; /* bit lengths of codes */ + uInt bb; /* bit length tree depth */ + inflate_huft *tb; /* bit length decoding tree */ + int nblens; /* # elements allocated at blens */ + } trees; /* if DTREE, decoding info for trees */ + struct { + inflate_huft *tl, *td; /* trees to free */ + inflate_codes_statef + *codes; + } decode; /* if CODES, current state */ + } sub; /* submode */ + uInt last; /* true if this block is the last block */ + + /* mode independent information */ + uInt bitk; /* bits in bit buffer */ + uLong bitb; /* bit buffer */ + Bytef *window; /* sliding window */ + Bytef *end; /* one byte after sliding window */ + Bytef *read; /* window read pointer */ + Bytef *write; /* window write pointer */ + check_func checkfn; /* check function */ + uLong check; /* check on output */ + +}; + + +/* defines for inflate input/output */ +/* update pointers and return */ +#define UPDBITS {s->bitb=b;s->bitk=k;} +#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;} +#define UPDOUT {s->write=q;} +#define UPDATE {UPDBITS UPDIN UPDOUT} +#define LEAVE {UPDATE return inflate_flush(s,z,r);} +/* get bytes and bits */ +#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;} +#define NEEDBYTE {if(n)r=Z_OK;else LEAVE} +#define NEXTBYTE (n--,*p++) +#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<<k;k+=8;}} +#define DUMPBITS(j) {b>>=(j);k-=(j);} +/* output bytes */ +#define WAVAIL (q<s->read?s->read-q-1:s->end-q) +#define LOADOUT {q=s->write;m=WAVAIL;} +#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=WAVAIL;}} +#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT} +#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;} +#define OUTBYTE(a) {*q++=(Byte)(a);m--;} +/* load local pointers */ +#define LOAD {LOADIN LOADOUT} + +/* And'ing with mask[n] masks the lower n bits */ +local uInt inflate_mask[] = { + 0x0000, + 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, + 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff +}; + +/* copy as much as possible from the sliding window to the output area */ +local int inflate_flush OF(( + inflate_blocks_statef *, + z_stream *, + int)); + +/*+++++*/ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +local int inflate_fast OF(( + uInt, + uInt, + inflate_huft *, + inflate_huft *, + inflate_blocks_statef *, + z_stream *)); + + +/*+++++*/ +/* infblock.c -- interpret and process block types to last block + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* Table for deflate from PKZIP's appnote.txt. */ +local uInt border[] = { /* Order of the bit length code lengths */ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +/* + Notes beyond the 1.93a appnote.txt: + + 1. Distance pointers never point before the beginning of the output + stream. + 2. Distance pointers can point back across blocks, up to 32k away. + 3. There is an implied maximum of 7 bits for the bit length table and + 15 bits for the actual data. + 4. If only one code exists, then it is encoded using one bit. (Zero + would be more efficient, but perhaps a little confusing.) If two + codes exist, they are coded using one bit each (0 and 1). + 5. There is no way of sending zero distance codes--a dummy must be + sent if there are none. (History: a pre 2.0 version of PKZIP would + store blocks with no distance codes, but this was discovered to be + too harsh a criterion.) Valid only for 1.93a. 2.04c does allow + zero distance codes, which is sent as one code of zero bits in + length. + 6. There are up to 286 literal/length codes. Code 256 represents the + end-of-block. Note however that the static length tree defines + 288 codes just to fill out the Huffman codes. Codes 286 and 287 + cannot be used though, since there is no length base or extra bits + defined for them. Similarily, there are up to 30 distance codes. + However, static trees define 32 codes (all 5 bits) to fill out the + Huffman codes, but the last two had better not show up in the data. + 7. Unzip can check dynamic Huffman blocks for complete code sets. + The exception is that a single code would not be complete (see #4). + 8. The five bits following the block type is really the number of + literal codes sent minus 257. + 9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits + (1+6+6). Therefore, to output three times the length, you output + three codes (1+1+1), whereas to output four times the same length, + you only need two codes (1+3). Hmm. + 10. In the tree reconstruction algorithm, Code = Code + Increment + only if BitLength(i) is not zero. (Pretty obvious.) + 11. Correction: 4 Bits: # of Bit Length codes - 4 (4 - 19) + 12. Note: length code 284 can represent 227-258, but length code 285 + really is 258. The last length deserves its own, short code + since it gets used a lot in very redundant files. The length + 258 is special since 258 - 3 (the min match length) is 255. + 13. The literal/length and distance code bit lengths are read as a + single stream of lengths. It is possible (and advantageous) for + a repeat code (16, 17, or 18) to go across the boundary between + the two sets of lengths. + */ + + +local void inflate_blocks_reset(s, z, c) +inflate_blocks_statef *s; +z_stream *z; +uLongf *c; +{ + if (s->checkfn != Z_NULL) + *c = s->check; + if (s->mode == BTREE || s->mode == DTREE) + ZFREE(z, s->sub.trees.blens, s->sub.trees.nblens * sizeof(uInt)); + if (s->mode == CODES) + { + inflate_codes_free(s->sub.decode.codes, z); + inflate_trees_free(s->sub.decode.td, z); + inflate_trees_free(s->sub.decode.tl, z); + } + s->mode = TYPE; + s->bitk = 0; + s->bitb = 0; + s->read = s->write = s->window; + if (s->checkfn != Z_NULL) + s->check = (*s->checkfn)(0L, Z_NULL, 0); + Trace((stderr, "inflate: blocks reset\n")); +} + + +local inflate_blocks_statef *inflate_blocks_new(z, c, w) +z_stream *z; +check_func c; +uInt w; +{ + inflate_blocks_statef *s; + + if ((s = (inflate_blocks_statef *)ZALLOC + (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL) + return s; + if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL) + { + ZFREE(z, s, sizeof(struct inflate_blocks_state)); + return Z_NULL; + } + s->end = s->window + w; + s->checkfn = c; + s->mode = TYPE; + Trace((stderr, "inflate: blocks allocated\n")); + inflate_blocks_reset(s, z, &s->check); + return s; +} + + +local int inflate_blocks(s, z, r) +inflate_blocks_statef *s; +z_stream *z; +int r; +{ + uInt t; /* temporary storage */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + + /* copy input/output information to locals (UPDATE macro restores) */ + LOAD + + /* process input based on current state */ + while (1) switch (s->mode) + { + case TYPE: + NEEDBITS(3) + t = (uInt)b & 7; + s->last = t & 1; + switch (t >> 1) + { + case 0: /* stored */ + Trace((stderr, "inflate: stored block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + t = k & 7; /* go to byte boundary */ + DUMPBITS(t) + s->mode = LENS; /* get length of stored block */ + break; + case 1: /* fixed */ + Trace((stderr, "inflate: fixed codes block%s\n", + s->last ? " (last)" : "")); + { + uInt bl, bd; + inflate_huft *tl, *td; + + inflate_trees_fixed(&bl, &bd, &tl, &td); + s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z); + if (s->sub.decode.codes == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + s->sub.decode.tl = Z_NULL; /* don't try to free these */ + s->sub.decode.td = Z_NULL; + } + DUMPBITS(3) + s->mode = CODES; + break; + case 2: /* dynamic */ + Trace((stderr, "inflate: dynamic codes block%s\n", + s->last ? " (last)" : "")); + DUMPBITS(3) + s->mode = TABLE; + break; + case 3: /* illegal */ + DUMPBITS(3) + s->mode = BADB; + z->msg = "invalid block type"; + r = Z_DATA_ERROR; + LEAVE + } + break; + case LENS: + NEEDBITS(32) + if (((~b) >> 16) != (b & 0xffff)) + { + s->mode = BADB; + z->msg = "invalid stored block lengths"; + r = Z_DATA_ERROR; + LEAVE + } + s->sub.left = (uInt)b & 0xffff; + b = k = 0; /* dump bits */ + Tracev((stderr, "inflate: stored length %u\n", s->sub.left)); + s->mode = s->sub.left ? STORED : TYPE; + break; + case STORED: + if (n == 0) + LEAVE + NEEDOUT + t = s->sub.left; + if (t > n) t = n; + if (t > m) t = m; + zmemcpy(q, p, t); + p += t; n -= t; + q += t; m -= t; + if ((s->sub.left -= t) != 0) + break; + Tracev((stderr, "inflate: stored end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + s->mode = s->last ? DRY : TYPE; + break; + case TABLE: + NEEDBITS(14) + s->sub.trees.table = t = (uInt)b & 0x3fff; +#ifndef PKZIP_BUG_WORKAROUND + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + s->mode = BADB; + z->msg = "too many length or distance symbols"; + r = Z_DATA_ERROR; + LEAVE + } +#endif + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if (t < 19) + t = 19; + if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL) + { + r = Z_MEM_ERROR; + LEAVE + } + s->sub.trees.nblens = t; + DUMPBITS(14) + s->sub.trees.index = 0; + Tracev((stderr, "inflate: table sizes ok\n")); + s->mode = BTREE; + case BTREE: + while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10)) + { + NEEDBITS(3) + s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7; + DUMPBITS(3) + } + while (s->sub.trees.index < 19) + s->sub.trees.blens[border[s->sub.trees.index++]] = 0; + s->sub.trees.bb = 7; + t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb, + &s->sub.trees.tb, z); + if (t != Z_OK) + { + r = t; + if (r == Z_DATA_ERROR) + s->mode = BADB; + LEAVE + } + s->sub.trees.index = 0; + Tracev((stderr, "inflate: bits tree ok\n")); + s->mode = DTREE; + case DTREE: + while (t = s->sub.trees.table, + s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f)) + { + inflate_huft *h; + uInt i, j, c; + + t = s->sub.trees.bb; + NEEDBITS(t) + h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]); + t = h->word.what.Bits; + c = h->more.Base; + if (c < 16) + { + DUMPBITS(t) + s->sub.trees.blens[s->sub.trees.index++] = c; + } + else /* c == 16..18 */ + { + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + NEEDBITS(t + i) + DUMPBITS(t) + j += (uInt)b & inflate_mask[i]; + DUMPBITS(i) + i = s->sub.trees.index; + t = s->sub.trees.table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || + (c == 16 && i < 1)) + { + s->mode = BADB; + z->msg = "invalid bit length repeat"; + r = Z_DATA_ERROR; + LEAVE + } + c = c == 16 ? s->sub.trees.blens[i - 1] : 0; + do { + s->sub.trees.blens[i++] = c; + } while (--j); + s->sub.trees.index = i; + } + } + inflate_trees_free(s->sub.trees.tb, z); + s->sub.trees.tb = Z_NULL; + { + uInt bl, bd; + inflate_huft *tl, *td; + inflate_codes_statef *c; + + bl = 9; /* must be <= 9 for lookahead assumptions */ + bd = 6; /* must be <= 9 for lookahead assumptions */ + t = s->sub.trees.table; + t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), + s->sub.trees.blens, &bl, &bd, &tl, &td, z); + if (t != Z_OK) + { + if (t == (uInt)Z_DATA_ERROR) + s->mode = BADB; + r = t; + LEAVE + } + Tracev((stderr, "inflate: trees ok\n")); + if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL) + { + inflate_trees_free(td, z); + inflate_trees_free(tl, z); + r = Z_MEM_ERROR; + LEAVE + } + ZFREE(z, s->sub.trees.blens, s->sub.trees.nblens * sizeof(uInt)); + s->sub.decode.codes = c; + s->sub.decode.tl = tl; + s->sub.decode.td = td; + } + s->mode = CODES; + case CODES: + UPDATE + if ((r = inflate_codes(s, z, r)) != Z_STREAM_END) + return inflate_flush(s, z, r); + r = Z_OK; + inflate_codes_free(s->sub.decode.codes, z); + inflate_trees_free(s->sub.decode.td, z); + inflate_trees_free(s->sub.decode.tl, z); + LOAD + Tracev((stderr, "inflate: codes end, %lu total out\n", + z->total_out + (q >= s->read ? q - s->read : + (s->end - s->read) + (q - s->window)))); + if (!s->last) + { + s->mode = TYPE; + break; + } + if (k > 7) /* return unused byte, if any */ + { + Assert(k < 16, "inflate_codes grabbed too many bytes") + k -= 8; + n++; + p--; /* can always return one */ + } + s->mode = DRY; + case DRY: + FLUSH + if (s->read != s->write) + LEAVE + s->mode = DONEB; + case DONEB: + r = Z_STREAM_END; + LEAVE + case BADB: + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +local int inflate_blocks_free(s, z, c) +inflate_blocks_statef *s; +z_stream *z; +uLongf *c; +{ + inflate_blocks_reset(s, z, c); + ZFREE(z, s->window, s->end - s->window); + ZFREE(z, s, sizeof(struct inflate_blocks_state)); + Trace((stderr, "inflate: blocks freed\n")); + return Z_OK; +} + +/* + * This subroutine adds the data at next_in/avail_in to the output history + * without performing any output. The output buffer must be "caught up"; + * i.e. no pending output (hence s->read equals s->write), and the state must + * be BLOCKS (i.e. we should be willing to see the start of a series of + * BLOCKS). On exit, the output will also be caught up, and the checksum + * will have been updated if need be. + */ +local int inflate_addhistory(s, z) +inflate_blocks_statef *s; +z_stream *z; +{ + uLong b; /* bit buffer */ /* NOT USED HERE */ + uInt k; /* bits in bit buffer */ /* NOT USED HERE */ + uInt t; /* temporary storage */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + + if (s->read != s->write) + return Z_STREAM_ERROR; + if (s->mode != TYPE) + return Z_DATA_ERROR; + + /* we're ready to rock */ + LOAD + /* while there is input ready, copy to output buffer, moving + * pointers as needed. + */ + while (n) { + t = n; /* how many to do */ + /* is there room until end of buffer? */ + if (t > m) t = m; + /* update check information */ + if (s->checkfn != Z_NULL) + s->check = (*s->checkfn)(s->check, q, t); + zmemcpy(q, p, t); + q += t; + p += t; + n -= t; + z->total_out += t; + s->read = q; /* drag read pointer forward */ +/* WRAP */ /* expand WRAP macro by hand to handle s->read */ + if (q == s->end) { + s->read = q = s->window; + m = WAVAIL; + } + } + UPDATE + return Z_OK; +} + + +/* + * At the end of a Deflate-compressed PPP packet, we expect to have seen + * a `stored' block type value but not the (zero) length bytes. + */ +local int inflate_packet_flush(s) + inflate_blocks_statef *s; +{ + if (s->mode != LENS) + return Z_DATA_ERROR; + s->mode = TYPE; + return Z_OK; +} + + +/*+++++*/ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* simplify the use of the inflate_huft type with some defines */ +#define base more.Base +#define next more.Next +#define exop word.what.Exop +#define bits word.what.Bits + + +local int huft_build OF(( + uIntf *, /* code lengths in bits */ + uInt, /* number of codes */ + uInt, /* number of "simple" codes */ + uIntf *, /* list of base values for non-simple codes */ + uIntf *, /* list of extra bits for non-simple codes */ + inflate_huft * FAR*,/* result: starting table */ + uIntf *, /* maximum lookup bits (returns actual) */ + z_stream *)); /* for zalloc function */ + +local voidpf falloc OF(( + voidpf, /* opaque pointer (not used) */ + uInt, /* number of items */ + uInt)); /* size of item */ + +local void ffree OF(( + voidpf q, /* opaque pointer (not used) */ + voidpf p, /* what to free (not used) */ + uInt n)); /* number of bytes (not used) */ + +/* Tables for deflate from PKZIP's appnote.txt. */ +local uInt cplens[] = { /* Copy lengths for literal codes 257..285 */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + /* actually lengths - 2; also see note #13 above about 258 */ +local uInt cplext[] = { /* Extra bits for literal codes 257..285 */ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 192, 192}; /* 192==invalid */ +local uInt cpdist[] = { /* Copy offsets for distance codes 0..29 */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577}; +local uInt cpdext[] = { /* Extra bits for distance codes */ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13}; + +/* + Huffman code decoding is performed using a multi-level table lookup. + The fastest way to decode is to simply build a lookup table whose + size is determined by the longest code. However, the time it takes + to build this table can also be a factor if the data being decoded + is not very long. The most common codes are necessarily the + shortest codes, so those codes dominate the decoding time, and hence + the speed. The idea is you can have a shorter table that decodes the + shorter, more probable codes, and then point to subsidiary tables for + the longer codes. The time it costs to decode the longer codes is + then traded against the time it takes to make longer tables. + + This results of this trade are in the variables lbits and dbits + below. lbits is the number of bits the first level table for literal/ + length codes can decode in one step, and dbits is the same thing for + the distance codes. Subsequent tables are also less than or equal to + those sizes. These values may be adjusted either when all of the + codes are shorter than that, in which case the longest code length in + bits is used, or when the shortest code is *longer* than the requested + table size, in which case the length of the shortest code in bits is + used. + + There are two different values for the two tables, since they code a + different number of possibilities each. The literal/length table + codes 286 possible values, or in a flat code, a little over eight + bits. The distance table codes 30 possible values, or a little less + than five bits, flat. The optimum values for speed end up being + about one bit more than those, so lbits is 8+1 and dbits is 5+1. + The optimum values may differ though from machine to machine, and + possibly even between compilers. Your mileage may vary. + */ + + +/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */ +#define BMAX 15 /* maximum bit length of any code */ +#define N_MAX 288 /* maximum number of codes in any set */ + +#ifdef DEBUG_ZLIB + uInt inflate_hufts; +#endif + +local int huft_build(b, n, s, d, e, t, m, zs) +uIntf *b; /* code lengths in bits (all assumed <= BMAX) */ +uInt n; /* number of codes (assumed <= N_MAX) */ +uInt s; /* number of simple-valued codes (0..s-1) */ +uIntf *d; /* list of base values for non-simple codes */ +uIntf *e; /* list of extra bits for non-simple codes */ +inflate_huft * FAR *t; /* result: starting table */ +uIntf *m; /* maximum lookup bits, returns actual */ +z_stream *zs; /* for zalloc function */ +/* Given a list of code lengths and a maximum table size, make a set of + tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + if the given code set is incomplete (the tables are still built in this + case), Z_DATA_ERROR if the input is invalid (all zero length codes or an + over-subscribed set of lengths), or Z_MEM_ERROR if not enough memory. */ +{ + + uInt a; /* counter for codes of length k */ + uInt c[BMAX+1]; /* bit length count table */ + uInt f; /* i repeats in table every f entries */ + int g; /* maximum code length */ + int h; /* table level */ + register uInt i; /* counter, current code */ + register uInt j; /* counter */ + register int k; /* number of bits in current code */ + int l; /* bits per table (returned in m) */ + register uIntf *p; /* pointer into c[], b[], or v[] */ + inflate_huft *q; /* points to current table */ + struct inflate_huft_s r; /* table entry for structure assignment */ + inflate_huft *u[BMAX]; /* table stack */ + uInt v[N_MAX]; /* values in order of bit length */ + register int w; /* bits before this table == (l * h) */ + uInt x[BMAX+1]; /* bit offsets, then code stack */ + uIntf *xp; /* pointer into x */ + int y; /* number of dummy codes added */ + uInt z; /* number of entries in current table */ + + + /* Generate counts for each bit length */ + p = c; +#define C0 *p++ = 0; +#define C2 C0 C0 C0 C0 +#define C4 C2 C2 C2 C2 + C4 /* clear c[]--assume BMAX+1 is 16 */ + p = b; i = n; + do { + c[*p++]++; /* assume all entries <= BMAX */ + } while (--i); + if (c[0] == n) /* null input--all zero length codes */ + { + *t = (inflate_huft *)Z_NULL; + *m = 0; + return Z_OK; + } + + + /* Find minimum and maximum length, bound *m by those */ + l = *m; + for (j = 1; j <= BMAX; j++) + if (c[j]) + break; + k = j; /* minimum code length */ + if ((uInt)l < j) + l = j; + for (i = BMAX; i; i--) + if (c[i]) + break; + g = i; /* maximum code length */ + if ((uInt)l > i) + l = i; + *m = l; + + + /* Adjust last length count to fill out codes, if needed */ + for (y = 1 << j; j < i; j++, y <<= 1) + if ((y -= c[j]) < 0) + return Z_DATA_ERROR; + if ((y -= c[i]) < 0) + return Z_DATA_ERROR; + c[i] += y; + + + /* Generate starting offsets into the value table for each length */ + x[1] = j = 0; + p = c + 1; xp = x + 2; + while (--i) { /* note that i == g from above */ + *xp++ = (j += *p++); + } + + + /* Make a table of values in order of bit lengths */ + p = b; i = 0; + do { + if ((j = *p++) != 0) + v[x[j]++] = i; + } while (++i < n); + + + /* Generate the Huffman codes and for each, make the table entries */ + x[0] = i = 0; /* first Huffman code is zero */ + p = v; /* grab values in bit order */ + h = -1; /* no tables yet--level -1 */ + w = -l; /* bits decoded == (l * h) */ + u[0] = (inflate_huft *)Z_NULL; /* just to keep compilers happy */ + q = (inflate_huft *)Z_NULL; /* ditto */ + z = 0; /* ditto */ + + /* go through the bit lengths (k already is bits in shortest code) */ + for (; k <= g; k++) + { + a = c[k]; + while (a--) + { + /* here i is the Huffman code of length k bits for value *p */ + /* make tables up to required level */ + while (k > w + l) + { + h++; + w += l; /* previous table always l bits */ + + /* compute minimum size table less than or equal to l bits */ + z = (z = g - w) > (uInt)l ? l : z; /* table size upper limit */ + if ((f = 1 << (j = k - w)) > a + 1) /* try a k-w bit table */ + { /* too few codes for k-w bit table */ + f -= a + 1; /* deduct codes from patterns left */ + xp = c + k; + if (j < z) + while (++j < z) /* try smaller tables up to z bits */ + { + if ((f <<= 1) <= *++xp) + break; /* enough codes to use up j bits */ + f -= *xp; /* else deduct codes from patterns */ + } + } + z = 1 << j; /* table entries for j-bit table */ + + /* allocate and link in new table */ + if ((q = (inflate_huft *)ZALLOC + (zs,z + 1,sizeof(inflate_huft))) == Z_NULL) + { + if (h) + inflate_trees_free(u[0], zs); + return Z_MEM_ERROR; /* not enough memory */ + } + q->word.Nalloc = z + 1; +#ifdef DEBUG_ZLIB + inflate_hufts += z + 1; +#endif + *t = q + 1; /* link to list for huft_free() */ + *(t = &(q->next)) = Z_NULL; + u[h] = ++q; /* table starts after link */ + + /* connect to last table, if there is one */ + if (h) + { + x[h] = i; /* save pattern for backing up */ + r.bits = (Byte)l; /* bits to dump before this table */ + r.exop = (Byte)j; /* bits in this table */ + r.next = q; /* pointer to this table */ + j = i >> (w - l); /* (get around Turbo C bug) */ + u[h-1][j] = r; /* connect to last table */ + } + } + + /* set up table entry in r */ + r.bits = (Byte)(k - w); + if (p >= v + n) + r.exop = 128 + 64; /* out of values--invalid code */ + else if (*p < s) + { + r.exop = (Byte)(*p < 256 ? 0 : 32 + 64); /* 256 is end-of-block */ + r.base = *p++; /* simple code is just the value */ + } + else + { + r.exop = (Byte)e[*p - s] + 16 + 64; /* non-simple--look up in lists */ + r.base = d[*p++ - s]; + } + + /* fill code-like entries with r */ + f = 1 << (k - w); + for (j = i >> w; j < z; j += f) + q[j] = r; + + /* backwards increment the k-bit code i */ + for (j = 1 << (k - 1); i & j; j >>= 1) + i ^= j; + i ^= j; + + /* backup over finished tables */ + while ((i & ((1 << w) - 1)) != x[h]) + { + h--; /* don't need to update q */ + w -= l; + } + } + } + + + /* Return Z_BUF_ERROR if we were given an incomplete table */ + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; +} + + +local int inflate_trees_bits(c, bb, tb, z) +uIntf *c; /* 19 code lengths */ +uIntf *bb; /* bits tree desired/actual depth */ +inflate_huft * FAR *tb; /* bits tree result */ +z_stream *z; /* for zfree function */ +{ + int r; + + r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL, tb, bb, z); + if (r == Z_DATA_ERROR) + z->msg = "oversubscribed dynamic bit lengths tree"; + else if (r == Z_BUF_ERROR) + { + inflate_trees_free(*tb, z); + z->msg = "incomplete dynamic bit lengths tree"; + r = Z_DATA_ERROR; + } + return r; +} + + +local int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, z) +uInt nl; /* number of literal/length codes */ +uInt nd; /* number of distance codes */ +uIntf *c; /* that many (total) code lengths */ +uIntf *bl; /* literal desired/actual bit depth */ +uIntf *bd; /* distance desired/actual bit depth */ +inflate_huft * FAR *tl; /* literal/length tree result */ +inflate_huft * FAR *td; /* distance tree result */ +z_stream *z; /* for zfree function */ +{ + int r; + + /* build literal/length tree */ + if ((r = huft_build(c, nl, 257, cplens, cplext, tl, bl, z)) != Z_OK) + { + if (r == Z_DATA_ERROR) + z->msg = "oversubscribed literal/length tree"; + else if (r == Z_BUF_ERROR) + { + inflate_trees_free(*tl, z); + z->msg = "incomplete literal/length tree"; + r = Z_DATA_ERROR; + } + return r; + } + + /* build distance tree */ + if ((r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, z)) != Z_OK) + { + if (r == Z_DATA_ERROR) + z->msg = "oversubscribed literal/length tree"; + else if (r == Z_BUF_ERROR) { +#ifdef PKZIP_BUG_WORKAROUND + r = Z_OK; + } +#else + inflate_trees_free(*td, z); + z->msg = "incomplete literal/length tree"; + r = Z_DATA_ERROR; + } + inflate_trees_free(*tl, z); + return r; +#endif + } + + /* done */ + return Z_OK; +} + + +/* build fixed tables only once--keep them here */ +local int fixed_lock = 0; +local int fixed_built = 0; +#define FIXEDH 530 /* number of hufts used by fixed tables */ +local uInt fixed_left = FIXEDH; +local inflate_huft fixed_mem[FIXEDH]; +local uInt fixed_bl; +local uInt fixed_bd; +local inflate_huft *fixed_tl; +local inflate_huft *fixed_td; + + +local voidpf falloc(q, n, s) +voidpf q; /* opaque pointer (not used) */ +uInt n; /* number of items */ +uInt s; /* size of item */ +{ + Assert(s == sizeof(inflate_huft) && n <= fixed_left, + "inflate_trees falloc overflow"); + if (q) s++; /* to make some compilers happy */ + fixed_left -= n; + return (voidpf)(fixed_mem + fixed_left); +} + + +local void ffree(q, p, n) +voidpf q; +voidpf p; +uInt n; +{ + Assert(0, "inflate_trees ffree called!"); + if (q) q = p; /* to make some compilers happy */ +} + + +local int inflate_trees_fixed(bl, bd, tl, td) +uIntf *bl; /* literal desired/actual bit depth */ +uIntf *bd; /* distance desired/actual bit depth */ +inflate_huft * FAR *tl; /* literal/length tree result */ +inflate_huft * FAR *td; /* distance tree result */ +{ + /* build fixed tables if not built already--lock out other instances */ + while (++fixed_lock > 1) + fixed_lock--; + if (!fixed_built) + { + int k; /* temporary variable */ + unsigned c[288]; /* length list for huft_build */ + z_stream z; /* for falloc function */ + + /* set up fake z_stream for memory routines */ + z.zalloc = falloc; + z.zfree = ffree; + z.opaque = Z_NULL; + + /* literal table */ + for (k = 0; k < 144; k++) + c[k] = 8; + for (; k < 256; k++) + c[k] = 9; + for (; k < 280; k++) + c[k] = 7; + for (; k < 288; k++) + c[k] = 8; + fixed_bl = 7; + huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl, &z); + + /* distance table */ + for (k = 0; k < 30; k++) + c[k] = 5; + fixed_bd = 5; + huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd, &z); + + /* done */ + fixed_built = 1; + } + fixed_lock--; + *bl = fixed_bl; + *bd = fixed_bd; + *tl = fixed_tl; + *td = fixed_td; + return Z_OK; +} + + +local int inflate_trees_free(t, z) +inflate_huft *t; /* table to free */ +z_stream *z; /* for zfree function */ +/* Free the malloc'ed tables built by huft_build(), which makes a linked + list of the tables it made, with the links in a dummy first entry of + each table. */ +{ + register inflate_huft *p, *q; + + /* Go through linked list, freeing from the malloced (t[-1]) address. */ + p = t; + while (p != Z_NULL) + { + q = (--p)->next; + ZFREE(z, p, p->word.Nalloc * sizeof(inflate_huft)); + p = q; + } + return Z_OK; +} + +/*+++++*/ +/* infcodes.c -- process literals and length/distance pairs + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* simplify the use of the inflate_huft type with some defines */ +#define base more.Base +#define next more.Next +#define exop word.what.Exop +#define bits word.what.Bits + +/* inflate codes private state */ +struct inflate_codes_state { + + /* mode */ + enum { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ + START, /* x: set up for LEN */ + LEN, /* i: get length/literal/eob next */ + LENEXT, /* i: getting length extra (have base) */ + DIST, /* i: get distance next */ + DISTEXT, /* i: getting distance extra */ + COPY, /* o: copying bytes in window, waiting for space */ + LIT, /* o: got literal, waiting for output space */ + WASH, /* o: got eob, possibly still output waiting */ + END, /* x: got eob and all data flushed */ + BADCODE} /* x: got error */ + mode; /* current inflate_codes mode */ + + /* mode dependent information */ + uInt len; + union { + struct { + inflate_huft *tree; /* pointer into tree */ + uInt need; /* bits needed */ + } code; /* if LEN or DIST, where in tree */ + uInt lit; /* if LIT, literal */ + struct { + uInt get; /* bits to get for extra */ + uInt dist; /* distance back to copy from */ + } copy; /* if EXT or COPY, where and how much */ + } sub; /* submode */ + + /* mode independent information */ + Byte lbits; /* ltree bits decoded per branch */ + Byte dbits; /* dtree bits decoder per branch */ + inflate_huft *ltree; /* literal/length/eob tree */ + inflate_huft *dtree; /* distance tree */ + +}; + + +local inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z) +uInt bl, bd; +inflate_huft *tl, *td; +z_stream *z; +{ + inflate_codes_statef *c; + + if ((c = (inflate_codes_statef *) + ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL) + { + c->mode = START; + c->lbits = (Byte)bl; + c->dbits = (Byte)bd; + c->ltree = tl; + c->dtree = td; + Tracev((stderr, "inflate: codes new\n")); + } + return c; +} + + +local int inflate_codes(s, z, r) +inflate_blocks_statef *s; +z_stream *z; +int r; +{ + uInt j; /* temporary storage */ + inflate_huft *t; /* temporary pointer */ + uInt e; /* extra bits or operation */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + Bytef *f; /* pointer to copy strings from */ + inflate_codes_statef *c = s->sub.decode.codes; /* codes state */ + + /* copy input/output information to locals (UPDATE macro restores) */ + LOAD + + /* process input and output based on current state */ + while (1) switch (c->mode) + { /* waiting for "i:"=input, "o:"=output, "x:"=nothing */ + case START: /* x: set up for LEN */ +#ifndef SLOW + if (m >= 258 && n >= 10) + { + UPDATE + r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z); + LOAD + if (r != Z_OK) + { + c->mode = r == Z_STREAM_END ? WASH : BADCODE; + break; + } + } +#endif /* !SLOW */ + c->sub.code.need = c->lbits; + c->sub.code.tree = c->ltree; + c->mode = LEN; + case LEN: /* i: get length/literal/eob next */ + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e == 0) /* literal */ + { + c->sub.lit = t->base; + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", t->base)); + c->mode = LIT; + break; + } + if (e & 16) /* length */ + { + c->sub.copy.get = e & 15; + c->len = t->base; + c->mode = LENEXT; + break; + } + if ((e & 64) == 0) /* next table */ + { + c->sub.code.need = e; + c->sub.code.tree = t->next; + break; + } + if (e & 32) /* end of block */ + { + Tracevv((stderr, "inflate: end of block\n")); + c->mode = WASH; + break; + } + c->mode = BADCODE; /* invalid code */ + z->msg = "invalid literal/length code"; + r = Z_DATA_ERROR; + LEAVE + case LENEXT: /* i: getting length extra (have base) */ + j = c->sub.copy.get; + NEEDBITS(j) + c->len += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + c->sub.code.need = c->dbits; + c->sub.code.tree = c->dtree; + Tracevv((stderr, "inflate: length %u\n", c->len)); + c->mode = DIST; + case DIST: /* i: get distance next */ + j = c->sub.code.need; + NEEDBITS(j) + t = c->sub.code.tree + ((uInt)b & inflate_mask[j]); + DUMPBITS(t->bits) + e = (uInt)(t->exop); + if (e & 16) /* distance */ + { + c->sub.copy.get = e & 15; + c->sub.copy.dist = t->base; + c->mode = DISTEXT; + break; + } + if ((e & 64) == 0) /* next table */ + { + c->sub.code.need = e; + c->sub.code.tree = t->next; + break; + } + c->mode = BADCODE; /* invalid code */ + z->msg = "invalid distance code"; + r = Z_DATA_ERROR; + LEAVE + case DISTEXT: /* i: getting distance extra */ + j = c->sub.copy.get; + NEEDBITS(j) + c->sub.copy.dist += (uInt)b & inflate_mask[j]; + DUMPBITS(j) + Tracevv((stderr, "inflate: distance %u\n", c->sub.copy.dist)); + c->mode = COPY; + case COPY: /* o: copying bytes in window, waiting for space */ +#ifndef __TURBOC__ /* Turbo C bug for following expression */ + f = (uInt)(q - s->window) < c->sub.copy.dist ? + s->end - (c->sub.copy.dist - (q - s->window)) : + q - c->sub.copy.dist; +#else + f = q - c->sub.copy.dist; + if ((uInt)(q - s->window) < c->sub.copy.dist) + f = s->end - (c->sub.copy.dist - (q - s->window)); +#endif + while (c->len) + { + NEEDOUT + OUTBYTE(*f++) + if (f == s->end) + f = s->window; + c->len--; + } + c->mode = START; + break; + case LIT: /* o: got literal, waiting for output space */ + NEEDOUT + OUTBYTE(c->sub.lit) + c->mode = START; + break; + case WASH: /* o: got eob, possibly more output */ + FLUSH + if (s->read != s->write) + LEAVE + c->mode = END; + case END: + r = Z_STREAM_END; + LEAVE + case BADCODE: /* x: got error */ + r = Z_DATA_ERROR; + LEAVE + default: + r = Z_STREAM_ERROR; + LEAVE + } +} + + +local void inflate_codes_free(c, z) +inflate_codes_statef *c; +z_stream *z; +{ + ZFREE(z, c, sizeof(struct inflate_codes_state)); + Tracev((stderr, "inflate: codes free\n")); +} + +/*+++++*/ +/* inflate_util.c -- data and routines common to blocks and codes + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* copy as much as possible from the sliding window to the output area */ +local int inflate_flush(s, z, r) +inflate_blocks_statef *s; +z_stream *z; +int r; +{ + uInt n; + Bytef *p, *q; + + /* local copies of source and destination pointers */ + p = z->next_out; + q = s->read; + + /* compute number of bytes to copy as far as end of window */ + n = (uInt)((q <= s->write ? s->write : s->end) - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + /* update counters */ + z->avail_out -= n; + z->total_out += n; + + /* update check information */ + if (s->checkfn != Z_NULL) + s->check = (*s->checkfn)(s->check, q, n); + + /* copy as far as end of window */ + zmemcpy(p, q, n); + p += n; + q += n; + + /* see if more to copy at beginning of window */ + if (q == s->end) + { + /* wrap pointers */ + q = s->window; + if (s->write == s->end) + s->write = s->window; + + /* compute bytes to copy */ + n = (uInt)(s->write - q); + if (n > z->avail_out) n = z->avail_out; + if (n && r == Z_BUF_ERROR) r = Z_OK; + + /* update counters */ + z->avail_out -= n; + z->total_out += n; + + /* update check information */ + if (s->checkfn != Z_NULL) + s->check = (*s->checkfn)(s->check, q, n); + + /* copy */ + zmemcpy(p, q, n); + p += n; + q += n; + } + + /* update pointers */ + z->next_out = p; + s->read = q; + + /* done */ + return r; +} + + +/*+++++*/ +/* inffast.c -- process literals and length/distance pairs fast + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* simplify the use of the inflate_huft type with some defines */ +#define base more.Base +#define next more.Next +#define exop word.what.Exop +#define bits word.what.Bits + +/* macros for bit input with no checking and for returning unused bytes */ +#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<<k;k+=8;}} +#define UNGRAB {n+=(c=k>>3);p-=c;k&=7;} + +/* Called with number of bytes left to write in window at least 258 + (the maximum string length) and number of input bytes available + at least ten. The ten bytes are six bytes for the longest length/ + distance pair plus four bytes for overloading the bit buffer. */ + +local int inflate_fast(bl, bd, tl, td, s, z) +uInt bl, bd; +inflate_huft *tl, *td; +inflate_blocks_statef *s; +z_stream *z; +{ + inflate_huft *t; /* temporary pointer */ + uInt e; /* extra bits or operation */ + uLong b; /* bit buffer */ + uInt k; /* bits in bit buffer */ + Bytef *p; /* input data pointer */ + uInt n; /* bytes available there */ + Bytef *q; /* output window write pointer */ + uInt m; /* bytes to end of window or read pointer */ + uInt ml; /* mask for literal/length tree */ + uInt md; /* mask for distance tree */ + uInt c; /* bytes to copy */ + uInt d; /* distance back to copy from */ + Bytef *r; /* copy source pointer */ + + /* load input, output, bit values */ + LOAD + + /* initialize masks */ + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + /* do until not enough input or output space for fast loop */ + do { /* assume called with m >= 258 && n >= 10 */ + /* get literal/length code */ + GRABBITS(20) /* max bits for literal/length code */ + if ((e = (t = tl + ((uInt)b & ml))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + continue; + } + do { + DUMPBITS(t->bits) + if (e & 16) + { + /* get extra bits for length */ + e &= 15; + c = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * length %u\n", c)); + + /* decode distance base of block to copy */ + GRABBITS(15); /* max bits for distance code */ + e = (t = td + ((uInt)b & md))->exop; + do { + DUMPBITS(t->bits) + if (e & 16) + { + /* get extra bits to add to distance base */ + e &= 15; + GRABBITS(e) /* get extra bits (up to 13) */ + d = t->base + ((uInt)b & inflate_mask[e]); + DUMPBITS(e) + Tracevv((stderr, "inflate: * distance %u\n", d)); + + /* do the copy */ + m -= c; + if ((uInt)(q - s->window) >= d) /* offset before dest */ + { /* just copy */ + r = q - d; + *q++ = *r++; c--; /* minimum count is three, */ + *q++ = *r++; c--; /* so unroll loop a little */ + } + else /* else offset after destination */ + { + e = d - (q - s->window); /* bytes from offset to end */ + r = s->end - e; /* pointer to offset */ + if (c > e) /* if source crosses, */ + { + c -= e; /* copy to end of window */ + do { + *q++ = *r++; + } while (--e); + r = s->window; /* copy rest from start of window */ + } + } + do { /* copy all or what's left */ + *q++ = *r++; + } while (--c); + break; + } + else if ((e & 64) == 0) + e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop; + else + { + z->msg = "invalid distance code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + } while (1); + break; + } + if ((e & 64) == 0) + { + if ((e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop) == 0) + { + DUMPBITS(t->bits) + Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ? + "inflate: * literal '%c'\n" : + "inflate: * literal 0x%02x\n", t->base)); + *q++ = (Byte)t->base; + m--; + break; + } + } + else if (e & 32) + { + Tracevv((stderr, "inflate: * end of block\n")); + UNGRAB + UPDATE + return Z_STREAM_END; + } + else + { + z->msg = "invalid literal/length code"; + UNGRAB + UPDATE + return Z_DATA_ERROR; + } + } while (1); + } while (m >= 258 && n >= 10); + + /* not enough input or output--restore pointers and return */ + UNGRAB + UPDATE + return Z_OK; +} + + +/*+++++*/ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* From: zutil.c,v 1.8 1995/05/03 17:27:12 jloup Exp */ + +char *zlib_version = ZLIB_VERSION; + +char *z_errmsg[] = { +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +""}; + + +/*+++++*/ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* From: adler32.c,v 1.6 1995/05/03 17:27:08 jloup Exp */ + +#define BASE 65521L /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf) {s1 += *buf++; s2 += s1;} +#define DO2(buf) DO1(buf); DO1(buf); +#define DO4(buf) DO2(buf); DO2(buf); +#define DO8(buf) DO4(buf); DO4(buf); +#define DO16(buf) DO8(buf); DO8(buf); + +/* ========================================================================= */ +uLong adler32(adler, buf, len) + uLong adler; + Bytef *buf; + uInt len; +{ + unsigned long s1 = adler & 0xffff; + unsigned long s2 = (adler >> 16) & 0xffff; + int k; + + if (buf == Z_NULL) return 1L; + + while (len > 0) { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) { + DO16(buf); + k -= 16; + } + if (k != 0) do { + DO1(buf); + } while (--k); + s1 %= BASE; + s2 %= BASE; + } + return (s2 << 16) | s1; +} diff --git a/arch/ppc/coffboot/zlib.h b/arch/ppc/coffboot/zlib.h new file mode 100644 index 000000000..f4ab77617 --- /dev/null +++ b/arch/ppc/coffboot/zlib.h @@ -0,0 +1,432 @@ +/* $Id: zlib.h,v 1.1 1997/08/30 04:51:49 ralf Exp $ */ + +/* + * This file is derived from zlib.h and zconf.h from the zlib-0.95 + * distribution by Jean-loup Gailly and Mark Adler, with some additions + * by Paul Mackerras to aid in implementing Deflate compression and + * decompression for PPP packets. + */ + +/* + * ==FILEVERSION 960122== + * + * This marker is used by the Linux installation script to determine + * whether an up-to-date version of this file is already installed. + */ + +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 0.95, Aug 16th, 1995. + + Copyright (C) 1995 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + gzip@prep.ai.mit.edu madler@alumni.caltech.edu + */ + +#ifndef _ZLIB_H +#define _ZLIB_H + +/* #include "zconf.h" */ /* included directly here */ + +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* From: zconf.h,v 1.12 1995/05/03 17:27:12 jloup Exp */ + +/* + The library does not install any signal handler. It is recommended to + add at least a handler for SIGSEGV when decompressing; the library checks + the consistency of the input data whenever possible but may go nuts + for some forms of corrupted input. + */ + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + * Compile with -DUNALIGNED_OK if it is OK to access shorts or ints + * at addresses which are not a multiple of their size. + * Under DOS, -DFAR=far or -DFAR=__far may be needed. + */ + +#ifndef STDC +# if defined(MSDOS) || defined(__STDC__) || defined(__cplusplus) +# define STDC +# endif +#endif + +#ifdef __MWERKS__ /* Metrowerks CodeWarrior declares fileno() in unix.h */ +# include <unix.h> +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +#ifndef FAR +# define FAR +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2 */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + 1 << (windowBits+2) + 1 << (memLevel+9) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +typedef unsigned char Byte; /* 8 bits */ +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +typedef Byte FAR Bytef; +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +/* end of original zconf.h */ + +#define ZLIB_VERSION "0.95P" + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms may be added later and will have the same + stream interface. + + For compression the application must provide the output buffer and + may optionally provide the input buffer for optimization. For decompression, + the application must provide the input buffer and may optionally provide + the output buffer for optimization. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address, uInt nbytes)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidp opaque; /* private data object passed to zalloc and zfree */ + + Byte data_type; /* best guess about the data type: ascii or binary */ + +} z_stream; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 +#define Z_FULL_FLUSH 2 +#define Z_SYNC_FLUSH 3 /* experimental: partial_flush + byte align */ +#define Z_FINISH 4 +#define Z_PACKET_FLUSH 5 +/* See deflate() below for the usage of these constants */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +/* error codes for the compression/decompression functions */ + +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_DEFAULT_STRATEGY 0 + +#define Z_BINARY 0 +#define Z_ASCII 1 +#define Z_UNKNOWN 2 +/* Used to set the data_type field */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +extern char *zlib_version; +/* The application can compare zlib_version and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + */ + + /* basic functions */ + +extern int inflateInit OF((z_stream *strm)); +/* + Initializes the internal stream state for decompression. The fields + zalloc and zfree must be initialized before by the caller. If zalloc and + zfree are set to Z_NULL, inflateInit updates them to use default allocation + functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory. msg is set to null if there is no error message. + inflateInit does not perform any decompression: this will be done by + inflate(). +*/ + + +extern int inflate OF((z_stream *strm, int flush)); +/* + Performs one or both of the following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() always provides as much output as possible + (until there is no more input data or no more space in the output buffer). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). + + If the parameter flush is set to Z_PARTIAL_FLUSH or Z_PACKET_FLUSH, + inflate flushes as much output as possible to the output buffer. The + flushing behavior of inflate is not specified for values of the flush + parameter other than Z_PARTIAL_FLUSH, Z_PACKET_FLUSH or Z_FINISH, but the + current implementation actually flushes as much output as possible + anyway. For Z_PACKET_FLUSH, inflate checks that once all the input data + has been consumed, it is expecting to see the length field of a stored + block; if not, it returns Z_DATA_ERROR. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster routine + may be used for the single inflate() call. + + inflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if the end of the + compressed data has been reached and all uncompressed output has been + produced, Z_DATA_ERROR if the input data was corrupted, Z_STREAM_ERROR if + the stream structure was inconsistent (for example if next_in or next_out + was NULL), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if no + progress is possible or if there was not enough room in the output buffer + when Z_FINISH is used. In the Z_DATA_ERROR case, the application may then + call inflateSync to look for a good compression block. */ + + +extern int inflateEnd OF((z_stream *strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* advanced functions */ + +extern int inflateInit2 OF((z_stream *strm, + int windowBits)); +/* + This is another version of inflateInit with more compression options. The + fields next_out, zalloc and zfree must be initialized before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library (the value 16 will be allowed soon). The + default value is 15 if inflateInit is used instead. If a compressed stream + with a larger window size is given as input, inflate() will return with + the error code Z_DATA_ERROR instead of trying to allocate a larger window. + + If next_out is not null, the library will use this buffer for the history + buffer; the buffer must either be large enough to hold the entire output + data, or have at least 1<<windowBits bytes. If next_out is null, the + library will allocate its own buffer (and leave next_out null). next_in + need not be provided here but must be provided by the application for the + next call of inflate(). + + If the history buffer is provided by the application, next_out must + never be changed by the application since the decompressor maintains + history information inside this buffer from call to call; the application + can only reset next_out to the beginning of the history buffer when + avail_out is zero and all output has been consumed. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was + not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as + windowBits < 8). msg is set to null if there is no error message. + inflateInit2 does not perform any decompression: this will be done by + inflate(). +*/ + +extern int inflateSync OF((z_stream *strm)); +/* + Skips invalid compressed data until the special marker (see deflate() + above) can be found, or until all available input is skipped. No output + is provided. + + inflateSync returns Z_OK if the special marker has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no marker has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +extern int inflateReset OF((z_stream *strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +extern int inflateIncomp OF((z_stream *strm)); +/* + This function adds the data at next_in (avail_in bytes) to the output + history without performing any output. There must be no pending output, + and the decompressor must be expecting to see the start of a block. + Calling this function is equivalent to decompressing a stored block + containing the data at next_in (except that the data is not output). +*/ + + /* checksum functions */ + +/* + This function is not related to compression but is exported + anyway because it might be useful in applications using the + compression library. +*/ + +extern uLong adler32 OF((uLong adler, Bytef *buf, uInt len)); + +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +#ifndef _Z_UTIL_H + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +#endif /* _ZLIB_H */ diff --git a/arch/ppc/config.in b/arch/ppc/config.in index 9df35fa24..1cea7ce3f 100644 --- a/arch/ppc/config.in +++ b/arch/ppc/config.in @@ -1,36 +1,44 @@ -# +# $Id: config.in,v 1.19 1997/09/04 01:54:26 davem Exp $ # For a description of the syntax of this configuration file, # see the Configure script. # mainmenu_name "Linux/PowerPC Kernel Configuration" -if [ "`uname`" != "Linux" ]; then +mainmenu_option next_comment +comment 'Platform support' +define_bool CONFIG_PPC y + +if [ "`uname`" != "Linux" -o "`uname -m`" != "ppc" ]; then define_bool CONFIG_CROSSCOMPILE y else define_bool CONFIG_NATIVE y fi -bool 'Build PowerMac Kernel (not PReP)?' CONFIG_PMAC -bool 'Build PReP Kernel (not PowerMac)?' CONFIG_PREP -bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL -bool 'Used Harddrive LED on IBM 83x workstations as heartbeat?' CONFIG_HEARTBEAT -bool 'Used PowerPC specific powersaving?' CONFIG_POWERSAVING +bool 'Build PowerMac Kernel (not PReP or CHRP)?' CONFIG_PMAC +bool 'Build PReP Kernel (not PowerMac or CHRP)?' CONFIG_PREP +bool 'Build CHRP Kernel (not PReP or PowerMac)?' CONFIG_CHRP + choice 'Processor type' \ - "Common CONFIG_MCOMMON \ - 601 CONFIG_M601 \ - 603 CONFIG_M603 \ - 604 CONFIG_M604" Common + "Common CONFIG_COMMON \ + 601 CONFIG_601 \ + 603 CONFIG_603 \ + 604 CONFIG_604" Common +endmenu + +mainmenu_option next_comment +comment 'General setup' +bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL bool 'Enable loadable module support' CONFIG_MODULES if [ "$CONFIG_MODULES" = "y" ]; then bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS bool 'Kernel daemon support (e.g. autoload of modules)' CONFIG_KERNELD fi - -mainmenu_option next_comment define_bool CONFIG_PCI y -bool 'PCI bridge optimization (experimental)' CONFIG_PCI_OPTIMIZE +if [ "$CONFIG_PREP" = "y" ]; then + bool 'PCI bridge optimization' CONFIG_PCI_OPTIMIZE +fi bool 'Networking support' CONFIG_NET bool 'Sysctl support' CONFIG_SYSCTL bool 'System V IPC' CONFIG_SYSVIPC @@ -39,8 +47,24 @@ bool 'System V IPC' CONFIG_SYSVIPC define_bool CONFIG_BINFMT_ELF y define_bool CONFIG_KERNEL_ELF y tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC -tristate 'Kernel support for JAVA binaries' CONFIG_BINFMT_JAVA +tristate 'Kernel support for JAVA binaries (obsolete)' CONFIG_BINFMT_JAVA + +if [ "$CONFIG_PMAC" = "y" ]; then + define_bool CONFIG_PMAC_CONSOLE y + define_bool CONFIG_MAC_KEYBOARD y + define_bool CONFIG_MAC_FLOPPY y + bool 'Support for Open Firmware device tree in /proc' CONFIG_PROC_DEVICETREE + bool 'Include xmon kernel debugger' CONFIG_XMON +fi + +if [ "$CONFIG_PMAC_CONSOLE" = "y" ]; then + bool 'Support for ATI Mach64 display cards' CONFIG_ATY_VIDEO + bool 'Support for IMS Twin Turbo display card' CONFIG_IMSTT_VIDEO +else + define_bool CONFIG_VGA_CONSOLE y +fi +endmenu source drivers/pnp/Config.in source drivers/block/Config.in @@ -83,6 +107,7 @@ fi endmenu source fs/Config.in + source drivers/char/Config.in mainmenu_option next_comment @@ -93,12 +118,12 @@ if [ "$CONFIG_SOUND" != "n" ]; then fi endmenu -mainmenu_option next_comment +#mainmenu_option next_comment #comment 'Kernel hacking' #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC #bool 'Kernel profiling support' CONFIG_PROFILE #if [ "$CONFIG_PROFILE" = "y" ]; then # int ' Profile shift count' CONFIG_PROFILE_SHIFT 2 #fi -endmenu +#endmenu diff --git a/arch/ppc/defconfig b/arch/ppc/defconfig new file mode 100644 index 000000000..ea39e83ea --- /dev/null +++ b/arch/ppc/defconfig @@ -0,0 +1,250 @@ +# +# Automatically generated by make menuconfig: don't edit +# + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_NATIVE=y +# CONFIG_PMAC is not set +CONFIG_PREP=y +CONFIG_MCOMMON=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KERNELD=y +CONFIG_PCI=y +CONFIG_PCI_OPTIMIZE=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_BINFMT_JAVA is not set +CONFIG_VGA_CONSOLE=y + +# +# Plug and Play support +# +# CONFIG_PNP is not set + +# +# Floppy, IDE, and other block devices +# +CONFIG_BLK_DEV_FD=y +CONFIG_BLK_DEV_IDE=y +# CONFIG_BLK_DEV_HD_IDE is not set +CONFIG_BLK_DEV_IDEDISK=y +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_RZ1000 is not set +# CONFIG_BLK_DEV_TRITON is not set +# CONFIG_IDE_CHIPSETS is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_BLK_DEV_XD is not set +# CONFIG_BLK_DEV_EZ is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_ST=y +CONFIG_BLK_DEV_SR=y +CONFIG_BLK_DEV_SR_VENDOR=y +# CONFIG_CHR_DEV_SG is not set +# CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_CONSTANTS=y + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AHA1740 is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA_DMA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_NCR53C7xx is not set +CONFIG_SCSI_NCR53C8XX=y +# CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT is not set +CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE=y +CONFIG_SCSI_NCR53C8XX_IOMAPPED=y +CONFIG_SCSI_NCR53C8XX_MAX_TAGS=4 +CONFIG_SCSI_NCR53C8XX_SYNC=5 +# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set +# CONFIG_SCSI_PPA is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_QLOGIC_ISP is not set +# CONFIG_SCSI_SEAGATE is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SCSI_MESH is not set +# CONFIG_SCSI_MAC53C94 is not set + +# +# Network device support +# + +# +# Networking options +# +# CONFIG_NETLINK is not set +# CONFIG_FIREWALL is not set +# CONFIG_NET_ALIAS is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ACCT is not set +# CONFIG_IP_ROUTER is not set +# CONFIG_NET_IPIP is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_XTP is not set +# CONFIG_INET_PCTCP is not set +# CONFIG_INET_RARP is not set +CONFIG_PATH_MTU_DISCOVERY=y +# CONFIG_IP_NOSR is not set +CONFIG_SKB_LARGE=y +# CONFIG_IPV6 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_AX25 is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_BRIDGE is not set +# CONFIG_LLC is not set +# CONFIG_WAN_ROUTER is not set +CONFIG_NETDEVICES=y +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_NET_ETHERNET=y +CONFIG_NET_VENDOR_3COM=y +# CONFIG_EL1 is not set +# CONFIG_EL2 is not set +# CONFIG_ELPLUS is not set +# CONFIG_EL16 is not set +CONFIG_EL3=y +# CONFIG_VORTEX is not set +CONFIG_LANCE=y +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_EISA=y +CONFIG_PCNET32=y +# CONFIG_AC3200 is not set +# CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set +CONFIG_DE4X5=y +# CONFIG_DEC_ELCP is not set +# CONFIG_DGRS is not set +# CONFIG_EEXPRESS_PRO100 is not set +# CONFIG_TLAN is not set +# CONFIG_ES3210 is not set +# CONFIG_ZNET is not set +# CONFIG_NET_POCKET is not set +# CONFIG_FDDI is not set +# CONFIG_DLCI is not set +# CONFIG_PLIP is not set +CONFIG_PPP=y +# CONFIG_NET_RADIO is not set +# CONFIG_SLIP is not set +# CONFIG_TR is not set +# CONFIG_SHAPER is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# CD-ROM drivers (not for SCSI or IDE/ATAPI drives) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Filesystems +# +# CONFIG_QUOTA is not set +# CONFIG_MINIX_FS is not set +CONFIG_EXT2_FS=y +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +# CONFIG_VFAT_FS is not set +# CONFIG_UMSDOS_FS is not set +CONFIG_PROC_FS=y +CONFIG_NFS_FS=y +# CONFIG_ROOT_NFS is not set +CONFIG_NFSD=y +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +CONFIG_ISO9660_FS=y +# CONFIG_HPFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_MAC_PARTITION=y + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +# CONFIG_SOFTCURSOR is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_EXTENDED=y +# CONFIG_SERIAL_MANY_PORTS is not set +# CONFIG_SERIAL_SHARE_IRQ is not set +# CONFIG_SERIAL_MULTIPORT is not set +# CONFIG_HUB6 is not set +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_PRINTER is not set +CONFIG_MOUSE=y +# CONFIG_ATIXL_BUSMOUSE is not set +# CONFIG_BUSMOUSE is not set +# CONFIG_MS_BUSMOUSE is not set +CONFIG_PSMOUSE=y +# CONFIG_82C710_MOUSE is not set +# CONFIG_PC110_PAD is not set +# CONFIG_UMISC is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_FTAPE is not set +# CONFIG_APM is not set +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_NVRAM is not set +# CONFIG_JOYSTICK is not set + +# +# Sound +# +# CONFIG_SOUND is not set diff --git a/arch/ppc/ignore b/arch/ppc/ignore new file mode 100644 index 000000000..693fff61a --- /dev/null +++ b/arch/ppc/ignore @@ -0,0 +1,65 @@ +.config +compile.h +.version +.objects +.blurb +*.cort +*.paul +*.old +.defines +version.h +find_name +checks +#* +.objects +.object_files +System.map +asm +.menuconfig* +CVS +ppc_defs.h +mk_defs +mkprep +*.s +.depend +.hdepend +*~ +*.o +znetboot +zvmlinux +vmlinux +zImage +hack-coff +coffboot +vmlinux.coff +.depend +.cvsignore +RCS +SCCS +CVS.adm +RCSLOG +cvslog.* +tags +TAGS +.make.state +.nse_depinfo +*~ +#* +.#* +,* +_$* +*$ +*.old +*.bak +*.BAK +*.orig +*.rej +*.a +*.olb +*.o +*.obj +*.so +*.exe +*.Z +*.elc +*.ln diff --git a/arch/ppc/kernel/Makefile b/arch/ppc/kernel/Makefile index 941cd9e38..6fd50a6fb 100644 --- a/arch/ppc/kernel/Makefile +++ b/arch/ppc/kernel/Makefile @@ -7,39 +7,21 @@ # # Note 2! The CFLAGS definitions are now in the main makefile... -.c.s: - $(CC) $(CFLAGS) -S $< -.s.o: - $(AS) $(ASFLAGS) -o $*.o $< -.c.o: - $(CC) $(CFLAGS) -c $< -.S.s: - $(CPP) $(CFLAGS) -D__ASSEMBLY__ $< -o $*.s .S.o: - $(CPP) $(CFLAGS) -D__ASSEMBLY__ $< -o $*.s - $(AS) $(ASFLAGS) -o $*.o $*.s - rm $*.s + $(CC) -D__ASSEMBLY__ -c $< -o $*.o -HOST_CC = gcc - -OBJS = misc.o port_io.o pci.o traps.o process.o \ - signal.o syscalls.o ptrace.o ksyms.o irq.o bitops.o strcase.o ppc_htab.o +O_TARGET := kernel.o +O_OBJS := misc.o traps.o process.o signal.o syscalls.o \ + align.o ptrace.o irq.o openpic.o bitops.o ppc_htab.o idle.o \ + time.o prep_time.o pmac_time.o chrp_time.o \ + setup.o prep_setup.o pmac_setup.o pmac_support.o chrp_setup.o \ + pci.o prep_pci.o pmac_pci.o chrp_pci.o \ + residual.o prom.o +OX_OBJS := ppc_ksyms.o all: head.o kernel.o -head.o: head.S $(TOPDIR)/include/linux/tasks.h ppc_defs.h - -ifeq ($(CONFIG_PREP),y) -OBJS += prep_setup.o prep_time.o -endif - -ifeq ($(CONFIG_PMAC),y) -OBJS += pmac_setup.o pmac_support.o align.o pmac_time.o -endif - -ifeq ($(CONFIG_MODULES),y) -OBJS = ksyms.o -endif +head.o: head.S $(TOPDIR)/include/linux/tasks.h ppc_defs.h ppc_defs.h: mk_defs.c ppc_defs.head \ $(TOPDIR)/include/asm/mmu.h \ @@ -52,25 +34,7 @@ ppc_defs.h: mk_defs.c ppc_defs.head \ rm mk_defs.s checks: checks.c - $(HOSTCC) ${CFLAGS} -o checks checks.c - checks - -kernel.o: $(OBJS) - $(LD) -r -o kernel.o $(OBJS) + $(HOSTCC) -fno-builtin -I$(TOPDIR)/include -D__KERNEL__ -o checks checks.c + ./checks -fastdep: - $(TOPDIR)/scripts/mkdep *.[Sch] > .depend - -dep: - $(CPP) -M *.S *.c > .depend - -modules: - -dummy: - -# -# include a dependency file if one exists -# -ifeq (.depend,$(wildcard .depend)) -include .depend -endif +include $(TOPDIR)/Rules.make diff --git a/arch/ppc/kernel/align.c b/arch/ppc/kernel/align.c new file mode 100644 index 000000000..39cb04f77 --- /dev/null +++ b/arch/ppc/kernel/align.c @@ -0,0 +1,290 @@ +/* + * align.c - handle alignment exceptions for the Power PC. + * + * Paul Mackerras August 1996. + * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au). + */ +#include <linux/kernel.h> +#include <linux/mm.h> +#include <asm/ptrace.h> +#include <asm/processor.h> +#include <asm/uaccess.h> +#include <asm/system.h> + +struct aligninfo { + unsigned char len; + unsigned char flags; +}; + +#define INVALID { 0, 0 } + +#define LD 1 /* load */ +#define ST 2 /* store */ +#define SE 4 /* sign-extend value */ +#define F 8 /* to/from fp regs */ +#define U 0x10 /* update index register */ +#define M 0x20 /* multiple load/store */ +#define S 0x40 /* single-precision fp, or byte-swap value */ +#define HARD 0x80 /* string, stwcx. */ + +/* + * The PowerPC stores certain bits of the instruction that caused the + * alignment exception in the DSISR register. This array maps those + * bits to information about the operand length and what the + * instruction would do. + */ +static struct aligninfo aligninfo[128] = { + { 4, LD }, /* 00 0 0000: lwz / lwarx */ + INVALID, /* 00 0 0001 */ + { 4, ST }, /* 00 0 0010: stw */ + INVALID, /* 00 0 0011 */ + { 2, LD }, /* 00 0 0100: lhz */ + { 2, LD+SE }, /* 00 0 0101: lha */ + { 2, ST }, /* 00 0 0110: sth */ + { 4, LD+M }, /* 00 0 0111: lmw */ + { 4, LD+F+S }, /* 00 0 1000: lfs */ + { 8, LD+F }, /* 00 0 1001: lfd */ + { 4, ST+F+S }, /* 00 0 1010: stfs */ + { 8, ST+F }, /* 00 0 1011: stfd */ + INVALID, /* 00 0 1100 */ + INVALID, /* 00 0 1101 */ + INVALID, /* 00 0 1110 */ + INVALID, /* 00 0 1111 */ + { 4, LD+U }, /* 00 1 0000: lwzu */ + INVALID, /* 00 1 0001 */ + { 4, ST+U }, /* 00 1 0010: stwu */ + INVALID, /* 00 1 0011 */ + { 2, LD+U }, /* 00 1 0100: lhzu */ + { 2, LD+SE+U }, /* 00 1 0101: lhau */ + { 2, ST+U }, /* 00 1 0110: sthu */ + { 4, ST+M }, /* 00 1 0111: stmw */ + { 4, LD+F+S+U }, /* 00 1 1000: lfsu */ + { 8, LD+F+U }, /* 00 1 1001: lfdu */ + { 4, ST+F+S+U }, /* 00 1 1010: stfsu */ + { 8, ST+F+U }, /* 00 1 1011: stfdu */ + INVALID, /* 00 1 1100 */ + INVALID, /* 00 1 1101 */ + INVALID, /* 00 1 1110 */ + INVALID, /* 00 1 1111 */ + INVALID, /* 01 0 0000 */ + INVALID, /* 01 0 0001 */ + INVALID, /* 01 0 0010 */ + INVALID, /* 01 0 0011 */ + INVALID, /* 01 0 0100 */ + INVALID, /* 01 0 0101: lwax?? */ + INVALID, /* 01 0 0110 */ + INVALID, /* 01 0 0111 */ + { 0, LD+HARD }, /* 01 0 1000: lswx */ + { 0, LD+HARD }, /* 01 0 1001: lswi */ + { 0, ST+HARD }, /* 01 0 1010: stswx */ + { 0, ST+HARD }, /* 01 0 1011: stswi */ + INVALID, /* 01 0 1100 */ + INVALID, /* 01 0 1101 */ + INVALID, /* 01 0 1110 */ + INVALID, /* 01 0 1111 */ + INVALID, /* 01 1 0000 */ + INVALID, /* 01 1 0001 */ + INVALID, /* 01 1 0010 */ + INVALID, /* 01 1 0011 */ + INVALID, /* 01 1 0100 */ + INVALID, /* 01 1 0101: lwaux?? */ + INVALID, /* 01 1 0110 */ + INVALID, /* 01 1 0111 */ + INVALID, /* 01 1 1000 */ + INVALID, /* 01 1 1001 */ + INVALID, /* 01 1 1010 */ + INVALID, /* 01 1 1011 */ + INVALID, /* 01 1 1100 */ + INVALID, /* 01 1 1101 */ + INVALID, /* 01 1 1110 */ + INVALID, /* 01 1 1111 */ + INVALID, /* 10 0 0000 */ + INVALID, /* 10 0 0001 */ + { 0, ST+HARD }, /* 10 0 0010: stwcx. */ + INVALID, /* 10 0 0011 */ + INVALID, /* 10 0 0100 */ + INVALID, /* 10 0 0101 */ + INVALID, /* 10 0 0110 */ + INVALID, /* 10 0 0111 */ + { 4, LD+S }, /* 10 0 1000: lwbrx */ + INVALID, /* 10 0 1001 */ + { 4, ST+S }, /* 10 0 1010: stwbrx */ + INVALID, /* 10 0 1011 */ + { 2, LD+S }, /* 10 0 1100: lhbrx */ + INVALID, /* 10 0 1101 */ + { 2, ST+S }, /* 10 0 1110: sthbrx */ + INVALID, /* 10 0 1111 */ + INVALID, /* 10 1 0000 */ + INVALID, /* 10 1 0001 */ + INVALID, /* 10 1 0010 */ + INVALID, /* 10 1 0011 */ + INVALID, /* 10 1 0100 */ + INVALID, /* 10 1 0101 */ + INVALID, /* 10 1 0110 */ + INVALID, /* 10 1 0111 */ + INVALID, /* 10 1 1000 */ + INVALID, /* 10 1 1001 */ + INVALID, /* 10 1 1010 */ + INVALID, /* 10 1 1011 */ + INVALID, /* 10 1 1100 */ + INVALID, /* 10 1 1101 */ + INVALID, /* 10 1 1110 */ + { 0, ST+HARD }, /* 10 1 1111: dcbz */ + { 4, LD }, /* 11 0 0000: lwzx */ + INVALID, /* 11 0 0001 */ + { 4, ST }, /* 11 0 0010: stwx */ + INVALID, /* 11 0 0011 */ + { 2, LD }, /* 11 0 0100: lhzx */ + { 2, LD+SE }, /* 11 0 0101: lhax */ + { 2, ST }, /* 11 0 0110: sthx */ + INVALID, /* 11 0 0111 */ + { 4, LD+F+S }, /* 11 0 1000: lfsx */ + { 8, LD+F }, /* 11 0 1001: lfdx */ + { 4, ST+F+S }, /* 11 0 1010: stfsx */ + { 8, ST+F }, /* 11 0 1011: stfdx */ + INVALID, /* 11 0 1100 */ + INVALID, /* 11 0 1101 */ + INVALID, /* 11 0 1110 */ + INVALID, /* 11 0 1111 */ + { 4, LD+U }, /* 11 1 0000: lwzux */ + INVALID, /* 11 1 0001 */ + { 4, ST+U }, /* 11 1 0010: stwux */ + INVALID, /* 11 1 0011 */ + { 2, LD+U }, /* 11 1 0100: lhzux */ + { 2, LD+SE+U }, /* 11 1 0101: lhaux */ + { 2, ST+U }, /* 11 1 0110: sthux */ + INVALID, /* 11 1 0111 */ + { 4, LD+F+S+U }, /* 11 1 1000: lfsux */ + { 8, LD+F+U }, /* 11 1 1001: lfdux */ + { 4, ST+F+S+U }, /* 11 1 1010: stfsux */ + { 8, ST+F+U }, /* 11 1 1011: stfdux */ + INVALID, /* 11 1 1100 */ + INVALID, /* 11 1 1101 */ + INVALID, /* 11 1 1110 */ + INVALID, /* 11 1 1111 */ +}; + +#define SWAP(a, b) (t = (a), (a) = (b), (b) = t) + +int +fix_alignment(struct pt_regs *regs) +{ + int instr, nb, flags; + int i, t; + int reg, areg; + unsigned char *addr; + union { + long l; + float f; + double d; + unsigned char v[8]; + } data; + + instr = (regs->dsisr >> 10) & 0x7f; + nb = aligninfo[instr].len; + if (nb == 0) + return 0; /* too hard or invalid instruction bits */ + flags = aligninfo[instr].flags; + addr = (unsigned char *) regs->dar; + reg = (regs->dsisr >> 5) & 0x1f; /* source/dest register */ + + /* Verify the address of the operand */ + if (user_mode(regs)) { + if (verify_area((flags & ST? VERIFY_WRITE: VERIFY_READ), addr, nb)) + return -EFAULT; /* bad address */ + } + + if ((flags & F) && last_task_used_math == current) + giveup_fpu(); + + if (flags & M) + return 0; /* too hard for now */ + + /* If we read the operand, copy it in */ + if (flags & LD) { + if (nb == 2) { + data.v[0] = data.v[1] = 0; + if (__get_user(data.v[2], addr) + || __get_user(data.v[3], addr+1)) + return -EFAULT; + } else { + for (i = 0; i < nb; ++i) + if (__get_user(data.v[i], addr+i)) + return -EFAULT; + } + } + + switch (flags & ~U) { + case LD+SE: + if (data.v[2] >= 0x80) + data.v[0] = data.v[1] = -1; + /* fall through */ + case LD: + regs->gpr[reg] = data.l; + break; + case LD+S: + if (nb == 2) { + SWAP(data.v[2], data.v[3]); + } else { + SWAP(data.v[0], data.v[3]); + SWAP(data.v[1], data.v[2]); + } + regs->gpr[reg] = data.l; + break; + case ST: + data.l = regs->gpr[reg]; + break; + case ST+S: + data.l = regs->gpr[reg]; + if (nb == 2) { + SWAP(data.v[2], data.v[3]); + } else { + SWAP(data.v[0], data.v[3]); + SWAP(data.v[1], data.v[2]); + } + break; + case LD+F: + current->tss.fpr[reg] = data.d; + break; + case ST+F: + data.d = current->tss.fpr[reg]; + break; + /* these require some floating point conversions... */ + /* note that giveup_fpu enables the FPU for the kernel */ + /* we'd like to use the assignment, but we have to compile + * the kernel with -msoft-float so it doesn't use the + * fp regs for copying 8-byte objects. */ + case LD+F+S: + giveup_fpu(); + cvt_fd(&data.f, ¤t->tss.fpr[reg]); + /* current->tss.fpr[reg] = data.f; */ + break; + case ST+F+S: + giveup_fpu(); + cvt_df(¤t->tss.fpr[reg], &data.f); + /* data.f = current->tss.fpr[reg]; */ + break; + default: + printk("align: can't handle flags=%x\n", flags); + return 0; + } + + if (flags & ST) { + if (nb == 2) { + if (__put_user(data.v[2], addr) + || __put_user(data.v[3], addr+1)) + return -EFAULT; + } else { + for (i = 0; i < nb; ++i) + if (__put_user(data.v[i], addr+i)) + return -EFAULT; + } + } + + if (flags & U) { + areg = regs->dsisr & 0x1f; /* register to update */ + regs->gpr[areg] = regs->dar; + } + + return 1; +} diff --git a/arch/ppc/kernel/bitops.c b/arch/ppc/kernel/bitops.c new file mode 100644 index 000000000..fb5a19e3a --- /dev/null +++ b/arch/ppc/kernel/bitops.c @@ -0,0 +1,201 @@ +/* + * Copyright (C) 1996 Paul Mackerras. + */ + +#include <linux/kernel.h> +#include <asm/bitops.h> + +/* + * I left these here since the problems with "cc" make it difficult to keep + * them in bitops.h -- Cort + */ +void set_bit(int nr, volatile void *addr) +{ + unsigned int t; + unsigned int mask = 1 << (nr & 0x1f); + volatile unsigned int *p = ((volatile unsigned int *)addr) + (nr >> 5); + + if ((unsigned long)addr & 3) + printk(KERN_ERR "set_bit(%x, %p)\n", nr, addr); + __asm__ __volatile__("\n\ +1: lwarx %0,0,%2 + or %0,%0,%1 + stwcx. %0,0,%2 + bne 1b" + : "=&r" (t) /*, "=m" (*p)*/ + : "r" (mask), "r" (p) + : "cc"); +} + +void clear_bit(int nr, volatile void *addr) +{ + unsigned int t; + unsigned int mask = 1 << (nr & 0x1f); + volatile unsigned int *p = ((volatile unsigned int *)addr) + (nr >> 5); + + if ((unsigned long)addr & 3) + printk(KERN_ERR "clear_bit(%x, %p)\n", nr, addr); + __asm__ __volatile__("\n\ +1: lwarx %0,0,%2 + andc %0,%0,%1 + stwcx. %0,0,%2 + bne 1b" + : "=&r" (t) /*, "=m" (*p)*/ + : "r" (mask), "r" (p) + : "cc"); +} + +void change_bit(int nr, volatile void *addr) +{ + unsigned int t; + unsigned int mask = 1 << (nr & 0x1f); + volatile unsigned int *p = ((volatile unsigned int *)addr) + (nr >> 5); + + if ((unsigned long)addr & 3) + printk(KERN_ERR "change_bit(%x, %p)\n", nr, addr); + __asm__ __volatile__("\n\ +1: lwarx %0,0,%2 + xor %0,%0,%1 + stwcx. %0,0,%2 + bne 1b" + : "=&r" (t) /*, "=m" (*p)*/ + : "r" (mask), "r" (p) + : "cc"); +} + +int test_and_set_bit(int nr, volatile void *addr) +{ + unsigned int old, t; + unsigned int mask = 1 << (nr & 0x1f); + volatile unsigned int *p = ((volatile unsigned int *)addr) + (nr >> 5); + + if ((unsigned long)addr & 3) + printk(KERN_ERR "test_and_set_bit(%x, %p)\n", nr, addr); + __asm__ __volatile__("\n\ +1: lwarx %0,0,%3 + or %1,%0,%2 + stwcx. %1,0,%3 + bne 1b" + : "=&r" (old), "=&r" (t) /*, "=m" (*p)*/ + : "r" (mask), "r" (p) + : "cc"); + + return (old & mask) != 0; +} + +int test_and_clear_bit(int nr, volatile void *addr) +{ + unsigned int old, t; + unsigned int mask = 1 << (nr & 0x1f); + volatile unsigned int *p = ((volatile unsigned int *)addr) + (nr >> 5); + + if ((unsigned long)addr & 3) + printk(KERN_ERR "test_and_clear_bit(%x, %p)\n", nr, addr); + __asm__ __volatile__("\n\ +1: lwarx %0,0,%3 + andc %1,%0,%2 + stwcx. %1,0,%3 + bne 1b" + : "=&r" (old), "=&r" (t) /*, "=m" (*p)*/ + : "r" (mask), "r" (p) + : "cc"); + + return (old & mask) != 0; +} + +int test_and_change_bit(int nr, volatile void *addr) +{ + unsigned int old, t; + unsigned int mask = 1 << (nr & 0x1f); + volatile unsigned int *p = ((volatile unsigned int *)addr) + (nr >> 5); + + if ((unsigned long)addr & 3) + printk(KERN_ERR "test_and_change_bit(%x, %p)\n", nr, addr); + __asm__ __volatile__("\n\ +1: lwarx %0,0,%3 + xor %1,%0,%2 + stwcx. %1,0,%3 + bne 1b" + : "=&r" (old), "=&r" (t) /*, "=m" (*p)*/ + : "r" (mask), "r" (p) + : "cc"); + + return (old & mask) != 0; +} + +/* I put it in bitops.h -- Cort */ +#if 0 +int ffz(unsigned int x) +{ + int n; + + x = ~x & (x+1); /* set LS zero to 1, other bits to 0 */ + __asm__ ("cntlzw %0,%1" : "=r" (n) : "r" (x)); + return 31 - n; +} + +/* + * This implementation of find_{first,next}_zero_bit was stolen from + * Linus' asm-alpha/bitops.h. + */ + +int find_first_zero_bit(void * addr, int size) +{ + unsigned int * p = ((unsigned int *) addr); + unsigned int result = 0; + unsigned int tmp; + + if (size == 0) + return 0; + while (size & ~31UL) { + if (~(tmp = *(p++))) + goto found_middle; + result += 32; + size -= 32; + } + if (!size) + return result; + tmp = *p; + tmp |= ~0UL << size; +found_middle: + return result + ffz(tmp); +} + +/* + * Find next zero bit in a bitmap reasonably efficiently.. + */ +int find_next_zero_bit(void * addr, int size, int offset) +{ + unsigned int * p = ((unsigned int *) addr) + (offset >> 5); + unsigned int result = offset & ~31UL; + unsigned int tmp; + + if (offset >= size) + return size; + size -= result; + offset &= 31UL; + if (offset) { + tmp = *(p++); + tmp |= ~0UL >> (32-offset); + if (size < 32) + goto found_first; + if (~tmp) + goto found_middle; + size -= 32; + result += 32; + } + while (size & ~31UL) { + if (~(tmp = *(p++))) + goto found_middle; + result += 32; + size -= 32; + } + if (!size) + return result; + tmp = *p; +found_first: + tmp |= ~0UL << size; +found_middle: + return result + ffz(tmp); +} +#endif diff --git a/arch/ppc/kernel/checks.c b/arch/ppc/kernel/checks.c new file mode 100644 index 000000000..312229552 --- /dev/null +++ b/arch/ppc/kernel/checks.c @@ -0,0 +1,57 @@ +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/config.h> + +#include <asm/pgtable.h> +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/smp_lock.h> + +/* + * Do various before compile checks of data structures + * -- Cort + */ +int main(void) +{ + int ret = 0; +#if 0 + if ( sizeof(struct thread_struct) % 16 ) + { + printf("Thread struct is not modulo 16 bytes: " + "%d bytes total, %d bytes off\n", + sizeof(struct thread_struct), + sizeof(struct thread_struct)%16); + ret = -1; + } +#endif + + if ( sizeof(struct pt_regs) % 16 ) + { + printf("pt_regs struct is not modulo 16 bytes: " + "%d bytes total, %d bytes off\n", + sizeof(struct pt_regs), + sizeof(struct pt_regs)%16); + ret = -1; + + } + + printf("Task size : %d bytes\n" + "Tss size : %d bytes\n" + "pt_regs size : %d bytes\n" + "Kernel stack size: %d bytes\n", + sizeof(struct task_struct), sizeof(struct thread_struct), + sizeof(struct pt_regs), + sizeof(union task_union) - sizeof(struct task_struct)); + return ret; +} diff --git a/arch/ppc/kernel/chrp_pci.c b/arch/ppc/kernel/chrp_pci.c new file mode 100644 index 000000000..8af9ba9d6 --- /dev/null +++ b/arch/ppc/kernel/chrp_pci.c @@ -0,0 +1,156 @@ +/* + * CHRP pci routines. + */ + +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/bios32.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/init.h> +#include <linux/openpic.h> + +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/hydra.h> + +/* LongTrail */ +#define pci_config_addr(bus, dev, offset) \ + (0xfec00000 | ((bus)<<16) | ((dev)<<8) | (offset)) + +int chrp_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val) +{ + if (bus > 7) { + *val = 0xff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + *val = in_8((unsigned char *)pci_config_addr(bus, dev_fn, offset)); + if (offset == PCI_INTERRUPT_LINE) { + /* PCI interrupts are controlled by the OpenPIC */ + if (*val) + *val = openpic_to_irq(*val); + } + return PCIBIOS_SUCCESSFUL; +} + +int chrp_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val) +{ + if (bus > 7) { + *val = 0xffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + *val = in_le16((unsigned short *)pci_config_addr(bus, dev_fn, offset)); + return PCIBIOS_SUCCESSFUL; +} + +int chrp_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val) +{ + if (bus > 7) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + *val = in_le32((unsigned int *)pci_config_addr(bus, dev_fn, offset)); + return PCIBIOS_SUCCESSFUL; +} + +int chrp_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val) +{ + if (bus > 7) + return PCIBIOS_DEVICE_NOT_FOUND; + out_8((unsigned char *)pci_config_addr(bus, dev_fn, offset), val); + return PCIBIOS_SUCCESSFUL; +} + +int chrp_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val) +{ + if (bus > 7) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le16((unsigned short *)pci_config_addr(bus, dev_fn, offset), val); + return PCIBIOS_SUCCESSFUL; +} + +int chrp_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val) +{ + if (bus > 7) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32((unsigned int *)pci_config_addr(bus, dev_fn, offset), val); + return PCIBIOS_SUCCESSFUL; +} + +int chrp_pcibios_find_device(unsigned short vendor, unsigned short dev_id, + unsigned short index, unsigned char *bus_ptr, + unsigned char *dev_fn_ptr) +{ + int num, devfn; + unsigned int x, vendev; + + if (vendor == 0xffff) + return PCIBIOS_BAD_VENDOR_ID; + vendev = (dev_id << 16) + vendor; + num = 0; + for (devfn = 0; devfn < 32; devfn++) { + chrp_pcibios_read_config_dword(0, devfn<<3, PCI_VENDOR_ID, &x); + if (x == vendev) { + if (index == num) { + *bus_ptr = 0; + *dev_fn_ptr = devfn<<3; + return PCIBIOS_SUCCESSFUL; + } + ++num; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + +int chrp_pcibios_find_class(unsigned int class_code, unsigned short index, + unsigned char *bus_ptr, unsigned char *dev_fn_ptr) +{ + int devnr, x, num; + + num = 0; + for (devnr = 0; devnr < 32; devnr++) { + chrp_pcibios_read_config_dword(0, devnr<<3, PCI_CLASS_REVISION, &x); + if ((x>>8) == class_code) { + if (index == num) { + *bus_ptr = 0; + *dev_fn_ptr = devnr<<3; + return PCIBIOS_SUCCESSFUL; + } + ++num; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + +__initfunc(volatile struct Hydra *find_hydra(void)) +{ + u_char bus, dev; + volatile struct Hydra *hydra = 0; + if (chrp_pcibios_find_device(PCI_VENDOR_ID_APPLE, + PCI_DEVICE_ID_APPLE_HYDRA, 0, &bus, &dev) + == PCIBIOS_SUCCESSFUL) + chrp_pcibios_read_config_dword(bus, dev, PCI_BASE_ADDRESS_0, + (unsigned int *)&hydra); + return hydra; +} + +__initfunc(void hydra_post_openpic_init(void)) +{ + openpic_set_sense(HYDRA_INT_SCSI_DMA, 0); + openpic_set_sense(HYDRA_INT_SCCA_TX_DMA, 0); + openpic_set_sense(HYDRA_INT_SCCA_RX_DMA, 0); + openpic_set_sense(HYDRA_INT_SCCB_TX_DMA, 0); + openpic_set_sense(HYDRA_INT_SCCB_RX_DMA, 0); + openpic_set_sense(HYDRA_INT_SCSI, 1); + openpic_set_sense(HYDRA_INT_SCCA, 1); + openpic_set_sense(HYDRA_INT_SCCB, 1); + openpic_set_sense(HYDRA_INT_VIA, 1); + openpic_set_sense(HYDRA_INT_ADB, 1); + openpic_set_sense(HYDRA_INT_ADB_NMI, 0); +} diff --git a/arch/ppc/kernel/chrp_setup.c b/arch/ppc/kernel/chrp_setup.c new file mode 100644 index 000000000..0a5544b96 --- /dev/null +++ b/arch/ppc/kernel/chrp_setup.c @@ -0,0 +1,180 @@ +/* + * linux/arch/ppc/kernel/setup.c + * + * Copyright (C) 1995 Linus Torvalds + * Adapted from 'alpha' version by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + */ + +/* + * bootup setup stuff.. + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/tty.h> +#include <linux/major.h> +#include <linux/interrupt.h> +#include <linux/reboot.h> +#include <linux/init.h> +#include <linux/blk.h> +#include <linux/ioport.h> + +#include <asm/mmu.h> +#include <asm/processor.h> +#include <asm/io.h> +#include <asm/pgtable.h> + +/* for the mac fs */ +kdev_t boot_dev; + +extern PTE *Hash, *Hash_end; +extern unsigned long Hash_size, Hash_mask; +extern int probingmem; +extern unsigned long loops_per_sec; + +unsigned long empty_zero_page[1024]; +extern unsigned char aux_device_present; + +#ifdef CONFIG_BLK_DEV_RAM +extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ +extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */ +extern int rd_image_start; /* starting block # of image */ +#endif + + +extern char saved_command_line[256]; + +long TotalMemory; + +int +chrp_get_cpuinfo(char *buffer) +{ + int pvr = _get_PVR(); + int len; + char *model; + + switch (pvr>>16) + { + case 1: + model = "601"; + break; + case 3: + model = "603"; + break; + case 4: + model = "604"; + break; + case 6: + model = "603e"; + break; + case 7: + model = "603ev"; + break; + case 9: + model = "604e"; + break; + default: + model = "unknown"; + break; + } + + len = sprintf(buffer, "PowerPC %s rev %d.%d\n", model, + (pvr & 0xff00) >> 8, pvr & 0xff); + + len += sprintf(buffer+len, "bogomips\t: %lu.%02lu\n", + (loops_per_sec+2500)/500000, + ((loops_per_sec+2500)/5000) % 100); + +#if 0 + /* + * Ooh's and aah's info about zero'd pages in idle task + */ + { + extern unsigned int zerocount, zerototal, zeropage_hits,zeropage_calls; + len += sprintf(buffer+len,"zero pages\t: total %u (%uKb) " + "current: %u (%uKb) hits: %u/%u (%lu%%)\n", + zerototal, (zerototal*PAGE_SIZE)>>10, + zerocount, (zerocount*PAGE_SIZE)>>10, + zeropage_hits,zeropage_calls, + /* : 1 below is so we don't div by zero */ + (zeropage_hits*100) / + ((zeropage_calls)?zeropage_calls:1)); + } +#endif + return len; +} + +__initfunc(void +chrp_setup_arch(char **cmdline_p, unsigned long * memory_start_p, + unsigned long * memory_end_p)) +{ + extern char cmd_line[]; + extern char _etext[], _edata[], _end[]; + extern int panic_timeout; + + /* Save unparsed command line copy for /proc/cmdline */ + strcpy( saved_command_line, cmd_line ); + *cmdline_p = cmd_line; + + *memory_start_p = (unsigned long) Hash+Hash_size; + (unsigned long *)*memory_end_p = (unsigned long *)(TotalMemory+KERNELBASE); + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_sec = 50000000; + + /* reboot on panic */ + panic_timeout = 180; + + init_task.mm->start_code = PAGE_OFFSET; + init_task.mm->end_code = (unsigned long) _etext; + init_task.mm->end_data = (unsigned long) _edata; + init_task.mm->brk = (unsigned long) _end; + + aux_device_present = 0xaa; + + switch ( _machine ) + { + case _MACH_chrp: + ROOT_DEV = to_kdev_t(0x0801); /* sda1 */ + break; + } + +#ifdef CONFIG_BLK_DEV_RAM +#if 0 + ROOT_DEV = to_kdev_t(0x0200); /* floppy */ + rd_prompt = 1; + rd_doload = 1; + rd_image_start = 0; +#endif + /* initrd_start and size are setup by boot/head.S and kernel/head.S */ + if ( initrd_start ) + { + if (initrd_end > *memory_end_p) + { + printk("initrd extends beyond end of memory " + "(0x%08lx > 0x%08lx)\ndisabling initrd\n", + initrd_end,*memory_end_p); + initrd_start = 0; + } + } +#endif + + printk("Boot arguments: %s\n", cmd_line); + + request_region(0x20,0x20,"pic1"); + request_region(0xa0,0x20,"pic2"); + request_region(0x00,0x20,"dma1"); + request_region(0x40,0x20,"timer"); + request_region(0x80,0x10,"dma page reg"); + request_region(0xc0,0x20,"dma2"); +} diff --git a/arch/ppc/kernel/chrp_time.c b/arch/ppc/kernel/chrp_time.c new file mode 100644 index 000000000..bf58953cd --- /dev/null +++ b/arch/ppc/kernel/chrp_time.c @@ -0,0 +1,132 @@ +/* + * linux/arch/i386/kernel/time.c + * + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + * + * Adapted for PowerPC (PreP) by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + * copied and modified from intel version + * + */ +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/timex.h> +#include <linux/kernel_stat.h> +#include <linux/mc146818rtc.h> + +#include <asm/segment.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/nvram.h> + +#include "time.h" + +int chrp_cmos_clock_read(int addr) +{ + outb(addr>>8, NVRAM_AS1); + outb(addr, NVRAM_AS0); + return (inb(NVRAM_DATA)); +} + +void chrp_cmos_clock_write(unsigned long val, int addr) +{ + outb(addr>>8, NVRAM_AS1); + outb(addr, NVRAM_AS0); + outb(val,NVRAM_DATA); + return; +} + +/* + * Set the hardware clock. -- Cort + */ +int chrp_set_rtc_time(unsigned long nowtime) +{ + unsigned char save_control, save_freq_select; + struct rtc_time tm; + + to_tm(nowtime, &tm); + + save_control = chrp_cmos_clock_read(RTC_CONTROL); /* tell the clock it's being set */ + + chrp_cmos_clock_write((save_control|RTC_SET), RTC_CONTROL); + + save_freq_select = chrp_cmos_clock_read(RTC_FREQ_SELECT); /* stop and reset prescaler */ + + chrp_cmos_clock_write((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); + + tm.tm_year -= 1900; + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + BIN_TO_BCD(tm.tm_sec); + BIN_TO_BCD(tm.tm_min); + BIN_TO_BCD(tm.tm_hour); + BIN_TO_BCD(tm.tm_mon); + BIN_TO_BCD(tm.tm_mday); + BIN_TO_BCD(tm.tm_year); + } + chrp_cmos_clock_write(tm.tm_sec,RTC_SECONDS); + chrp_cmos_clock_write(tm.tm_min,RTC_MINUTES); + chrp_cmos_clock_write(tm.tm_hour,RTC_HOURS); + chrp_cmos_clock_write(tm.tm_mon,RTC_MONTH); + chrp_cmos_clock_write(tm.tm_mday,RTC_DAY_OF_MONTH); + chrp_cmos_clock_write(tm.tm_year,RTC_YEAR); + + /* The following flags have to be released exactly in this order, + * otherwise the DS12887 (popular MC146818A clone with integrated + * battery and quartz) will not reset the oscillator and will not + * update precisely 500 ms later. You won't find this mentioned in + * the Dallas Semiconductor data sheets, but who believes data + * sheets anyway ... -- Markus Kuhn + */ + chrp_cmos_clock_write(save_control, RTC_CONTROL); + chrp_cmos_clock_write(save_freq_select, RTC_FREQ_SELECT); + + if ( (time_state == TIME_ERROR) || (time_state == TIME_BAD) ) + time_state = TIME_OK; + return 0; +} + +unsigned long chrp_get_rtc_time(void) +{ + unsigned int year, mon, day, hour, min, sec; + int i; + + /* The Linux interpretation of the CMOS clock register contents: + * When the Update-In-Progress (UIP) flag goes from 1 to 0, the + * RTC registers show the second which has precisely just started. + * Let's hope other operating systems interpret the RTC the same way. + */ + /* read RTC exactly on falling edge of update flag */ + for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */ + if (chrp_cmos_clock_read(RTC_FREQ_SELECT) & RTC_UIP) + break; + for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */ + if (!(chrp_cmos_clock_read(RTC_FREQ_SELECT) & RTC_UIP)) + break; + do { /* Isn't this overkill ? UIP above should guarantee consistency */ + sec = chrp_cmos_clock_read(RTC_SECONDS); + min = chrp_cmos_clock_read(RTC_MINUTES); + hour = chrp_cmos_clock_read(RTC_HOURS); + day = chrp_cmos_clock_read(RTC_DAY_OF_MONTH); + mon = chrp_cmos_clock_read(RTC_MONTH); + year = chrp_cmos_clock_read(RTC_YEAR); + } while (sec != chrp_cmos_clock_read(RTC_SECONDS)); + if (!(chrp_cmos_clock_read(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) + { + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + } + if ((year += 1900) < 1970) + year += 100; + return mktime(year, mon, day, hour, min, sec); +} + diff --git a/arch/ppc/kernel/head.S b/arch/ppc/kernel/head.S index 25f2dd8a0..3038e8cc1 100644 --- a/arch/ppc/kernel/head.S +++ b/arch/ppc/kernel/head.S @@ -45,14 +45,13 @@ #define TOPHYS(x) (x - KERNELBASE) - /* this is a very kludgey way of loading up the BATs on the prep system. I'll kill this horrible macro and write something clean when I have a chance -- Cort */ #define LOAD_BATS(RA,RB) \ mfspr RA,PVR ; \ - srwi r5,r5,16 ; \ + srwi RA,RA,16 ; \ cmpi 0,RA,1 ; \ beq 199f ; \ /* load bats for 60x */ ; \ @@ -132,21 +131,10 @@ mtspr DBAT3L,RB ; \ 200: - - - .text .globl _stext _stext: -#ifdef CONFIG_PREP - . = 0x100 -_GLOBAL(HardReset) - b _start - -#endif /* CONFIG_PREP */ - -#ifdef CONFIG_PMAC /* * _start is defined this way because the XCOFF loader in the OpenFirmware * on the powermac expects the entry point to be a procedure descriptor. @@ -156,7 +144,7 @@ _GLOBAL(HardReset) _start: .long TOPHYS(__start),0,0 -/* +/* PMAC * Enter here with the kernel text, data and bss loaded starting at * 0, running with virtual == physical mapping. * r5 points to the prom entry point (the client interface handler @@ -165,41 +153,71 @@ _start: * pointer (r1) points to just below the end of the half-meg region * from 0x380000 - 0x400000, which is mapped in already. */ +/* PREP + * This is jumped to on prep systems right after the kernel is relocated + * to its proper place in memory by the boot loader. The expected layout + * of the regs is: + * r3: ptr to residual data + * r4: initrd_start or if no initrd then 0 + * r5: initrd_end - unused if r4 is 0 + * r6: Start of command line string + * r7: End of command line string + * + * This just gets a minimal mmu environment setup so we can call + * start_here() to do the real work. + * -- Cort + */ + .globl __start __start: /* - * Use the first pair of BAT registers to map the 1st 8MB + * Use the first pair of BAT registers to map the 1st 16MB * of RAM to KERNELBASE. */ - mfspr r9,PVR - rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */ - cmpi 0,r9,1 - lis r7,KERNELBASE@h - bne 4f - ori r7,r7,4 /* set up BAT registers for 601 */ - li r8,0x7f - b 5f -4: ori r7,r7,0xff /* set up BAT registers for 604 */ - li r8,2 - mtspr DBAT0U,r7 - mtspr DBAT0L,r8 -5: mtspr IBAT0U,r7 - mtspr IBAT0L,r8 - isync - + mfspr r9,PVR + rlwinm r9,r9,16,16,31 /* r9 = 1 for 601, 4 for 604 */ + cmpi 0,r9,1 + lis r11,KERNELBASE@h + bne 4f + ori r11,r11,4 /* set up BAT registers for 601 */ + li r8,0x7f + ori r11,r11,4 /* set up BAT registers for 601 */ + li r8,0x7f + oris r9,r11,0x800000@h /* set up BAT reg for 2nd 8M */ + oris r10,r8,0x800000@h /* set up BAT reg for 2nd 8M */ + mtspr IBAT1U,r9 + mtspr IBAT1L,r10 + b 5f +4: ori r11,r11,0x1ff /* set up BAT registers for 604 */ + li r8,2 + mtspr DBAT0U,r11 + mtspr DBAT0L,r8 +5: mtspr IBAT0U,r11 + mtspr IBAT0L,r8 + isync /* - * Now we have the 1st 8M of RAM mapped at KERNELBASE, so we can - * refer to addresses of data items, procedures, etc. normally. + * we now have the 1st 16M of ram mapped with the bats. + * prep needs the mmu to be turned on here, but pmac already has it on. + * this shouldn't bother the pmac since it just gets turned on again + * as we jump to our code at KERNELBASE. -- Cort */ - lis r7,start_here@ha /* jump up to our copy at KERNELBASE */ - addi r7,r7,start_here@l - mtlr r7 - blr -#endif /* CONFIG_PMAC */ - - - + mfmsr r0 + ori r0,r0,MSR_DR|MSR_IR + mtspr SRR1,r0 + lis r0,start_here@h + ori r0,r0,start_here@l + mtspr SRR0,r0 + SYNC + rfi /* enables MMU */ + +/* + * GCC sometimes accesses words at negative offsets from the stack + * pointer, although the SysV ABI says it shouldn't. To cope with + * this, we leave this much untouched space on the stack on exception + * entry. + */ +#define STACK_UNDERHEAD 64 /* * Macros for storing registers into and loading registers from @@ -286,10 +304,8 @@ label: \ .long hdlr; \ .long int_return -#ifndef CONFIG_PREP /* System reset */ STD_EXCEPTION(0x100, Reset, UnknownException) -#endif /* ndef CONFIG_PREP */ /* Machine check */ STD_EXCEPTION(0x200, MachineCheck, MachineCheckException) @@ -319,7 +335,7 @@ DataAccess: /* Instruction access exception */ . = 0x400 -InstructionAccess: +InstructionAccess: EXCEPTION_PROLOG andis. r0,r23,0x4000 /* no pte found? */ beq 1f /* if so, try to put a PTE */ @@ -338,7 +354,7 @@ InstructionAccess: .long int_return /* External interrupt */ - STD_EXCEPTION(0x500, HardwareInterrupt, handle_IRQ) + STD_EXCEPTION(0x500, HardwareInterrupt, do_IRQ) /* Alignment exception */ . = 0x600 @@ -376,21 +392,7 @@ FPUnavailable: .long KernelFP .long int_return -/* Decrementer */ -#ifdef CONFIG_PREP -/* - ignored for now... */ -_ORG(0x0900) - mtspr SPRG0,r1 - lis r1,0x7FFF - ori r1,r1,0xFFFF - mtspr DEC,r1 - mfspr r1,SPRG0 - rfi -#endif /* CONFIG_PREP */ -#ifdef CONFIG_PMAC STD_EXCEPTION(0x900, Decrementer, timer_interrupt) -#endif /* CONFIG_PMAC */ - STD_EXCEPTION(0xa00, Trap_0a, UnknownException) STD_EXCEPTION(0xb00, Trap_0b, UnknownException) @@ -596,11 +598,6 @@ transfer_to_handler: andi. r23,r23,MSR_PR mfspr r23,SPRG3 /* if from user, fix up tss */ beq 2f -#ifdef CONFIG_PMAC - lwz r24,GPR1(r21) - stw r22,LAST_PC(r23) - stw r24,USER_STACK(r23) -#endif /* CONFIG_PMAC */ addi r24,r1,STACK_FRAME_OVERHEAD stw r24,PT_REGS(r23) 2: addi r2,r23,-TSS /* set r2 to current */ @@ -630,7 +627,7 @@ load_up_fpu: REST_32FPRS(0, r5) /* use last_task_used_math instead of fpu_tss */ - lis r3,last_task_used_math@h/*a*/ + lis r3,last_task_used_math@ha addis r3,r3,-KERNELBASE@h subi r4,r5,TSS addis r4,r4,KERNELBASE@h @@ -654,7 +651,7 @@ load_up_fpu: REST_GPR(20, r21) REST_2GPRS(22, r21) lwz r21,GPR21(r21) - SYNC + SYNC rfi /* @@ -682,14 +679,15 @@ Hash_msk = (((1 << Hash_bits) - 1) * 64) .globl hash_page hash_page: /* Get PTE (linux-style) and check access */ - lwz r5,PG_TABLES(r5) /* task's page tables */ - lis r2,-KERNELBASE@h - add r5,r5,r2 /* convert to phys addr */ + lwz r5,MM-TSS(r5) + addis r5,r5,-KERNELBASE@h /* get physical current->mm */ + lwz r5,PGD(r5) /* get current->mm->pgd */ + addis r5,r5,-KERNELBASE@h /* convert to phys addr */ rlwimi r5,r3,12,20,29 /* insert top 10 bits of address */ lwz r5,0(r5) /* get pmd entry */ rlwinm. r5,r5,0,0,19 /* extract address of pte page */ beqlr- /* return if no mapping */ - add r2,r5,r2 + addis r2,r5,-KERNELBASE@h rlwimi r2,r3,22,20,29 /* insert next 10 bits of address */ lwz r6,0(r2) /* get linux-style pte */ ori r4,r4,1 /* set _PAGE_PRESENT bit in access */ @@ -819,58 +817,62 @@ start_here: rlwinm r9,r9,16,16,31 cmpi 0,r9,1 beq 4f /* not needed for 601 */ - mfspr r7,HID0 - andi. r0,r7,HID0_DCE - ori r7,r7,HID0_ICE|HID0_DCE - ori r8,r7,HID0_ICFI + mfspr r11,HID0 + andi. r0,r11,HID0_DCE + ori r11,r11,HID0_ICE|HID0_DCE + ori r8,r11,HID0_ICFI bne 3f /* don't invalidate the D-cache */ ori r8,r8,HID0_DCI /* unless it wasn't enabled */ -3: sync - mtspr HID0,r8 /* enable and invalidate caches */ +3: + /* turn on dpm for 603 */ + cmpi 0,r9,3 + bne 10f + oris r11,r11,HID0_DPM@h +10: sync - mtspr HID0,r7 /* enable caches */ + mtspr HID0,r8 /* enable and invalidate caches */ + mtspr HID0,r11 /* enable caches */ sync isync cmpi 0,r9,4 /* check for 604 */ cmpi 1,r9,9 /* or 604e */ cror 2,2,6 bne 4f - ori r7,r7,HID0_SIED|HID0_BHTE /* for 604[e], enable */ - mtspr HID0,r7 /* superscalar exec & br history tbl */ + ori r11,r11,HID0_SIED|HID0_BHTE /* for 604[e], enable */ + mtspr HID0,r11 /* superscalar exec & br history tbl */ 4: /* ptr to current */ lis r2,init_task_union@h ori r2,r2,init_task_union@l /* ptr to phys current tss */ - addis r3,r2,-KERNELBASE@h - addi r3,r3,TSS /* init task's TSS */ - mtspr SPRG3,r3 + addis r11,r2,-KERNELBASE@h + addi r11,r11,TSS /* init task's TSS */ + mtspr SPRG3,r11 /* stack */ addi r1,r2,TASK_UNION_SIZE li r0,0 stwu r0,-STACK_FRAME_OVERHEAD(r1) /* Clear out the BSS */ - lis r7,_end@ha - addi r7,r7,_end@l + lis r11,_end@ha + addi r11,r11,_end@l lis r8,__bss_start@ha addi r8,r8,__bss_start@l - subf r7,r8,r7 - addi r7,r7,3 - rlwinm. r7,r7,30,2,31 + subf r11,r8,r11 + addi r11,r11,3 + rlwinm. r11,r11,30,2,31 beq 2f addi r8,r8,-4 - mtctr r7 + mtctr r11 li r0,0 3: stwu r0,4(r8) bdnz 3b 2: /* - * Initialize the prom stuff (powermacs only) and the MMU. + * Initialize the prom stuff and the MMU. */ -#ifdef CONFIG_PMAC + bl identify_machine bl prom_init -#endif /* CONFIG_PMAC */ bl MMU_init /* @@ -889,11 +891,6 @@ start_here: rfi /* Load up the kernel context */ 2: -#ifdef CONFIG_PREP - /* reload the bats now that MMU_init() has setup them up -- Cort */ - LOAD_BATS(r3,r0) -#endif - SYNC /* Force all PTE updates to finish */ tlbia /* Clear all TLB entries */ mtspr SDR1,r6 @@ -905,8 +902,19 @@ start_here: addi r3,r3,1 /* increment VSID */ addis r4,r4,0x1000 /* address of next segment */ bdnz 3b -#ifdef CONFIG_PMAC - li r0,0 /* zot the BATs */ + + lis r3,_machine@ha + addis r3,r3,-KERNELBASE@h + lwz r0,_machine@l(r3) + cmpi 0,r0,_MACH_Pmac + beq 99f + +/* on prep reload the bats now that MMU_init() has setup them up -- Cort */ + LOAD_BATS(r3,r14) + b 100f + +/* on pmac clear the bats out */ +99: li r0,0 /* zot the BATs */ #if 1 mtspr IBAT0U,r0 mtspr IBAT0L,r0 @@ -925,7 +933,7 @@ start_here: mtspr IBAT3L,r0 mtspr DBAT3U,r0 mtspr DBAT3L,r0 -#endif +100: /* Now turn on the MMU for real! */ li r4,MSR_KERNEL lis r3,start_kernel@h @@ -934,60 +942,29 @@ start_here: mtspr SRR1,r4 rfi /* enable MMU and jump to start_kernel */ -#ifdef CONFIG_PREP -/* - * This is jumped to on prep systems right after the kernel is relocated - * to its proper place in memory by the boot loader. The expected layout - * of the regs is: - * R3: End of image - * R4: Start of image - 0x400 - * R11: Start of command line string - * R12: End of command line string - * - * This just gets a minimal mmu environment setup so we can call - * start_here() to do the real work. - * -- Cort - */ - .globl __start -__start: - .globl _start -_start: - lis r7,0xF000 /* To mask upper 4 bits */ -/* save pointer to residual data */ - lis r1,resptr@h - ori r1,r1,resptr@l - addis r1,r1,-KERNELBASE@h - stw r3,0(r1) -/* save argument string */ - li r0,0 /* Null terminate string */ - stb r0,0(r12) - lis r1,cmd_line@h - ori r1,r1,cmd_line@l - addis r1,r1,-KERNELBASE@h - subi r1,r1,1 - subi r11,r11,1 -00: lbzu r0,1(r11) - cmpi 0,r0,0 - stbu r0,1(r1) - bne 00b -/* setup the msr with sane values */ - li r0,MSR_ - mtmsr r0 -/* turn on the mmu with bats covering kernel enough to get started */ - LOAD_BATS(r3,r0) + + .globl reset_SDR1 +reset_SDR1: + lis r6,_SDR1@ha + lwz r6,_SDR1@l(r6) mfmsr r3 - ori r3,r3,MSR_DR|MSR_IR + li r4,MSR_IR|MSR_DR + andc r3,r3,r4 + lis r4,2f@h + addis r4,r4,-KERNELBASE@h + ori r4,r4,2f@l + mtspr SRR0,r4 mtspr SRR1,r3 - lis r3,10f@h - ori r3,r3,10f@l + rfi +2: /* load new SDR1 */ + tlbia + mtspr SDR1,r6 + /* turn the mmu back on */ + li r4,MSR_KERNEL + mflr r3 mtspr SRR0,r3 - SYNC - rfi /* enables MMU */ -10: lis r7,start_here@ha /* jump up to our copy at KERNELBASE */ - addi r7,r7,start_here@l - mtlr r7 - blr -#endif /* CONFIG_PREP */ + mtspr SRR1,r4 + rfi /* * FP unavailable trap from kernel - print a message, but let @@ -1022,31 +999,23 @@ giveup_fpu_unmapped: giveup_fpu: li r6,0 1: - addis r3,r6,last_task_used_math@h/*a*/ + addis r3,r6,last_task_used_math@ha lwz r4,last_task_used_math@l(r3) -#if 0 - addis r3,r6,fpu_tss@ha - lwz r4,fpu_tss@l(r3) -#endif mfmsr r5 ori r5,r5,MSR_FP SYNC mtmsr r5 /* enable use of fpu now */ SYNC cmpi 0,r4,0 + beqlr- /* if no previous owner, done */ add r4,r4,r6 - beqlr /* if no previous owner, done */ addi r4,r4,TSS /* want TSS of last_task_used_math */ li r5,0 stw r5,last_task_used_math@l(r3) -#if 0 - stw r5,fpu_tss@l(r3) -#endif SAVE_32FPRS(0, r4) mffs fr0 stfd fr0,TSS_FPSCR-4(r4) lwz r5,PT_REGS(r4) - lwz r5,PT_REGS(r4) add r5,r5,r6 lwz r3,_MSR-STACK_FRAME_OVERHEAD(r5) li r4,MSR_FP @@ -1268,7 +1237,7 @@ int_return: cmpi 0,r4,0 beq+ 1f addi r3,r1,STACK_FRAME_OVERHEAD - bl handle_IRQ + bl do_IRQ b 3b 1: lis r4,bh_mask@ha lwz r4,bh_mask@l(r4) @@ -1341,7 +1310,7 @@ _GLOBAL(fake_interrupt) li r0,0x0fac stw r0,TRAP(r1) addi r3,r1,STACK_FRAME_OVERHEAD - bl handle_IRQ + bl do_IRQ addi r1,r1,INT_FRAME_SIZE+STACK_UNDERHEAD lwz r0,4(r1) mtlr r0 @@ -1384,11 +1353,11 @@ _GLOBAL(flush_instruction_cache) * and invalidate the corresponding instruction cache blocks. * This is a no-op on the 601. * - * store_cache_range(unsigned long start, unsigned long stop) + * flush_icache_range(unsigned long start, unsigned long stop) */ CACHE_LINE_SIZE = 32 LG_CACHE_LINE_SIZE = 5 -_GLOBAL(store_cache_range) +_GLOBAL(flush_icache_range) mfspr r5,PVR rlwinm r5,r5,16,16,31 cmpi 0,r5,1 @@ -1521,7 +1490,6 @@ _GLOBAL(flush_hash_page) _GLOBAL(__main) blr -#ifdef CONFIG_PMAC /* * These exception handlers are used when we have called a prom * routine after we have taken over the exception vectors and MMU. @@ -1716,7 +1684,6 @@ enter_prom: lwz r31,28(r1) lwz r1,0(r1) blr -#endif /* * We put a few things here that have to be page-aligned. @@ -1726,7 +1693,6 @@ enter_prom: .data .globl sdata sdata: - .space 2*4096 .globl empty_zero_page empty_zero_page: .space 4096 @@ -1735,7 +1701,6 @@ empty_zero_page: swapper_pg_dir: .space 4096 -#ifdef CONFIG_PREP /* * This space gets a copy of optional info passed to us by the bootstrap * Used to pass parameters into the kernel like root=/dev/sda1, etc. @@ -1743,5 +1708,4 @@ swapper_pg_dir: .globl cmd_line cmd_line: .space 512 -#endif /* CONFIG_PREP */ diff --git a/arch/ppc/kernel/idle.c b/arch/ppc/kernel/idle.c new file mode 100644 index 000000000..bfe1a4146 --- /dev/null +++ b/arch/ppc/kernel/idle.c @@ -0,0 +1,279 @@ +/* + * $Id: idle.c,v 1.4 1997/08/23 22:46:01 cort Exp $ + * + * Idle daemon for PowerPC. Idle daemon will handle any action + * that needs to be taken when the system becomes idle. + * + * Written by Cort Dougan (cort@cs.nmt.edu) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#define __KERNEL_SYSCALLS__ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/config.h> + +#include <asm/pgtable.h> +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/smp_lock.h> +#include <asm/processor.h> + +int zero_paged(void *unused); +int power_saved(void *unused); + +asmlinkage int sys_idle(void) +{ + int ret = -EPERM; + + if (current->pid != 0) + goto out; + + /* + * want one per cpu since it would be nice to have all + * processors who aren't doing anything + * zero-ing pages since this daemon is lock-free + * -- Cort + */ + kernel_thread(zero_paged, NULL, 0); + /* no powersaving modes on 601 */ + /*if( (_get_PVR()>>16) != 1 ) + kernel_thread(power_saved, NULL, 0);*/ + + /* endless loop with no priority at all */ + current->priority = -100; + current->counter = -100; + for (;;) + { + schedule(); + } + ret = 0; +out: + return ret; +} + +/* + * vars for idle task zero'ing out pages + */ +unsigned long zero_list = 0; /* head linked list of pre-zero'd pages */ +unsigned long bytecount = 0; /* pointer into the currently being zero'd page */ +unsigned long zerocount = 0; /* # currently pre-zero'd pages */ +unsigned long zerototal = 0; /* # pages zero'd over time -- for ooh's and ahhh's */ +unsigned long pageptr = 0; /* current page being zero'd */ +unsigned long zeropage_hits = 0;/* # zero'd pages request that we've done */ +unsigned long zeropage_calls = 0;/* # zero'd pages request that've been made */ +static struct wait_queue * page_zerod_wait = NULL; +#define PAGE_THRESHOLD 96 /* how many pages to keep pre-zero'd */ + +/* + * Returns a pre-zero'd page from the list otherwise returns + * NULL. + */ +unsigned long get_prezerod_page(void) +{ + unsigned long page; + + atomic_inc((atomic_t *)&zeropage_calls); + if ( zero_list ) + { + /* atomically remove this page from the list */ + asm ( "101:lwarx %1,0,%2\n" /* reserve zero_list */ + " lwz %0,0(%1)\n" /* get next -- new zero_list */ + " stwcx. %0,0,%2\n" /* update zero_list */ + " bne- 101b\n" /* if lost reservation try again */ + : "=&r" (zero_list), "=&r" (page) + : "r" (&zero_list) + : "cc" ); + /* we can update zerocount after the fact since it is not + * used for anything but control of a loop which doesn't + * matter since it won't effect anything if it zero's one + * less page -- Cort + */ + atomic_inc((atomic_t *)&zeropage_hits); + atomic_dec((atomic_t *)&zerocount); + wake_up(&page_zerod_wait); + resched_force(); + + /* zero out the pointer to next in the page */ + *(unsigned long *)page = 0; + return page; + } + return 0; +} + +/* + * Experimental stuff to zero out pages in the idle task + * to speed up get_free_pages() -- Cort + * Zero's out pages until we need to resched or + * we've reached the limit of zero'd pages. + */ + +int zero_paged(void *unused) +{ + extern pte_t *get_pte( struct mm_struct *mm, unsigned long address ); + pgd_t *dir; + pmd_t *pmd; + pte_t *pte; + + sprintf(current->comm, "zero_paged (idle)"); + current->blocked = ~0UL; + + printk("Started zero_paged\n"); + + __sti(); + while ( 1 ) + { + /* don't want to be pre-empted by swapper or power_saved */ + current->priority = -98; + current->counter = -98; + /* we don't want to run until we have something to do */ + while ( zerocount >= PAGE_THRESHOLD ) + sleep_on(&page_zerod_wait); + /* + * Mark a page as reserved so we can mess with it + * If we're interrupted we keep this page and our place in it + * since we validly hold it and it's reserved for us. + */ + pageptr = __get_free_pages(GFP_ATOMIC, 0, 0 ); + if ( !pageptr ) + { + printk("!pageptr in zero_paged\n"); + goto retry; + } + + if ( resched_needed() ) + schedule(); + + /* + * Make the page no cache so we don't blow our cache with 0's + */ + dir = pgd_offset( init_task.mm, pageptr ); + if (dir) + { + pmd = pmd_offset(dir, pageptr & PAGE_MASK); + if (pmd && pmd_present(*pmd)) + { + pte = pte_offset(pmd, pageptr & PAGE_MASK); + if (pte && pte_present(*pte)) + { + pte_uncache(*pte); + flush_tlb_page(find_vma(init_task.mm,pageptr),pageptr); + } + } + } + + /* + * Important here to not take time away from real processes. + */ + for ( bytecount = 0; bytecount < PAGE_SIZE ; bytecount += 4 ) + { + if ( resched_needed() ) + schedule(); + *(unsigned long *)(bytecount + pageptr) = 0; + } + + /* + * If we finished zero-ing out a page add this page to + * the zero_list atomically -- we can't use + * down/up since we can't sleep in idle. + * Disabling interrupts is also a bad idea since we would + * steal time away from real processes. + * We can also have several zero_paged's running + * on different processors so we can't interfere with them. + * So we update the list atomically without locking it. + * -- Cort + */ + /* turn cache on for this page */ + pte_cache(*pte); + flush_tlb_page(find_vma(init_task.mm,pageptr),pageptr); + + /* atomically add this page to the list */ + asm ( "101:lwarx %0,0,%1\n" /* reserve zero_list */ + " stw %0,0(%2)\n" /* update *pageptr */ +#ifdef __SMP__ + " sync\n" /* let store settle */ +#endif + " mr %0,%2\n" /* update zero_list in reg */ + " stwcx. %2,0,%1\n" /* update zero_list in mem */ + " bne- 101b\n" /* if lost reservation try again */ + : "=&r" (zero_list) + : "r" (&zero_list), "r" (pageptr) + : "cc" ); + /* + * This variable is used in the above loop and nowhere + * else so the worst that could happen is we would + * zero out one more or one less page than we want + * per processor on the machine. This is because + * we could add our page to the list but not have + * zerocount updated yet when another processor + * reads it. -- Cort + */ + atomic_inc((atomic_t *)&zerocount); + atomic_inc((atomic_t *)&zerototal); +retry: + schedule(); + } +} + +int power_saved(void *unused) +{ + unsigned long msr, hid0; + sprintf(current->comm, "power_saved (idle)"); + current->blocked = ~0UL; + + printk("Power saving daemon started\n"); + + __sti(); + while (1) + { + /* don't want to be pre-empted by swapper */ + current->priority = -99; + current->counter = -99; + /* go ahead and wakeup page_zerod() */ + wake_up(&page_zerod_wait); + schedule(); + asm volatile( + /* clear powersaving modes and set nap mode */ + "mfspr %3,1008 \n\t" + "andc %3,%3,%4 \n\t" + "or %3,%3,%5 \n\t" + "mtspr 1008,%3 \n\t" + /* enter the mode */ + "mfmsr %0 \n\t" + "oris %0,%0,%2 \n\t" + "sync \n\t" + "mtmsr %0 \n\t" + "isync \n\t" + : "=&r" (msr) + : "0" (msr), "i" (MSR_POW>>16), + "r" (hid0), + "r" (HID0_DOZE|HID0_NAP|HID0_SLEEP), + "r" (HID0_NAP)); + /* + * The ibm carolina spec says that the eagle memory + * controller will detect the need for a snoop + * and wake up the processor so we don't need to + * check for cache operations that need to be + * snooped. The ppc book says the run signal + * must be asserted while napping for this though. + * + * Paul, what do you know about the pmac here? + * -- Cort + */ + schedule(); + } +} + diff --git a/arch/ppc/kernel/irq.c b/arch/ppc/kernel/irq.c index 171bfaa5c..da3c53697 100644 --- a/arch/ppc/kernel/irq.c +++ b/arch/ppc/kernel/irq.c @@ -1,13 +1,13 @@ /* * arch/ppc/kernel/irq.c * - * Power Macintosh version - * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au) - * * Derived from arch/i386/kernel/irq.c * Copyright (C) 1992 Linus Torvalds * Adapted from arch/i386 by Gary Thomas - * Modified by Cort Dougan (cort@cs.nmt.edu) + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * Updated and modified by Cort Dougan (cort@cs.nmt.edu) + * Adapted for Power Macintosh by Paul Mackerras + * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au) * * This file contains the code used by various IRQ handling routines: * asking for different IRQ's should be done through these routines @@ -15,11 +15,7 @@ * shouldn't result in any weird surprises, and installing new handlers * should be easier. */ - -/* - * IRQ's are in fact implemented a bit like signal handlers for the kernel. - * Naturally it's not a 1:1 relation, but there are similarities. - */ + #include <linux/ptrace.h> #include <linux/errno.h> @@ -30,121 +26,164 @@ #include <linux/interrupt.h> #include <linux/timex.h> #include <linux/config.h> - +#include <linux/init.h> +#include <linux/malloc.h> +#include <linux/openpic.h> +#include <linux/pci.h> +#include <linux/bios32.h> +#include <linux/openpic.h> + +#include <asm/hydra.h> #include <asm/system.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/bitops.h> -#define IRQ_FLAG ((unsigned *)0xf3000020) -#define IRQ_ENABLE ((unsigned *)0xf3000024) -#define IRQ_ACK ((unsigned *)0xf3000028) -#define IRQ_LEVEL ((unsigned *)0xf300002c) - -#define KEYBOARD_IRQ 20 /* irq number for command-power interrupt */ - -#undef SHOW_IRQ 1 +#undef SHOW_IRQ +#define OPENPIC_DEBUG unsigned lost_interrupts = 0; - unsigned int local_irq_count[NR_CPUS]; -static struct irqaction irq_action[32]; +static struct irqaction irq_action[NR_IRQS]; +static int spurious_interrupts = 0; +int __ppc_bh_counter; +static unsigned int cached_irq_mask = 0xffffffff; +static void no_action(int cpl, void *dev_id, struct pt_regs *regs) { } + +#define cached_21 (((char *)(&cached_irq_mask))[3]) +#define cached_A1 (((char *)(&cached_irq_mask))[2]) /* - * This contains the irq mask for both irq controllers + * These are set to the appropriate functions by init_IRQ() */ -static unsigned int cached_irq_mask = 0xffff; +void (*mask_and_ack_irq)(int irq_nr); +void (*set_irq_mask)(int irq_nr); -#define cached_21 (((char *)(&cached_irq_mask))[0]) -#define cached_A1 (((char *)(&cached_irq_mask))[1]) +/* prep */ +#define PREP_IRQ_MASK (((unsigned int)cached_A1)<<8) | (unsigned int)cached_21 +extern unsigned long route_pci_interrupts(void); -int __ppc_bh_counter; +/* pmac */ +#define IRQ_FLAG ((unsigned *)0xf3000020) +#define IRQ_ENABLE ((unsigned *)0xf3000024) +#define IRQ_ACK ((unsigned *)0xf3000028) +#define IRQ_LEVEL ((unsigned *)0xf300002c) +#define KEYBOARD_IRQ 20 /* irq number for command-power interrupt */ +#define PMAC_IRQ_MASK (~ld_le32(IRQ_ENABLE)) -void *null_handler(int,void *,struct pt_regs *); +/* chrp */ +volatile struct Hydra *Hydra = NULL; -/* - * disable and enable intrs in software. This is used - * from the non-realtime parts of Linux to disable interrupts. - * The realtime part disables/enables intrs in the hardware. - * -- Cort - */ -unsigned long soft_intr_enable = 1; -void _soft_cli(void) +void i8259_mask_and_ack_irq(int irq_nr) { - soft_intr_enable = 0; + spin_lock(&irq_controller_lock); + cached_irq_mask |= 1 << irq_nr; + if (irq_nr > 7) { + inb(0xA1); /* DUMMY */ + outb(cached_A1,0xA1); + outb(0x62,0x20); /* Specific EOI to cascade */ + /*outb(0x20,0xA0);*/ + outb(0x60|(irq_nr-8), 0xA0); /* specific eoi */ + } else { + inb(0x21); /* DUMMY */ + outb(cached_21,0x21); + /*outb(0x20,0x20);*/ + outb(0x60|irq_nr,0x20); /* specific eoi */ + + } + spin_unlock(&irq_controller_lock); } -void _soft_sti(void) +void pmac_mask_and_ack_irq(int irq_nr) { - soft_intr_enable = 1; - if ( lost_interrupts ) - { - printk("lost_interrupts from _soft_sti() %x\n",lost_interrupts); - fake_interrupt(); - } + spin_lock(&irq_controller_lock); + cached_irq_mask |= 1 << irq_nr; + out_le32(IRQ_ENABLE, ld_le32(IRQ_ENABLE) & ~(1 << irq_nr)); + out_le32(IRQ_ACK, 1U << irq_nr); + spin_unlock(&irq_controller_lock); } -void * -null_handler(int a, void *b, struct pt_regs *regs) +void chrp_mask_and_ack_irq(int irq_nr) { - /*printk("irq.c: null_handler() called. Should not have happened.\n");*/ + /* spinlocks are done by i8259_mask_and_ack() - Cort */ + if (is_8259_irq(irq_nr)) + i8259_mask_and_ack_irq(irq_nr); + openpic_eoi(0); } -void -disable_irq(unsigned int irq_nr) -{ - int s = _disable_interrupts(); - unsigned char mask; -#ifdef CONFIG_PMAC - out_le32(IRQ_ENABLE, ld_le32(IRQ_ENABLE) & ~(1 << irq_nr)); -#else /* CONFIG_PMAC */ - mask = 1 << (irq_nr & 7); - if (irq_nr < 8) - { - cached_21 |= mask; - outb(cached_21,0x21); - } else - { - cached_A1 |= mask; +void i8259_set_irq_mask(int irq_nr) +{ + if (irq_nr > 7) { outb(cached_A1,0xA1); - } -#endif /* CONFIG_PMAC */ - _enable_interrupts(s); + } else { + outb(cached_21,0x21); + } } -void -enable_irq(unsigned int irq_nr) +void pmac_set_irq_mask(int irq_nr) { - int s = _disable_interrupts(); -#ifdef CONFIG_PMAC - unsigned bit = 1U << irq_nr; - - out_le32(IRQ_ENABLE, ld_le32(IRQ_ENABLE) & ~(1 << irq_nr)); - out_le32(IRQ_ENABLE, ld_le32(IRQ_ENABLE) | bit); - + /* this could be being enabled or disabled - so use cached_irq_mask */ + out_le32(IRQ_ENABLE, ~cached_irq_mask /* enable all unmasked */ ); /* * Unfortunately, setting the bit in the enable register * when the device interrupt is already on *doesn't* set * the bit in the flag register or request another interrupt. */ - if ((ld_le32(IRQ_LEVEL) & bit) && !(ld_le32(IRQ_FLAG) & bit)) - lost_interrupts |= bit; -#else /* CONFIG_PMAC */ - if (irq_nr < 8) { - cached_21 &= ~(1 << (irq_nr & 7)); - outb(cached_21,0x21); + if ((ld_le32(IRQ_LEVEL) & (1UL<<irq_nr)) && !(ld_le32(IRQ_FLAG) & (1UL<<irq_nr))) + lost_interrupts |= (1UL<<irq_nr); +} + +void chrp_set_irq_mask(int irq_nr) +{ + if (is_8259_irq(irq_nr)) { + i8259_set_irq_mask(irq_nr); } else { - cached_A1 &= ~(1 << (irq_nr-8 & 7)); - outb(cached_A1,0xA1); - } -#endif /* CONFIG_PMAC */ + /* disable? */ + if ( cached_irq_mask & (1UL<<irq_nr) ) + openpic_disable_irq(irq_to_openpic(irq_nr)); + /* enable */ + else + openpic_disable_irq(irq_to_openpic(irq_nr)); + } +} + +/* + * These have to be protected by the spinlock + * before being called. + */ +static inline void mask_irq(unsigned int irq_nr) +{ + cached_irq_mask |= 1 << irq_nr; + set_irq_mask(irq_nr); +} - _enable_interrupts(s); +static inline void unmask_irq(unsigned int irq_nr) +{ + cached_irq_mask &= ~(1 << irq_nr); + set_irq_mask(irq_nr); } +void disable_irq(unsigned int irq_nr) +{ + unsigned long flags; + + spin_lock_irqsave(&irq_controller_lock, flags); + mask_irq(irq_nr); + spin_unlock_irqrestore(&irq_controller_lock, flags); + synchronize_irq(); +} + +void enable_irq(unsigned int irq_nr) +{ + unsigned long flags; + + spin_lock_irqsave(&irq_controller_lock, flags); + unmask_irq(irq_nr); + spin_unlock_irqrestore(&irq_controller_lock, flags); +} int get_irq_list(char *buf) { @@ -153,7 +192,7 @@ int get_irq_list(char *buf) for (i = 0 ; i < NR_IRQS ; i++) { action = irq_action + i; - if (!action || !action->handler) + if (!action || !action->handler) continue; len += sprintf(buf+len, "%2d: %10u %s", i, kstat.interrupts[i], action->name); @@ -162,152 +201,143 @@ int get_irq_list(char *buf) } len += sprintf(buf+len, "\n"); } -/* - * Linus - should you add NMI counts here ????? - */ #ifdef __SMP_PROF__ len+=sprintf(buf+len, "IPI: %8lu received\n", ipi_count); -#endif +#endif + len += sprintf(buf+len, "99: %10u spurious or short\n", + spurious_interrupts); return len; } -asmlinkage void handle_IRQ(struct pt_regs *regs) +asmlinkage void do_IRQ(struct pt_regs *regs) { int irq; - unsigned bits; + unsigned long bits; struct irqaction *action; int cpu = smp_processor_id(); + int status; hardirq_enter(cpu); -#ifdef CONFIG_PMAC - bits = ld_le32(IRQ_FLAG) | lost_interrupts; - lost_interrupts = 0; - - for (irq = NR_IRQS; irq >= 0; --irq) - if (bits & (1U << irq)) - break; -#else /* CONFIG_PMAC */ -#if 1 - if ( lost_interrupts ) - { - irq = ffz(~lost_interrupts); - lost_interrupts &= ~irq; - goto retry; - } - outb(0x0C, 0x20); - irq = inb(0x20) & 7; - if (irq == 2) + + /* + * I'll put this ugly mess of code into a function + * such as get_pending_irq() or some such clear thing + * so we don't have a switch in the irq code and + * the chrp code is merged a bit with the prep. + * -- Cort + */ + switch ( _machine ) { -retry_cascade: - outb(0x0C, 0xA0); - irq = inb(0xA0); - /* if no intr left */ - if ( !(irq & 128 ) ) + case _MACH_Pmac: + bits = ld_le32(IRQ_FLAG) | lost_interrupts; + lost_interrupts = 0; + for (irq = NR_IRQS - 1; irq >= 0; --irq) + if (bits & (1U << irq)) + break; + break; + case _MACH_chrp: + irq = openpic_irq(0); + if (irq == IRQ_8259_CASCADE) + { + /* + * This magic address generates a PCI IACK cycle. + * + * This should go in the above mask/ack code soon. -- Cort + */ + irq = (*(volatile unsigned char *)0xfec80000) & 0x0f; + } + else if (irq >= 64) + { + /* + * OpenPIC interrupts >64 will be used for other purposes + * like interprocessor interrupts and hardware errors + */ +#ifdef OPENPIC_DEBUG + printk("OpenPIC interrupt %d\n", irq); +#endif + if (irq==99) + spurious_interrupts++; + } + else { + /* + * Here we should process IPI timer + * for now the interrupt is dismissed. + */ goto out; - irq = (irq&7) + 8; - } -retry: + } + break; + case _MACH_IBM: + case _MACH_Motorola: +#if 1 + outb(0x0C, 0x20); + irq = inb(0x20) & 7; + if (irq == 2) + { +retry_cascade: + outb(0x0C, 0xA0); + irq = inb(0xA0); + /* if no intr left */ + if ( !(irq & 128 ) ) + goto out; + irq = (irq&7) + 8; + } + bits = 1UL << irq; #else - /* get the irr from the intr controller */ - outb(0x0A, 0x20); - bits = inb(0x20); - /* handle cascade */ - if ( bits ) - { - bits &= 4; - outb(0x0A, 0xA0); - bits = inb(0xA0)<<8; + /* + * get the isr from the intr controller since + * the bit in the irr has been cleared + */ + outb(0x0a, 0x20); + bits = inb(0x20)&0xff; + /* handle cascade */ + if ( bits & 4 ) + { + bits &= ~4UL; + outb(0x0a, 0xA0); + bits |= inb(0xA0)<<8; + } + /* ignore masked irqs */ + bits &= ~cached_irq_mask; +#endif + break; } - /* get lost interrupts */ - bits |= lost_interrupts; - /* save intrs that are masked out */ - lost_interrupts = bits & cached_irq_mask; - /* get rid of intrs being masked */ - bits &= ~cached_irq_mask; - /* non-specifc eoi */ - outb(0x20,0x20); - if ( bits & 0xff00 ) - outb(0x20,0xA0); - - printk("bits %04X lost %04X mask %04x\n", - bits, lost_interrupts,cached_irq_mask); - - for (irq = NR_IRQS; irq >= 0; --irq) - if (bits & (1U << irq)) - break; -#endif -#endif /* CONFIG_PMAC */ if (irq < 0) { - printk("Bogus interrupt from PC = %lx, irq %d\n",regs->nip,irq); + printk("Bogus interrupt from PC = %lx\n", regs->nip); goto out; } -#ifdef CONFIG_PMAC - out_le32(IRQ_ACK, 1U << irq); -#else /* CONFIG_PMAC */ - /* mask out the irq while handling it */ - disable_irq(irq); - /* - * send eoi to interrupt controller right away or lower - * priority intrs would be ignored even if with intrs enabled - */ - if (irq > 7) - { - outb(0xE0|(irq-8), 0xA0); - outb(0xE2, 0x20); - } else - { - outb(0xE0|irq, 0x20); - } -#endif /* !CONFIG_PMAC */ + mask_and_ack_irq(irq); - /* - * now that we've acked the irq, if intrs are disabled in software - * we're in the real-time system and non-rt linux has disabled them - * so we just queue it up and return -- Cort - */ - if ( ! soft_intr_enable ) - { - lost_interrupts |= 1UL << irq; - /* can't printk - kernel expects intrs off! */ - /*printk("irq %d while intrs soft disabled\n", irq);*/ - goto out; - } - - action = irq + irq_action; + status = 0; + action = irq_action + irq; kstat.interrupts[irq]++; - if (action->handler) { - action->handler(irq, action->dev_id, regs); - _disable_interrupts(); /* in case the handler turned them on */ + if ( action && action->handler) + { + if (!(action->flags & SA_INTERRUPT)) + __sti(); + status |= action->flags; + action->handler(irq, action->dev_id, regs); + /*if (status & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq);*/ + __cli(); /* in case the handler turned them on */ + spin_lock(&irq_controller_lock); + unmask_irq(irq); + spin_unlock(&irq_controller_lock); } else { + if ( irq == 7 ) /* i8259 gives us irq 7 on 'short' intrs */ + spurious_interrupts++; disable_irq( irq ); } -#ifdef CONFIG_PREP - /* re-enable if the interrupt was good and isn't one-shot */ - if ( action->handler && !(action->flags & SA_ONESHOT) ) - enable_irq(irq); + /* make sure we don't miss any cascade intrs due to eoi-ing irq 2 */ - if ( irq > 7 ) + if ( is_prep && (irq > 7) ) goto retry_cascade; -#endif - - hardirq_exit(cpu); - /* - * This should be conditional: we should really get - * a return code from the irq handler to tell us - * whether the handler wants us to do software bottom - * half handling or not.. - */ - if (1) - if (bh_active & bh_mask) - do_bottom_half(); - return; + /* do_bottom_half is called if necessary from int_return in head.S */ out: hardirq_exit(cpu); - } int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), @@ -315,13 +345,13 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *) { struct irqaction * action; unsigned long flags; - -#ifdef SHOW_IRQ + +#ifdef SHOW_IRQ printk("request_irq(): irq %d handler %08x name %s dev_id %04x\n", irq,handler,devname,dev_id); #endif /* SHOW_IRQ */ - - if (irq > NR_IRQS) + + if (irq >= NR_IRQS) return -EINVAL; action = irq + irq_action; if (action->handler) @@ -339,17 +369,16 @@ int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *) restore_flags(flags); return 0; } - void free_irq(unsigned int irq, void *dev_id) { struct irqaction * action = irq + irq_action; unsigned long flags; -#ifdef SHOW_IRQ +#ifdef SHOW_IRQ printk("free_irq(): irq %d dev_id %04x\n", irq, dev_id); #endif /* SHOW_IRQ */ - - if (irq > NR_IRQS) { + + if (irq >= NR_IRQS) { printk("Trying to free IRQ%d\n",irq); return; } @@ -370,114 +399,119 @@ void free_irq(unsigned int irq, void *dev_id) unsigned long probe_irq_on (void) { - unsigned int i, irqs = 0, irqmask; - unsigned long delay; - - /* first, snaffle up any unassigned irqs */ - for (i = 15; i > 0; i--) { - if (!request_irq(i, null_handler, SA_ONESHOT, "probe", NULL)) { - enable_irq(i); - irqs |= (1 << i); - } - } - - /* wait for spurious interrupts to mask themselves out again */ - for (delay = jiffies + 2; delay > jiffies; ); /* min 10ms delay */ - - /* now filter out any obviously spurious interrupts */ - irqmask = (((unsigned int)cached_A1)<<8) | (unsigned int)cached_21; - for (i = 15; i > 0; i--) { - if (irqs & (1 << i) & irqmask) { - irqs ^= (1 << i); - free_irq(i, NULL); - } - } -#ifdef DEBUG - printk("probe_irq_on: irqs=0x%04x irqmask=0x%04x\n", irqs, irqmask); -#endif - return irqs; + return 0; } int probe_irq_off (unsigned long irqs) { - unsigned int i, irqmask; - - irqmask = (((unsigned int)cached_A1)<<8) | (unsigned int)cached_21; - for (i = 15; i > 0; i--) { - if (irqs & (1 << i)) { - free_irq(i, NULL); - } - } -#ifdef SHOW_IRQ - printk("probe_irq_off: irqs=0x%04x irqmask=0x%04x\n", irqs, irqmask); -#endif - irqs &= irqmask; - if (!irqs) - return 0; - i = ffz(~irqs); - if (irqs != (irqs & (1 << i))) - i = -i; - return i; + return 0; } -void init_IRQ(void) +__initfunc(static void i8259_init(void)) { -#ifdef CONFIG_PMAC - extern void xmon_irq(int, void *, struct pt_regs *); - - *IRQ_ENABLE = 0; - request_irq(KEYBOARD_IRQ, xmon_irq, 0, "NMI", 0); -#else /* CONFIG_PMAC */ - /* Initialize interrupt controllers */ + /* init master interrupt controller */ outb(0x11, 0x20); /* Start init sequence */ outb(0x40, 0x21); /* Vector base */ -#if 1 - outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */ -#else + /*outb(0x04, 0x21);*/ /* edge tiggered, Cascade (slave) on IRQ2 */ outb(0x0C, 0x21); /* level triggered, Cascade (slave) on IRQ2 */ -#endif - outb(0x01, 0x21); /* Select 8086 mode */ outb(0xFF, 0x21); /* Mask all */ - + + /* init slave interrupt controller */ outb(0x11, 0xA0); /* Start init sequence */ outb(0x48, 0xA1); /* Vector base */ -#if 1 - outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */ -#else + /*outb(0x02, 0xA1);*/ /* edge triggered, Cascade (slave) on IRQ2 */ outb(0x0A, 0x21); /* level triggered, Cascade (slave) on IRQ2 */ -#endif outb(0x01, 0xA1); /* Select 8086 mode */ outb(0xFF, 0xA1); /* Mask all */ - - /* - * Program level mode for irq's that 'can' be level triggered. - * This does not effect irq's 0,1,2 and 8 since they must be - * edge triggered. This is not the PIC controller. The default - * here is for edge trigger mode. -- Cort - */ -#if 0 - outb(0xf8, 0x4d0); /* level triggered */ - outb(0xff, 0x4d1); -#endif -#if 0 - outb(0x00, 0x4D0); /* All edge triggered */ - outb(0xCF, 0x4D1); /* Trigger mode */ -#endif outb(cached_A1, 0xA1); outb(cached_21, 0x21); - if (request_irq(2, null_handler, SA_INTERRUPT, "cascade", NULL)) - printk("Unable to get IRQ2 for cascade\n"); + if (request_irq(2, no_action, SA_INTERRUPT, "cascade", NULL) != 0) + panic("Could not allocate cascade IRQ!"); enable_irq(2); /* Enable cascade interrupt */ - -#define TIMER0_COUNT 0x40 -#define TIMER_CONTROL 0x43 - /* set timer to periodic mode */ - outb_p(0x34,TIMER_CONTROL); /* binary, mode 2, LSB/MSB, ch 0 */ - /* set the clock to ~100 Hz */ - outb_p(LATCH & 0xff , TIMER0_COUNT); /* LSB */ - outb(LATCH >> 8 , TIMER0_COUNT); /* MSB */ - - route_pci_interrupts(); -#endif /* CONFIG_PMAC */ +} + +__initfunc(void init_IRQ(void)) +{ + extern void xmon_irq(int, void *, struct pt_regs *); + + switch (_machine) + { + case _MACH_Pmac: + mask_and_ack_irq = pmac_mask_and_ack_irq; + set_irq_mask = pmac_set_irq_mask; + + *IRQ_ENABLE = 0; +#ifdef CONFIG_XMON + request_irq(KEYBOARD_IRQ, xmon_irq, 0, "NMI", 0); +#endif /* CONFIG_XMON */ + break; + case _MACH_Motorola: + case _MACH_IBM: + mask_and_ack_irq = i8259_mask_and_ack_irq; + set_irq_mask = i8259_set_irq_mask; + + i8259_init(); + route_pci_interrupts(); + /* + * According to the Carolina spec from ibm irq's 0,1,2, and 8 + * must be edge triggered. Also, the pci intrs must be level + * triggered and _only_ isa intrs can be level sensitive + * which are 3-7,9-12,14-15. 13 is special - it can be level. + * + * power on default is 0's in both regs - all edge. + * + * These edge/level control regs allow edge/level status + * to be decided on a irq basis instead of on a PIC basis. + * It's still pretty ugly. + * - Cort + */ + { + unsigned char irq_mode1 = 0, irq_mode2 = 0; + irq_mode1 = 0; /* to get rid of compiler warnings */ + /* + * On Carolina, irq 15 and 13 must be level (scsi/ide/net). + */ + if ( _machine == _MACH_IBM ) + irq_mode2 |= 0xa0; + /* + * Sound on the Powerstack reportedly needs to be edge triggered + */ + if ( _machine == _MACH_Motorola ) + { + /*irq_mode2 &= ~0x04L; + outb( irq_mode1 , 0x4d0 ); + outb( irq_mode2 , 0x4d1 );*/ + } + + } + break; + case _MACH_chrp: + mask_and_ack_irq = chrp_mask_and_ack_irq; + set_irq_mask = chrp_set_irq_mask; + if ((Hydra = find_hydra())) { + printk("Hydra Mac I/O at %p\n", Hydra); + out_le32(&Hydra->Feature_Control, HYDRA_FC_SCC_CELL_EN | + HYDRA_FC_SCSI_CELL_EN | + HYDRA_FC_SCCA_ENABLE | + HYDRA_FC_SCCB_ENABLE | + HYDRA_FC_ARB_BYPASS | + HYDRA_FC_MPIC_ENABLE | + HYDRA_FC_SLOW_SCC_PCLK | + HYDRA_FC_MPIC_IS_MASTER); + OpenPIC = (volatile struct OpenPIC *)&Hydra->OpenPIC; + } else if (!OpenPIC /* && find_xxx */) { + printk("Unknown openpic implementation\n"); + /* other OpenPIC implementations */ + /* ... */ + } + if (OpenPIC) + openpic_init(); + else + panic("No OpenPIC found"); + if (Hydra) + hydra_post_openpic_init(); + i8259_init(); + break; + } } diff --git a/arch/ppc/kernel/misc.S b/arch/ppc/kernel/misc.S index 430cd7a5d..9c6d013b6 100644 --- a/arch/ppc/kernel/misc.S +++ b/arch/ppc/kernel/misc.S @@ -64,6 +64,7 @@ _GLOBAL(_hard_sti) mtmsr r3 /* Update machine state */ blr +#if 0 /* * Restore 'flags' * __restore_flags(long val) @@ -79,11 +80,13 @@ _GLOBAL(__restore_flags) mtmsr r3 isync blr - +#endif + /* * We were about to enable interrupts but we have to simulate * some interrupts that were lost by enable_irq first. */ + .globl do_lost_interrupts do_lost_interrupts: stwu r1,-16(r1) mflr r0 @@ -243,7 +246,6 @@ _GLOBAL(_outsl) bdnz 00b blr -#ifdef CONFIG_PMAC _GLOBAL(ide_insw) mtctr r5 subi r4,r4,2 @@ -259,7 +261,6 @@ _GLOBAL(ide_outsw) sthx r5,0,r3 bdnz 00b blr -#endif /* * Extended precision shifts @@ -298,7 +299,7 @@ _GLOBAL(abs) _GLOBAL(_get_SP) mr r3,r1 /* Close enough */ blr - + _GLOBAL(_get_PVR) mfspr r3,PVR blr @@ -308,6 +309,7 @@ cvt_fd: lfs 0,0(r3) stfd 0,0(r4) blr + /* * Fetch the current SR register * get_SR(int index) @@ -317,7 +319,6 @@ _GLOBAL(get_SR) mr r3,r4 blr - _GLOBAL(cvt_df) cvt_df: lfd 0,0(r3) diff --git a/arch/ppc/kernel/mk_defs.c b/arch/ppc/kernel/mk_defs.c index 3f404a37a..80f10c59e 100644 --- a/arch/ppc/kernel/mk_defs.c +++ b/arch/ppc/kernel/mk_defs.c @@ -29,7 +29,6 @@ void main(void) { - /*DEFINE(KERNELBASE, KERNELBASE);*/ DEFINE(STATE, offsetof(struct task_struct, state)); DEFINE(NEXT_TASK, offsetof(struct task_struct, next_task)); DEFINE(COUNTER, offsetof(struct task_struct, counter)); @@ -37,54 +36,20 @@ main(void) DEFINE(SIGNAL, offsetof(struct task_struct, signal)); DEFINE(TSS, offsetof(struct task_struct, tss)); DEFINE(KSP, offsetof(struct thread_struct, ksp)); - DEFINE(PG_TABLES, offsetof(struct thread_struct, pg_tables)); -#ifdef CONFIG_PMAC - DEFINE(LAST_PC, offsetof(struct thread_struct, last_pc)); - DEFINE(USER_STACK, offsetof(struct thread_struct, user_stack)); -#endif + /*DEFINE(PG_TABLES, offsetof(struct thread_struct, pg_tables));*/ + DEFINE(MM, offsetof(struct task_struct, mm)); + DEFINE(PGD, offsetof(struct mm_struct, pgd)); DEFINE(LAST_SYSCALL, offsetof(struct thread_struct, last_syscall)); DEFINE(PT_REGS, offsetof(struct thread_struct, regs)); DEFINE(PF_TRACESYS, PF_TRACESYS); DEFINE(TASK_FLAGS, offsetof(struct task_struct, flags)); DEFINE(TSS_FPR0, offsetof(struct thread_struct, fpr[0])); -#if 0 - DEFINE(TSS_FPR1, offsetof(struct thread_struct, fpr[1])); - DEFINE(TSS_FPR2, offsetof(struct thread_struct, fpr[2])); - DEFINE(TSS_FPR3, offsetof(struct thread_struct, fpr[3])); - DEFINE(TSS_FPR4, offsetof(struct thread_struct, fpr[4])); - DEFINE(TSS_FPR5, offsetof(struct thread_struct, fpr[5])); - DEFINE(TSS_FPR6, offsetof(struct thread_struct, fpr[6])); - DEFINE(TSS_FPR7, offsetof(struct thread_struct, fpr[7])); - DEFINE(TSS_FPR8, offsetof(struct thread_struct, fpr[8])); - DEFINE(TSS_FPR9, offsetof(struct thread_struct, fpr[9])); - DEFINE(TSS_FPR10, offsetof(struct thread_struct, fpr[10])); - DEFINE(TSS_FPR11, offsetof(struct thread_struct, fpr[11])); - DEFINE(TSS_FPR12, offsetof(struct thread_struct, fpr[12])); - DEFINE(TSS_FPR13, offsetof(struct thread_struct, fpr[13])); - DEFINE(TSS_FPR14, offsetof(struct thread_struct, fpr[14])); - DEFINE(TSS_FPR15, offsetof(struct thread_struct, fpr[15])); - DEFINE(TSS_FPR16, offsetof(struct thread_struct, fpr[16])); - DEFINE(TSS_FPR17, offsetof(struct thread_struct, fpr[17])); - DEFINE(TSS_FPR18, offsetof(struct thread_struct, fpr[18])); - DEFINE(TSS_FPR19, offsetof(struct thread_struct, fpr[19])); - DEFINE(TSS_FPR20, offsetof(struct thread_struct, fpr[20])); - DEFINE(TSS_FPR21, offsetof(struct thread_struct, fpr[21])); - DEFINE(TSS_FPR22, offsetof(struct thread_struct, fpr[22])); - DEFINE(TSS_FPR23, offsetof(struct thread_struct, fpr[23])); - DEFINE(TSS_FPR24, offsetof(struct thread_struct, fpr[24])); - DEFINE(TSS_FPR25, offsetof(struct thread_struct, fpr[25])); - DEFINE(TSS_FPR26, offsetof(struct thread_struct, fpr[26])); - DEFINE(TSS_FPR27, offsetof(struct thread_struct, fpr[27])); - DEFINE(TSS_FPR28, offsetof(struct thread_struct, fpr[28])); - DEFINE(TSS_FPR29, offsetof(struct thread_struct, fpr[29])); - DEFINE(TSS_FPR30, offsetof(struct thread_struct, fpr[30])); - DEFINE(TSS_FPR31, offsetof(struct thread_struct, fpr[31])); -#endif DEFINE(TSS_FPSCR, offsetof(struct thread_struct, fpscr)); /* Interrupt register frame */ DEFINE(TASK_UNION_SIZE, sizeof(union task_union)); DEFINE(STACK_FRAME_OVERHEAD, STACK_FRAME_OVERHEAD); DEFINE(INT_FRAME_SIZE, STACK_FRAME_OVERHEAD + sizeof(struct pt_regs)); + /* in fact we only use gpr0 - gpr9 and gpr20 - gpr23 */ DEFINE(GPR0, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[0])); DEFINE(GPR1, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[1])); DEFINE(GPR2, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, gpr[2])); diff --git a/arch/ppc/kernel/openpic.c b/arch/ppc/kernel/openpic.c new file mode 100644 index 000000000..7eb5e5aa9 --- /dev/null +++ b/arch/ppc/kernel/openpic.c @@ -0,0 +1,546 @@ +/* + * arch/ppc/kernel/openpic.c -- OpenPIC Interrupt Handling + * + * Copyright (C) 1997 Geert Uytterhoeven + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + + +/* + * Note: Interprocessor Interrupt (IPI) and Timer support is incomplete + */ + + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/openpic.h> +#include <asm/ptrace.h> +#include <asm/signal.h> +#include <asm/io.h> +#include <asm/irq.h> + + +#define REGISTER_DEBUG +#undef REGISTER_DEBUG + + +#define VEC_TIMER 0x40 /* and up */ +#define VEC_IPI 0x50 /* and up */ +#define VEC_SOURCE 0x10 /* and up */ +#define VEC_SPURIOUS 99 + + +volatile struct OpenPIC *OpenPIC; + +static u_int Version; +static u_int NumProcessors; +static u_int NumSources; +static u_int VendorID; +static u_int DeviceID; +static u_int Stepping; +static u_int TimerFrequency; + + + /* + * Accesses to the current processor's registers + */ + +#ifndef __powerpc__ +#define THIS_CPU Private +#define CHECK_THIS_CPU do {} while (0) +#else +#define THIS_CPU Processor[cpu] +#define CHECK_THIS_CPU check_arg_cpu(cpu) +#endif + + + /* + * Sanity checks + */ + +#if 1 +#define check_arg_ipi(ipi) \ + if (ipi < 0 || ipi >= OPENPIC_NUM_IPI) \ + printk("openpic.c:%d: illegal ipi %d\n", __LINE__, ipi); +#define check_arg_timer(timer) \ + if (timer < 0 || timer >= OPENPIC_NUM_TIMERS) \ + printk("openpic.c:%d: illegal timer %d\n", __LINE__, timer); +#define check_arg_vec(vec) \ + if (vec < 0 || vec >= OPENPIC_NUM_VECTORS) \ + printk("openpic.c:%d: illegal vector %d\n", __LINE__, vec); +#define check_arg_pri(pri) \ + if (pri < 0 || pri >= OPENPIC_NUM_PRI) \ + printk("openpic.c:%d: illegal priority %d\n", __LINE__, pri); +#define check_arg_irq(irq) \ + if (irq < 0 || irq >= NumSources) \ + printk("openpic.c:%d: illegal irq %d\n", __LINE__, irq); +#define check_arg_cpu(cpu) \ + if (cpu < 0 || cpu >= NumProcessors) \ + printk("openpic.c:%d: illegal cpu %d\n", __LINE__, cpu); +#else +#define check_arg_ipi(ipi) do {} while (0) +#define check_arg_timer(timer) do {} while (0) +#define check_arg_vec(vec) do {} while (0) +#define check_arg_pri(pri) do {} while (0) +#define check_arg_irq(irq) do {} while (0) +#define check_arg_cpu(cpu) do {} while (0) +#endif + + + /* + * Dummy interrupt handler + */ + +static void no_action(int ir1, void *dev, struct pt_regs *regs) +{} + + + /* + * I/O functions + */ + +#ifdef __i386__ +static inline u_int ld_le32(volatile u_int *addr) +{ + return *addr; +} + +static inline void out_le32(volatile u_int *addr, u_int val) +{ + *addr = val; +} +#endif + +static inline u_int openpic_read(volatile u_int *addr) +{ + u_int val; + + val = ld_le32(addr); +#ifdef REGISTER_DEBUG + printk("openpic_read(0x%08x) = 0x%08x\n", (u_int)addr, val); +#endif + return val; +} + +static inline void openpic_write(volatile u_int *addr, u_int val) +{ +#ifdef REGISTER_DEBUG + printk("openpic_write(0x%08x, 0x%08x)\n", (u_int)addr, val); +#endif + out_le32(addr, val); +} + + +static inline u_int openpic_readfield(volatile u_int *addr, u_int mask) +{ + u_int val = openpic_read(addr); + return val & mask; +} + +static inline void openpic_writefield(volatile u_int *addr, u_int mask, + u_int field) +{ + u_int val = openpic_read(addr); + openpic_write(addr, (val & ~mask) | (field & mask)); +} + +static inline void openpic_clearfield(volatile u_int *addr, u_int mask) +{ + openpic_writefield(addr, mask, 0); +} + +static inline void openpic_setfield(volatile u_int *addr, u_int mask) +{ + openpic_writefield(addr, mask, mask); +} + + + /* + * Update a Vector/Priority register in a safe manner. The interrupt will + * be disabled. + */ + +static void openpic_safe_writefield(volatile u_int *addr, u_int mask, + u_int field) +{ + openpic_setfield(addr, OPENPIC_MASK); + /* wait until it's not in use */ + while (openpic_read(addr) & OPENPIC_ACTIVITY); + openpic_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK); +} + + +/* -------- Global Operations ---------------------------------------------- */ + + + /* + * Initialize the OpenPIC + */ + +void openpic_init(void) +{ + u_int t, i; + const char *version, *vendor, *device; + + if (!OpenPIC) { + printk("No OpenPIC present\n"); + return; + } + + t = openpic_read(&OpenPIC->Global.Feature_Reporting0); + Version = t & OPENPIC_FEATURE_VERSION_MASK; + NumProcessors = ((t & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >> + OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1; + NumSources = ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >> + OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1; + + switch (Version) { + case 1: + version = "1.0"; + break; + case 2: + version = "1.2"; + break; + default: + version = "?.?"; + break; + } + printk("OpenPIC Version %s (%d CPUs and %d IRQ sources) at %p\n", version, + NumProcessors, NumSources, OpenPIC); + + t = openpic_read(&OpenPIC->Global.Vendor_Identification); + VendorID = t & OPENPIC_VENDOR_ID_VENDOR_ID_MASK; + DeviceID = (t & OPENPIC_VENDOR_ID_DEVICE_ID_MASK) >> + OPENPIC_VENDOR_ID_DEVICE_ID_SHIFT; + Stepping = (t & OPENPIC_VENDOR_ID_STEPPING_MASK) >> + OPENPIC_VENDOR_ID_STEPPING_SHIFT; + switch (VendorID) { + case OPENPIC_VENDOR_ID_APPLE: + vendor = "Apple"; + break; + default: + vendor = "Unknown"; + break; + } + switch (DeviceID) { + case OPENPIC_DEVICE_ID_APPLE_HYDRA: + device = "Hydra"; + break; + default: + device = "Unknown"; + break; + } + printk("OpenPIC Vendor %d (%s), Device %d (%s), Stepping %d\n", VendorID, + vendor, DeviceID, device, Stepping); + + TimerFrequency = openpic_read(&OpenPIC->Global.Timer_Frequency); + printk("OpenPIC timer frequency is "); + if (TimerFrequency) + printk("%d Hz\n", TimerFrequency); + else + printk("not set\n"); + + /* Initialize timer interrupts */ + for (i = 0; i < OPENPIC_NUM_TIMERS; i++) { + /* Disabled, Priority 0 */ + openpic_inittimer(i, 0, VEC_TIMER+i); + /* No processor */ + openpic_maptimer(i, 0); + } + + /* Initialize IPI interrupts */ + for (i = 0; i < OPENPIC_NUM_IPI; i++) { + /* Disabled, Priority 0 */ + openpic_initipi(i, 0, VEC_IPI+i); + } + + /* Initialize external interrupts */ + /* SIOint (8259 cascade) is special */ + openpic_initirq(0, 8, VEC_SOURCE, 1, 1); /* 0,1 gives interrupt storm */ + /* Processor 0 */ + openpic_mapirq(0, 1<<0); + for (i = 1; i < NumSources; i++) { + /* Enabled, Priority 8 */ + openpic_initirq(i, 8, VEC_SOURCE+i, 0, 1); + /* Processor 0 */ + openpic_mapirq(i, 1<<0); + } + + /* Initialize the spurious interrupt */ + openpic_set_spurious(VEC_SPURIOUS); + + if (request_irq(IRQ_8259_CASCADE, no_action, SA_INTERRUPT, + "OpenPIC cascade", NULL)) + printk("Unable to get OpenPIC IRQ 0 for cascade\n"); + openpic_set_priority(0, 0); + openpic_disable_8259_pass_through(); +} + + + /* + * Reset the OpenPIC + */ + +void openpic_reset(void) +{ + openpic_setfield(&OpenPIC->Global.Global_Configuration0, + OPENPIC_CONFIG_RESET); +} + + + /* + * Enable/disable 8259 Pass Through Mode + */ + +void openpic_enable_8259_pass_through(void) +{ + openpic_clearfield(&OpenPIC->Global.Global_Configuration0, + OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); +} + +void openpic_disable_8259_pass_through(void) +{ + openpic_setfield(&OpenPIC->Global.Global_Configuration0, + OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); +} + + +#ifndef __i386__ + /* + * Find out the current interrupt + */ + +u_int openpic_irq(u_int cpu) +{ + u_int vec; + + check_arg_cpu(cpu); + vec = openpic_readfield(&OpenPIC->THIS_CPU.Interrupt_Acknowledge, + OPENPIC_VECTOR_MASK); +#if 0 +if (vec != 22 /* SCSI */) +printk("++openpic_irq: %d\n", vec); +#endif + return vec; +} +#endif + + + /* + * Signal end of interrupt (EOI) processing + */ + +#ifndef __powerpc__ +void openpic_eoi(void) +#else +void openpic_eoi(u_int cpu) +#endif +{ +#if 0 +printk("++openpic_eoi:\n"); +#endif + check_arg_cpu(cpu); + openpic_write(&OpenPIC->THIS_CPU.EOI, 0); +} + + + /* + * Get/set the current task priority + */ + +#ifndef __powerpc__ +u_int openpic_get_priority(void) +#else +u_int openpic_get_priority(u_int cpu) +#endif +{ + CHECK_THIS_CPU; + return openpic_readfield(&OpenPIC->THIS_CPU.Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK); +} + +#ifndef __powerpc__ +void openpic_set_priority(u_int pri) +#else +void openpic_set_priority(u_int cpu, u_int pri) +#endif +{ + CHECK_THIS_CPU; + check_arg_pri(pri); + openpic_writefield(&OpenPIC->THIS_CPU.Current_Task_Priority, + OPENPIC_CURRENT_TASK_PRIORITY_MASK, pri); +} + + /* + * Get/set the spurious vector + */ + +u_int openpic_get_spurious(void) +{ + return openpic_readfield(&OpenPIC->Global.Spurious_Vector, + OPENPIC_VECTOR_MASK); +} + +void openpic_set_spurious(u_int vec) +{ + check_arg_vec(vec); + openpic_writefield(&OpenPIC->Global.Spurious_Vector, OPENPIC_VECTOR_MASK, + vec); +} + + + /* + * Initialize one or more CPUs + */ + +void openpic_init_processor(u_int cpumask) +{ + openpic_write(&OpenPIC->Global.Processor_Initialization, cpumask); +} + + +/* -------- Interprocessor Interrupts -------------------------------------- */ + + + /* + * Initialize an interprocessor interrupt (and disable it) + * + * ipi: OpenPIC interprocessor interrupt number + * pri: interrupt source priority + * vec: the vector it will produce + */ + +void openpic_initipi(u_int ipi, u_int pri, u_int vec) +{ + check_arg_timer(ipi); + check_arg_pri(pri); + check_arg_vec(vec); + openpic_safe_writefield(&OpenPIC->Global.IPI_Vector_Priority(ipi), + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, + (pri << OPENPIC_PRIORITY_SHIFT) | vec); +} + + + /* + * Send an IPI to one or more CPUs + */ + +#ifndef __powerpc__ +void openpic_cause_IPI(u_int ipi, u_int cpumask) +#else +void openpic_cause_IPI(u_int cpu, u_int ipi, u_int cpumask) +#endif +{ + CHECK_THIS_CPU; + check_arg_ipi(ipi); + openpic_write(&OpenPIC->THIS_CPU.IPI_Dispatch(ipi), cpumask); +} + + +/* -------- Timer Interrupts ----------------------------------------------- */ + + + /* + * Initialize a timer interrupt (and disable it) + * + * timer: OpenPIC timer number + * pri: interrupt source priority + * vec: the vector it will produce + */ + +void openpic_inittimer(u_int timer, u_int pri, u_int vec) +{ + check_arg_timer(timer); + check_arg_pri(pri); + check_arg_vec(vec); + openpic_safe_writefield(&OpenPIC->Global.Timer[timer].Vector_Priority, + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, + (pri << OPENPIC_PRIORITY_SHIFT) | vec); +} + + + /* + * Map a timer interrupt to one or more CPUs + */ + +void openpic_maptimer(u_int timer, u_int cpumask) +{ + check_arg_timer(timer); + openpic_write(&OpenPIC->Global.Timer[timer].Destination, cpumask); +} + + +/* -------- Interrupt Sources ---------------------------------------------- */ + + + /* + * Enable/disable an interrupt source + */ + +void openpic_enable_irq(u_int irq) +{ + check_arg_irq(irq); + openpic_clearfield(&OpenPIC->Source[irq].Vector_Priority, OPENPIC_MASK); +} + +void openpic_disable_irq(u_int irq) +{ + check_arg_irq(irq); + openpic_setfield(&OpenPIC->Source[irq].Vector_Priority, OPENPIC_MASK); +} + + + /* + * Initialize an interrupt source (and disable it!) + * + * irq: OpenPIC interrupt number + * pri: interrupt source priority + * vec: the vector it will produce + * pol: polarity (1 for positive, 0 for negative) + * sense: 1 for level, 0 for edge + */ + +void openpic_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense) +{ + check_arg_irq(irq); + check_arg_pri(pri); + check_arg_vec(vec); + openpic_safe_writefield(&OpenPIC->Source[irq].Vector_Priority, + OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK | + OPENPIC_SENSE_POLARITY | OPENPIC_SENSE_LEVEL, + (pri << OPENPIC_PRIORITY_SHIFT) | vec | + (pol ? OPENPIC_SENSE_POLARITY : 0) | + (sense ? OPENPIC_SENSE_LEVEL : 0)); +} + + + /* + * Map an interrupt source to one or more CPUs + */ + +void openpic_mapirq(u_int irq, u_int cpumask) +{ + check_arg_irq(irq); + openpic_write(&OpenPIC->Source[irq].Destination, cpumask); +} + + + /* + * Set the sense for an interrupt source (and disable it!) + * + * sense: 1 for level, 0 for edge + */ + +void openpic_set_sense(u_int irq, int sense) +{ + check_arg_irq(irq); + openpic_safe_writefield(&OpenPIC->Source[irq].Vector_Priority, + OPENPIC_SENSE_LEVEL, + (sense ? OPENPIC_SENSE_LEVEL : 0)); +} + + diff --git a/arch/ppc/kernel/pci-bridge.c b/arch/ppc/kernel/pci-bridge.c new file mode 100644 index 000000000..0e4420340 --- /dev/null +++ b/arch/ppc/kernel/pci-bridge.c @@ -0,0 +1,428 @@ +/* + * Support for PCI bridges found on Power Macintoshes. + * At present the "bandit" and "chaos" bridges are supported. + * Fortunately you access configuration space in the same + * way with either bridge. + * + * Copyright (C) 1997 Paul Mackerras (paulus@cs.anu.edu.au) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/bios32.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/init.h> +#include <asm/io.h> +#include <asm/prom.h> +#include <asm/pci-bridge.h> + +struct bridge_data { + volatile unsigned int *cfg_addr; + volatile unsigned char *cfg_data; + void *io_base; + int bus_number; + int max_bus; + struct bridge_data *next; + struct device_node *node; +}; + +static struct bridge_data **bridges, *bridge_list; +static int max_bus; + +static void add_bridges(struct device_node *dev, unsigned long *mem_ptr); + +/* + * Magic constants for enabling cache coherency in the bandit/PSX bridge. + */ +#define APPLE_VENDID 0x106b +#define BANDIT_DEVID 1 +#define BANDIT_REVID 3 + +#define BANDIT_DEVNUM 11 +#define BANDIT_MAGIC 0x50 +#define BANDIT_COHERENT 0x40 + +/* + * For a bandit bridge, turn on cache coherency if necessary. + * N.B. we can't use pcibios_*_config_* here because bridges[] + * is not initialized yet. + */ +static void init_bandit(struct bridge_data *bp) +{ + unsigned int vendev, magic; + int rev; + + /* read the word at offset 0 in config space for device 11 */ + out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + PCI_VENDOR_ID); + udelay(2); + vendev = in_le32((volatile unsigned int *)bp->cfg_data); + if (vendev != (BANDIT_DEVID << 16) + APPLE_VENDID) { + printk(KERN_WARNING "bandit isn't? (%x)\n", vendev); + return; + } + + /* read the revision id */ + out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + PCI_REVISION_ID); + udelay(2); + rev = in_8(bp->cfg_data); + if (rev != BANDIT_REVID) + printk(KERN_WARNING "Unknown revision %d for bandit at %p\n", + rev, bp->io_base); + + /* read the word at offset 0x50 */ + out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + BANDIT_MAGIC); + udelay(2); + magic = in_le32((volatile unsigned int *)bp->cfg_data); + if ((magic & BANDIT_COHERENT) != 0) + return; + magic |= BANDIT_COHERENT; + udelay(2); + out_le32((volatile unsigned int *)bp->cfg_data, magic); + printk(KERN_INFO "Cache coherency enabled for bandit/PSX at %p\n", + bp->io_base); +} + +unsigned long pmac_find_bridges(unsigned long mem_start, unsigned long mem_end) +{ + int bus; + struct bridge_data *bridge; + + bridge_list = 0; + max_bus = 0; + add_bridges(find_devices("bandit"), &mem_start); + add_bridges(find_devices("chaos"), &mem_start); + bridges = (struct bridge_data **) mem_start; + mem_start += (max_bus + 1) * sizeof(struct bridge_data *); + memset(bridges, 0, (max_bus + 1) * sizeof(struct bridge_data *)); + for (bridge = bridge_list; bridge != NULL; bridge = bridge->next) + for (bus = bridge->bus_number; bus <= bridge->max_bus; ++bus) + bridges[bus] = bridge; + + return mem_start; +} + +static void add_bridges(struct device_node *dev, unsigned long *mem_ptr) +{ + int *bus_range; + int len; + struct bridge_data *bp; + + for (; dev != NULL; dev = dev->next) { + if (dev->n_addrs < 1) { + printk(KERN_WARNING "Can't use %s: no address\n", + dev->full_name); + continue; + } + bus_range = (int *) get_property(dev, "bus-range", &len); + if (bus_range == NULL || len < 2 * sizeof(int)) { + printk(KERN_WARNING "Can't get bus-range for %s\n", + dev->full_name); + continue; + } + if (bus_range[1] == bus_range[0]) + printk(KERN_INFO "PCI bus %d", bus_range[0]); + else + printk(KERN_INFO "PCI buses %d..%d", bus_range[0], + bus_range[1]); + printk(" controlled by %s at %x\n", + dev->name, dev->addrs[0].address); + bp = (struct bridge_data *) *mem_ptr; + *mem_ptr += sizeof(struct bridge_data); + bp->cfg_addr = (volatile unsigned int *) + (dev->addrs[0].address + 0x800000); + bp->cfg_data = (volatile unsigned char *) + (dev->addrs[0].address + 0xc00000); + bp->io_base = (void *) dev->addrs[0].address; + ioremap(dev->addrs[0].address, 0x800000); + bp->bus_number = bus_range[0]; + bp->max_bus = bus_range[1]; + bp->next = bridge_list; + bp->node = dev; + bridge_list = bp; + if (bp->max_bus > max_bus) + max_bus = bp->max_bus; + + if (strcmp(dev->name, "bandit") == 0) + init_bandit(bp); + } +} + +void *pci_io_base(unsigned int bus) +{ + struct bridge_data *bp; + + if (bus > max_bus || (bp = bridges[bus]) == 0) + return 0; + return bp->io_base; +} + +int pci_device_loc(struct device_node *dev, unsigned char *bus_ptr, + unsigned char *devfn_ptr) +{ + unsigned int *reg; + int len; + + reg = (unsigned int *) get_property(dev, "reg", &len); + if (reg == 0 || len < 5 * sizeof(unsigned int)) { + /* doesn't look like a PCI device */ + *bus_ptr = 0xff; + *devfn_ptr = 0xff; + return -1; + } + *bus_ptr = reg[0] >> 16; + *devfn_ptr = reg[0] >> 8; + return 0; +} + +int pmac_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val) +{ + struct bridge_data *bp; + + *val = 0xff; + if (bus > max_bus || (bp = bridges[bus]) == 0) + return PCIBIOS_DEVICE_NOT_FOUND; + if (bus == bp->bus_number) { + if (dev_fn < (11 << 3)) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32(bp->cfg_addr, + (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + + (offset & ~3)); + } else { + out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + } + udelay(2); + *val = in_8(bp->cfg_data + (offset & 3)); + + if (offset == PCI_INTERRUPT_LINE) { + /* + * Open Firmware often doesn't initialize this + * register properly, so we find the node and see + * if it has an AAPL,interrupts property. + */ + struct device_node *node; + unsigned int *reg; + + for (node = bp->node->child; node != 0; node = node->sibling) { + reg = (unsigned int *) get_property(node, "reg", 0); + if (reg == 0 || ((reg[0] >> 8) & 0xff) != dev_fn) + continue; + /* this is the node, see if it has interrupts */ + if (node->n_intrs > 0) + *val = node->intrs[0]; + break; + } + } + + return PCIBIOS_SUCCESSFUL; +} + +int pmac_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val) +{ + struct bridge_data *bp; + + *val = 0xffff; + if (bus > max_bus || (bp = bridges[bus]) == 0 || (offset & 1) != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + if (bus == bp->bus_number) { + if (dev_fn < (11 << 3)) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32(bp->cfg_addr, + (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + + (offset & ~3)); + } else { + out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + } + udelay(2); + *val = in_le16((volatile unsigned short *)(bp->cfg_data + (offset & 3))); + return PCIBIOS_SUCCESSFUL; +} + +int pmac_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val) +{ + struct bridge_data *bp; + + *val = 0xffffffff; + if (bus > max_bus || (bp = bridges[bus]) == 0 || (offset & 3) != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + if (bus == bp->bus_number) { + if (dev_fn < (11 << 3)) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32(bp->cfg_addr, + (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + + offset); + } else { + out_le32(bp->cfg_addr, (dev_fn << 8) + offset + 1); + } + udelay(2); + *val = in_le32((volatile unsigned int *)bp->cfg_data); + return PCIBIOS_SUCCESSFUL; +} + +int pmac_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val) +{ + struct bridge_data *bp; + + if (bus > max_bus || (bp = bridges[bus]) == 0) + return PCIBIOS_DEVICE_NOT_FOUND; + if (bus == bp->bus_number) { + if (dev_fn < (11 << 3)) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32(bp->cfg_addr, + (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + + (offset & ~3)); + } else { + out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + } + udelay(2); + out_8(bp->cfg_data + (offset & 3), val); + return PCIBIOS_SUCCESSFUL; +} + +int pmac_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val) +{ + struct bridge_data *bp; + + if (bus > max_bus || (bp = bridges[bus]) == 0 || (offset & 1) != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + if (bus == bp->bus_number) { + if (dev_fn < (11 << 3)) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32(bp->cfg_addr, + (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + + (offset & ~3)); + } else { + out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + } + udelay(2); + out_le16((volatile unsigned short *)(bp->cfg_data + (offset & 3)), val); + return PCIBIOS_SUCCESSFUL; +} + +int pmac_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val) +{ + struct bridge_data *bp; + + if (bus > max_bus || (bp = bridges[bus]) == 0 || (offset & 3) != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + if (bus == bp->bus_number) { + if (dev_fn < (11 << 3)) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32(bp->cfg_addr, + (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + + offset); + } else { + out_le32(bp->cfg_addr, (dev_fn << 8) + offset + 1); + } + udelay(2); + out_le32((volatile unsigned int *)bp->cfg_data, val); + return PCIBIOS_SUCCESSFUL; +} + +int pmac_pcibios_find_device(unsigned short vendor, unsigned short dev_id, + unsigned short index, unsigned char *bus_ptr, + unsigned char *dev_fn_ptr) +{ + int bus, unit, fn, num, devfn; + unsigned int x, vendev; + unsigned char h; + + if (vendor == 0xffff) + return PCIBIOS_BAD_VENDOR_ID; + vendev = (dev_id << 16) + vendor; + num = 0; + for (bus = 0; bus <= max_bus; ++bus) { + if (bridges[bus] == 0) + continue; + unit = fn = 0; + if (bus == bridges[bus]->bus_number) + unit = 11; + while (unit < 32) { + devfn = PCI_DEVFN(unit, fn); + if (pcibios_read_config_dword(bus, devfn, + PCI_VENDOR_ID, &x) + == PCIBIOS_SUCCESSFUL && x == vendev) { + if (index == num) { + *bus_ptr = bus; + *dev_fn_ptr = devfn; + return PCIBIOS_SUCCESSFUL; + } + ++num; + } + if (fn != 0) { + if (++fn >= 8) { + ++unit; + fn = 0; + } + continue; + } + if (pcibios_read_config_byte(bus, devfn, + PCI_HEADER_TYPE, &h) + == PCIBIOS_SUCCESSFUL && (h & 0x80) != 0) + ++fn; + else + ++unit; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + +int pmac_pcibios_find_class(unsigned int class_code, unsigned short index, + unsigned char *bus_ptr, unsigned char *dev_fn_ptr) +{ + int bus, unit, fn, num, devfn; + unsigned int x; + unsigned char h; + + num = 0; + for (bus = 0; bus <= max_bus; ++bus) { + if (bridges[bus] == 0) + continue; + unit = fn = 0; + if (bus == bridges[bus]->bus_number) + unit = 11; + while (unit < 32) { + devfn = PCI_DEVFN(unit, fn); + if (pcibios_read_config_dword(bus, devfn, + PCI_CLASS_REVISION, &x) + == PCIBIOS_SUCCESSFUL && (x >> 8) == class_code) { + if (index == num) { + *bus_ptr = bus; + *dev_fn_ptr = devfn; + return PCIBIOS_SUCCESSFUL; + } + ++num; + } + if (fn != 0) { + if (++fn >= 8) { + ++unit; + fn = 0; + } + continue; + } + if (pcibios_read_config_byte(bus, devfn, + PCI_HEADER_TYPE, &h) + == PCIBIOS_SUCCESSFUL && (h & 0x80) != 0) + ++fn; + else + ++unit; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + +__initfunc(unsigned long route_pci_interrupts(void)) +{ + return 0; +} diff --git a/arch/ppc/kernel/pci.c b/arch/ppc/kernel/pci.c index 612a24bff..49f2f37be 100644 --- a/arch/ppc/kernel/pci.c +++ b/arch/ppc/kernel/pci.c @@ -1,630 +1,194 @@ /* - * PCI support - * -- rough emulation of "PCI BIOS" functions - * - * Note: these are very motherboard specific! Some way needs to - * be worked out to handle the differences. + * $Id: pci.c,v 1.12 1997/08/27 05:05:28 cort Exp $ + * Common pmac/prep/chrp pci routines. -- Cort */ -#include <linux/config.h> -#include <linux/types.h> -#include <linux/bios32.h> +#include <linux/kernel.h> #include <linux/pci.h> +/*#include <linux/bios32.h>*/ +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/init.h> -#include <asm/byteorder.h> -#include <asm/io.h> -#include <asm/ptrace.h> #include <asm/processor.h> +#include <asm/io.h> +#include <asm/prom.h> +#include <asm/pci-bridge.h> -/* - * PCI interrupt configuration. This is motherboard specific. - */ -/* Which PCI interrupt line does a given device [slot] use? */ -/* Note: This really should be two dimensional based in slot/pin used */ -unsigned char *Motherboard_map; -unsigned char *Motherboard_map_name; - -/* How is the 82378 PIRQ mapping setup? */ -unsigned char *Motherboard_routes; - -/* Tables for known hardware */ - -/* Motorola PowerStack */ -static char Blackhawk_pci_IRQ_map[16] = -{ - 0, /* Slot 0 - unused */ - 0, /* Slot 1 - unused */ - 0, /* Slot 2 - unused */ - 0, /* Slot 3 - unused */ - 0, /* Slot 4 - unused */ - 0, /* Slot 5 - unused */ - 0, /* Slot 6 - unused */ - 0, /* Slot 7 - unused */ - 0, /* Slot 8 - unused */ - 0, /* Slot 9 - unused */ - 0, /* Slot 10 - unused */ - 0, /* Slot 11 - unused */ - 3, /* Slot 12 - SCSI */ - 0, /* Slot 13 - unused */ - 1, /* Slot 14 - Ethernet */ - 0, /* Slot 15 - unused */ -}; - -static char Blackhawk_pci_IRQ_routes[] = -{ - 0, /* Line 0 - Unused */ - 9, /* Line 1 */ - 11, /* Line 2 */ - 14, /* Line 3 */ - 15 /* Line 4 */ -}; - -/* Motorola MVME16xx */ -static char Genesis_pci_IRQ_map[16] = -{ - 0, /* Slot 0 - unused */ - 0, /* Slot 1 - unused */ - 0, /* Slot 2 - unused */ - 0, /* Slot 3 - unused */ - 0, /* Slot 4 - unused */ - 0, /* Slot 5 - unused */ - 0, /* Slot 6 - unused */ - 0, /* Slot 7 - unused */ - 0, /* Slot 8 - unused */ - 0, /* Slot 9 - unused */ - 0, /* Slot 10 - unused */ - 0, /* Slot 11 - unused */ - 3, /* Slot 12 - SCSI */ - 0, /* Slot 13 - unused */ - 1, /* Slot 14 - Ethernet */ - 0, /* Slot 15 - unused */ -}; - -static char Genesis_pci_IRQ_routes[] = -{ - 0, /* Line 0 - Unused */ - 10, /* Line 1 */ - 11, /* Line 2 */ - 14, /* Line 3 */ - 15 /* Line 4 */ -}; - -/* Motorola Series-E */ -static char Comet_pci_IRQ_map[16] = -{ - 0, /* Slot 0 - unused */ - 0, /* Slot 1 - unused */ - 0, /* Slot 2 - unused */ - 0, /* Slot 3 - unused */ - 0, /* Slot 4 - unused */ - 0, /* Slot 5 - unused */ - 0, /* Slot 6 - unused */ - 0, /* Slot 7 - unused */ - 0, /* Slot 8 - unused */ - 0, /* Slot 9 - unused */ - 0, /* Slot 10 - unused */ - 0, /* Slot 11 - unused */ - 3, /* Slot 12 - SCSI */ - 0, /* Slot 13 - unused */ - 1, /* Slot 14 - Ethernet */ - 0, /* Slot 15 - unused */ -}; - -static char Comet_pci_IRQ_routes[] = -{ - 0, /* Line 0 - Unused */ - 10, /* Line 1 */ - 11, /* Line 2 */ - 14, /* Line 3 */ - 15 /* Line 4 */ -}; - -/* BeBox */ -static char BeBox_pci_IRQ_map[16] = -{ - 0, /* Slot 0 - unused */ - 0, /* Slot 1 - unused */ - 0, /* Slot 2 - unused */ - 0, /* Slot 3 - unused */ - 0, /* Slot 4 - unused */ - 0, /* Slot 5 - unused */ - 0, /* Slot 6 - unused */ - 0, /* Slot 7 - unused */ - 0, /* Slot 8 - unused */ - 0, /* Slot 9 - unused */ - 0, /* Slot 10 - unused */ - 0, /* Slot 11 - unused */ - 16, /* Slot 12 - SCSI */ - 0, /* Slot 13 - unused */ - 0, /* Slot 14 - unused */ - 0, /* Slot 15 - unused */ -}; - -static char BeBox_pci_IRQ_routes[] = -{ - 0, /* Line 0 - Unused */ - 9, /* Line 1 */ - 11, /* Line 2 */ - 14, /* Line 3 */ - 15 /* Line 4 */ -}; - -/* IBM Nobis */ -static char Nobis_pci_IRQ_map[16] = -{ - 0, /* Slot 0 - unused */ - 0, /* Slot 1 - unused */ - 0, /* Slot 2 - unused */ - 0, /* Slot 3 - unused */ - 0, /* Slot 4 - unused */ - 0, /* Slot 5 - unused */ - 0, /* Slot 6 - unused */ - 0, /* Slot 7 - unused */ - 0, /* Slot 8 - unused */ - 0, /* Slot 9 - unused */ - 0, /* Slot 10 - unused */ - 0, /* Slot 11 - unused */ - 3, /* Slot 12 - SCSI */ - 0, /* Slot 13 - unused */ - 0, /* Slot 14 - unused */ - 0, /* Slot 15 - unused */ -}; - -static char Nobis_pci_IRQ_routes[] = -{ - 0, /* Line 0 - Unused */ - 13, /* Line 1 */ - 13, /* Line 2 */ - 13, /* Line 3 */ - 13 /* Line 4 */ -}; - +unsigned long io_base; +unsigned long pci_dram_offset; /* - * ibm 830 (and 850?). - * This is actually based on the Carolina motherboard - * -- Cort - */ -static char ibm8xx_pci_IRQ_map[23] = { - 0, /* Slot 0 - unused */ - 0, /* Slot 1 - unused */ - 0, /* Slot 2 - unused */ - 0, /* Slot 3 - unused */ - 0, /* Slot 4 - unused */ - 0, /* Slot 5 - unused */ - 0, /* Slot 6 - unused */ - 0, /* Slot 7 - unused */ - 0, /* Slot 8 - unused */ - 0, /* Slot 9 - unused */ - 0, /* Slot 10 - unused */ - 0, /* Slot 11 - FireCoral */ - 4, /* Slot 12 - Ethernet PCIINTD# */ - 2, /* Slot 13 - PCI Slot #2 */ - 2, /* Slot 14 - S3 Video PCIINTD# */ - 0, /* Slot 15 - onboard SCSI (INDI) [1] */ - 3, /* Slot 16 - NCR58C810 RS6000 Only PCIINTC# */ - 0, /* Slot 17 - unused */ - 2, /* Slot 18 - PCI Slot 2 PCIINTx# (See below) */ - 0, /* Slot 19 - unused */ - 0, /* Slot 20 - unused */ - 0, /* Slot 21 - unused */ - 2, /* Slot 22 - PCI slot 1 PCIINTx# (See below) */ -}; -static char ibm8xx_pci_IRQ_routes[] = { - 0, /* Line 0 - unused */ - 13, /* Line 1 */ - 10, /* Line 2 */ - 15, /* Line 3 */ - 15, /* Line 4 */ -}; -/* This just changes the PCI slots & onboard SCSI + S3 to IRQ10, but - * it really needs some logic to set them to unique IRQ's, or even - * add some logic to the drivers to ask an irq.c routine to re-map - * the IRQ if it needs one to itself... + * It would be nice if we could create a include/asm/pci.h and have just + * function ptrs for all these in there, but that isn't the case. + * We have a function, pcibios_*() which calls the function ptr ptr_pcibios_*() + * which has been setup by pcibios_init(). This is all to avoid a check + * for pmac/prep every time we call one of these. It should also make the move + * to a include/asm/pcibios.h easier, we can drop the ptr_ on these functions + * and create pci.h + * -- Cort */ -static char Carolina_PIRQ_routes[] = { - 0xad, /* INTB# = 10, INTA# = 13 */ - 0xff /* INTD# = 15, INTC# = 15 */ -}; -/* We have to turn on LEVEL mode for changed IRQ's */ -/* All PCI IRQ's need to be level mode, so this should be something - * other than hard-coded as well... IRQ's are individually mappable - * to either edge or level. - */ -#define CAROLINA_IRQ_EDGE_MASK_LO 0x00 /* IRQ's 0-7 */ -#define CAROLINA_IRQ_EDGE_MASK_HI 0xA4 /* IRQ's 8-15 [10,13,15] */ -#define PCI_DEVICE_ID_IBM_CORAL 0x000a - -#undef PCI_DEBUG - -#ifdef PCI_STATS -int PCI_conversions[2]; -#endif - - -unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end) -{ - return mem_start; +int (*ptr_pcibios_read_config_byte)(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val); +int (*ptr_pcibios_read_config_word)(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val); +int (*ptr_pcibios_read_config_dword)(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val); +int (*ptr_pcibios_write_config_byte)(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val); +int (*ptr_pcibios_write_config_word)(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val); +int (*ptr_pcibios_write_config_dword)(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val); +int (*ptr_pcibios_find_device)(unsigned short vendor, unsigned short dev_id, + unsigned short index, unsigned char *bus_ptr, + unsigned char *dev_fn_ptr); +int (*ptr_pcibios_find_class)(unsigned int class_code, unsigned short index, + unsigned char *bus_ptr, unsigned char *dev_fn_ptr); + +extern int pmac_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val); +extern int pmac_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val); +extern int pmac_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val); +extern int pmac_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val); +extern int pmac_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val); +extern int pmac_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val); +extern int pmac_pcibios_find_device(unsigned short vendor, unsigned short dev_id, + unsigned short index, unsigned char *bus_ptr, + unsigned char *dev_fn_ptr); +extern int pmac_pcibios_find_class(unsigned int class_code, unsigned short index, + unsigned char *bus_ptr, unsigned char *dev_fn_ptr); + +extern int chrp_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val); +extern int chrp_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val); +extern int chrp_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val); +extern int chrp_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val); +extern int chrp_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val); +extern int chrp_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val); +extern int chrp_pcibios_find_device(unsigned short vendor, unsigned short dev_id, + unsigned short index, unsigned char *bus_ptr, + unsigned char *dev_fn_ptr); +extern int chrp_pcibios_find_class(unsigned int class_code, unsigned short index, + unsigned char *bus_ptr, unsigned char *dev_fn_ptr); + +extern int prep_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val); +extern int prep_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val); +extern int prep_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val); +extern int prep_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val); +extern int prep_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val); +extern int prep_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val); +extern int prep_pcibios_find_device(unsigned short vendor, unsigned short dev_id, + unsigned short index, unsigned char *bus_ptr, + unsigned char *dev_fn_ptr); +extern int prep_pcibios_find_class(unsigned int class_code, unsigned short index, + unsigned char *bus_ptr, unsigned char *dev_fn_ptr); + + +int pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val) +{ + return ptr_pcibios_read_config_byte(bus,dev_fn,offset,val); } - -int -pcibios_present (void) +int pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val) { -#ifdef PCI_DEBUG - printk("PCI [BIOS] present?\n"); -#endif - return (1); + return ptr_pcibios_read_config_word(bus,dev_fn,offset,val); } - -int -pcibios_read_config_dword (unsigned char bus, - unsigned char dev, unsigned char offset, unsigned int *val) +int pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val) { - unsigned long _val; - unsigned long *ptr; - dev >>= 3; -#ifdef PCI_DEBUG - printk("PCI Read config dword[%d.%d.%x] = ", bus, dev, offset); -#endif - if ((bus != 0) || (dev < 11) || (dev > 16)) - { - *val = 0xFFFFFFFF; - return PCIBIOS_DEVICE_NOT_FOUND; - } else - { - ptr = (unsigned long *)(0x80800000 | (1<<dev) | offset); -#ifdef PCI_DEBUG - printk("[%x] ", ptr); -#endif - _val = le32_to_cpu(*ptr); - } -#ifdef PCI_DEBUG - printk("%x\n", _val); -#endif - *val = _val; - return PCIBIOS_SUCCESSFUL; + return ptr_pcibios_read_config_dword(bus,dev_fn,offset,val); } - -int -pcibios_read_config_word (unsigned char bus, - unsigned char dev, unsigned char offset, unsigned short *val) +int pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val) { - unsigned short _val; - unsigned short *ptr; - dev >>= 3; -#ifdef PCI_DEBUG - printk("PCI Read config word[%d.%d.%x] = ", bus, dev, offset); -#endif - if ((bus != 0) || (dev < 11) || (dev > 16)) - { - *val = (unsigned short)0xFFFFFFFF; - return PCIBIOS_DEVICE_NOT_FOUND; - } else - { - ptr = (unsigned short *)(0x80800000 | (1<<dev) | offset); -#ifdef PCI_DEBUG - printk("[%x] ", ptr); -#endif - _val = le16_to_cpu(*ptr); - } -#ifdef PCI_DEBUG - printk("%x\n", _val); -#endif - *val = _val; - return PCIBIOS_SUCCESSFUL; + return ptr_pcibios_write_config_byte(bus,dev_fn,offset,val); } - -int -pcibios_read_config_byte (unsigned char bus, - unsigned char dev, unsigned char offset, unsigned char *val) +int pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val) { - unsigned char _val; - volatile unsigned char *ptr; - dev >>= 3; - /* Note: the configuration registers don't always have this right! */ - if (offset == PCI_INTERRUPT_LINE) - { - if (Motherboard_map[dev] <= 4) - { - *val = Motherboard_routes[Motherboard_map[dev]]; - /*printk("dev %d map %d route %d\n", - dev,Motherboard_map[dev], - Motherboard_routes[Motherboard_map[dev]]);*/ - } else - { /* Pseudo interrupts [for BeBox] */ - *val = Motherboard_map[dev]; - } -#ifdef PCI_DEBUG - printk("PCI Read Interrupt Line[%d.%d] = %d\n", bus, dev, *val); -#endif - return PCIBIOS_SUCCESSFUL; - } -#ifdef PCI_DEBUG - printk("PCI Read config byte[%d.%d.%x] = ", bus, dev, offset); -#endif - if ((bus != 0) || (dev < 11) || (dev > 16)) - { - *val = 0xFFFFFFFF; - return PCIBIOS_DEVICE_NOT_FOUND; - } else - { - ptr = (unsigned char *)(0x80800000 | (1<<dev) | offset ^ 1); -#ifdef PCI_DEBUG - printk("[%x] ", ptr); -#endif - _val = *ptr; - } -#ifdef PCI_DEBUG - printk("%x\n", _val); -#endif - *val = _val; - return PCIBIOS_SUCCESSFUL; + return ptr_pcibios_write_config_word(bus,dev_fn,offset,val); } - -int -pcibios_write_config_dword (unsigned char bus, - unsigned char dev, unsigned char offset, unsigned int val) +int pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val) { - unsigned long _val; - unsigned long *ptr; - dev >>= 3; - _val = le32_to_cpu(val); -#ifdef PCI_DEBUG - printk("PCI Write config dword[%d.%d.%x] = %x\n", bus, dev, offset, _val); -#endif - if ((bus != 0) || (dev < 11) || (dev > 16)) - { - return PCIBIOS_DEVICE_NOT_FOUND; - } else - { - ptr = (unsigned long *)(0x80800000 | (1<<dev) | offset); - *ptr = _val; - } - return PCIBIOS_SUCCESSFUL; + return ptr_pcibios_write_config_dword(bus,dev_fn,offset,val); } - -int -pcibios_write_config_word (unsigned char bus, - unsigned char dev, unsigned char offset, unsigned short val) +int pcibios_find_device(unsigned short vendor, unsigned short dev_id, + unsigned short index, unsigned char *bus_ptr, + unsigned char *dev_fn_ptr) { - unsigned short _val; - unsigned short *ptr; - dev >>= 3; - _val = le16_to_cpu(val); -#ifdef PCI_DEBUG - printk("PCI Write config word[%d.%d.%x] = %x\n", bus, dev, offset, _val); -#endif - if ((bus != 0) || (dev < 11) || (dev > 16)) - { - return PCIBIOS_DEVICE_NOT_FOUND; - } else - { - ptr = (unsigned short *)(0x80800000 | (1<<dev) | offset); - *ptr = _val; - } - return PCIBIOS_SUCCESSFUL; + return ptr_pcibios_find_device(vendor,dev_id,index,bus_ptr,dev_fn_ptr); } - -int -pcibios_write_config_byte (unsigned char bus, - unsigned char dev, unsigned char offset, unsigned char val) +int pcibios_find_class(unsigned int class_code, unsigned short index, + unsigned char *bus_ptr, unsigned char *dev_fn_ptr) { - unsigned char _val; - unsigned char *ptr; - dev >>= 3; - _val = val; -#ifdef PCI_DEBUG - printk("PCI Write config byte[%d.%d.%x] = %x\n", bus, dev, offset, _val); -#endif - if ((bus != 0) || (dev < 11) || (dev > 16)) - { - return PCIBIOS_DEVICE_NOT_FOUND; - } else - { - ptr = (unsigned char *)(0x80800000 | (1<<dev) | offset ^ 1); - *ptr = _val; - } - return PCIBIOS_SUCCESSFUL; + return ptr_pcibios_find_class(class_code,index,bus_ptr,dev_fn_ptr); } -int -pcibios_find_device (unsigned short vendor, unsigned short device_id, - unsigned short index, unsigned char *bus, - unsigned char *dev) +int pcibios_present(void) { - unsigned long w, desired = (device_id << 16) | vendor; - int devnr; - - if (vendor == 0xffff) { - return PCIBIOS_BAD_VENDOR_ID; - } - - for (devnr = 11; devnr < 16; devnr++) - { - pcibios_read_config_dword(0, devnr<<3, PCI_VENDOR_ID, &w); - if (w == desired) { - if (index == 0) { - *bus = 0; - *dev = devnr<<3; - return PCIBIOS_SUCCESSFUL; - } - --index; - } - } - return PCIBIOS_DEVICE_NOT_FOUND; + return 1; } -int -pcibios_find_class (unsigned int class_code, unsigned short index, - unsigned char *bus, unsigned char *dev) -{ - int dev_nr, class, indx; - indx = 0; -#ifdef PCI_DEBUG - printk("pcibios_find_class - class: %x, index: %x", class_code, index); -#endif - for (dev_nr = 11; dev_nr < 16; dev_nr++) - { - pcibios_read_config_dword(0, dev_nr<<3, PCI_CLASS_REVISION, &class); - if ((class>>8) == class_code) - { - if (index == indx) - { - *bus = 0; - *dev = dev_nr<<3; -#ifdef PCI_DEBUG - printk(" - device: %x\n", dev_nr); -#endif - return (0); - } - indx++; - } - } -#ifdef PCI_DEBUG - printk(" - not found\n"); -#endif - return PCIBIOS_DEVICE_NOT_FOUND; -} - -const char *pcibios_strerror(int error) -{ - static char buf[32]; - switch (error) - { case PCIBIOS_SUCCESSFUL: - return ("PCI BIOS: no error"); - case PCIBIOS_FUNC_NOT_SUPPORTED: - return ("PCI BIOS: function not supported"); - case PCIBIOS_BAD_VENDOR_ID: - return ("PCI BIOS: bad vendor ID"); - case PCIBIOS_DEVICE_NOT_FOUND: - return ("PCI BIOS: device not found"); - case PCIBIOS_BAD_REGISTER_NUMBER: - return ("PCI BIOS: bad register number"); - case PCIBIOS_SET_FAILED: - return ("PCI BIOS: set failed"); - case PCIBIOS_BUFFER_TOO_SMALL: - return ("PCI BIOS: buffer too small"); - default: - sprintf(buf, "PCI BIOS: invalid error #%d", error); - return(buf); +__initfunc(unsigned long +pcibios_init(unsigned long mem_start,unsigned long mem_end)) +{ + switch (_machine) { + case _MACH_Motorola: + case _MACH_IBM: + ptr_pcibios_read_config_byte = prep_pcibios_read_config_byte; + ptr_pcibios_read_config_word = prep_pcibios_read_config_word; + ptr_pcibios_read_config_dword = prep_pcibios_read_config_dword; + ptr_pcibios_write_config_byte = prep_pcibios_write_config_byte; + ptr_pcibios_write_config_word = prep_pcibios_write_config_word; + ptr_pcibios_write_config_dword = prep_pcibios_write_config_dword; + ptr_pcibios_find_device = prep_pcibios_find_device; + ptr_pcibios_find_class = prep_pcibios_find_class; + break; + case _MACH_Pmac: + ptr_pcibios_read_config_byte = pmac_pcibios_read_config_byte; + ptr_pcibios_read_config_word = pmac_pcibios_read_config_word; + ptr_pcibios_read_config_dword = pmac_pcibios_read_config_dword; + ptr_pcibios_write_config_byte = pmac_pcibios_write_config_byte; + ptr_pcibios_write_config_word = pmac_pcibios_write_config_word; + ptr_pcibios_write_config_dword = pmac_pcibios_write_config_dword; + ptr_pcibios_find_device = pmac_pcibios_find_device; + ptr_pcibios_find_class = pmac_pcibios_find_class; + break; + case _MACH_chrp: + ptr_pcibios_read_config_byte = chrp_pcibios_read_config_byte; + ptr_pcibios_read_config_word = chrp_pcibios_read_config_word; + ptr_pcibios_read_config_dword = chrp_pcibios_read_config_dword; + ptr_pcibios_write_config_byte = chrp_pcibios_write_config_byte; + ptr_pcibios_write_config_word = chrp_pcibios_write_config_word; + ptr_pcibios_write_config_dword = chrp_pcibios_write_config_dword; + ptr_pcibios_find_device = chrp_pcibios_find_device; + ptr_pcibios_find_class = chrp_pcibios_find_class; + break; } -} - -/* - * Note: This routine has to access the PCI configuration space - * for the PCI bridge chip (Intel 82378). - */ -unsigned long pcibios_init(unsigned long mem_start,unsigned long mem_end) -{ return mem_start; } -unsigned long route_pci_interrupts(void) +__initfunc(unsigned long +pcibios_fixup(unsigned long mem_start, unsigned long mem_end)) { - unsigned char *ibc_pirq = (unsigned char *)0x80800860; - unsigned char *ibc_pcicon = (unsigned char *)0x80800840; - extern unsigned long isBeBox[]; - int i; - - if ( _machine == _MACH_Motorola) - { - switch (inb(0x800) & 0xF0) - { - case 0x10: /* MVME16xx */ - Motherboard_map_name = "Genesis"; - Motherboard_map = Genesis_pci_IRQ_map; - Motherboard_routes = Genesis_pci_IRQ_routes; - break; - case 0x20: /* Series E */ - Motherboard_map_name = "Series E"; - Motherboard_map = Comet_pci_IRQ_map; - Motherboard_routes = Comet_pci_IRQ_routes; - break; - case 0x40: /* PowerStack */ - default: /* Can't hurt, can it? */ - Motherboard_map_name = "Blackhawk (Powerstack)"; - Motherboard_map = Blackhawk_pci_IRQ_map; - Motherboard_routes = Blackhawk_pci_IRQ_routes; - break; - } - } else - { - if ( _machine == _MACH_IBM ) - { - unsigned char pl_id; - unsigned long flags; - unsigned index; - unsigned char fn, bus; - unsigned int addr; - unsigned char dma_mode, ide_mode; - int i; - - Motherboard_map_name = "IBM 8xx (Carolina)"; - Motherboard_map = ibm8xx_pci_IRQ_map; - Motherboard_routes = ibm8xx_pci_IRQ_routes; -ll_printk("before loop\n"); - - for (index = 0; - !pcibios_find_device (PCI_VENDOR_ID_IBM, - PCI_DEVICE_ID_IBM_CORAL, - index, &bus, &fn); ++index) - { - pcibios_read_config_dword(bus, fn, 0x10, &addr); - addr &= ~0x3; - outb(0x26, addr); - dma_mode = inb(addr+4); - outb(0x25, addr); - ide_mode = inb(addr+4); - /*printk("CORAL I/O at 0x%x, DMA mode: %x, IDE mode: %x", - addr, dma_mode, ide_mode);*/ - /* Make CDROM non-DMA */ - ide_mode = (ide_mode & 0x0F) | 0x20; - outb(0x25, addr); - outb(ide_mode, addr+4); - dma_mode = dma_mode & ~0x80; - outb(0x26, addr); - outb(dma_mode, addr+4); - outb(0x26, addr); - dma_mode = inb(addr+4); - outb(0x25, addr); - ide_mode = inb(addr+4); - /*printk("=> DMA mode: %x, IDE mode: %x\n", - dma_mode, ide_mode);*/ - } - - /* Setup the PCI INT mappings for the Carolina */ - /* These are PCI Interrupt Route Control [1|2] Register */ - outb(Carolina_PIRQ_routes[0], 0x0890); - outb(Carolina_PIRQ_routes[1], 0x0891); - - pl_id=inb(0x0852); - /*printk("CPU Planar ID is %#0x\n", pl_id);*/ - - if (pl_id == 0x0C) { - /* INDI */ - Motherboard_map[12] = 1; - } -ll_printk("before edge/level\n"); -#if 0 - /*printk("Changing IRQ mode\n");*/ - pl_id=inb(0x04d0); - /*printk("Low mask is %#0x\n", pl_id);*/ - outb(pl_id|CAROLINA_IRQ_EDGE_MASK_LO, 0x04d0); - - pl_id=inb(0x04d1); - /*printk("Hi mask is %#0x\n", pl_id);*/ - outb(pl_id|CAROLINA_IRQ_EDGE_MASK_HI, 0x04d1); - pl_id=inb(0x04d1); - /*printk("Hi mask now %#0x\n", pl_id);*/ -#endif - } - } - - /* Set up mapping from slots */ - for (i = 1; i <= 4; i++) - { - ibc_pirq[i-1] = Motherboard_routes[i]; - } - /* Enable PCI interrupts */ - *ibc_pcicon |= 0x20; + return mem_start; } diff --git a/arch/ppc/kernel/pmac_pci.c b/arch/ppc/kernel/pmac_pci.c new file mode 100644 index 000000000..a48385b3f --- /dev/null +++ b/arch/ppc/kernel/pmac_pci.c @@ -0,0 +1,423 @@ +/* + * Support for PCI bridges found on Power Macintoshes. + * At present the "bandit" and "chaos" bridges are supported. + * Fortunately you access configuration space in the same + * way with either bridge. + * + * Copyright (C) 1997 Paul Mackerras (paulus@cs.anu.edu.au) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/bios32.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/init.h> +#include <asm/io.h> +#include <asm/prom.h> +#include <asm/pci-bridge.h> + +struct bridge_data { + volatile unsigned int *cfg_addr; + volatile unsigned char *cfg_data; + void *io_base; + int bus_number; + int max_bus; + struct bridge_data *next; + struct device_node *node; +}; + +static struct bridge_data **bridges, *bridge_list; +static int max_bus; + +static void add_bridges(struct device_node *dev, unsigned long *mem_ptr); + +/* + * Magic constants for enabling cache coherency in the bandit/PSX bridge. + */ +#define APPLE_VENDID 0x106b +#define BANDIT_DEVID 1 +#define BANDIT_REVID 3 + +#define BANDIT_DEVNUM 11 +#define BANDIT_MAGIC 0x50 +#define BANDIT_COHERENT 0x40 + +/* + * For a bandit bridge, turn on cache coherency if necessary. + * N.B. we can't use pcibios_*_config_* here because bridges[] + * is not initialized yet. + */ +static void init_bandit(struct bridge_data *bp) +{ + unsigned int vendev, magic; + int rev; + + /* read the word at offset 0 in config space for device 11 */ + out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + PCI_VENDOR_ID); + udelay(2); + vendev = in_le32((volatile unsigned int *)bp->cfg_data); + if (vendev != (BANDIT_DEVID << 16) + APPLE_VENDID) { + printk(KERN_WARNING "bandit isn't? (%x)\n", vendev); + return; + } + + /* read the revision id */ + out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + PCI_REVISION_ID); + udelay(2); + rev = in_8(bp->cfg_data); + if (rev != BANDIT_REVID) + printk(KERN_WARNING "Unknown revision %d for bandit at %p\n", + rev, bp->io_base); + + /* read the word at offset 0x50 */ + out_le32(bp->cfg_addr, (1UL << BANDIT_DEVNUM) + BANDIT_MAGIC); + udelay(2); + magic = in_le32((volatile unsigned int *)bp->cfg_data); + if ((magic & BANDIT_COHERENT) != 0) + return; + magic |= BANDIT_COHERENT; + udelay(2); + out_le32((volatile unsigned int *)bp->cfg_data, magic); + printk(KERN_INFO "Cache coherency enabled for bandit/PSX at %p\n", + bp->io_base); +} + +unsigned long pmac_find_bridges(unsigned long mem_start, unsigned long mem_end) +{ + int bus; + struct bridge_data *bridge; + + bridge_list = 0; + max_bus = 0; + add_bridges(find_devices("bandit"), &mem_start); + add_bridges(find_devices("chaos"), &mem_start); + bridges = (struct bridge_data **) mem_start; + mem_start += (max_bus + 1) * sizeof(struct bridge_data *); + memset(bridges, 0, (max_bus + 1) * sizeof(struct bridge_data *)); + for (bridge = bridge_list; bridge != NULL; bridge = bridge->next) + for (bus = bridge->bus_number; bus <= bridge->max_bus; ++bus) + bridges[bus] = bridge; + + return mem_start; +} + +static void add_bridges(struct device_node *dev, unsigned long *mem_ptr) +{ + int *bus_range; + int len; + struct bridge_data *bp; + + for (; dev != NULL; dev = dev->next) { + if (dev->n_addrs < 1) { + printk(KERN_WARNING "Can't use %s: no address\n", + dev->full_name); + continue; + } + bus_range = (int *) get_property(dev, "bus-range", &len); + if (bus_range == NULL || len < 2 * sizeof(int)) { + printk(KERN_WARNING "Can't get bus-range for %s\n", + dev->full_name); + continue; + } + if (bus_range[1] == bus_range[0]) + printk(KERN_INFO "PCI bus %d", bus_range[0]); + else + printk(KERN_INFO "PCI buses %d..%d", bus_range[0], + bus_range[1]); + printk(" controlled by %s at %x\n", + dev->name, dev->addrs[0].address); + bp = (struct bridge_data *) *mem_ptr; + *mem_ptr += sizeof(struct bridge_data); + bp->cfg_addr = (volatile unsigned int *) + (dev->addrs[0].address + 0x800000); + bp->cfg_data = (volatile unsigned char *) + (dev->addrs[0].address + 0xc00000); + bp->io_base = (void *) dev->addrs[0].address; + ioremap(dev->addrs[0].address, 0x800000); + bp->bus_number = bus_range[0]; + bp->max_bus = bus_range[1]; + bp->next = bridge_list; + bp->node = dev; + bridge_list = bp; + if (bp->max_bus > max_bus) + max_bus = bp->max_bus; + + if (strcmp(dev->name, "bandit") == 0) + init_bandit(bp); + } +} + +void *pci_io_base(unsigned int bus) +{ + struct bridge_data *bp; + + if (bus > max_bus || (bp = bridges[bus]) == 0) + return 0; + return bp->io_base; +} + +int pci_device_loc(struct device_node *dev, unsigned char *bus_ptr, + unsigned char *devfn_ptr) +{ + unsigned int *reg; + int len; + + reg = (unsigned int *) get_property(dev, "reg", &len); + if (reg == 0 || len < 5 * sizeof(unsigned int)) { + /* doesn't look like a PCI device */ + *bus_ptr = 0xff; + *devfn_ptr = 0xff; + return -1; + } + *bus_ptr = reg[0] >> 16; + *devfn_ptr = reg[0] >> 8; + return 0; +} + +int pmac_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char *val) +{ + struct bridge_data *bp; + + *val = 0xff; + if (bus > max_bus || (bp = bridges[bus]) == 0) + return PCIBIOS_DEVICE_NOT_FOUND; + if (bus == bp->bus_number) { + if (dev_fn < (11 << 3)) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32(bp->cfg_addr, + (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + + (offset & ~3)); + } else { + out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + } + udelay(2); + *val = in_8(bp->cfg_data + (offset & 3)); + + if (offset == PCI_INTERRUPT_LINE) { + /* + * Open Firmware often doesn't initialize this + * register properly, so we find the node and see + * if it has an AAPL,interrupts property. + */ + struct device_node *node; + unsigned int *reg; + + for (node = bp->node->child; node != 0; node = node->sibling) { + reg = (unsigned int *) get_property(node, "reg", 0); + if (reg == 0 || ((reg[0] >> 8) & 0xff) != dev_fn) + continue; + /* this is the node, see if it has interrupts */ + if (node->n_intrs > 0) + *val = node->intrs[0]; + break; + } + } + + return PCIBIOS_SUCCESSFUL; +} + +int pmac_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short *val) +{ + struct bridge_data *bp; + + *val = 0xffff; + if (bus > max_bus || (bp = bridges[bus]) == 0 || (offset & 1) != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + if (bus == bp->bus_number) { + if (dev_fn < (11 << 3)) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32(bp->cfg_addr, + (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + + (offset & ~3)); + } else { + out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + } + udelay(2); + *val = in_le16((volatile unsigned short *)(bp->cfg_data + (offset & 3))); + return PCIBIOS_SUCCESSFUL; +} + +int pmac_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int *val) +{ + struct bridge_data *bp; + + *val = 0xffffffff; + if (bus > max_bus || (bp = bridges[bus]) == 0 || (offset & 3) != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + if (bus == bp->bus_number) { + if (dev_fn < (11 << 3)) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32(bp->cfg_addr, + (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + + offset); + } else { + out_le32(bp->cfg_addr, (dev_fn << 8) + offset + 1); + } + udelay(2); + *val = in_le32((volatile unsigned int *)bp->cfg_data); + return PCIBIOS_SUCCESSFUL; +} + +int pmac_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned char val) +{ + struct bridge_data *bp; + + if (bus > max_bus || (bp = bridges[bus]) == 0) + return PCIBIOS_DEVICE_NOT_FOUND; + if (bus == bp->bus_number) { + if (dev_fn < (11 << 3)) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32(bp->cfg_addr, + (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + + (offset & ~3)); + } else { + out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + } + udelay(2); + out_8(bp->cfg_data + (offset & 3), val); + return PCIBIOS_SUCCESSFUL; +} + +int pmac_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned short val) +{ + struct bridge_data *bp; + + if (bus > max_bus || (bp = bridges[bus]) == 0 || (offset & 1) != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + if (bus == bp->bus_number) { + if (dev_fn < (11 << 3)) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32(bp->cfg_addr, + (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + + (offset & ~3)); + } else { + out_le32(bp->cfg_addr, (dev_fn << 8) + (offset & ~3) + 1); + } + udelay(2); + out_le16((volatile unsigned short *)(bp->cfg_data + (offset & 3)), val); + return PCIBIOS_SUCCESSFUL; +} + +int pmac_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, + unsigned char offset, unsigned int val) +{ + struct bridge_data *bp; + + if (bus > max_bus || (bp = bridges[bus]) == 0 || (offset & 3) != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + if (bus == bp->bus_number) { + if (dev_fn < (11 << 3)) + return PCIBIOS_DEVICE_NOT_FOUND; + out_le32(bp->cfg_addr, + (1UL << (dev_fn >> 3)) + ((dev_fn & 7) << 8) + + offset); + } else { + out_le32(bp->cfg_addr, (dev_fn << 8) + offset + 1); + } + udelay(2); + out_le32((volatile unsigned int *)bp->cfg_data, val); + return PCIBIOS_SUCCESSFUL; +} + +int pmac_pcibios_find_device(unsigned short vendor, unsigned short dev_id, + unsigned short index, unsigned char *bus_ptr, + unsigned char *dev_fn_ptr) +{ + int bus, unit, fn, num, devfn; + unsigned int x, vendev; + unsigned char h; + + if (vendor == 0xffff) + return PCIBIOS_BAD_VENDOR_ID; + vendev = (dev_id << 16) + vendor; + num = 0; + for (bus = 0; bus <= max_bus; ++bus) { + if (bridges[bus] == 0) + continue; + unit = fn = 0; + if (bus == bridges[bus]->bus_number) + unit = 11; + while (unit < 32) { + devfn = PCI_DEVFN(unit, fn); + if (pcibios_read_config_dword(bus, devfn, + PCI_VENDOR_ID, &x) + == PCIBIOS_SUCCESSFUL && x == vendev) { + if (index == num) { + *bus_ptr = bus; + *dev_fn_ptr = devfn; + return PCIBIOS_SUCCESSFUL; + } + ++num; + } + if (fn != 0) { + if (++fn >= 8) { + ++unit; + fn = 0; + } + continue; + } + if (pcibios_read_config_byte(bus, devfn, + PCI_HEADER_TYPE, &h) + == PCIBIOS_SUCCESSFUL && (h & 0x80) != 0) + ++fn; + else + ++unit; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + +int pmac_pcibios_find_class(unsigned int class_code, unsigned short index, + unsigned char *bus_ptr, unsigned char *dev_fn_ptr) +{ + int bus, unit, fn, num, devfn; + unsigned int x; + unsigned char h; + + num = 0; + for (bus = 0; bus <= max_bus; ++bus) { + if (bridges[bus] == 0) + continue; + unit = fn = 0; + if (bus == bridges[bus]->bus_number) + unit = 11; + while (unit < 32) { + devfn = PCI_DEVFN(unit, fn); + if (pcibios_read_config_dword(bus, devfn, + PCI_CLASS_REVISION, &x) + == PCIBIOS_SUCCESSFUL && (x >> 8) == class_code) { + if (index == num) { + *bus_ptr = bus; + *dev_fn_ptr = devfn; + return PCIBIOS_SUCCESSFUL; + } + ++num; + } + if (fn != 0) { + if (++fn >= 8) { + ++unit; + fn = 0; + } + continue; + } + if (pcibios_read_config_byte(bus, devfn, + PCI_HEADER_TYPE, &h) + == PCIBIOS_SUCCESSFUL && (h & 0x80) != 0) + ++fn; + else + ++unit; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} diff --git a/arch/ppc/kernel/pmac_setup.c b/arch/ppc/kernel/pmac_setup.c new file mode 100644 index 000000000..74a8ff92d --- /dev/null +++ b/arch/ppc/kernel/pmac_setup.c @@ -0,0 +1,271 @@ +/* + * linux/arch/ppc/kernel/setup.c + * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Adapted for Power Macintosh by Paul Mackerras + * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au) + * + * Derived from "arch/alpha/kernel/setup.c" + * Copyright (C) 1995 Linus Torvalds + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +/* + * bootup setup stuff.. + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/tty.h> +#include <linux/string.h> +#include <linux/delay.h> +#include <linux/ioport.h> +#include <linux/major.h> +#include <asm/prom.h> +#include <asm/system.h> +#include <asm/pgtable.h> +#include <asm/io.h> +#include <asm/ide.h> +#include <asm/pci-bridge.h> +#include "time.h" + +/* + * A magic address and value to put into it on machines with the + * "ohare" I/O controller. This makes the IDE CD work on Starmaxes. + * Contributed by Harry Eaton. + */ +#define OMAGICPLACE ((volatile unsigned *) 0xf3000038) +#define OMAGICCONT 0xbeff7a + +extern int root_mountflags; + +extern char command_line[]; +extern char saved_command_line[256]; + +unsigned char drive_info; + +#define DEFAULT_ROOT_DEVICE 0x0801 /* sda1 - slightly silly choice */ + +extern unsigned long find_available_memory(void); + +void pmac_setup_arch(char **cmdline_p, + unsigned long * memory_start_p, unsigned long * memory_end_p) +{ + extern unsigned long *end_of_DRAM; + struct device_node *cpu; + int *fp; + + strcpy(saved_command_line, command_line); + *cmdline_p = command_line; + + *memory_start_p = find_available_memory(); + *memory_end_p = (unsigned long) end_of_DRAM; + + set_prom_callback(); + + *memory_start_p = copy_device_tree(*memory_start_p, *memory_end_p); + + /* Set loops_per_sec to a half-way reasonable value, + for use until calibrate_delay gets called. */ + cpu = find_type_devices("cpu"); + if (cpu != 0) { + fp = (int *) get_property(cpu, "clock-frequency", NULL); + if (fp != 0) { + switch (_get_PVR() >> 16) { + case 4: /* 604 */ + case 9: /* 604e */ + case 20: /* 620 */ + loops_per_sec = *fp; + break; + default: /* 601, 603, etc. */ + loops_per_sec = *fp / 2; + } + } else + loops_per_sec = 50000000; + } +} + +char *bootpath; +char bootdevice[256]; +void *boot_host; +int boot_target; +int boot_part; +kdev_t boot_dev; + +unsigned long +powermac_init(unsigned long mem_start, unsigned long mem_end) +{ + struct device_node *chosen_np, *ohare_np; + + mem_start = pmac_find_bridges(mem_start, mem_end); + ohare_np = find_devices("ohare"); + if (ohare_np != NULL) { + printk(KERN_INFO "Twiddling the magic ohare bits\n"); + out_le32(OMAGICPLACE, OMAGICCONT); + } + pmac_nvram_init(); + via_cuda_init(); + pmac_read_rtc_time(); + pmac_find_display(); + bootpath = NULL; + chosen_np = find_devices("chosen"); + if (chosen_np != NULL) + bootpath = (char *) get_property(chosen_np, "bootpath", NULL); + if (bootpath != NULL) { + /* + * There's a bug in the prom. (Why am I not surprised.) + * If you pass a path like scsi/sd@1:0 to canon, it returns + * something like /bandit@F2000000/gc@10/53c94@10000/sd@0,0 + * That is, the scsi target number doesn't get preserved. + */ + call_prom("canon", 3, 1, bootpath, bootdevice, sizeof(bootdevice)); + } + return mem_start; +} + +void +note_scsi_host(struct device_node *node, void *host) +{ + int l; + char *p; + + l = strlen(node->full_name); + if (strncmp(node->full_name, bootdevice, l) == 0 + && (bootdevice[l] == '/' || bootdevice[l] == 0)) { + boot_host = host; + p = strstr(bootpath, "/sd@"); + if (p != NULL) { + p += 4; + boot_target = simple_strtoul(p, NULL, 10); + p = strchr(p, ':'); + if (p != NULL) + boot_part = simple_strtoul(p + 1, NULL, 10); + } + } +} + +void find_boot_device(void) +{ + int dev; + + if (kdev_t_to_nr(ROOT_DEV) != 0) + return; + ROOT_DEV = to_kdev_t(DEFAULT_ROOT_DEVICE); + if (boot_host == NULL) + return; +#ifdef CONFIG_SCSI + dev = sd_find_target(boot_host, boot_target); + if (dev == 0) + return; + boot_dev = to_kdev_t(dev + boot_part); +#endif + /* XXX should cope with booting from IDE also */ +} + +void note_bootable_part(kdev_t dev, int part) +{ + static int found_boot = 0; + + if (!found_boot) { + find_boot_device(); + found_boot = 1; + } + if (dev == boot_dev) { + ROOT_DEV = MKDEV(MAJOR(dev), MINOR(dev) + part); + boot_dev = NODEV; + printk(" (root)"); + } +} + +void pmac_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq) +{ + struct device_node *np; + int i; + static struct device_node *atas; + static int atas_valid; + + *p = 0; + *irq = 0; + if (!atas_valid) { + atas = find_devices("ATA"); + atas_valid = 1; + } + for (i = (int)base, np = atas; i > 0 && np != NULL; --i, np = np->next) + ; + if (np == NULL) + return; + if (np->n_addrs == 0) { + printk("ide: no addresses for device %s\n", np->full_name); + return; + } + if (np->n_intrs == 0) { + printk("ide: no intrs for device %s, using 13\n", + np->full_name); + *irq = 13; + } else { + *irq = np->intrs[0]; + } + base = (unsigned long) ioremap(np->addrs[0].address, 0x200); + for (i = 0; i < 8; ++i) + *p++ = base + i * 0x10; + *p = base + 0x160; +} + +int +pmac_get_cpuinfo(char *buffer) +{ + int pvr = _get_PVR(); + char *model; + struct device_node *cpu; + int l, *fp; + + l = 0; + cpu = find_type_devices("cpu"); + if (cpu != 0) { + fp = (int *) get_property(cpu, "clock-frequency", NULL); + if (fp != 0) + l += sprintf(buffer, "%dMHz ", *fp / 1000000); + } + + switch (pvr>>16) { + case 1: + model = "601"; + break; + case 3: + model = "603"; + break; + case 4: + model = "604"; + break; + case 6: + model = "603e"; + break; + case 7: + model = "603ev"; + break; + case 9: + model = "604e"; + break; + default: + model = "unknown"; + break; + } + return l + sprintf(buffer+l, "PowerPC %s rev %d.%d\n", model, + (pvr & 0xff00) >> 8, pvr & 0xff); +} diff --git a/arch/ppc/kernel/pmac_support.c b/arch/ppc/kernel/pmac_support.c new file mode 100644 index 000000000..3c1895088 --- /dev/null +++ b/arch/ppc/kernel/pmac_support.c @@ -0,0 +1,69 @@ +/* + * Miscellaneous procedures for dealing with the PowerMac hardware. + */ +#include <linux/kernel.h> +#include <linux/stddef.h> +#include <linux/reboot.h> +#include <linux/nvram.h> +#include <asm/ptrace.h> +#include <asm/io.h> +#include <asm/cuda.h> +#include <asm/system.h> +#include <asm/prom.h> + +/* + * Read and write the non-volatile RAM on PowerMacs. + */ +static int nvram_naddrs; +static volatile unsigned char *nvram_addr; +static volatile unsigned char *nvram_data; + +void pmac_nvram_init(void) +{ + struct device_node *dp; + + dp = find_devices("nvram"); + if (dp == NULL) { + printk(KERN_ERR "Can't find NVRAM device\n"); + nvram_naddrs = 0; + return; + } + nvram_naddrs = dp->n_addrs; + if (nvram_naddrs == 1) { + nvram_data = ioremap(dp->addrs[0].address, dp->addrs[0].size); + } else if (nvram_naddrs == 2) { + nvram_addr = ioremap(dp->addrs[0].address, dp->addrs[0].size); + nvram_data = ioremap(dp->addrs[1].address, dp->addrs[1].size); + } else { + printk(KERN_ERR "Don't know how to access NVRAM with %d addresses\n", + nvram_naddrs); + } +} + +unsigned char nvram_read_byte(int addr) +{ + switch (nvram_naddrs) { + case 1: + return nvram_data[(addr & 0x1fff) << 4]; + case 2: + *nvram_addr = addr >> 5; + eieio(); + return nvram_data[(addr & 0x1f) << 4]; + } + return 0; +} + +void nvram_write_byte(unsigned char val, int addr) +{ + switch (nvram_naddrs) { + case 1: + nvram_data[(addr & 0x1fff) << 4] = val; + break; + case 2: + *nvram_addr = addr >> 5; + eieio(); + nvram_data[(addr & 0x1f) << 4] = val; + break; + } + eieio(); +} diff --git a/arch/ppc/kernel/pmac_time.c b/arch/ppc/kernel/pmac_time.c new file mode 100644 index 000000000..69a17321d --- /dev/null +++ b/arch/ppc/kernel/pmac_time.c @@ -0,0 +1,87 @@ +/* + * Support for periodic interrupts (100 per second) and for getting + * the current time from the RTC on Power Macintoshes. + * + * At present, we use the decrementer register in the 601 CPU + * for our periodic interrupts. This will probably have to be + * changed for other processors. + * + * Paul Mackerras August 1996. + * Copyright (C) 1996 Paul Mackerras. + */ +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <asm/cuda.h> +#include <asm/prom.h> +#include <asm/system.h> + +#include "time.h" + + +/* Apparently the RTC stores seconds since 1 Jan 1904 */ +#define RTC_OFFSET 2082844800 + +/* + * Query the OF and get the decr frequency. + * This was taken from the pmac time_init() when merging the prep/pmac + * time functions. + */ +void pmac_calibrate_decr(void) +{ + struct device_node *cpu; + int freq, *fp, divisor; + + /* + * The cpu node should have a timebase-frequency property + * to tell us the rate at which the decrementer counts. + */ + cpu = find_type_devices("cpu"); + if (cpu == 0) + panic("can't find cpu node in time_init"); + fp = (int *) get_property(cpu, "timebase-frequency", NULL); + if (fp == 0) + panic("can't get cpu timebase frequency"); + freq = *fp * 60; /* try to make freq/1e6 an integer */ + divisor = 60; + printk("time_init: decrementer frequency = %d/%d\n", + freq, divisor); + decrementer_count = freq / HZ / divisor; + count_period_num = divisor; + count_period_den = freq / 1000000; +} + +unsigned long +pmac_get_rtc_time(void) +{ + struct cuda_request req; + + /* Get the time from the RTC */ + cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME); + while (!req.got_reply) + cuda_poll(); + if (req.reply_len != 7) + printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n", + req.reply_len); + return (req.reply[3] << 24) + (req.reply[4] << 16) + + (req.reply[5] << 8) + req.reply[6] - RTC_OFFSET; +} + +int pmac_set_rtc_time(unsigned long nowtime) +{ + return 0; +} + +/* + * We can't do this in time_init, because via_cuda_init hasn't + * been called at that stage. + */ +void +pmac_read_rtc_time(void) +{ + xtime.tv_sec = pmac_get_rtc_time(); + xtime.tv_usec = 0; +} diff --git a/arch/ppc/kernel/ppc_asm.tmpl b/arch/ppc/kernel/ppc_asm.tmpl index e31dea266..7036f4a62 100644 --- a/arch/ppc/kernel/ppc_asm.tmpl +++ b/arch/ppc/kernel/ppc_asm.tmpl @@ -187,7 +187,3 @@ n: #define REST_8FPRS(n, base) REST_4FPRS(n, base); REST_4FPRS(n+4, base) #define REST_16FPRS(n, base) REST_8FPRS(n, base); REST_8FPRS(n+8, base) #define REST_32FPRS(n, base) REST_16FPRS(n, base); REST_16FPRS(n+16, base) - -/* Missing instructions */ -#define bdne bc 0,2, - diff --git a/arch/ppc/kernel/ppc_defs.head b/arch/ppc/kernel/ppc_defs.head new file mode 100644 index 000000000..fbe926459 --- /dev/null +++ b/arch/ppc/kernel/ppc_defs.head @@ -0,0 +1,3 @@ +/* + * WARNING! This file is automatically generated - DO NOT EDIT! + */ diff --git a/arch/ppc/kernel/ppc_htab.c b/arch/ppc/kernel/ppc_htab.c new file mode 100644 index 000000000..ea2a073eb --- /dev/null +++ b/arch/ppc/kernel/ppc_htab.c @@ -0,0 +1,221 @@ +/* + * $Id: ppc_htab.c,v 1.7 1997/08/24 19:33:32 cort Exp $ + * + * PowerPC hash table management proc entry. Will show information + * about the current hash table and will allow changes to it. + * + * Written by Cort Dougan (cort@cs.nmt.edu) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/proc_fs.h> +#include <linux/stat.h> + +#include <asm/uaccess.h> +#include <asm/bitops.h> +#include <asm/mmu.h> +#include <asm/processor.h> +#include <asm/residual.h> +#include <asm/io.h> +#include <asm/pgtable.h> + +static long ppc_htab_read(struct inode * inode, struct file * file, + char * buf, unsigned long nbytes); +static long ppc_htab_write(struct inode * inode, struct file * file, + const char * buffer, unsigned long count); +static long long ppc_htab_lseek(struct inode * inode, struct file * file, + long long offset, int orig); + +extern PTE *Hash, *Hash_end; +extern unsigned long Hash_size, Hash_mask; +extern unsigned long _SDR1; + +static struct file_operations ppc_htab_operations = { + ppc_htab_lseek, /* lseek */ + ppc_htab_read, /* read */ + ppc_htab_write, /* write */ + NULL, /* readdir */ + NULL, /* poll */ + NULL, /* ioctl */ + NULL, /* mmap */ + NULL, /* no special open code */ + NULL, /* no special release code */ + NULL /* can't fsync */ +}; + +/* + * proc files can do almost nothing.. + */ +struct inode_operations proc_ppc_htab_inode_operations = { + &ppc_htab_operations, /* default proc file-ops */ + NULL, /* create */ + NULL, /* lookup */ + NULL, /* link */ + NULL, /* unlink */ + NULL, /* symlink */ + NULL, /* mkdir */ + NULL, /* rmdir */ + NULL, /* mknod */ + NULL, /* rename */ + NULL, /* readlink */ + NULL, /* follow_link */ + NULL, /* readpage */ + NULL, /* writepage */ + NULL, /* bmap */ + NULL, /* truncate */ + NULL /* permission */ +}; + + +/* + * print some useful info about the hash table. This function + * is _REALLY_ slow (see the nested for loops below) but nothing + * in here should be really timing critical. -- Cort + */ +static long ppc_htab_read(struct inode * inode, struct file * file, + char * buf, unsigned long nbytes) +{ + int n = 0, valid; + unsigned int kptes = 0, overflow = 0, uptes = 0; + PTE *ptr; + struct task_struct *p; + char buffer[128]; + + if (nbytes < 0) + return -EINVAL; + + /* + * compute user/kernel pte's table this info can be + * misleading since there can be valid (v bit set) entries + * in the table but their vsid is used by no process (mm->context) + * due to the way tlb invalidation is handled on the ppc + * -- Cort + */ + for ( ptr = Hash ; ptr < Hash_end ; ptr += sizeof(PTE)) + { + if (ptr->v) + { + /* make sure someone is using this context/vsid */ + for_each_task(p) + { + if ( (ptr->vsid >> 4) == p->mm->context ) + { + valid = 1; + break; + } + } + if ( !valid ) + continue; + /* user not allowed read or write */ + if (ptr->pp == PP_RWXX) + kptes++; + else + uptes++; + if (ptr->h == 1) + overflow++; + } + } + + n += sprintf( buffer, + "Size\t\t: %luKb\n" + "Buckets\t\t: %lu\n" + "Address\t\t: %08lx\n" + "Entries\t\t: %lu\n" + "User ptes\t: %u\n" + "Kernel ptes\t: %u\n" + "Overflows\t: %u\n" + "Percent full\t: %%%lu\n", + (unsigned long)(Hash_size>>10), + (Hash_size/(sizeof(PTE)*8)), + (unsigned long)Hash, + Hash_size/sizeof(PTE), + uptes, + kptes, + overflow, + ((kptes+uptes)*100) / (Hash_size/sizeof(PTE)) + ); + + if (file->f_pos >= strlen(buffer)) + return 0; + if (n > strlen(buffer) - file->f_pos) + n = strlen(buffer) - file->f_pos; + copy_to_user(buf, buffer + file->f_pos, n); + file->f_pos += n; + return n; +} + +/* + * Can't _yet_ adjust the hash table size while running. -- Cort + */ +static long +ppc_htab_write(struct inode * inode, struct file * file, + const char * buffer, unsigned long count) +{ + unsigned long size; + extern void reset_SDR1(void); + + if ( current->uid != 0 ) + return -EACCES; + + /* only know how to set size right now */ + if ( strncmp( buffer, "size ", 5) ) + return -EINVAL; + + size = simple_strtoul( &buffer[5], NULL, 10 ); + + /* only allow to shrink */ + if ( size >= Hash_size>>10 ) + return -EINVAL; + + /* minimum size of htab */ + if ( size < 64 ) + return -EINVAL; + + /* make sure it's a multiple of 64k */ + if ( size % 64 ) + return -EINVAL; + + printk("Hash table resize to %luk\n", size); + /* + * We need to rehash all kernel entries for the new htab size. + * Kernel only since we do a flush_tlb_all(). Since it's kernel + * we only need to bother with vsids 0-15. To avoid problems of + * clobbering un-rehashed values we put the htab at a new spot + * and put everything there. + * -- Cort + */ + Hash_size = size<<10; + Hash_mask = (Hash_size >> 6) - 1; + _SDR1 = __pa(Hash) | (Hash_mask >> 10); + flush_tlb_all(); + + reset_SDR1(); + printk("done\n"); + return count; +} + + +static long long +ppc_htab_lseek(struct inode * inode, struct file * file, + long long offset, int orig) +{ + switch (orig) { + case 0: + file->f_pos = offset; + return(file->f_pos); + case 1: + file->f_pos += offset; + return(file->f_pos); + case 2: + return(-EINVAL); + default: + return(-EINVAL); + } +} + diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c new file mode 100644 index 000000000..fd2ad89ff --- /dev/null +++ b/arch/ppc/kernel/ppc_ksyms.c @@ -0,0 +1,140 @@ +#include <linux/config.h> +#include <linux/module.h> +#include <linux/smp.h> +#include <linux/elfcore.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/bios32.h> + +#include <asm/semaphore.h> +#include <asm/processor.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/atomic.h> +#include <asm/bitops.h> +#include <asm/checksum.h> +#include <asm/pgtable.h> +#include <asm/cuda.h> +#include <asm/prom.h> +#include <asm/system.h> +#include <asm/pci-bridge.h> + +void transfer_to_handler(); +void int_return(); +void syscall_trace(); +void do_IRQ(); +void MachineCheckException(); +void AlignmentException(); +void ProgramCheckException(); +void SingleStepException(); +void FloatingPointCheckException(); +void sys_sigreturn(); +extern unsigned lost_interrupts; +extern void do_lost_interrupts(unsigned long); + +EXPORT_SYMBOL(do_signal); +EXPORT_SYMBOL(syscall_trace); +EXPORT_SYMBOL(transfer_to_handler); +EXPORT_SYMBOL(int_return); +EXPORT_SYMBOL(do_IRQ); +EXPORT_SYMBOL(init_task_union); +EXPORT_SYMBOL(MachineCheckException); +EXPORT_SYMBOL(AlignmentException); +EXPORT_SYMBOL(ProgramCheckException); +EXPORT_SYMBOL(SingleStepException); +EXPORT_SYMBOL(sys_sigreturn); +EXPORT_SYMBOL(lost_interrupts); +EXPORT_SYMBOL(do_lost_interrupts); + +EXPORT_SYMBOL(atomic_add); +EXPORT_SYMBOL(atomic_sub); +EXPORT_SYMBOL(atomic_inc); +EXPORT_SYMBOL(atomic_inc_return); +EXPORT_SYMBOL(atomic_dec); +EXPORT_SYMBOL(atomic_dec_return); +EXPORT_SYMBOL(atomic_dec_and_test); + +EXPORT_SYMBOL(set_bit); +EXPORT_SYMBOL(clear_bit); +EXPORT_SYMBOL(change_bit); +EXPORT_SYMBOL(test_and_set_bit); +EXPORT_SYMBOL(test_and_clear_bit); +EXPORT_SYMBOL(test_and_change_bit); +#if 0 +EXPORT_SYMBOL(ffz); +EXPORT_SYMBOL(find_first_zero_bit); +EXPORT_SYMBOL(find_next_zero_bit); +#endif + +EXPORT_SYMBOL(strcpy); +EXPORT_SYMBOL(strncpy); +EXPORT_SYMBOL(strcat); +EXPORT_SYMBOL(strncat); +EXPORT_SYMBOL(strchr); +EXPORT_SYMBOL(strrchr); +EXPORT_SYMBOL(strpbrk); +EXPORT_SYMBOL(strtok); +EXPORT_SYMBOL(strstr); +EXPORT_SYMBOL(strlen); +EXPORT_SYMBOL(strnlen); +EXPORT_SYMBOL(strspn); +EXPORT_SYMBOL(strcmp); +EXPORT_SYMBOL(strncmp); +EXPORT_SYMBOL(memset); +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(memmove); +EXPORT_SYMBOL(memscan); +EXPORT_SYMBOL(memcmp); + +/* EXPORT_SYMBOL(csum_partial); already in net/netsyms.c */ +EXPORT_SYMBOL(csum_partial_copy_generic); +EXPORT_SYMBOL(ip_fast_csum); +EXPORT_SYMBOL(csum_tcpudp_magic); + +EXPORT_SYMBOL(__copy_tofrom_user); +EXPORT_SYMBOL(__clear_user); +EXPORT_SYMBOL(__strncpy_from_user); +EXPORT_SYMBOL(strlen_user); + +/* +EXPORT_SYMBOL(inb); +EXPORT_SYMBOL(inw); +EXPORT_SYMBOL(inl); +EXPORT_SYMBOL(outb); +EXPORT_SYMBOL(outw); +EXPORT_SYMBOL(outl); +EXPORT_SYMBOL(outsl);*/ + +EXPORT_SYMBOL(_insw); +EXPORT_SYMBOL(_outsw); +EXPORT_SYMBOL(_insl); +EXPORT_SYMBOL(_outsl); +EXPORT_SYMBOL(ioremap); + +EXPORT_SYMBOL(start_thread); + +EXPORT_SYMBOL(__down_interruptible); + +EXPORT_SYMBOL(__cli); +EXPORT_SYMBOL(__sti); +/*EXPORT_SYMBOL(__restore_flags);*/ +EXPORT_SYMBOL(_disable_interrupts); +EXPORT_SYMBOL(_enable_interrupts); +EXPORT_SYMBOL(flush_instruction_cache); +EXPORT_SYMBOL(_get_PVR); +EXPORT_SYMBOL(giveup_fpu); +EXPORT_SYMBOL(flush_icache_range); +EXPORT_SYMBOL(xchg_u32); + +EXPORT_SYMBOL(cuda_request); +EXPORT_SYMBOL(cuda_send_request); +EXPORT_SYMBOL(adb_register); +EXPORT_SYMBOL(abort); +EXPORT_SYMBOL(call_prom); +EXPORT_SYMBOL(find_devices); +EXPORT_SYMBOL(find_type_devices); +EXPORT_SYMBOL(find_path_device); +EXPORT_SYMBOL(get_property); +EXPORT_SYMBOL(pci_io_base); +EXPORT_SYMBOL(pci_device_loc); +EXPORT_SYMBOL(note_scsi_host); diff --git a/arch/ppc/kernel/prep_pci.c b/arch/ppc/kernel/prep_pci.c new file mode 100644 index 000000000..ffacd94c9 --- /dev/null +++ b/arch/ppc/kernel/prep_pci.c @@ -0,0 +1,436 @@ +/* + * $Id: prep_pci.c,v 1.7 1997/08/23 22:46:02 cort Exp $ + * PReP pci functions. + * Originally by Gary Thomas + * rewritten and updated by Cort Dougan (cort@cs.nmt.edu) + * + * The motherboard routes/maps will disappear shortly. -- Cort + */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/bios32.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/init.h> + +#include <asm/byteorder.h> +#include <asm/io.h> +#include <asm/ptrace.h> +#include <asm/processor.h> + +#define MAX_DEVNR 22 + +/* Which PCI interrupt line does a given device [slot] use? */ +/* Note: This really should be two dimensional based in slot/pin used */ +unsigned char *Motherboard_map; +unsigned char *Motherboard_map_name; + +/* How is the 82378 PIRQ mapping setup? */ +unsigned char *Motherboard_routes; + +/* Tables for known hardware */ + +/* Motorola PowerStack */ +static char Blackhawk_pci_IRQ_map[16] = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 3, /* Slot 12 - SCSI */ + 0, /* Slot 13 - unused */ + 1, /* Slot 14 - Ethernet */ + 0, /* Slot 15 - unused */ +}; + +static char Blackhawk_pci_IRQ_routes[] = +{ + 0, /* Line 0 - Unused */ + 9, /* Line 1 */ + 11, /* Line 2 */ + 14, /* Line 3 */ + 15 /* Line 4 */ +}; + +/* Motorola MVME16xx */ +static char Genesis_pci_IRQ_map[16] = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 3, /* Slot 12 - SCSI */ + 0, /* Slot 13 - unused */ + 1, /* Slot 14 - Ethernet */ + 0, /* Slot 15 - unused */ +}; + +static char Genesis_pci_IRQ_routes[] = +{ + 0, /* Line 0 - Unused */ + 10, /* Line 1 */ + 11, /* Line 2 */ + 14, /* Line 3 */ + 15 /* Line 4 */ +}; + +/* Motorola Series-E */ +static char Comet_pci_IRQ_map[16] = +{ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 3, /* Slot 12 - SCSI */ + 0, /* Slot 13 - unused */ + 1, /* Slot 14 - Ethernet */ + 0, /* Slot 15 - unused */ +}; + +static char Comet_pci_IRQ_routes[] = +{ + 0, /* Line 0 - Unused */ + 10, /* Line 1 */ + 11, /* Line 2 */ + 14, /* Line 3 */ + 15 /* Line 4 */ +}; + +/* + * ibm 830 (and 850?). + * This is actually based on the Carolina motherboard + * -- Cort + */ +static char ibm8xx_pci_IRQ_map[23] = { + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - FireCoral */ + 4, /* Slot 12 - Ethernet PCIINTD# */ + 2, /* Slot 13 - PCI Slot #2 */ + 2, /* Slot 14 - S3 Video PCIINTD# */ + 0, /* Slot 15 - onboard SCSI (INDI) [1] */ + 3, /* Slot 16 - NCR58C810 RS6000 Only PCIINTC# */ + 0, /* Slot 17 - unused */ + 2, /* Slot 18 - PCI Slot 2 PCIINTx# (See below) */ + 0, /* Slot 19 - unused */ + 0, /* Slot 20 - unused */ + 0, /* Slot 21 - unused */ + 2, /* Slot 22 - PCI slot 1 PCIINTx# (See below) */ +}; +static char ibm8xx_pci_IRQ_routes[] = { + 0, /* Line 0 - unused */ + 13, /* Line 1 */ + 10, /* Line 2 */ + 15, /* Line 3 */ + 15, /* Line 4 */ +}; + +/* IBM Nobis and 850 */ +static char Nobis_pci_IRQ_map[23] ={ + 0, /* Slot 0 - unused */ + 0, /* Slot 1 - unused */ + 0, /* Slot 2 - unused */ + 0, /* Slot 3 - unused */ + 0, /* Slot 4 - unused */ + 0, /* Slot 5 - unused */ + 0, /* Slot 6 - unused */ + 0, /* Slot 7 - unused */ + 0, /* Slot 8 - unused */ + 0, /* Slot 9 - unused */ + 0, /* Slot 10 - unused */ + 0, /* Slot 11 - unused */ + 3, /* Slot 12 - SCSI */ + 0, /* Slot 13 - unused */ + 0, /* Slot 14 - unused */ + 0, /* Slot 15 - unused */ +}; + +static char Nobis_pci_IRQ_routes[] = { + 0, /* Line 0 - Unused */ + 13, /* Line 1 */ + 13, /* Line 2 */ + 13, /* Line 3 */ + 13 /* Line 4 */ +}; + +/* We have to turn on LEVEL mode for changed IRQ's */ +/* All PCI IRQ's need to be level mode, so this should be something + * other than hard-coded as well... IRQ's are individually mappable + * to either edge or level. + */ +#define CAROLINA_IRQ_EDGE_MASK_LO 0x00 /* IRQ's 0-7 */ +#define CAROLINA_IRQ_EDGE_MASK_HI 0xA4 /* IRQ's 8-15 [10,13,15] */ + +int +prep_pcibios_read_config_dword (unsigned char bus, + unsigned char dev, unsigned char offset, unsigned int *val) +{ + unsigned long _val; + unsigned long *ptr; + dev >>= 3; + + if ((bus != 0) || (dev > MAX_DEVNR)) + { + *val = 0xFFFFFFFF; + return PCIBIOS_DEVICE_NOT_FOUND; + } else + { + ptr = (unsigned long *)(0x80800000 | (1<<dev) | offset); + _val = le32_to_cpu(*ptr); + } + *val = _val; + return PCIBIOS_SUCCESSFUL; +} + +int +prep_pcibios_read_config_word (unsigned char bus, + unsigned char dev, unsigned char offset, unsigned short *val) +{ + unsigned short _val; + unsigned short *ptr; + dev >>= 3; + if ((bus != 0) || (dev > MAX_DEVNR)) + { + *val = (unsigned short)0xFFFFFFFF; + return PCIBIOS_DEVICE_NOT_FOUND; + } else + { + ptr = (unsigned short *)(0x80800000 | (1<<dev) | offset); + _val = le16_to_cpu(*ptr); + } + *val = _val; + return PCIBIOS_SUCCESSFUL; +} + +int +prep_pcibios_read_config_byte (unsigned char bus, + unsigned char dev, unsigned char offset, unsigned char *val) +{ + unsigned char _val; + volatile unsigned char *ptr; + dev >>= 3; + /* Note: the configuration registers don't always have this right! */ + if (offset == PCI_INTERRUPT_LINE) + { + *val = Motherboard_routes[Motherboard_map[dev]]; +/*printk("dev %d map %d route %d on board %d\n", + dev,Motherboard_map[dev], + Motherboard_routes[Motherboard_map[dev]], + *(unsigned char *)(0x80800000 | (1<<dev) | (offset ^ 1)));*/ + return PCIBIOS_SUCCESSFUL; + } + if ((bus != 0) || (dev > MAX_DEVNR)) + { + *(unsigned long *)val = (unsigned long) 0xFFFFFFFF; + return PCIBIOS_DEVICE_NOT_FOUND; + } else + { + ptr = (unsigned char *)(0x80800000 | (1<<dev) | (offset ^ 1)); + _val = *ptr; + } + *val = _val; + return PCIBIOS_SUCCESSFUL; +} + +int +prep_pcibios_write_config_dword (unsigned char bus, + unsigned char dev, unsigned char offset, unsigned int val) +{ + unsigned long _val; + unsigned long *ptr; + dev >>= 3; + _val = le32_to_cpu(val); + if ((bus != 0) || (dev > MAX_DEVNR)) + { + return PCIBIOS_DEVICE_NOT_FOUND; + } else + { + ptr = (unsigned long *)(0x80800000 | (1<<dev) | offset); + *ptr = _val; + } + return PCIBIOS_SUCCESSFUL; +} + +int +prep_pcibios_write_config_word (unsigned char bus, + unsigned char dev, unsigned char offset, unsigned short val) +{ + unsigned short _val; + unsigned short *ptr; + dev >>= 3; + _val = le16_to_cpu(val); + if ((bus != 0) || (dev > MAX_DEVNR)) + { + return PCIBIOS_DEVICE_NOT_FOUND; + } else + { + ptr = (unsigned short *)(0x80800000 | (1<<dev) | offset); + *ptr = _val; + } + return PCIBIOS_SUCCESSFUL; +} + +int +prep_pcibios_write_config_byte (unsigned char bus, + unsigned char dev, unsigned char offset, unsigned char val) +{ + unsigned char _val; + unsigned char *ptr; + dev >>= 3; + _val = val; + if ((bus != 0) || (dev > MAX_DEVNR)) + { + return PCIBIOS_DEVICE_NOT_FOUND; + } else + { + ptr = (unsigned char *)(0x80800000 | (1<<dev) | (offset^1)); + *ptr = _val; + } + return PCIBIOS_SUCCESSFUL; +} + +int prep_pcibios_find_device (unsigned short vendor, unsigned short device_id, + unsigned short index, unsigned char *bus, + unsigned char *devfn) +{ + unsigned int curr = 0; + struct pci_dev *dev; +/*printk("pcibios_find_device(): vendor %04x devid %04x index %d\n", + vendor,device_id,index);*/ + for (dev = pci_devices; dev; dev = dev->next) { +/*printk(" dev->vendor %04x dev->device %04x\n", + dev->vendor,dev->device);*/ + if (dev->vendor == vendor && dev->device == device_id) { + if (curr == index) { + *devfn = dev->devfn; + *bus = dev->bus->number; + return PCIBIOS_SUCCESSFUL; + } + ++curr; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + +/* + * Given the class, find the n'th instance of that device + * in the system. + */ +int prep_pcibios_find_class (unsigned int class_code, unsigned short index, + unsigned char *bus, unsigned char *devfn) +{ + unsigned int curr = 0; + struct pci_dev *dev; + + for (dev = pci_devices; dev; dev = dev->next) { + if (dev->class == class_code) { + if (curr == index) { + *devfn = dev->devfn; + *bus = dev->bus->number; + return PCIBIOS_SUCCESSFUL; + } + ++curr; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + +__initfunc(unsigned long route_pci_interrupts(void)) +{ + unsigned char *ibc_pirq = (unsigned char *)0x80800860; + unsigned char *ibc_pcicon = (unsigned char *)0x80800840; + int i; + + if ( _machine == _MACH_Motorola) + { + switch (inb(0x800) & 0xF0) + { + case 0x10: /* MVME16xx */ + Motherboard_map_name = "Genesis"; + Motherboard_map = Genesis_pci_IRQ_map; + Motherboard_routes = Genesis_pci_IRQ_routes; + break; + case 0x20: /* Series E */ + Motherboard_map_name = "Series E"; + Motherboard_map = Comet_pci_IRQ_map; + Motherboard_routes = Comet_pci_IRQ_routes; + break; + case 0x40: /* PowerStack */ + default: /* Can't hurt, can it? */ + Motherboard_map_name = "Blackhawk (Powerstack)"; + Motherboard_map = Blackhawk_pci_IRQ_map; + Motherboard_routes = Blackhawk_pci_IRQ_routes; + break; + } + } else if ( _machine == _MACH_IBM ) + { + unsigned char pl_id; + + if (inb(0x0852) == 0xFF) { + Motherboard_map_name = "IBM 850/860 Portable\n"; + Motherboard_map = Nobis_pci_IRQ_map; + Motherboard_routes = Nobis_pci_IRQ_routes; + } else { + Motherboard_map_name = "IBM 8xx (Carolina)"; + Motherboard_map = ibm8xx_pci_IRQ_map; + Motherboard_routes = ibm8xx_pci_IRQ_routes; + } + /*printk("Changing IRQ mode\n");*/ + pl_id=inb(0x04d0); + /*printk("Low mask is %#0x\n", pl_id);*/ + outb(pl_id|CAROLINA_IRQ_EDGE_MASK_LO, 0x04d0); + + pl_id=inb(0x04d1); + /*printk("Hi mask is %#0x\n", pl_id);*/ + outb(pl_id|CAROLINA_IRQ_EDGE_MASK_HI, 0x04d1); + pl_id=inb(0x04d1); + /*printk("Hi mask now %#0x\n", pl_id);*/ + } else + { + printk("No known machine pci routing!\n"); + return -1; + } + + /* Set up mapping from slots */ + for (i = 1; i <= 4; i++) + { + ibc_pirq[i-1] = Motherboard_routes[i]; + } + /* Enable PCI interrupts */ + *ibc_pcicon |= 0x20; + return 0; +} diff --git a/arch/ppc/kernel/prep_setup.c b/arch/ppc/kernel/prep_setup.c new file mode 100644 index 000000000..18e013964 --- /dev/null +++ b/arch/ppc/kernel/prep_setup.c @@ -0,0 +1,294 @@ +/* + * linux/arch/ppc/kernel/setup.c + * + * Copyright (C) 1995 Linus Torvalds + * Adapted from 'alpha' version by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + */ + +/* + * bootup setup stuff.. + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/tty.h> +#include <linux/major.h> +#include <linux/interrupt.h> +#include <linux/reboot.h> +#include <linux/init.h> +#include <linux/blk.h> +#include <linux/ioport.h> + +#include <asm/mmu.h> +#include <asm/processor.h> +#include <asm/residual.h> +#include <asm/io.h> +#include <asm/pgtable.h> +#include <asm/ide.h> + +/* for the mac fs */ +kdev_t boot_dev; + +extern PTE *Hash, *Hash_end; +extern unsigned long Hash_size, Hash_mask; +extern int probingmem; +extern unsigned long loops_per_sec; + +unsigned long empty_zero_page[1024]; +extern unsigned char aux_device_present; + +#ifdef CONFIG_BLK_DEV_RAM +extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ +extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */ +extern int rd_image_start; /* starting block # of image */ +#endif + + +extern char saved_command_line[256]; + +void prep_ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq) +{ + ide_ioreg_t port = base; + int i = 8; + + while (i--) + *p++ = port++; + *p++ = base + 0x206; + if (irq != NULL) + *irq = 0; +} + + +int +prep_get_cpuinfo(char *buffer) +{ + extern char *Motherboard_map_name; + extern RESIDUAL res; + int i; + int pvr = _get_PVR(); + int len; + char *model; + + switch (pvr>>16) + { + case 1: + model = "601"; + break; + case 3: + model = "603"; + break; + case 4: + model = "604"; + break; + case 6: + model = "603e"; + break; + case 7: + model = "603ev"; + break; + default: + model = "unknown"; + break; + } + +#ifdef __SMP__ +#define CD(X) (cpu_data[n].X) +#else +#define CD(X) (X) +#define CPUN 0 +#endif + + len = sprintf(buffer,"processor\t: %d\n" + "cpu\t\t: %s\n" + "revision\t: %d.%d\n" + "upgrade\t\t: %s\n" + "clock\t\t: %dMHz\n" + "bus clock\t: %dMHz\n" + "machine\t\t: %s (sn %s)\n" + "pci map\t\t: %s\n", + CPUN, + model, + MAJOR(pvr), MINOR(pvr), + (inb(IBM_EQUIP_PRESENT) & 2) ? "not upgrade" : "upgrade", + (res.VitalProductData.ProcessorHz > 1024) ? + res.VitalProductData.ProcessorHz>>20 : + res.VitalProductData.ProcessorHz, + (res.VitalProductData.ProcessorBusHz > 1024) ? + res.VitalProductData.ProcessorBusHz>>20 : + res.VitalProductData.ProcessorBusHz, + res.VitalProductData.PrintableModel, + res.VitalProductData.Serial, + Motherboard_map_name + ); + + /* print info about SIMMs */ + len += sprintf(buffer+len,"simms\t\t: "); + for ( i = 0 ; (res.ActualNumMemories) && (i < MAX_MEMS) ; i++ ) + { + if ( res.Memories[i].SIMMSize != 0 ) + len += sprintf(buffer+len,"%d:%dM ",i, + (res.Memories[i].SIMMSize > 1024) ? + res.Memories[i].SIMMSize>>20 : + res.Memories[i].SIMMSize); + } + len += sprintf(buffer+len,"\n"); + + /* TLB */ + len += sprintf(buffer+len,"tlb\t\t:"); + switch(res.VitalProductData.TLBAttrib) + { + case CombinedTLB: + len += sprintf(buffer+len," %d entries\n", + res.VitalProductData.TLBSize); + break; + case SplitTLB: + len += sprintf(buffer+len," (split I/D) %d/%d entries\n", + res.VitalProductData.I_TLBSize, + res.VitalProductData.D_TLBSize); + break; + case NoneTLB: + len += sprintf(buffer+len," not present\n"); + break; + } + + /* L1 */ + len += sprintf(buffer+len,"l1\t\t: "); + switch(res.VitalProductData.CacheAttrib) + { + case CombinedCAC: + len += sprintf(buffer+len,"%dkB LineSize\n", + res.VitalProductData.CacheSize, + res.VitalProductData.CacheLineSize); + break; + case SplitCAC: + len += sprintf(buffer+len,"(split I/D) %dkB/%dkB Linesize %dB/%dB\n", + res.VitalProductData.I_CacheSize, + res.VitalProductData.D_CacheSize, + res.VitalProductData.D_CacheLineSize, + res.VitalProductData.D_CacheLineSize); + break; + case NoneCAC: + len += sprintf(buffer+len,"not present\n"); + break; + } + + /* L2 */ + if ( (inb(IBM_EQUIP_PRESENT) & 1) == 0) /* l2 present */ + { + len += sprintf(buffer+len,"l2\t\t: %dkB %s\n", + ((inb(IBM_L2_STATUS) >> 5) & 1) ? 512 : 256, + (inb(IBM_SYS_CTL) & 64) ? "enabled" : "disabled"); + } + else + { + len += sprintf(buffer+len,"l2\t\t: not present\n"); + } + + + len += sprintf(buffer+len, "bogomips\t: %lu.%02lu\n", + CD(loops_per_sec+2500)/500000, + (CD(loops_per_sec+2500)/5000) % 100); + + /* + * Ooh's and aah's info about zero'd pages in idle task + */ + { + extern unsigned int zerocount, zerototal, zeropage_hits,zeropage_calls; + len += sprintf(buffer+len,"zero pages\t: total %u (%uKb) " + "current: %u (%uKb) hits: %u/%u (%lu%%)\n", + zerototal, (zerototal*PAGE_SIZE)>>10, + zerocount, (zerocount*PAGE_SIZE)>>10, + zeropage_hits,zeropage_calls, + /* : 1 below is so we don't div by zero */ + (zeropage_hits*100) / + ((zeropage_calls)?zeropage_calls:1)); + } + return len; +} + +__initfunc(void +prep_setup_arch(char **cmdline_p, unsigned long * memory_start_p, + unsigned long * memory_end_p)) +{ + extern char cmd_line[]; + extern char _etext[], _edata[], _end[]; + extern int panic_timeout; + unsigned char reg; + + /* Save unparsed command line copy for /proc/cmdline */ + strcpy( saved_command_line, cmd_line ); + *cmdline_p = cmd_line; + + *memory_start_p = (unsigned long) Hash+Hash_size; + (unsigned long *)*memory_end_p = (unsigned long *)(res.TotalMemory+KERNELBASE); + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_sec = 50000000; + + /* reboot on panic */ + panic_timeout = 180; + + init_task.mm->start_code = PAGE_OFFSET; + init_task.mm->end_code = (unsigned long) _etext; + init_task.mm->end_data = (unsigned long) _edata; + init_task.mm->brk = (unsigned long) _end; + + aux_device_present = 0xaa; + /* Set up floppy in PS/2 mode */ + outb(0x09, SIO_CONFIG_RA); + reg = inb(SIO_CONFIG_RD); + reg = (reg & 0x3F) | 0x40; + outb(reg, SIO_CONFIG_RD); + outb(reg, SIO_CONFIG_RD); /* Have to write twice to change! */ + + switch ( _machine ) + { + case _MACH_IBM: + ROOT_DEV = to_kdev_t(0x0301); /* hda1 */ + break; + case _MACH_Motorola: + ROOT_DEV = to_kdev_t(0x0801); /* sda1 */ + break; + } + +#ifdef CONFIG_BLK_DEV_RAM +#if 0 + ROOT_DEV = to_kdev_t(0x0200); /* floppy */ + rd_prompt = 1; + rd_doload = 1; + rd_image_start = 0; +#endif + /* initrd_start and size are setup by boot/head.S and kernel/head.S */ + if ( initrd_start ) + { + if (initrd_end > *memory_end_p) + { + printk("initrd extends beyond end of memory " + "(0x%08lx > 0x%08lx)\ndisabling initrd\n", + initrd_end,*memory_end_p); + initrd_start = 0; + } + } +#endif + + printk("Boot arguments: %s\n", cmd_line); + + print_residual_device_info(); + + request_region(0x20,0x20,"pic1"); + request_region(0xa0,0x20,"pic2"); + request_region(0x00,0x20,"dma1"); + request_region(0x40,0x20,"timer"); + request_region(0x80,0x10,"dma page reg"); + request_region(0xc0,0x20,"dma2"); +} diff --git a/arch/ppc/kernel/prep_time.c b/arch/ppc/kernel/prep_time.c new file mode 100644 index 000000000..da0151ede --- /dev/null +++ b/arch/ppc/kernel/prep_time.c @@ -0,0 +1,239 @@ +/* + * linux/arch/i386/kernel/time.c + * + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + * + * Adapted for PowerPC (PreP) by Gary Thomas + * Modified by Cort Dougan (cort@cs.nmt.edu) + * copied and modified from intel version + * + */ +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/timex.h> +#include <linux/kernel_stat.h> + +#include <asm/segment.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/nvram.h> + +#include "time.h" + +/* + * The motorola uses the m48t18 rtc (includes DS1643) whose registers + * are at a higher end of nvram (1ff8-1fff) than the ibm mc146818 + * rtc (ds1386) which has regs at addr 0-d). The intel gets + * past this because the bios emulates the mc146818. + * + * Why in the world did they have to use different clocks? + * + * Right now things are hacked to check which machine we're on then + * use the appropriate macro. This is very very ugly and I should + * probably have a function that checks which machine we're on then + * does things correctly transparently or a function pointer which + * is setup at boot time to use the correct addresses. + * -- Cort + */ +/* + * translate from mc146818 to m48t18 addresses + */ +unsigned int clock_transl[] = { MOTO_RTC_SECONDS,0 /* alarm */, + MOTO_RTC_MINUTES,0 /* alarm */, + MOTO_RTC_HOURS,0 /* alarm */, /* 4,5 */ + MOTO_RTC_DAY_OF_WEEK, + MOTO_RTC_DAY_OF_MONTH, + MOTO_RTC_MONTH, + MOTO_RTC_YEAR, /* 9 */ + MOTO_RTC_CONTROLA, MOTO_RTC_CONTROLB /* 10,11 */ +}; + +int prep_cmos_clock_read(int addr) +{ + if ( _machine == _MACH_IBM ) + return CMOS_READ(addr); + else if ( _machine == _MACH_Motorola ) + { + outb(clock_transl[addr]>>8, NVRAM_AS1); + outb(clock_transl[addr], NVRAM_AS0); + return (inb(NVRAM_DATA)); + } + + printk("Unknown machine in prep_cmos_clock_read()!\n"); + return -1; +} + +void prep_cmos_clock_write(unsigned long val, int addr) +{ + if ( _machine == _MACH_IBM ) + { + CMOS_WRITE(val,addr); + return; + } + else if ( _machine == _MACH_Motorola ) + { + outb(clock_transl[addr]>>8, NVRAM_AS1); + outb(clock_transl[addr], NVRAM_AS0); + outb(val,NVRAM_DATA); + return; + } + printk("Unknown machine in prep_cmos_clock_write()!\n"); +} + +/* + * Set the hardware clock. -- Cort + */ +int prep_set_rtc_time(unsigned long nowtime) +{ + unsigned char save_control, save_freq_select; + struct rtc_time tm; + + to_tm(nowtime, &tm); + + save_control = prep_cmos_clock_read(RTC_CONTROL); /* tell the clock it's being set */ + + prep_cmos_clock_write((save_control|RTC_SET), RTC_CONTROL); + + save_freq_select = prep_cmos_clock_read(RTC_FREQ_SELECT); /* stop and reset prescaler */ + + prep_cmos_clock_write((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); + + tm.tm_year -= 1900; + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + BIN_TO_BCD(tm.tm_sec); + BIN_TO_BCD(tm.tm_min); + BIN_TO_BCD(tm.tm_hour); + BIN_TO_BCD(tm.tm_mon); + BIN_TO_BCD(tm.tm_mday); + BIN_TO_BCD(tm.tm_year); + } + prep_cmos_clock_write(tm.tm_sec,RTC_SECONDS); + prep_cmos_clock_write(tm.tm_min,RTC_MINUTES); + prep_cmos_clock_write(tm.tm_hour,RTC_HOURS); + prep_cmos_clock_write(tm.tm_mon,RTC_MONTH); + prep_cmos_clock_write(tm.tm_mday,RTC_DAY_OF_MONTH); + prep_cmos_clock_write(tm.tm_year,RTC_YEAR); + + /* The following flags have to be released exactly in this order, + * otherwise the DS12887 (popular MC146818A clone with integrated + * battery and quartz) will not reset the oscillator and will not + * update precisely 500 ms later. You won't find this mentioned in + * the Dallas Semiconductor data sheets, but who believes data + * sheets anyway ... -- Markus Kuhn + */ + prep_cmos_clock_write(save_control, RTC_CONTROL); + prep_cmos_clock_write(save_freq_select, RTC_FREQ_SELECT); + + if ( (time_state == TIME_ERROR) || (time_state == TIME_BAD) ) + time_state = TIME_OK; + return 0; +} + +unsigned long prep_get_rtc_time(void) +{ + unsigned int year, mon, day, hour, min, sec; + int i; + + /* The Linux interpretation of the CMOS clock register contents: + * When the Update-In-Progress (UIP) flag goes from 1 to 0, the + * RTC registers show the second which has precisely just started. + * Let's hope other operating systems interpret the RTC the same way. + */ + /* read RTC exactly on falling edge of update flag */ + for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */ + if (prep_cmos_clock_read(RTC_FREQ_SELECT) & RTC_UIP) + break; + for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */ + if (!(prep_cmos_clock_read(RTC_FREQ_SELECT) & RTC_UIP)) + break; + do { /* Isn't this overkill ? UIP above should guarantee consistency */ + sec = prep_cmos_clock_read(RTC_SECONDS); + min = prep_cmos_clock_read(RTC_MINUTES); + hour = prep_cmos_clock_read(RTC_HOURS); + day = prep_cmos_clock_read(RTC_DAY_OF_MONTH); + mon = prep_cmos_clock_read(RTC_MONTH); + year = prep_cmos_clock_read(RTC_YEAR); + } while (sec != prep_cmos_clock_read(RTC_SECONDS)); + if (!(prep_cmos_clock_read(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) + { + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + } + if ((year += 1900) < 1970) + year += 100; + return mktime(year, mon, day, hour, min, sec); +} + +#if 0 +void time_init(void) +{ + void (*irq_handler)(int, void *,struct pt_regs *); + + xtime.tv_sec = prep_get_rtc_time(); + xtime.tv_usec = 0; + + prep_calibrate_decr(); + + /* If we have the CPU hardware time counters, use them */ + irq_handler = timer_interrupt; + if (request_irq(TIMER_IRQ, irq_handler, 0, "timer", NULL) != 0) + panic("Could not allocate timer IRQ!"); +} + +/* + * timer_interrupt() needs to keep up the real-time clock, + * as well as call the "do_timer()" routine every clocktick + */ +static inline void timer_interrupt(int irq, void *dev, struct pt_regs * regs) +{ + prep_calibrate_decr_handler(irq,dev,regs); + do_timer(regs); + + /* update the hw clock if: + * the time is marked out of sync (TIME_ERROR) + * or ~11 minutes have expired since the last update -- Cort + * If we have an externally synchronized Linux clock, then update + * CMOS clock accordingly every ~11 minutes. prep_set_rtc_mmss() has to be + * called as close as possible to 500 ms before the new second starts. + */ + if ( time_state == TIME_BAD || + xtime.tv_sec > last_rtc_update + 660 ) + /*if (time_state != TIME_BAD && xtime.tv_sec > last_rtc_update + 660 && + xtime.tv_usec > 500000 - (tick >> 1) && + xtime.tv_usec < 500000 + (tick >> 1))*/ + if (prep_set_rtc_time(xtime.tv_sec) == 0) + last_rtc_update = xtime.tv_sec; + else + last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ + + +#ifdef CONFIG_HEARTBEAT + /* use hard disk LED as a heartbeat instead -- much more useful + for debugging -- Cort */ + switch(kstat.interrupts[0] % 101) + { + /* act like an actual heart beat -- ie thump-thump-pause... */ + case 0: + case 20: + outb(1,IBM_HDD_LED); + break; + case 7: + case 27: + outb(0,IBM_HDD_LED); + break; + case 100: + break; + } +#endif +} +#endif diff --git a/arch/ppc/kernel/process.c b/arch/ppc/kernel/process.c index f24872063..5e194fedb 100644 --- a/arch/ppc/kernel/process.c +++ b/arch/ppc/kernel/process.c @@ -7,7 +7,7 @@ * Derived from "arch/i386/kernel/process.c" * Copyright (C) 1995 Linus Torvalds * - * Modified by Cort Dougan (cort@cs.nmt.edu) and + * Updated and modified by Cort Dougan (cort@cs.nmt.edu) and * Paul Mackerras (paulus@cs.anu.edu.au) * * This program is free software; you can redistribute it and/or @@ -28,22 +28,22 @@ #include <linux/ptrace.h> #include <linux/malloc.h> #include <linux/user.h> -#include <linux/a.out.h> +#include <linux/elf.h> #include <linux/config.h> +#include <linux/elf.h> #include <asm/pgtable.h> #include <asm/uaccess.h> #include <asm/system.h> #include <asm/io.h> #include <asm/smp_lock.h> +#include <asm/processor.h> -int dump_fpu(void); +int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs); void switch_to(struct task_struct *, struct task_struct *); -void print_backtrace(unsigned long *); -void show_regs(struct pt_regs * regs); -void inline zero_paged(void); extern unsigned long _get_SP(void); + #undef SHOW_TASK_SWITCHES 1 #undef CHECK_STACK 1 #undef IDLE_ZERO 1 @@ -59,7 +59,7 @@ task_top(struct task_struct *tsk) { return ((unsigned long)tsk) + sizeof(struct task_struct); } - + static struct vm_area_struct init_mmap = INIT_MMAP; static struct fs_struct init_fs = INIT_FS; static struct files_struct init_files = INIT_FILES; @@ -69,9 +69,12 @@ struct mm_struct init_mm = INIT_MM; union task_union init_task_union = { INIT_TASK }; int -dump_fpu(void) +dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs) { - return (1); + if (last_task_used_math == current) + giveup_fpu(); + memcpy(fpregs, ¤t->tss.fpr[0], sizeof(*fpregs)); + return 1; } /* check to make sure the kernel stack is healthy */ @@ -80,7 +83,6 @@ int check_stack(struct task_struct *tsk) unsigned long stack_top = kernel_stack_top(tsk); unsigned long tsk_top = task_top(tsk); int ret = 0; - unsigned long *i; #if 0 /* check tss magic */ @@ -98,7 +100,7 @@ int check_stack(struct task_struct *tsk) if ( (tsk->tss.ksp > stack_top) || (tsk->tss.ksp < tsk_top) ) { printk("stack out of bounds: %s/%d\n" - " tsk_top %08x ksp %08x stack_top %08x\n", + " tsk_top %08lx ksp %08lx stack_top %08lx\n", tsk->comm,tsk->pid, tsk_top, tsk->tss.ksp, stack_top); ret |= 2; @@ -108,7 +110,7 @@ int check_stack(struct task_struct *tsk) if ( (tsk == current) && ((_get_SP() > stack_top ) || (_get_SP() < tsk_top)) ) { printk("current stack ptr out of bounds: %s/%d\n" - " tsk_top %08x sp %08x stack_top %08x\n", + " tsk_top %08lx sp %08lx stack_top %08lx\n", current->comm,current->pid, tsk_top, _get_SP(), stack_top); ret |= 4; @@ -143,15 +145,12 @@ switch_to(struct task_struct *prev, struct task_struct *new) { struct thread_struct *new_tss, *old_tss; int s = _disable_interrupts(); - struct pt_regs *regs = (struct pt_regs *)(new->tss.ksp+STACK_FRAME_OVERHEAD); #if CHECK_STACK check_stack(prev); check_stack(new); #endif - /* turn off fpu for task last to run */ - /*prev->tss.regs->msr &= ~MSR_FP;*/ - + #ifdef SHOW_TASK_SWITCHES printk("%s/%d (%x) -> %s/%d (%x) ctx %x\n", prev->comm,prev->pid,prev->tss.regs->nip, @@ -160,289 +159,29 @@ switch_to(struct task_struct *prev, struct task_struct *new) new_tss = &new->tss; old_tss = ¤t->tss; _switch(old_tss, new_tss, new->mm->context); - /* turn off fpu for task last to run */ _enable_interrupts(s); } - -#include <linux/mc146818rtc.h> -asmlinkage int sys_debug(long a, long b, long c, long d, long e, long f,struct pt_regs *regs) -{ -#if 1 - struct task_struct *p; - printk("sys_debug(): r3 %x r4 %x r5 %x r6 %x\n", a,b,c,d); - printk("last %x\n", last_task_used_math); - printk("cur %x regs %x/%x tss %x/%x\n", - current, current->tss.regs,regs,¤t->tss,current->tss); - for_each_task(p) - { - if ((long)p < KERNELBASE) - { - printk("nip %x lr %x r3 %x\n", regs->nip,regs->link,a); - print_mm_info(); - __cli(); - while(1); - } - } - return regs->gpr[3]; -#endif -#if 0 - /* set the time in the cmos clock */ - unsigned long hwtime, nowtime; - struct rtc_time tm; - - hwtime = get_cmos_time(); - to_tm(hwtime, &tm); - printk("hw: H:M:S M/D/Y %02d:%02d:%02d %d/%d/%d\n", - tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_mon, - tm.tm_mday, tm.tm_year); - return; -#endif -} -/* - * vars for idle task zero'ing out pages - */ -unsigned long zero_list = 0; /* head linked list of pre-zero'd pages */ -unsigned long bytecount = 0; /* pointer into the currently being zero'd page */ -unsigned long zerocount = 0; /* # currently pre-zero'd pages */ -unsigned long zerototal = 0; /* # pages zero'd over time -- for ooh's and ahhh's */ -unsigned long pageptr = 0; /* current page being zero'd */ -unsigned long zeropage_hits = 0;/* # zero'd pages request that we've done */ - -/* - * Returns a pre-zero'd page from the list otherwise returns - * NULL. - */ -unsigned long get_prezerod_page(void) +asmlinkage int sys_debug(long a, long b, long c, long d, long e, long f,struct pt_regs *regs) { - unsigned long page; - unsigned long s; - - if ( zero_list ) - { - /* atomically remove this page from the list */ - asm ( "101:lwarx %1,0,%2\n" /* reserve zero_list */ - " lwz %0,0(%1)\n" /* get next -- new zero_list */ - " stwcx. %0,0,%2\n" /* update zero_list */ - " bne- 101b\n" /* if lost reservation try again */ - : "=&r" (zero_list), "=&r" (page) - : "r" (&zero_list) - : "cc" ); - /* we can update zerocount after the fact since it is not - * used for anything but control of a loop which doesn't - * matter since it won't effect anything if it zero's one - * less page -- Cort - */ - atomic_inc((atomic_t *)&zeropage_hits); - atomic_dec((atomic_t *)&zerocount); - /* zero out the pointer to next in the page */ - *(unsigned long *)page = 0; - return page; - } return 0; } -/* - * Experimental stuff to zero out pages in the idle task - * to speed up get_free_pages() -- Cort - * Zero's out pages until we need to resched or - * we've reached the limit of zero'd pages. - */ -void inline zero_paged(void) -{ - extern pte_t *get_pte( struct mm_struct *mm, unsigned long address ); - unsigned long tmp; - pte_t ptep; - pgd_t *dir; - pmd_t *pmd; - pte_t *pte; - - sprintf(current->comm, "zero_paged"); - printk("Started zero_paged\n"); - /* want priority over idle task and powerd */ - current->priority = -98; - current->counter = -98; - __sti(); - - while ( zerocount < 128 ) - { - /* - * Mark a page as reserved so we can mess with it - * If we're interrupted we keep this page and our place in it - * since we validly hold it and it's reserved for us. - */ - pageptr = __get_free_pages(GFP_ATOMIC, 0, 0 ); - if ( !pageptr ) - { - printk("!pageptr in zero_paged\n"); - goto retry; - } - - if ( need_resched ) - schedule(); - - /* - * Make the page no cache so we don't blow our cache with 0's - */ - dir = pgd_offset( init_task.mm, pageptr ); - if (dir) - { - pmd = pmd_offset(dir, pageptr & PAGE_MASK); - if (pmd && pmd_present(*pmd)) - { - pte = pte_offset(pmd, pageptr & PAGE_MASK); - if (pte && pte_present(*pte)) - { - pte_uncache(*pte); - flush_tlb_page(find_vma(init_task.mm,pageptr),pageptr); - } - } - } - - /* - * Important here to not take time away from real processes. - */ - for ( bytecount = 0; bytecount < PAGE_SIZE ; bytecount += 4 ) - { - if ( need_resched ) - schedule(); - *(unsigned long *)(bytecount + pageptr) = 0; - } - - /* - * If we finished zero-ing out a page add this page to - * the zero_list atomically -- we can't use - * down/up since we can't sleep in idle. - * Disabling interrupts is also a bad idea since we would - * steal time away from real processes. - * We can also have several zero_paged's running - * on different processors so we can't interfere with them. - * So we update the list atomically without locking it. - * -- Cort - */ - /* turn cache on for this page */ - pte_cache(*pte); - flush_tlb_page(find_vma(init_task.mm,pageptr),pageptr); - - /* atomically add this page to the list */ - asm ( "101:lwarx %0,0,%1\n" /* reserve zero_list */ - " stw %0,0(%2)\n" /* update *pageptr */ -#ifdef __SMP__ - " sync\n" /* let store settle */ -#endif - " mr %0,%2\n" /* update zero_list in reg */ - " stwcx. %2,0,%1\n" /* update zero_list in mem */ - " bne- 101b\n" /* if lost reservation try again */ - : "=&r" (zero_list) - : "r" (&zero_list), "r" (pageptr) - : "cc" ); - /* - * This variable is used in the above loop and nowhere - * else so the worst that could happen is we would - * zero out one more or one less page than we want - * per processor on the machine. This is because - * we could add our page to the list but not have - * zerocount updated yet when another processor - * reads it. -- Cort - */ - atomic_inc((atomic_t *)&zerocount); - atomic_inc((atomic_t *)&zerototal); -retry: - schedule(); - } -} - -void powerd(void) -{ - unsigned long msr, hid0; - - sprintf(current->comm, "powerd"); - __sti(); - while (1) - { - /* want priority over idle task 'swapper' -- Cort */ - current->priority = -99; - current->counter = -99; - asm volatile( - /* clear powersaving modes and set nap mode */ - "mfspr %3,1008 \n\t" - "andc %3,%3,%4 \n\t" - "or %3,%3,%5 \n\t" - "mtspr 1008,%3 \n\t" - /* enter the mode */ - "mfmsr %0 \n\t" - "oris %0,%0,%2 \n\t" - "sync \n\t" - "mtmsr %0 \n\t" - "isync \n\t" - : "=&r" (msr) - : "0" (msr), "i" (MSR_POW>>16), - "r" (hid0), - "r" (HID0_DOZE|HID0_NAP|HID0_SLEEP), - "r" (HID0_NAP)); - if ( need_resched ) - schedule(); - /* - * The ibm carolina spec says that the eagle memory - * controller will detect the need for a snoop - * and wake up the processor so we don't need to - * check for cache operations that need to be - * snooped. The ppc book says the run signal - * must be asserted while napping for this though. - * -- Cort - */ - } -} - -asmlinkage int sys_idle(void) -{ - int ret = -EPERM; - if (current->pid != 0) - goto out; - -#ifdef IDLE_ZERO - /* - * want one per cpu since it would be nice to have all - * processors who aren't doing anything - * zero-ing pages since this daemon is lock-free - * -- Cort - */ - kernel_thread(zero_paged, NULL, 0); -#endif /* IDLE_ZERO */ - -#ifdef CONFIG_POWERSAVING - /* no powersaving modes on 601 - one per processor */ - if( (_get_PVR()>>16) != 1 ) - kernel_thread(powerd, NULL, 0); -#endif /* CONFIG_POWERSAVING */ - - /* endless loop with no priority at all */ - current->priority = -100; - current->counter = -100; - for (;;) - { - schedule(); - } - ret = 0; -out: - return ret; -} - void show_regs(struct pt_regs * regs) { int i; - printk("NIP: %08X XER: %08X LR: %08X REGS: %08X TRAP: %04x\n", + printk("NIP: %08lX XER: %08lX LR: %08lX REGS: %p TRAP: %04lx\n", regs->nip, regs->xer, regs->link, regs,regs->trap); - printk("MSR: %08x EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", + printk("MSR: %08lx EE: %01x PR: %01x FP: %01x ME: %01x IR/DR: %01x%01x\n", regs->msr, regs->msr&MSR_EE ? 1 : 0, regs->msr&MSR_PR ? 1 : 0, regs->msr & MSR_FP ? 1 : 0,regs->msr&MSR_ME ? 1 : 0, regs->msr&MSR_IR ? 1 : 0, regs->msr&MSR_DR ? 1 : 0); - printk("TASK = %x[%d] '%s' mm->pgd %08X ", + printk("TASK = %p[%d] '%s' mm->pgd %p ", current, current->pid, current->comm, current->mm->pgd); - printk("Last syscall: %d ", current->tss.last_syscall); - printk("\nlast math %08X\n", last_task_used_math); + printk("Last syscall: %ld ", current->tss.last_syscall); + printk("\nlast math %p\n", last_task_used_math); for (i = 0; i < 32; i++) { long r; @@ -451,9 +190,9 @@ void show_regs(struct pt_regs * regs) printk("GPR%02d: ", i); } - if ( get_user(r, &(regs->gpr[i])) ) + if ( __get_user(r, &(regs->gpr[i])) ) goto out; - printk("%08X ", r); + printk("%08lX ", r); if ((i % 8) == 7) { printk("\n"); @@ -480,14 +219,14 @@ release_thread(struct task_struct *t) } /* - * Copy a thread.. - */ + * Copy a thread.. + */ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, struct task_struct * p, struct pt_regs * regs) { - int i; struct pt_regs * childregs; + /* Copy registers */ childregs = ((struct pt_regs *) ((unsigned long)p + sizeof(union task_union) @@ -497,13 +236,13 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp, if ((childregs->msr & MSR_PR) == 0) childregs->gpr[2] = (unsigned long) p; /* `current' in new task */ childregs->gpr[3] = 0; /* Result from fork() */ - p->tss.ksp = (unsigned long)(childregs) - STACK_FRAME_OVERHEAD; - p->tss.regs = (struct pt_regs *)(childregs); - if (usp >= (unsigned long)regs) - { /* Stack is in kernel space - must adjust */ - childregs->gpr[1] = (long)(childregs+1); - } else - { /* Provided stack is in user space */ + p->tss.ksp = (unsigned long) childregs - STACK_FRAME_OVERHEAD; + p->tss.regs = childregs; + if (usp >= (unsigned long) regs) { + /* Stack is in kernel space - must adjust */ + childregs->gpr[1] = (unsigned long)(childregs + 1); + } else { + /* Provided stack is in user space */ childregs->gpr[1] = usp; } @@ -511,24 +250,75 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long usp, /* * copy fpu info - assume lazy fpu switch now always - * this should really be conditional on whether or - * not the process has used the fpu * -- Cort */ if ( last_task_used_math == current ) giveup_fpu(); - + memcpy(&p->tss.fpr, ¤t->tss.fpr, sizeof(p->tss.fpr)); p->tss.fpscr = current->tss.fpscr; childregs->msr &= ~MSR_FP; - + return 0; } +/* + * XXX ld.so expects the auxiliary table to start on + * a 16-byte boundary, so we have to find it and + * move it up. :-( + */ +static inline void shove_aux_table(unsigned long sp) +{ + int argc; + char *p; + unsigned long e; + unsigned long aux_start, offset; + + if (__get_user(argc, (int *)sp)) + return; + sp += sizeof(int) + (argc + 1) * sizeof(char *); + /* skip over the environment pointers */ + do { + if (__get_user(p, (char **)sp)) + return; + sp += sizeof(char *); + } while (p != NULL); + aux_start = sp; + /* skip to the end of the auxiliary table */ + do { + if (__get_user(e, (unsigned long *)sp)) + return; + sp += 2 * sizeof(unsigned long); + } while (e != AT_NULL); + offset = ((aux_start + 15) & ~15) - aux_start; + if (offset != 0) { + do { + sp -= sizeof(unsigned long); + if (__get_user(e, (unsigned long *)sp) + || __put_user(e, (unsigned long *)(sp + offset))) + return; + } while (sp > aux_start); + } +} + +/* + * Set up a thread for executing a new program + */ +void start_thread(struct pt_regs *regs, unsigned long nip, unsigned long sp) +{ + set_fs(USER_DS); + regs->nip = nip; + regs->gpr[1] = sp; + regs->msr = MSR_USER; + shove_aux_table(sp); +} + + asmlinkage int sys_fork(int p1, int p2, int p3, int p4, int p5, int p6, struct pt_regs *regs) { int ret; + lock_kernel(); ret = do_fork(SIGCHLD, regs->gpr[1], regs); unlock_kernel(); @@ -541,26 +331,26 @@ asmlinkage int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2, { int error; char * filename; - filename = (int) getname((char *) a0); + + filename = getname((char *) a0); error = PTR_ERR(filename); - if(IS_ERR(filename)) + if (IS_ERR(filename)) goto out; if ( last_task_used_math == current ) last_task_used_math = NULL; error = do_execve(filename, (char **) a1, (char **) a2, regs); - putname(filename); - out: unlock_kernel(); return error; } -asmlinkage int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6, struct pt_regs *regs) +asmlinkage int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6, + struct pt_regs *regs) { unsigned long clone_flags = p1; int res; - + lock_kernel(); res = do_fork(clone_flags, regs->gpr[1], regs); unlock_kernel(); @@ -571,34 +361,23 @@ void print_backtrace(unsigned long *sp) { int cnt = 0; - int i; + unsigned long i; + printk("Call backtrace: "); - while ( !get_user(i, sp) && i) - { - if ( get_user( i, &sp[1] ) ) - return; - printk("%08X ", i); - if ( get_user( (ulong)sp, sp) ) - return; - if (cnt == 6 ) cnt = 7; /* wraparound early -- Cort */ - if (++cnt == 8) - { + while (sp) { + if (__get_user( i, &sp[1] )) + break; + if (cnt++ % 7 == 0) printk("\n"); - } + printk("%08lX ", i); if (cnt > 32) break; + if (__get_user(sp, (unsigned long **)sp)) + break; } printk("\n"); } -inline void start_thread(struct pt_regs * regs, - unsigned long eip, unsigned long esp) -{ - set_fs(USER_DS); - regs->nip = eip; - regs->gpr[1] = esp; - regs->msr = MSR_USER; -} - +#if 0 /* * Low level print for debugging - Cort */ @@ -651,3 +430,4 @@ void ll_puts(const char *s) orig_x = x; orig_y = y; } +#endif /* CONFIG_PREP */ diff --git a/arch/ppc/kernel/prom.c b/arch/ppc/kernel/prom.c new file mode 100644 index 000000000..c7a566a14 --- /dev/null +++ b/arch/ppc/kernel/prom.c @@ -0,0 +1,524 @@ +/* + * Procedures for interfacing to the Open Firmware PROM on + * Power Macintosh computers. + * + * In particular, we are interested in the device tree + * and in using some of its services (exit, write to stdout). + * + * Paul Mackerras August 1996. + * Copyright (C) 1996 Paul Mackerras. + */ + +#include <stdarg.h> +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/blk.h> +#include <asm/prom.h> +#include <asm/page.h> +#include <asm/processor.h> + +#define getpromprop(node, name, buf, len) \ + ((int)call_prom("getprop", 4, 1, (node), (name), (buf), (len))) + +ihandle prom_stdout; +ihandle prom_chosen; + +char command_line[256]; +int screen_initialized = 0; + +char prom_display_path[128]; + +struct prom_args { + const char *service; + int nargs; + int nret; + void *args[10]; +} prom_args; + +struct pci_address { + unsigned a_hi; + unsigned a_mid; + unsigned a_lo; +}; + +struct pci_reg_property { + struct pci_address addr; + unsigned size_hi; + unsigned size_lo; +}; + +struct pci_range { + struct pci_address addr; + unsigned phys; + unsigned size_hi; + unsigned size_lo; +}; + +void (*prom_entry)(void *); +extern int prom_trashed; + +static int prom_callback(struct prom_args *); +static unsigned long inspect_node(phandle, struct device_node *, unsigned long, + unsigned long, unsigned long); +static void check_display(void); +static int prom_next_node(phandle *); + +extern int pmac_display_supported(const char *); +extern void enter_prom(void *); + +void +prom_exit() +{ + struct prom_args args; + + args.service = "exit"; + args.nargs = 0; + args.nret = 0; + enter_prom(&args); + for (;;) /* should never get here */ + ; +} + +void * +call_prom(const char *service, int nargs, int nret, ...) +{ + va_list list; + int i; + + if (prom_trashed) + panic("prom called after its memory was reclaimed"); + prom_args.service = service; + prom_args.nargs = nargs; + prom_args.nret = nret; + va_start(list, nret); + for (i = 0; i < nargs; ++i) + prom_args.args[i] = va_arg(list, void *); + va_end(list); + for (i = 0; i < nret; ++i) + prom_args.args[i + nargs] = 0; + enter_prom(&prom_args); + return prom_args.args[nargs]; +} + +void +prom_print(const char *msg) +{ + const char *p, *q; + const char *crlf = "\r\n"; + + if (screen_initialized) + return; + for (p = msg; *p != 0; p = q) { + for (q = p; *q != 0 && *q != '\n'; ++q) + ; + if (q > p) + call_prom("write", 3, 1, prom_stdout, p, q - p); + if (*q != 0) { + ++q; + call_prom("write", 3, 1, prom_stdout, crlf, 2); + } + } +} + +/* + * We enter here early on, when the Open Firmware prom is still + * handling exceptions and the MMU hash table for us. + */ +void +prom_init(char *params, int unused, void (*pp)(void *)) +{ + /* First get a handle for the stdout device */ + if ( ! have_of() ) + return; + prom_entry = pp; + prom_chosen = call_prom("finddevice", 1, 1, "/chosen"); + if (prom_chosen == (void *)-1) + prom_exit(); + call_prom("getprop", 4, 1, prom_chosen, "stdout", &prom_stdout, + (void *) sizeof(prom_stdout)); + + /* + * If we were booted via quik, params points to the physical address + * of the command-line parameters. + * If we were booted from an xcoff image (i.e. netbooted or + * booted from floppy), we get the command line from the bootargs + * property of the /chosen node. If an initial ramdisk is present, + * params and unused are used for initrd_start and initrd_size, + * otherwise they contain 0xdeadbeef. + */ + command_line[0] = 0; + if ((unsigned long) params >= 0x4000 + && (unsigned long) params < 0x800000 + && unused == 0) { + strncpy(command_line, params+KERNELBASE, sizeof(command_line)); + } else { +#ifdef CONFIG_BLK_DEV_INITRD + if ((unsigned long) params - KERNELBASE < 0x800000 + && unused != 0 && unused != 0xdeadbeef) { + initrd_start = (unsigned long) params; + initrd_end = initrd_start + unused; + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); + } +#endif + call_prom("getprop", 4, 1, prom_chosen, "bootargs", + command_line, sizeof(command_line)); + } + command_line[sizeof(command_line) - 1] = 0; + + check_display(); +} + +/* + * If we have a display that we don't know how to drive, + * we will want to try to execute OF's open method for it + * later. However, OF may fall over if we do that after + * we've taken over the MMU and done set_prom_callback. + * So we check whether we will need to open the display, + * and if so, open it now. + */ +static void +check_display() +{ + phandle node; + ihandle ih; + char type[16], name[64], path[128]; + + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + getpromprop(node, "device_type", type, sizeof(type)); + if (strcmp(type, "display") != 0) + continue; + name[0] = 0; + getpromprop(node, "name", name, sizeof(name)); + if (pmac_display_supported(name)) + /* we have a supported display */ + return; + } + printk(KERN_INFO "No supported display found\n"); + for (node = 0; prom_next_node(&node); ) { + type[0] = 0; + getpromprop(node, "device_type", type, sizeof(type)); + if (strcmp(type, "display") != 0) + continue; + /* It seems OF doesn't null-terminate the path :-( */ + memset(path, 0, sizeof(path)); + if ((int) call_prom("package-to-path", 3, 1, + node, path, sizeof(path) - 1) < 0) { + printk(KERN_WARNING "can't get path for display %p\n", + node); + continue; + } + ih = call_prom("open", 1, 1, path); + if (ih == 0 || ih == (ihandle) -1) { + printk(KERN_WARNING "couldn't open display %s\n", + path); + continue; + } + printk(KERN_INFO "Opened display device %s using " + "Open Firmware\n", path); + strcpy(prom_display_path, path); + break; + } +} + +static int +prom_next_node(phandle *nodep) +{ + phandle node; + + if ((node = *nodep) != 0 + && (*nodep = call_prom("child", 1, 1, node)) != 0) + return 1; + if ((*nodep = call_prom("peer", 1, 1, node)) != 0) + return 1; + for (;;) { + if ((node = call_prom("parent", 1, 1, node)) == 0) + return 0; + if ((*nodep = call_prom("peer", 1, 1, node)) != 0) + return 1; + } +} + +/* + * Callback routine for the PROM to call us. + * No services are implemented yet :-) + */ +static int +prom_callback(struct prom_args *argv) +{ + printk("uh oh, prom callback '%s' (%d/%d)\n", argv->service, + argv->nargs, argv->nret); + return -1; +} + +/* + * Register a callback with the Open Firmware PROM so it can ask + * us to map/unmap memory, etc. + */ +void +set_prom_callback() +{ + call_prom("set-callback", 1, 1, prom_callback); +} + +void +abort() +{ +#ifdef CONFIG_XMON + xmon(0); +#endif + prom_exit(); +} + +/* + * Make a copy of the device tree from the PROM. + */ + +static struct device_node *allnodes; +static struct device_node **allnextp; + +#define ALIGN(x) (((x) + sizeof(unsigned long)-1) & -sizeof(unsigned long)) + +unsigned long +copy_device_tree(unsigned long mem_start, unsigned long mem_end) +{ + phandle root; + + root = call_prom("peer", 1, 1, (phandle)0); + if (root == (phandle)0) + panic("couldn't get device tree root\n"); + allnextp = &allnodes; + mem_start = inspect_node(root, 0, 0, mem_start, mem_end); + *allnextp = 0; + return mem_start; +} + +static unsigned long +inspect_node(phandle node, struct device_node *dad, unsigned long base_address, + unsigned long mem_start, unsigned long mem_end) +{ + struct reg_property *reg, *rp; + struct pci_reg_property *pci_addrs; + int l, i; + phandle child; + struct device_node *np; + struct property *pp, **prev_propp; + char *prev_name; + + np = (struct device_node *) mem_start; + mem_start += sizeof(struct device_node); + memset(np, 0, sizeof(*np)); + np->node = node; + *allnextp = np; + allnextp = &np->allnext; + np->parent = dad; + if (dad != 0) { + /* we temporarily use the `next' field as `last_child'. */ + if (dad->next == 0) + dad->child = np; + else + dad->next->sibling = np; + dad->next = np; + } + + /* get and store all properties */ + prev_propp = &np->properties; + prev_name = 0; + for (;;) { + pp = (struct property *) mem_start; + pp->name = (char *) (pp + 1); + if ((int) call_prom("nextprop", 3, 1, node, prev_name, + pp->name) <= 0) + break; + mem_start = ALIGN((unsigned long)pp->name + + strlen(pp->name) + 1); + pp->value = (unsigned char *) mem_start; + pp->length = (int) + call_prom("getprop", 4, 1, node, pp->name, pp->value, + mem_end - mem_start); + if (pp->length < 0) + panic("hey, where did property %s go?", pp->name); + mem_start = ALIGN(mem_start + pp->length); + prev_name = pp->name; + *prev_propp = pp; + prev_propp = &pp->next; + } + *prev_propp = 0; + + np->name = get_property(np, "name", 0); + np->type = get_property(np, "device_type", 0); + + /* get all the device addresses and interrupts */ + reg = (struct reg_property *) mem_start; + pci_addrs = (struct pci_reg_property *) + get_property(np, "assigned-addresses", &l); + i = 0; + if (pci_addrs != 0) { + while ((l -= sizeof(struct pci_reg_property)) >= 0) { + /* XXX assumes PCI addresses mapped 1-1 to physical */ + reg[i].address = pci_addrs[i].addr.a_lo; + reg[i].size = pci_addrs[i].size_lo; + ++i; + } + } else { + rp = (struct reg_property *) get_property(np, "reg", &l); + if (rp != 0) { + while ((l -= sizeof(struct reg_property)) >= 0) { + reg[i].address = rp[i].address + base_address; + reg[i].size = rp[i].size; + ++i; + } + } + } + if (i > 0) { + np->addrs = reg; + np->n_addrs = i; + mem_start += i * sizeof(struct reg_property); + } + + np->intrs = (int *) get_property(np, "AAPL,interrupts", &l); + if (np->intrs != 0) + np->n_intrs = l / sizeof(int); + + /* get the node's full name */ + l = (int) call_prom("package-to-path", 3, 1, node, + (char *) mem_start, mem_end - mem_start); + if (l >= 0) { + np->full_name = (char *) mem_start; + np->full_name[l] = 0; + mem_start = ALIGN(mem_start + l + 1); + } + + if (np->type != 0 && strcmp(np->type, "dbdma") == 0 && np->n_addrs > 0) + base_address = np->addrs[0].address; + + child = call_prom("child", 1, 1, node); + while (child != (void *)0) { + mem_start = inspect_node(child, np, base_address, + mem_start, mem_end); + child = call_prom("peer", 1, 1, child); + } + + return mem_start; +} + +/* + * Construct a return a list of the device_nodes with a given name. + */ +struct device_node * +find_devices(const char *name) +{ + struct device_node *head, **prevp, *np; + + prevp = &head; + for (np = allnodes; np != 0; np = np->allnext) { + if (np->name != 0 && strcasecmp(np->name, name) == 0) { + *prevp = np; + prevp = &np->next; + } + } + *prevp = 0; + return head; +} + +/* + * Construct a return a list of the device_nodes with a given type. + */ +struct device_node * +find_type_devices(const char *type) +{ + struct device_node *head, **prevp, *np; + + prevp = &head; + for (np = allnodes; np != 0; np = np->allnext) { + if (np->type != 0 && strcasecmp(np->type, type) == 0) { + *prevp = np; + prevp = &np->next; + } + } + *prevp = 0; + return head; +} + +/* + * Find the device_node with a given full_name. + */ +struct device_node * +find_path_device(const char *path) +{ + struct device_node *np; + + for (np = allnodes; np != 0; np = np->allnext) + if (np->full_name != 0 && strcasecmp(np->full_name, path) == 0) + return np; + return NULL; +} + +/* + * Find a property with a given name for a given node + * and return the value. + */ +unsigned char * +get_property(struct device_node *np, const char *name, int *lenp) +{ + struct property *pp; + + for (pp = np->properties; pp != 0; pp = pp->next) + if (strcmp(pp->name, name) == 0) { + if (lenp != 0) + *lenp = pp->length; + return pp->value; + } + return 0; +} + +void +print_properties(struct device_node *np) +{ + struct property *pp; + char *cp; + int i, n; + + for (pp = np->properties; pp != 0; pp = pp->next) { + printk(KERN_INFO "%s", pp->name); + for (i = strlen(pp->name); i < 16; ++i) + printk(" "); + cp = (char *) pp->value; + for (i = pp->length; i > 0; --i, ++cp) + if ((i > 1 && (*cp < 0x20 || *cp > 0x7e)) + || (i == 1 && *cp != 0)) + break; + if (i == 0 && pp->length > 1) { + /* looks like a string */ + printk(" %s\n", (char *) pp->value); + } else { + /* dump it in hex */ + n = pp->length; + if (n > 64) + n = 64; + if (pp->length % 4 == 0) { + unsigned int *p = (unsigned int *) pp->value; + + n /= 4; + for (i = 0; i < n; ++i) { + if (i != 0 && (i % 4) == 0) + printk("\n "); + printk(" %08x", *p++); + } + } else { + unsigned char *bp = pp->value; + + for (i = 0; i < n; ++i) { + if (i != 0 && (i % 16) == 0) + printk("\n "); + printk(" %02x", *bp++); + } + } + printk("\n"); + if (pp->length > 64) + printk(" ... (length = %d)\n", + pp->length); + } + } +} diff --git a/arch/ppc/kernel/ptrace.c b/arch/ppc/kernel/ptrace.c index a5ec62d20..8a5839d1f 100644 --- a/arch/ppc/kernel/ptrace.c +++ b/arch/ppc/kernel/ptrace.c @@ -171,8 +171,11 @@ repeat: goto repeat; } /* this is a hack for non-kernel-mapped video buffers and similar */ - if (MAP_NR(page) < max_mapnr) - *(unsigned long *) (page + (addr & ~PAGE_MASK)) = data; + if (MAP_NR(page) < max_mapnr) { + unsigned long phys_addr = page + (addr & ~PAGE_MASK); + *(unsigned long *) phys_addr = data; + flush_icache_range(phys_addr, phys_addr+4); + } /* we're bypassing pagetables, so we have to set the dirty bit ourselves */ /* this should also re-instate whatever read-only mode there was before */ set_pte(pgtable, pte_mkdirty(mk_pte(page, vma->vm_page_prot))); @@ -364,7 +367,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) case PTRACE_PEEKUSR: { unsigned long tmp; - if ((addr & 3) || addr < 0 || addr >= sizeof(struct user)) { + if ((addr & 3) || addr < 0 || addr > (PT_FPSCR << 2)) { ret = -EIO; goto out; } @@ -378,13 +381,11 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) if (addr < PT_FPR0) { tmp = get_reg(child, addr); } -#if 1 - else if (addr >= PT_FPR0 && addr < PT_FPR0 + 64) { + else if (addr >= PT_FPR0 && addr <= PT_FPSCR) { if (last_task_used_math == child) giveup_fpu(); tmp = ((long *)child->tss.fpr)[addr - PT_FPR0]; } -#endif else ret = -EIO; if (!ret) @@ -400,7 +401,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ ret = -EIO; - if ((addr & 3) || addr < 0 || addr >= sizeof(struct user)) + if ((addr & 3) || addr < 0 || addr >= ((PT_FPR0 + 64) << 2)) goto out; addr = addr >> 2; /* temporary hack. */ @@ -508,7 +509,7 @@ asmlinkage void syscall_trace(void) goto out; current->exit_code = SIGTRAP; current->state = TASK_STOPPED; - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); /* * this isn't the same as continuing with a signal, but it will do diff --git a/arch/ppc/kernel/residual.c b/arch/ppc/kernel/residual.c new file mode 100644 index 000000000..4084f9f62 --- /dev/null +++ b/arch/ppc/kernel/residual.c @@ -0,0 +1,144 @@ +/* + * $Id: residual.c,v 1.2 1997/08/25 06:54:56 cort Exp $ + * + * Code to deal with the PReP residual data. + * + * Written by: Cort Dougan (cort@cs.nmt.edu) + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/tty.h> +#include <linux/major.h> +#include <linux/interrupt.h> +#include <linux/reboot.h> +#include <linux/init.h> +#include <linux/blk.h> +#include <linux/ioport.h> +#include <linux/pci.h> + +#include <asm/mmu.h> +#include <asm/processor.h> +#include <asm/residual.h> +#include <asm/io.h> +#include <asm/pgtable.h> +#include <asm/ide.h> +#include <asm/pnp.h> + + +/* + * Spit out some info about residual data + */ +void print_residual_device_info(void) +{ + int i; + union _PnP_TAG_PACKET *pkt; + PPC_DEVICE *dev; +#define did dev->DeviceId + + /* make sure we have residual data first */ + if ( res.ResidualLength == 0 ) + return; + + printk("Residual: %ld devices\n", res.ActualNumDevices); + for ( i = 0; + i < res.ActualNumDevices ; + i++) + { + dev = &res.Devices[i]; + /* + * pci devices + */ + if ( did.BusId & PCIDEVICE ) + { + printk("PCI Device:"); + /* unknown vendor */ + if ( !strncmp( "Unknown", pci_strvendor(did.DevId>>16), 7) ) + printk(" id %08lx types %d/%d", did.DevId, + did.BaseType, did.SubType); + /* known vendor */ + else + printk(" %s %s", + pci_strvendor(did.DevId>>16), + pci_strdev(did.DevId>>16, + did.DevId&0xffff) + ); + + if ( did.BusId & PNPISADEVICE ) + { + printk(" pnp:"); + /* get pnp info on the device */ + pkt = (union _PnP_TAG_PACKET *) + &res.DevicePnPHeap[dev->AllocatedOffset]; + for (; pkt->S1_Pack.Tag != DF_END_TAG; + pkt++ ) + { + if ( (pkt->S1_Pack.Tag == S4_Packet) || + (pkt->S1_Pack.Tag == S4_Packet_flags) ) + printk(" irq %02x%02x", + pkt->S4_Pack.IRQMask[0], + pkt->S4_Pack.IRQMask[1]); + } + } + printk("\n"); + continue; + } + /* + * isa devices + */ + if ( did.BusId & ISADEVICE ) + { + printk("ISA Device: basetype: %d subtype: %d", + did.BaseType, did.SubType); + printk("\n"); + continue; + } + /* + * eisa devices + */ + if ( did.BusId & EISADEVICE ) + { + printk("EISA Device: basetype: %d subtype: %d", + did.BaseType, did.SubType); + printk("\n"); + continue; + } + /* + * proc bus devices + */ + if ( did.BusId & PROCESSORDEVICE ) + { + printk("ProcBus Device: basetype: %d subtype: %d", + did.BaseType, did.SubType); + printk("\n"); + continue; + } + /* + * pcmcia devices + */ + if ( did.BusId & PCMCIADEVICE ) + { + printk("PCMCIA Device: basetype: %d subtype: %d", + did.BaseType, did.SubType); + printk("\n"); + continue; + } + printk("Unknown bus access device: busid %lx\n", + did.BusId); + } +} + + + + + + diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c index e69de29bb..f3769cad0 100644 --- a/arch/ppc/kernel/setup.c +++ b/arch/ppc/kernel/setup.c @@ -0,0 +1,268 @@ +/* + * $Id: setup.c,v 1.16 1997/08/27 22:06:54 cort Exp $ + * Common prep/pmac boot and setup code. + */ + +#include <linux/config.h> +#include <linux/string.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/reboot.h> +#include <linux/openpic.h> + +#include <asm/cuda.h> +#include <asm/residual.h> +#include <asm/io.h> +#include <asm/ide.h> + +char saved_command_line[256]; +unsigned char aux_device_present; + +/* copy of the residual data */ +RESIDUAL res; +int _machine; + +/* + * Perhaps we can put the pmac screen_info[] here + * on pmac as well so we don't need the ifdef's. + * Until we get multiple-console support in here + * that is. -- Cort + */ +#if defined(CONFIG_CHRP) || defined(CONFIG_PREP ) +struct screen_info screen_info = { + 0, 25, /* orig-x, orig-y */ + { 0, 0 }, /* unused */ + 0, /* orig-video-page */ + 0, /* orig-video-mode */ + 80, /* orig-video-cols */ + 0,0,0, /* ega_ax, ega_bx, ega_cx */ + 25, /* orig-video-lines */ + 1, /* orig-video-isVGA */ + 16 /* orig-video-points */ +}; + +/* + * I really need to add multiple-console support... -- Cort + */ +int pmac_display_supported(char *name) +{ + return 0; +} +int sd_find_target(void *a, int b) +{ + return 0; +} +void pmac_find_display(void) +{ +} + +#endif + +/* + * Find out what kind of machine we're on and save any data we need + * from the early boot process (devtree is copied on pmac by prom_init() ) + */ +unsigned long identify_machine(unsigned long r3, unsigned long r4, unsigned long r5, + unsigned long r6, unsigned long r7) +{ + extern unsigned long initrd_start, initrd_end; + extern char cmd_line[256]; +#ifdef CONFIG_PMAC /* cheat for now - perhaps a check for OF could tell us */ + _machine = _MACH_Pmac; +#endif /* CONFIG_PMAC */ +#ifdef CONFIG_PREP + /* make a copy of residual data */ + if ( r3 ) + memcpy( (void *)&res,(void *)(r3+KERNELBASE), sizeof(RESIDUAL) ); + if (!strncmp(res.VitalProductData.PrintableModel,"IBM",3)) + _machine = _MACH_IBM; + else + _machine = _MACH_Motorola; +#endif /* CONFIG_PREP */ +#ifdef CONFIG_CHRP + _machine = _MACH_chrp; +#endif /* CONFIG_CHRP */ + + switch (_machine) + { + case _MACH_Pmac: + io_base = 0; + pci_dram_offset = 0; + break; + case _MACH_IBM: + case _MACH_Motorola: + io_base = 0x80000000; + pci_dram_offset = 0x80000000; +#ifdef CONFIG_BLK_DEV_RAM + /* take care of initrd if we have one */ + if ( r4 ) + { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } +#endif /* CONFIG_BLK_DEV_RAM */ + /* take care of cmd line */ + if ( r6 ) + { + + *(char *)(r7+KERNELBASE) = 0; + strcpy(cmd_line, (char *)(r6+KERNELBASE)); + } + break; + case _MACH_chrp: + /* LongTrail */ + io_base = 0xf8000000; + pci_dram_offset = 0; + /* take care of initrd if we have one */ + if ( r4 ) { + initrd_start = r4 + KERNELBASE; + initrd_end = r5 + KERNELBASE; + } + /* take care of cmd line */ + if ( r6 ) { + *(char *)(r7+KERNELBASE) = 0; + strcpy(cmd_line, (char *)(r6+KERNELBASE)); + } + break; + default: + printk("Unknown machine type in identify_machine!\n"); + } + return 0; +} + +/* cmd is ignored for now... */ +void machine_restart(char *cmd) +{ + struct cuda_request req; + unsigned long flags; + unsigned long i = 10000; + + switch(_machine) + { + case _MACH_Pmac: + cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_RESET_SYSTEM); + for (;;) + cuda_poll(); + break; + case _MACH_IBM: + case _MACH_Motorola: + _disable_interrupts(); + + /* set exception prefix high - to the prom */ + save_flags( flags ); + restore_flags( flags|MSR_IP ); + + /* make sure bit 0 (reset) is a 0 */ + outb( inb(0x92) & ~1L , 0x92 ); + /* signal a reset to system control port A - soft reset */ + outb( inb(0x92) | 1 , 0x92 ); + + while ( i != 0 ) i++; + panic("restart failed\n"); + break; + + case _MACH_chrp: + openpic_init_processor(1<<0); + break; + } +} + +void machine_power_off(void) +{ + struct cuda_request req; + + if ( _machine == _MACH_Pmac ) + { + cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_POWERDOWN); + for (;;) + cuda_poll(); + } + else /* prep or chrp */ + { + machine_restart(NULL); + } +} + +void machine_halt(void) +{ + if ( _machine == _MACH_Pmac ) + { +#if 0 + prom_exit(); /* doesn't work because prom is trashed */ +#else + machine_power_off(); /* for now */ +#endif + } + else /* prep or chrp */ + machine_restart(NULL); + +} + +void ide_init_hwif_ports (ide_ioreg_t *p, ide_ioreg_t base, int *irq) +{ + if ( _machine == _MACH_Pmac ) + pmac_ide_init_hwif_ports(p,base,irq); + else /* prep */ + prep_ide_init_hwif_ports(p,base,irq); + +} + +/* + * Will merge more into here later -- Cort + */ +int get_cpuinfo(char *buffer) +{ + extern int pmac_get_cpuinfo(char *); + extern int chrp_get_cpuinfo(char *); + extern int prep_get_cpuinfo(char *); + + + switch (_machine) + { + case _MACH_Pmac: + return pmac_get_cpuinfo(buffer); + break; + case _MACH_Motorola: + case _MACH_IBM: + return prep_get_cpuinfo(buffer); + break; + case _MACH_chrp: + return chrp_get_cpuinfo(buffer); + break; + } + printk("Unknown machine %d in get_cpuinfo()\n",_machine); + return 0; +} + + +__initfunc(unsigned long +bios32_init(unsigned long memory_start, unsigned long memory_end)) +{ + return memory_start; +} + +__initfunc(void setup_arch(char **cmdline_p, + unsigned long * memory_start_p, unsigned long * memory_end_p)) +{ + extern void pmac_setup_arch(char **, unsigned long *, unsigned long *); + extern void chrp_setup_arch(char **, unsigned long *, unsigned long *); + extern void prep_setup_arch(char **, unsigned long *, unsigned long *); + + switch (_machine) + { + case _MACH_Pmac: + pmac_setup_arch(cmdline_p,memory_start_p,memory_end_p); + break; + case _MACH_Motorola: + case _MACH_IBM: + prep_setup_arch(cmdline_p,memory_start_p,memory_end_p); + break; + case _MACH_chrp: + return chrp_setup_arch(cmdline_p,memory_start_p,memory_end_p); + break; + } + printk("Unknown machine %d in setup_arch()\n",_machine); +} + + + diff --git a/arch/ppc/kernel/signal.c b/arch/ppc/kernel/signal.c index 2f602059c..3151d266a 100644 --- a/arch/ppc/kernel/signal.c +++ b/arch/ppc/kernel/signal.c @@ -24,6 +24,7 @@ #include <linux/wait.h> #include <linux/ptrace.h> #include <linux/unistd.h> +#include <linux/elf.h> #include <asm/uaccess.h> #define _S(nr) (1<<((nr)-1)) @@ -36,6 +37,10 @@ #define PAUSE_AFTER_SIGNAL #undef PAUSE_AFTER_SIGNAL +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + asmlinkage int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options); /* @@ -71,32 +76,52 @@ printk("Task: %x[%d] - SIGSUSPEND at %x, Mask: %x\n", current, current->pid, reg } } +/* + * These are the flags in the MSR that the user is allowed to change + * by modifying the saved value of the MSR on the stack. SE and BE + * should not be in this list since gdb may want to change these. I.e, + * you should be able to step out of a signal handler to see what + * instruction executes next after the signal handler completes. + * Alternately, if you stepped into a signal handler, you should be + * able to continue 'til the next breakpoint from within the signal + * handler, even if the handler returns. + */ +#define MSR_USERCHANGE (MSR_FE0 | MSR_FE1) + /* * This sets regs->esp even though we don't actually use sigstacks yet.. */ asmlinkage int sys_sigreturn(struct pt_regs *regs) { - struct sigcontext_struct *sc; - struct pt_regs *int_regs; - int signo, ret; + struct sigcontext_struct *sc, sigctx; + int ret; + elf_gregset_t saved_regs; /* an array of ELF_NGREG unsigned longs */ -#if 1 - if (verify_area(VERIFY_READ, (void *) regs->gpr[1], sizeof(sc)) - || (regs->gpr[1] >= KERNELBASE)) + sc = (struct sigcontext_struct *)(regs->gpr[1] + __SIGNAL_FRAMESIZE); + if (copy_from_user(&sigctx, sc, sizeof(sigctx))) goto badframe; -#endif - sc = (struct sigcontext_struct *)(regs->gpr[1]+STACK_FRAME_OVERHEAD); - get_user(current->blocked, &sc->oldmask); - current->blocked &= _BLOCKABLE; - get_user(int_regs, &sc->regs); - get_user(signo, &sc->signal); + current->blocked = sigctx.oldmask & _BLOCKABLE; sc++; /* Pop signal 'context' */ #ifdef DEBUG_SIGNALS -printk("Sig return - Regs: %x, sc: %x, sig: %d\n", int_regs, sc, signo); + printk("Sig return - Regs: %p, sc: %p, sig: %d\n", sigctx.regs, sc, + sigctx.signal); #endif - if (sc == (struct sigcontext_struct *)(int_regs)) { - /* Last stacked signal */ - memcpy(regs, int_regs, sizeof(*regs)); + if (sc == (struct sigcontext_struct *)(sigctx.regs)) { + /* Last stacked signal - restore registers */ + if (last_task_used_math == current) + giveup_fpu(); + if (copy_from_user(saved_regs, sigctx.regs, sizeof(saved_regs))) + goto badframe; + saved_regs[PT_MSR] = (regs->msr & ~MSR_USERCHANGE) + | (saved_regs[PT_MSR] & MSR_USERCHANGE); + memcpy(regs, saved_regs, + MIN(sizeof(elf_gregset_t),sizeof(struct pt_regs))); + + if (copy_from_user(current->tss.fpr, + (unsigned long *)sigctx.regs + ELF_NGREG, + ELF_NFPREG * sizeof(double))) + goto badframe; + if (regs->trap == 0x0C00 /* System Call! */ && ((int)regs->result == -ERESTARTNOHAND || (int)regs->result == -ERESTARTSYS || @@ -106,15 +131,17 @@ printk("Sig return - Regs: %x, sc: %x, sig: %d\n", int_regs, sc, signo); regs->result = 0; } ret = regs->result; + } else { /* More signals to go */ - regs->gpr[1] = (unsigned long)sc - STACK_FRAME_OVERHEAD; - get_user(regs->gpr[3], &sc->signal); - get_user(int_regs, (struct pt_regs **) &sc->regs); - regs->gpr[4] = (unsigned long) int_regs; - regs->link = (unsigned long) (int_regs+1); - get_user(regs->nip, &sc->handler); - ret = regs->gpr[3]; + regs->gpr[1] = (unsigned long)sc - __SIGNAL_FRAMESIZE; + if (copy_from_user(&sigctx, sc, sizeof(sigctx))) + goto badframe; + regs->gpr[3] = ret = sigctx.signal; + regs->gpr[4] = (unsigned long) sigctx.regs; + regs->link = regs->gpr[4] + ELF_NGREG * sizeof(unsigned long) + + ELF_NFPREG * sizeof(double); + regs->nip = sigctx.handler; } return ret; @@ -142,6 +169,7 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) unsigned long *frame = NULL; unsigned long *trampoline; unsigned long *regs_ptr; + double *fpregs_ptr; unsigned long nip = 0; unsigned long signr; struct sigcontext_struct *sc; @@ -164,7 +192,7 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) if ((current->flags & PF_PTRACED) && signr != SIGKILL) { current->exit_code = signr; current->state = TASK_STOPPED; - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); if (!(signr = current->exit_code)) continue; @@ -204,7 +232,7 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) current->exit_code = signr; if (!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags & SA_NOCLDSTOP)) - notify_parent(current); + notify_parent(current, SIGCHLD); schedule(); continue; @@ -257,18 +285,33 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) nip = regs->nip; frame = (unsigned long *) regs->gpr[1]; - /* Build trampoline code on stack */ - frame -= 2; + /* + * Build trampoline code on stack, and save gp and fp regs. + * The 56 word hole is because programs using the rs6000/xcoff + * style calling sequence can save up to 19 gp regs and 18 fp regs + * on the stack before decrementing sp. + */ + frame -= 2 + 56; trampoline = frame; - /* verify stack is valid for writing regs struct */ - if (verify_area(VERIFY_WRITE,(void *)frame, sizeof(long)*2+sizeof(*regs)) - || ((unsigned long) frame >= KERNELBASE )) - goto badframe; - put_user(0x38007777UL, trampoline); /* li r0,0x7777 */ - put_user(0x44000002UL, trampoline+1); /* sc */ - frame -= sizeof(*regs) / sizeof(long); + frame -= ELF_NFPREG * sizeof(double) / sizeof(unsigned long); + fpregs_ptr = (double *) frame; + frame -= ELF_NGREG; regs_ptr = frame; - copy_to_user(regs_ptr, regs, sizeof(*regs)); + /* verify stack is valid for writing to */ + if (verify_area(VERIFY_WRITE, frame, + (ELF_NGREG + 2) * sizeof(long) + + ELF_NFPREG * sizeof(double))) + goto badframe; + if (last_task_used_math == current) + giveup_fpu(); + if (__copy_to_user(regs_ptr, regs, + MIN(sizeof(elf_gregset_t),sizeof(struct pt_regs))) + || __copy_to_user(fpregs_ptr, current->tss.fpr, + ELF_NFPREG * sizeof(double)) + || __put_user(0x38007777UL, trampoline) /* li r0,0x7777 */ + || __put_user(0x44000002UL, trampoline+1)) /* sc */ + goto badframe; + signr = 1; sa = current->sig->action; @@ -279,24 +322,29 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) continue; frame -= sizeof(struct sigcontext_struct) / sizeof(long); - if (verify_area(VERIFY_WRITE,(void *)frame, - sizeof(struct sigcontext_struct)/sizeof(long))) + if (verify_area(VERIFY_WRITE, frame, + sizeof(struct sigcontext_struct))) goto badframe; sc = (struct sigcontext_struct *)frame; nip = (unsigned long) sa->sa_handler; if (sa->sa_flags & SA_ONESHOT) sa->sa_handler = NULL; - put_user(nip, &sc->handler); - put_user(oldmask, &sc->oldmask); /* was current->blocked */ - put_user(regs_ptr, &sc->regs); - put_user(signr, &sc->signal); + if (__put_user(nip, &sc->handler) + || __put_user(oldmask, &sc->oldmask) + || __put_user(regs_ptr, &sc->regs) + || __put_user(signr, &sc->signal)) + goto badframe; current->blocked |= sa->sa_mask; regs->gpr[3] = signr; - regs->gpr[4] = (unsigned long)regs_ptr; + regs->gpr[4] = (unsigned long) regs_ptr; } + + frame -= __SIGNAL_FRAMESIZE / sizeof(unsigned long); + if (put_user(regs->gpr[1], frame)) + goto badframe; regs->link = (unsigned long)trampoline; regs->nip = nip; - regs->gpr[1] = (unsigned long)sc - STACK_FRAME_OVERHEAD; + regs->gpr[1] = (unsigned long) frame; /* The DATA cache must be flushed here to insure coherency */ /* between the DATA & INSTRUCTION caches. Since we just */ @@ -305,8 +353,8 @@ asmlinkage int do_signal(unsigned long oldmask, struct pt_regs * regs) /* cache for new data, we have to force the data to go on to */ /* memory and flush the instruction cache to force it to look */ /* there. The following function performs this magic */ - store_cache_range((unsigned long) trampoline, - (unsigned long) (trampoline + 2)); + flush_icache_range((unsigned long) trampoline, + (unsigned long) (trampoline + 2)); return 1; badframe: diff --git a/arch/ppc/kernel/syscalls.c b/arch/ppc/kernel/syscalls.c index bfdccdee6..b7bac7a6a 100644 --- a/arch/ppc/kernel/syscalls.c +++ b/arch/ppc/kernel/syscalls.c @@ -1,15 +1,25 @@ /* * linux/arch/ppc/kernel/sys_ppc.c * + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/i386/kernel/sys_i386.c" * Adapted from the i386 version by Gary Thomas * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au). * * This file contains various random system calls that * have a non-standard calling sequence on the Linux/PPC * platform. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * */ -#include <linux/config.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/mm.h> @@ -20,11 +30,11 @@ #include <linux/shm.h> #include <linux/stat.h> #include <linux/mman.h> +#include <linux/sys.h> #include <linux/ipc.h> #include <asm/uaccess.h> #include <asm/ipc.h> - void check_bugs(void) { @@ -32,32 +42,32 @@ check_bugs(void) asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on) { - printk("sys_ioperm()\n"); + printk(KERN_ERR "sys_ioperm()\n"); return -EIO; } int sys_iopl(int a1, int a2, int a3, int a4) { lock_kernel(); - printk( "sys_iopl(%x, %x, %x, %x)!\n", a1, a2, a3, a4); + printk(KERN_ERR "sys_iopl(%x, %x, %x, %x)!\n", a1, a2, a3, a4); unlock_kernel(); - return (ENOSYS); + return (-ENOSYS); } int sys_vm86(int a1, int a2, int a3, int a4) { lock_kernel(); - printk( "sys_vm86(%x, %x, %x, %x)!\n", a1, a2, a3, a4); + printk(KERN_ERR "sys_vm86(%x, %x, %x, %x)!\n", a1, a2, a3, a4); unlock_kernel(); - return (ENOSYS); + return (-ENOSYS); } int sys_modify_ldt(int a1, int a2, int a3, int a4) { lock_kernel(); - printk( "sys_modify_ldt(%x, %x, %x, %x)!\n", a1, a2, a3, a4); + printk(KERN_ERR "sys_modify_ldt(%x, %x, %x, %x)!\n", a1, a2, a3, a4); unlock_kernel(); - return (ENOSYS); + return (-ENOSYS); } /* @@ -65,7 +75,8 @@ int sys_modify_ldt(int a1, int a2, int a3, int a4) * * This is really horribly ugly. */ -asmlinkage int sys_ipc (uint call, int first, int second, int third, void *ptr, long fifth) +asmlinkage int +sys_ipc (uint call, int first, int second, int third, void *ptr, long fifth) { int version, ret; @@ -73,138 +84,113 @@ asmlinkage int sys_ipc (uint call, int first, int second, int third, void *ptr, version = call >> 16; /* hack for backward compatibility */ call &= 0xffff; - if (call <= SEMCTL) - switch (call) { - case SEMOP: - ret = sys_semop (first, (struct sembuf *)ptr, second); - goto out; - case SEMGET: - ret = sys_semget (first, second, third); - goto out; - case SEMCTL: { - union semun fourth; - ret = -EINVAL; - if (!ptr) - goto out; - ret = -EFAULT; - if (get_user(fourth.__pad, (void **) ptr)) - goto out; - ret = sys_semctl (first, second, third, fourth); - goto out; - } - default: - ret = -EINVAL; - goto out; + ret = -EINVAL; + switch (call) { + case SEMOP: + ret = sys_semop (first, (struct sembuf *)ptr, second); + break; + case SEMGET: + ret = sys_semget (first, second, third); + break; + case SEMCTL: { + union semun fourth; + + if (!ptr) + break; + if ((ret = verify_area (VERIFY_READ, ptr, sizeof(long))) + || (ret = get_user(fourth.__pad, (void **)ptr))) + break; + ret = sys_semctl (first, second, third, fourth); + break; } - if (call <= MSGCTL) - switch (call) { - case MSGSND: - ret = sys_msgsnd (first, (struct msgbuf *) ptr, - second, third); - goto out; - case MSGRCV: - switch (version) { - case 0: { - struct ipc_kludge tmp; - ret = -EINVAL; - if (!ptr) - goto out; - ret = -EFAULT; - if (copy_from_user(&tmp,(struct ipc_kludge *) ptr, - sizeof (tmp))) - goto out; - ret = sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp, third); - goto out; - } - case 1: default: - ret = sys_msgrcv (first, (struct msgbuf *) ptr, second, fifth, third); - goto out; + case MSGSND: + ret = sys_msgsnd (first, (struct msgbuf *) ptr, second, third); + break; + case MSGRCV: + switch (version) { + case 0: { + struct ipc_kludge tmp; + + if (!ptr) + break; + if ((ret = verify_area (VERIFY_READ, ptr, sizeof(tmp))) + || (ret = copy_from_user(&tmp, + (struct ipc_kludge *) ptr, + sizeof (tmp)))) + break; + ret = sys_msgrcv (first, tmp.msgp, second, tmp.msgtyp, + third); + break; } - case MSGGET: - ret = sys_msgget ((key_t) first, second); - goto out; - case MSGCTL: - ret = sys_msgctl (first, second, (struct msqid_ds *) ptr); - goto out; default: - ret = -EINVAL; - goto out; + ret = sys_msgrcv (first, (struct msgbuf *) ptr, + second, fifth, third); + break; } - if (call <= SHMCTL) - switch (call) { - case SHMAT: - switch (version) { - case 0: default: { - ulong raddr; - ret = sys_shmat (first, (char *) ptr, second, &raddr); - if (ret) - goto out; - ret = put_user (raddr, (ulong *) third); - goto out; + break; + case MSGGET: + ret = sys_msgget ((key_t) first, second); + break; + case MSGCTL: + ret = sys_msgctl (first, second, (struct msqid_ds *) ptr); + break; + case SHMAT: + switch (version) { + default: { + ulong raddr; + + if ((ret = verify_area(VERIFY_WRITE, (ulong*) third, + sizeof(ulong)))) + break; + ret = sys_shmat (first, (char *) ptr, second, &raddr); + if (ret) + break; + ret = put_user (raddr, (ulong *) third); + break; } - case 1: /* iBCS2 emulator entry point */ - ret = -EINVAL; - if (get_fs() != get_ds()) - goto out; - ret = sys_shmat (first, (char *) ptr, second, (ulong *) third); - goto out; - } - case SHMDT: - ret = sys_shmdt ((char *)ptr); - goto out; - case SHMGET: - ret = sys_shmget (first, second, third); - goto out; - case SHMCTL: - ret = sys_shmctl (first, second, (struct shmid_ds *) ptr); - goto out; - default: - ret = -EINVAL; - goto out; + case 1: /* iBCS2 emulator entry point */ + if (get_fs() != get_ds()) + break; + ret = sys_shmat (first, (char *) ptr, second, + (ulong *) third); + break; } - else - ret = -EINVAL; -out: - unlock_kernel(); - return ret; -} - + break; + case SHMDT: + ret = sys_shmdt ((char *)ptr); + break; + case SHMGET: + ret = sys_shmget (first, second, third); + break; + case SHMCTL: + ret = sys_shmctl (first, second, (struct shmid_ds *) ptr); + break; + } -#ifndef CONFIG_MODULES -void -scsi_register_module(void) -{ - lock_kernel(); - panic("scsi_register_module"); - unlock_kernel(); -} - -void -scsi_unregister_module(void) -{ - lock_kernel(); - panic("scsi_unregister_module"); unlock_kernel(); + return ret; } -#endif /* * sys_pipe() is the normal C calling standard for creating * a pipe. It's not the way unix traditionally does this, though. */ -asmlinkage int sys_pipe(unsigned long * fildes) +asmlinkage int sys_pipe(int *fildes) { int fd[2]; int error; - error = verify_area(VERIFY_WRITE,fildes,8); + error = verify_area(VERIFY_WRITE, fildes, 8); if (error) return error; + lock_kernel(); error = do_pipe(fd); + unlock_kernel(); if (error) return error; - put_user(fd[0],0+fildes); - put_user(fd[1],1+fildes); + if (__put_user(fd[0],0+fildes) + || __put_user(fd[1],1+fildes)) + return -EFAULT; /* should we close the fds? */ return 0; } @@ -213,13 +199,18 @@ asmlinkage unsigned long sys_mmap(unsigned long addr, size_t len, unsigned long fd, off_t offset) { struct file * file = NULL; + int ret = -EBADF; + + lock_kernel(); if (!(flags & MAP_ANONYMOUS)) { if (fd >= NR_OPEN || !(file = current->files->fd[fd])) - return -EBADF; + goto out; } flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); - - return do_mmap(file, addr, len, prot, flags, offset); + ret = do_mmap(file, addr, len, prot, flags, offset); +out: + unlock_kernel(); + return ret; } extern asmlinkage int sys_select(int, fd_set *, fd_set *, fd_set *, struct timeval *); @@ -233,15 +224,15 @@ extern asmlinkage int sys_select(int, fd_set *, fd_set *, fd_set *, struct timev asmlinkage int ppc_select(int n, fd_set *inp, fd_set *outp, fd_set *exp, struct timeval *tvp) { - int err; if ( (unsigned long)n >= 4096 ) { unsigned long *buffer = (unsigned long *)n; - if ( get_user(n, buffer) || - get_user(inp,buffer+1) || - get_user(outp,buffer+2) || - get_user(exp,buffer+3) || - get_user(tvp,buffer+4) ) + if (verify_area(VERIFY_READ, buffer, 5*sizeof(unsigned long)) + || __get_user(n, buffer) + || __get_user(inp, ((fd_set **)(buffer+1))) + || __get_user(outp, ((fd_set **)(buffer+2))) + || __get_user(exp, ((fd_set **)(buffer+3))) + || __get_user(tvp, ((struct timeval **)(buffer+4)))) return -EFAULT; } return sys_select(n, inp, outp, exp, tvp); diff --git a/arch/ppc/kernel/time.c b/arch/ppc/kernel/time.c index e69de29bb..fc172d7a7 100644 --- a/arch/ppc/kernel/time.c +++ b/arch/ppc/kernel/time.c @@ -0,0 +1,268 @@ +/* + * $Id: time.c,v 1.10 1997/08/27 22:06:56 cort Exp $ + * Common time routines among all ppc machines. + * + * Written by Cort Dougan (cort@cs.nmt.edu) to merge + * Paul Mackerras' version and mine for PReP and Pmac. + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/timex.h> +#include <linux/kernel_stat.h> +#include <linux/mc146818rtc.h> +#include <linux/time.h> + +#include <asm/segment.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/nvram.h> + +#include "time.h" + +/* this is set to the appropriate pmac/prep/chrp func in init_IRQ() */ +int (*set_rtc_time)(unsigned long); + +/* keep track of when we need to update the rtc */ +unsigned long last_rtc_update = 0; + +/* The decrementer counts down by 128 every 128ns on a 601. */ +#define DECREMENTER_COUNT_601 (1000000000 / HZ) +#define COUNT_PERIOD_NUM_601 1 +#define COUNT_PERIOD_DEN_601 1000 + +unsigned decrementer_count; /* count value for 1e6/HZ microseconds */ +unsigned count_period_num; /* 1 decrementer count equals */ +unsigned count_period_den; /* count_period_num / count_period_den us */ + +/* Accessor functions for the decrementer register. */ +inline unsigned long +get_dec(void) +{ + int ret; + + asm volatile("mfspr %0,22" : "=r" (ret) :); + return ret; +} + +inline void +set_dec(int val) +{ + asm volatile("mtspr 22,%0" : : "r" (val)); +} + +/* + * timer_interrupt - gets called when the decrementer overflows, + * with interrupts disabled. + * We set it up to overflow again in 1/HZ seconds. + */ +void timer_interrupt(struct pt_regs * regs) +{ + int dval, d; + while ((dval = get_dec()) < 0) { + /* + * Wait for the decrementer to change, then jump + * in and add decrementer_count to its value + * (quickly, before it changes again!) + */ + while ((d = get_dec()) == dval) + ; + set_dec(d + decrementer_count); + do_timer(regs); + /* + * update the rtc when needed + */ + if ( xtime.tv_sec > last_rtc_update + 660 ) + if (set_rtc_time(xtime.tv_sec) == 0) + last_rtc_update = xtime.tv_sec; + else + last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ + } +} + +/* + * This version of gettimeofday has microsecond resolution. + */ +void do_gettimeofday(struct timeval *tv) +{ + unsigned long flags; + + save_flags(flags); + cli(); + *tv = xtime; + tv->tv_usec += (decrementer_count - get_dec()) + * count_period_num / count_period_den; + if (tv->tv_usec >= 1000000) { + tv->tv_usec -= 1000000; + tv->tv_sec++; + } + restore_flags(flags); +} + +void do_settimeofday(struct timeval *tv) +{ + unsigned long flags; + int frac_tick; + + last_rtc_update = 0; /* so the rtc gets updated soon */ + + frac_tick = tv->tv_usec % (1000000 / HZ); + save_flags(flags); + cli(); + xtime.tv_sec = tv->tv_sec; + xtime.tv_usec = tv->tv_usec - frac_tick; + set_dec(frac_tick * count_period_den / count_period_num); + restore_flags(flags); +} + + +void +time_init(void) +{ + /* pmac hasn't yet called via_cuda_init() */ + if ( _machine != _MACH_Pmac ) + { + + if ( _machine == _MACH_chrp ) + xtime.tv_sec = chrp_get_rtc_time(); + else /* assume prep */ + xtime.tv_sec = prep_get_rtc_time(); + xtime.tv_usec = 0; + /* + * mark the rtc/on-chip timer as in sync + * so we don't update right away + */ + last_rtc_update = xtime.tv_sec; + } + + if ((_get_PVR() >> 16) == 1) { + /* 601 processor: dec counts down by 128 every 128ns */ + decrementer_count = DECREMENTER_COUNT_601; + count_period_num = COUNT_PERIOD_NUM_601; + count_period_den = COUNT_PERIOD_DEN_601; + } + + switch (_machine) + { + case _MACH_Pmac: + pmac_calibrate_decr(); + set_rtc_time = pmac_set_rtc_time; + break; + case _MACH_IBM: + case _MACH_Motorola: + prep_calibrate_decr(); + set_rtc_time = prep_set_rtc_time; + break; + case _MACH_chrp: + chrp_calibrate_decr(); + set_rtc_time = chrp_set_rtc_time; + break; + } + set_dec(decrementer_count); +} + +/* + * Uses the on-board timer to calibrate the on-chip decrementer register + * for prep systems. On the pmac the OF tells us what the frequency is + * but on prep we have to figure it out. + * -- Cort + */ +int calibrate_done = 0; +volatile int *done_ptr = &calibrate_done; +void prep_calibrate_decr(void) +{ + unsigned long flags; + + save_flags(flags); + +#define TIMER0_COUNT 0x40 +#define TIMER_CONTROL 0x43 + /* set timer to periodic mode */ + outb_p(0x34,TIMER_CONTROL);/* binary, mode 2, LSB/MSB, ch 0 */ + /* set the clock to ~100 Hz */ + outb_p(LATCH & 0xff , TIMER0_COUNT); /* LSB */ + outb(LATCH >> 8 , TIMER0_COUNT); /* MSB */ + + if (request_irq(0, prep_calibrate_decr_handler, 0, "timer", NULL) != 0) + panic("Could not allocate timer IRQ!"); + __sti(); + while ( ! *done_ptr ) /* nothing */; /* wait for calibrate */ + restore_flags(flags); + free_irq( 0, NULL); +} + +void prep_calibrate_decr_handler(int irq, void *dev, struct pt_regs * regs) +{ + int freq, divisor; + static unsigned long t1 = 0, t2 = 0; + + if ( !t1 ) + t1 = get_dec(); + else if (!t2) + { + t2 = get_dec(); + t2 = t1-t2; /* decr's in 1/HZ */ + t2 = t2*HZ; /* # decrs in 1s - thus in Hz */ + freq = t2 * 60; /* try to make freq/1e6 an integer */ + divisor = 60; + printk("time_init: decrementer frequency = %d/%d (%luMHz)\n", + freq, divisor,t2>>20); + decrementer_count = freq / HZ / divisor; + count_period_num = divisor; + count_period_den = freq / 1000000; + *done_ptr = 1; + } +} + +void chrp_calibrate_decr(void) +{ + int freq, fp, divisor; + + fp = 16666000; /* hardcoded for now */ + freq = fp*60; /* try to make freq/1e6 an integer */ + divisor = 60; + printk("time_init: decrementer frequency = %d/%d\n", freq, divisor); + decrementer_count = freq / HZ / divisor; + count_period_num = divisor; + count_period_den = freq / 1000000; +} + + +/* Converts Gregorian date to seconds since 1970-01-01 00:00:00. + * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 + * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. + * + * [For the Julian calendar (which was used in Russia before 1917, + * Britain & colonies before 1752, anywhere else before 1582, + * and is still in use by some communities) leave out the + * -year/100+year/400 terms, and add 10.] + * + * This algorithm was first published by Gauss (I think). + * + * WARNING: this function will overflow on 2106-02-07 06:28:16 on + * machines were long is 32-bit! (However, as time_t is signed, we + * will already get problems at other places on 2038-01-19 03:14:08) + */ +inline unsigned long mktime(unsigned int year, unsigned int mon, + unsigned int day, unsigned int hour, + unsigned int min, unsigned int sec) +{ + + if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */ + mon += 12; /* Puts Feb last since it has leap day */ + year -= 1; + } + return ((( + (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) + + year*365 - 719499 + )*24 + hour /* now have hours */ + )*60 + min /* now have minutes */ + )*60 + sec; /* finally seconds */ +} + diff --git a/arch/ppc/kernel/time.h b/arch/ppc/kernel/time.h new file mode 100644 index 000000000..9ce5921dc --- /dev/null +++ b/arch/ppc/kernel/time.h @@ -0,0 +1,74 @@ +/* + * $Id: time.h,v 1.5 1997/08/27 22:06:58 cort Exp $ + * Common time prototypes and such for all ppc machines. + * + * Written by Cort Dougan (cort@cs.nmt.edu) to merge + * Paul Mackerras' version and mine for PReP and Pmac. + */ + +#include <linux/mc146818rtc.h> + +/* time.c */ +__inline__ unsigned long get_dec(void); +__inline__ void set_dec(int val); +void prep_calibrate_decr_handler(int, void *,struct pt_regs *); +void prep_calibrate_decr(void); +void pmac_calibrate_decr(void); +void chrp_calibrate_decr(void); +extern unsigned decrementer_count; +extern unsigned count_period_num; +extern unsigned count_period_den; +extern unsigned long mktime(unsigned int, unsigned int,unsigned int, + unsigned int, unsigned int, unsigned int); + +/* pmac/prep/chrp_time.c */ +unsigned long prep_get_rtc_time(void); +unsigned long pmac_get_rtc_time(void); +unsigned long chrp_get_rtc_time(void); +int prep_set_rtc_time(unsigned long nowtime); +int pmac_set_rtc_time(unsigned long nowtime); +int chrp_set_rtc_time(unsigned long nowtime); +void pmac_read_rtc_time(void); + +#define TICK_SIZE tick +#define FEBRUARY 2 +#define STARTOFTIME 1970 +#define SECDAY 86400L +#define SECYR (SECDAY * 365) +#define leapyear(year) ((year) % 4 == 0) +#define days_in_year(a) (leapyear(a) ? 366 : 365) +#define days_in_month(a) (month_days[(a) - 1]) + +static int month_days[12] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +extern void inline to_tm(int tim, struct rtc_time * tm) +{ + register int i; + register long hms, day; + + day = tim / SECDAY; + hms = tim % SECDAY; + + /* Hours, minutes, seconds are easy */ + tm->tm_hour = hms / 3600; + tm->tm_min = (hms % 3600) / 60; + tm->tm_sec = (hms % 3600) % 60; + + /* Number of years in days */ + for (i = STARTOFTIME; day >= days_in_year(i); i++) + day -= days_in_year(i); + tm->tm_year = i; + + /* Number of months in days left */ + if (leapyear(tm->tm_year)) + days_in_month(FEBRUARY) = 29; + for (i = 1; day >= days_in_month(i); i++) + day -= days_in_month(i); + days_in_month(FEBRUARY) = 28; + tm->tm_mon = i; + + /* Days are what is left over (+1) from all that. */ + tm->tm_mday = day + 1; +} diff --git a/arch/ppc/kernel/traps.c b/arch/ppc/kernel/traps.c index 84ce1c5ca..edfcb4d63 100644 --- a/arch/ppc/kernel/traps.c +++ b/arch/ppc/kernel/traps.c @@ -1,9 +1,15 @@ /* * linux/arch/ppc/kernel/traps.c * - * Copyright (C) 1995 Gary Thomas - * Adapted for PowerPC by Gary Thomas + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * * Modified by Cort Dougan (cort@cs.nmt.edu) + * and Paul Mackerras (paulus@cs.anu.edu.au) */ /* @@ -24,9 +30,21 @@ #include <linux/config.h> #include <asm/pgtable.h> -#include <asm/segment.h> +#include <asm/uaccess.h> #include <asm/system.h> #include <asm/io.h> +#include <asm/processor.h> + +extern int fix_alignment(struct pt_regs *); +extern void bad_page_fault(struct pt_regs *, unsigned long); + +#ifdef CONFIG_XMON +extern int xmon_bpt(struct pt_regs *regs); +extern int xmon_sstep(struct pt_regs *regs); +extern void xmon(struct pt_regs *regs); +extern int xmon_iabr_match(struct pt_regs *regs); +extern void (*xmon_fault_handler)(struct pt_regs *regs); +#endif /* * Trap & Exception support @@ -43,40 +61,53 @@ _exception(int signr, struct pt_regs *regs) if (!user_mode(regs)) { show_regs(regs); - print_backtrace(regs->gpr[1]); - panic("Exception in kernel pc %x signal %d",regs->nip,signr); + print_backtrace((unsigned long *)regs->gpr[1]); +#ifdef CONFIG_XMON + xmon(regs); +#endif + panic("Exception in kernel pc %lx signal %d",regs->nip,signr); } force_sig(signr, current); } +void MachineCheckException(struct pt_regs *regs) { if ( !user_mode(regs) ) { +#ifdef CONFIG_XMON + if (xmon_fault_handler) { + xmon_fault_handler(regs); + return; + } +#endif printk("Machine check in kernel mode.\n"); printk("Caused by (from msr): "); - printk("regs %08x ",regs); + printk("regs %p ",regs); switch( regs->msr & 0x0000F000) { case (1<<12) : printk("Machine check signal - probably due to mm fault\n" "with mmu off\n"); - break; + break; case (1<<13) : printk("Transfer error ack signal\n"); - break; + break; case (1<<14) : printk("Data parity signal\n"); - break; + break; case (1<<15) : printk("Address parity signal\n"); - break; + break; default: printk("Unknown values in msr\n"); } show_regs(regs); - print_backtrace(regs->gpr[1]); - panic(""); + print_backtrace((unsigned long *)regs->gpr[1]); +#ifdef CONFIG_XMON + xmon(regs); +#endif + panic("machine check"); } _exception(SIGSEGV, regs); } @@ -105,33 +136,71 @@ RunModeException(struct pt_regs *regs) _exception(SIGTRAP, regs); } +void ProgramCheckException(struct pt_regs *regs) { - if (current->flags & PF_PTRACED) + if (regs->msr & 0x100000) { + /* IEEE FP exception */ + _exception(SIGFPE, regs); + } else if (regs->msr & 0x20000) { + /* trap exception */ +#ifdef CONFIG_XMON + if (xmon_bpt(regs)) + return; +#endif _exception(SIGTRAP, regs); - else + } else { _exception(SIGILL, regs); + } } +void SingleStepException(struct pt_regs *regs) { regs->msr &= ~MSR_SE; /* Turn off 'trace' bit */ +#ifdef CONFIG_XMON + if (xmon_sstep(regs)) + return; +#endif _exception(SIGTRAP, regs); } +void AlignmentException(struct pt_regs *regs) { + int fixed; + + if (last_task_used_math == current) + giveup_fpu(); + fixed = fix_alignment(regs); + if (fixed == 1) { + regs->nip += 4; /* skip over emulated instruction */ + return; + } + if (fixed == -EFAULT) { + /* fixed == -EFAULT means the operand address was bad */ + bad_page_fault(regs, regs->dar); + return; + } _exception(SIGBUS, regs); } +void +PromException(struct pt_regs *regs, int trap) +{ + regs->trap = trap; +#ifdef CONFIG_XMON + xmon(regs); +#endif + printk("Exception %lx in prom at PC: %lx, SR: %lx\n", + regs->trap, regs->nip, regs->msr); + /* probably should turn up the toes here */ +} + +void trace_syscall(struct pt_regs *regs) { - static int count; - printk("Task: %08X(%d), PC: %08X/%08X, Syscall: %3d, Result: %s%d\n", + printk("Task: %p(%d), PC: %08lX/%08lX, Syscall: %3ld, Result: %s%ld\n", current, current->pid, regs->nip, regs->link, regs->gpr[0], regs->ccr&0x10000000?"Error=":"", regs->gpr[3]); - if (++count == 20) - { - count = 0; - } } diff --git a/arch/ppc/lib/Makefile b/arch/ppc/lib/Makefile index 68b33f047..8b84ce2ae 100644 --- a/arch/ppc/lib/Makefile +++ b/arch/ppc/lib/Makefile @@ -1,38 +1,11 @@ -.c.s: - $(CC) $(CFLAGS) -S $< -.s.o: - $(AS) $(ASFLAGS) -o $*.o $< -.c.o: - $(CC) $(CFLAGS) -c $< -.S.s: - $(CPP) $(CFLAGS) -D__ASSEMBLY__ $< -o $*.s -.S.o: - $(CPP) $(CFLAGS) -D__ASSEMBLY__ $< -o $*.s - $(AS) $(ASFLAGS) -o $*.o $*.s - rm $*.s - -HOST_CC = gcc - -L_TARGET = lib.o -L_OBJS = checksum.o cksum_support.o string.o - -${L_TARGET}: $(L_OBJS) - $(LD) -r -o ${L_TARGET} $(L_OBJS) - - -fastdep: - $(TOPDIR)/scripts/mkdep *.[Sch] > .depend - -dep: - $(CPP) -M *.S *.c > .depend +# +# Makefile for ppc-specific library files.. +# -modules: +.S.o: + $(CC) -D__ASSEMBLY__ -c $< -o $*.o -dummy: +O_TARGET = lib.o +O_OBJS = checksum.o string.o strcase.o -# -# include a dependency file if one exists -# -ifeq (.depend,$(wildcard .depend)) -include .depend -endif +include $(TOPDIR)/Rules.make diff --git a/arch/ppc/lib/checksum.S b/arch/ppc/lib/checksum.S new file mode 100644 index 000000000..6c53d50ff --- /dev/null +++ b/arch/ppc/lib/checksum.S @@ -0,0 +1,192 @@ +/* + * This file contains assembly-language implementations + * of IP-style 1's complement checksum routines. + * + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Severely hacked about by Paul Mackerras (paulus@cs.anu.edu.au). + */ + +#include <linux/sys.h> +#include <asm/errno.h> +#include "../kernel/ppc_asm.tmpl" + +_TEXT() + +/* + * ip_fast_csum(buf, len) -- Optimized for IP header + * len is in words and is always >= 5. + */ +_GLOBAL(ip_fast_csum) + lwz r0,0(r3) + lwzu r5,4(r3) + addi r4,r4,-2 + addc r0,r0,r5 + mtctr r4 +1: lwzu r4,4(r3) + adde r0,r0,r4 + bdnz 1b + addze r0,r0 /* add in final carry */ + rlwinm r3,r0,16,0,31 /* fold two halves together */ + add r3,r0,r3 + not r3,r3 + srwi r3,r3,16 + blr + +/* + * Compute checksum of TCP or UDP pseudo-header: + * csum_tcpudp_magic(saddr, daddr, len, proto, sum) + */ +_GLOBAL(csum_tcpudp_magic) + rlwimi r5,r6,16,0,15 /* put proto in upper half of len */ + addc r0,r3,r4 /* add 4 32-bit words together */ + adde r0,r0,r5 + adde r0,r0,r7 + addze r0,r0 /* add in final carry */ + rlwinm r3,r0,16,0,31 /* fold two halves together */ + add r3,r0,r3 + not r3,r3 + srwi r3,r3,16 + blr + +/* + * computes the checksum of a memory block at buff, length len, + * and adds in "sum" (32-bit) + * + * csum_partial(buff, len, sum) + */ +_GLOBAL(csum_partial) + addic r0,r5,0 + subi r3,r3,4 + srwi. r6,r4,2 + beq 3f /* if we're doing < 4 bytes */ + andi. r5,r3,2 /* Align buffer to longword boundary */ + beq+ 1f + lhz r5,4(r3) /* do 2 bytes to get aligned */ + addi r3,r3,2 + subi r4,r4,2 + addc r0,r0,r5 + srwi. r6,r4,2 /* # words to do */ + beq 3f +1: mtctr r6 +2: lwzu r5,4(r3) /* the bdnz has zero overhead, so it should */ + adde r0,r0,r5 /* be unnecessary to unroll this loop */ + bdnz 2b + andi. r4,r4,3 +3: cmpi 0,r4,2 + blt+ 4f + lhz r5,4(r3) + addi r3,r3,2 + subi r4,r4,2 + adde r0,r0,r5 +4: cmpi 0,r4,1 + bne+ 5f + lbz r5,4(r3) + slwi r5,r5,8 /* Upper byte of word */ + adde r0,r0,r5 +5: addze r3,r0 /* add in final carry */ + blr + +/* + * Computes the checksum of a memory block at src, length len, + * and adds in "sum" (32-bit), while copying the block to dst. + * If an access exception occurs on src or dst, it stores -EFAULT + * to *src_err or *dst_err respectively, and (for an error on + * src) zeroes the rest of dst. + * + * csum_partial_copy_generic(src, dst, len, sum, src_err, dst_err) + */ +_GLOBAL(csum_partial_copy_generic) + addic r0,r6,0 + subi r3,r3,4 + subi r4,r4,4 + srwi. r6,r5,2 + beq 3f /* if we're doing < 4 bytes */ + andi. r9,r4,2 /* Align dst to longword boundary */ + beq+ 1f +81: lhz r6,4(r3) /* do 2 bytes to get aligned */ + addi r3,r3,2 + subi r5,r5,2 +91: sth r6,4(r4) + addi r4,r4,2 + addc r0,r0,r6 + srwi. r6,r5,2 /* # words to do */ + beq 3f +1: mtctr r6 +82: lwzu r6,4(r3) /* the bdnz has zero overhead, so it should */ +92: stwu r6,4(r4) /* be unnecessary to unroll this loop */ + adde r0,r0,r6 + bdnz 82b + andi. r5,r5,3 +3: cmpi 0,r5,2 + blt+ 4f +83: lhz r6,4(r3) + addi r3,r3,2 + subi r5,r5,2 +93: sth r6,4(r4) + addi r4,r4,2 + adde r0,r0,r6 +4: cmpi 0,r5,1 + bne+ 5f +84: lbz r6,4(r3) +94: stb r6,4(r4) + slwi r6,r6,8 /* Upper byte of word */ + adde r0,r0,r6 +5: addze r3,r0 /* add in final carry */ + blr + +.section .fixup,"ax" + +src_error_1: + li r6,0 + subi r5,r5,2 +95: sth r6,4(r4) + addi r4,r4,2 + srwi. r6,r5,2 + beq 3f + mtctr r6 +src_error_2: + li r6,0 +96: stwu r6,4(r4) + bdnz 96b +3: andi. r5,r5,3 + beq src_error +src_error_3: + li r6,0 + mtctr r5 + addi r4,r4,3 +97: stbu r6,1(r4) + bdnz 97b +src_error: + cmpi 0,r7,0 + beq 1f + li r6,-EFAULT + stw r6,0(r7) +1: addze r3,r0 + blr + +dst_error: + cmpi 0,r8,0 + beq 1f + li r6,-EFAULT + stw r6,0(r8) +1: addze r3,r0 + blr + +.section __ex_table,"a" + .long 81b,src_error_1 + .long 91b,dst_error + .long 82b,src_error_2 + .long 92b,dst_error + .long 83b,src_error_3 + .long 93b,dst_error + .long 84b,src_error_3 + .long 94b,dst_error + .long 95b,dst_error + .long 96b,dst_error + .long 97b,dst_error diff --git a/arch/ppc/lib/strcase.c b/arch/ppc/lib/strcase.c new file mode 100644 index 000000000..a9e40bce9 --- /dev/null +++ b/arch/ppc/lib/strcase.c @@ -0,0 +1,12 @@ +#include <linux/ctype.h> + +int strcasecmp(const char *s1, const char *s2) +{ + int c1, c2; + + do { + c1 = tolower(*s1++); + c2 = tolower(*s2++); + } while (c1 == c2 && c1 != 0); + return c1 - c2; +} diff --git a/arch/ppc/lib/string.S b/arch/ppc/lib/string.S new file mode 100644 index 000000000..96378dec1 --- /dev/null +++ b/arch/ppc/lib/string.S @@ -0,0 +1,345 @@ +/* + * String handling functions for PowerPC. + * + * Copyright (C) 1996 Paul Mackerras. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include "../kernel/ppc_asm.tmpl" +#include <asm/errno.h> + + .globl strcpy +strcpy: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r5) + bne 1b + blr + + .globl strncpy +strncpy: + cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r6) + bdnzf 2,1b /* dec ctr, branch if ctr != 0 && !cr0.eq */ + blr + + .globl strcat +strcat: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r0,1(r5) + cmpwi 0,r0,0 + bne 1b + addi r5,r5,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r5) + bne 1b + blr + + .globl strcmp +strcmp: + addi r5,r3,-1 + addi r4,r4,-1 +1: lbzu r3,1(r5) + cmpwi 1,r3,0 + lbzu r0,1(r4) + subf. r3,r0,r3 + beqlr 1 + beq 1b + blr + + .globl strlen +strlen: + addi r4,r3,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + bne 1b + subf r3,r3,r4 + blr + + .globl memset +memset: + rlwimi r4,r4,8,16,23 + rlwimi r4,r4,16,0,15 + addi r6,r3,-4 + cmplwi 0,r5,4 + blt 7f + stwu r4,4(r6) + beqlr + andi. r0,r6,3 + add r5,r0,r5 + subf r6,r0,r6 + rlwinm r0,r5,32-2,2,31 + mtctr r0 + bdz 6f +1: stwu r4,4(r6) + bdnz 1b +6: andi. r5,r5,3 +7: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r6,r6,3 +8: stbu r4,1(r6) + bdnz 8b + blr + + .globl bcopy +bcopy: + mr r6,r3 + mr r3,r4 + mr r4,r6 + b memcpy + + .globl memmove +memmove: + cmplw 0,r3,r4 + bgt backwards_memcpy + /* fall through */ + + .globl memcpy +memcpy: + rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ + addi r6,r3,-4 + addi r4,r4,-4 + beq 2f /* if less than 8 bytes to do */ + andi. r0,r6,3 /* get dest word aligned */ + mtctr r7 + bne 5f +1: lwz r7,4(r4) + lwzu r8,8(r4) + stw r7,4(r6) + stwu r8,8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f + lwzu r0,4(r4) + addi r5,r5,-4 + stwu r0,4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r4,r4,3 + addi r6,r6,3 +4: lbzu r0,1(r4) + stbu r0,1(r6) + bdnz 4b + blr +5: subfic r0,r0,4 + mtctr r0 +6: lbz r7,4(r4) + addi r4,r4,1 + stb r7,4(r6) + addi r6,r6,1 + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b + + .globl backwards_memcpy +backwards_memcpy: + rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ + add r6,r3,r5 + add r4,r4,r5 + beq 2f + andi. r0,r6,3 + mtctr r7 + bne 5f +1: lwz r7,-4(r4) + lwzu r8,-8(r4) + stw r7,-4(r6) + stwu r8,-8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f + lwzu r0,-4(r4) + subi r5,r5,4 + stwu r0,-4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 +4: lbzu r0,-1(r4) + stbu r0,-1(r6) + bdnz 4b + blr +5: mtctr r0 +6: lbzu r7,-1(r4) + stbu r7,-1(r6) + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b + + .globl memcmp +memcmp: + cmpwi 0,r5,0 + ble- 2f + mtctr r5 + addi r6,r3,-1 + addi r4,r4,-1 +1: lbzu r3,1(r6) + lbzu r0,1(r4) + subf. r3,r0,r3 + bdnzt 2,1b + blr +2: li r3,0 + blr + + .global memchr +memchr: + cmpwi 0,r5,0 + ble- 2f + mtctr r5 + addi r3,r3,-1 +1: lbzu r0,1(r3) + cmpw 0,r0,r4 + bdnzf 2,1b + beqlr +2: li r3,0 + blr + + .globl __copy_tofrom_user +__copy_tofrom_user: + rlwinm. r7,r5,32-3,3,31 /* r0 = r5 >> 3 */ + addi r6,r3,-4 + addi r4,r4,-4 + li r3,0 /* success return value */ + beq 2f /* if less than 8 bytes to do */ + andi. r0,r6,3 /* get dest word aligned */ + mtctr r7 + bne 5f +1: lwz r7,4(r4) +11: lwzu r8,8(r4) +12: stw r7,4(r6) +13: stwu r8,8(r6) + bdnz 1b + andi. r5,r5,7 +2: cmplwi 0,r5,4 + blt 3f +14: lwzu r0,4(r4) + addi r5,r5,-4 +15: stwu r0,4(r6) +3: cmpwi 0,r5,0 + beqlr + mtctr r5 + addi r4,r4,3 + addi r6,r6,3 +4: lbzu r0,1(r4) +16: stbu r0,1(r6) + bdnz 4b + blr +5: subfic r0,r0,4 + mtctr r0 +6: lbz r7,4(r4) + addi r4,r4,1 +17: stb r7,4(r6) + addi r6,r6,1 + bdnz 6b + subf r5,r0,r5 + rlwinm. r7,r5,32-3,3,31 + beq 2b + mtctr r7 + b 1b +99: li r3,-EFAULT + blr +.section __ex_table,"a" + .align 2 + .long 1b,99b + .long 11b,99b + .long 12b,99b + .long 13b,99b + .long 14b,99b + .long 15b,99b + .long 4b,99b + .long 16b,99b + .long 6b,99b + .long 17b,99b +.text + + .globl __clear_user +__clear_user: + addi r6,r3,-4 + li r3,0 + li r5,0 + cmplwi 0,r4,4 + blt 7f +11: stwu r5,4(r6) + beqlr + andi. r0,r6,3 + add r4,r0,r4 + subf r6,r0,r6 + rlwinm r0,r4,32-2,2,31 + mtctr r0 + bdz 6f +1: stwu r5,4(r6) + bdnz 1b +6: andi. r4,r4,3 +7: cmpwi 0,r4,0 + beqlr + mtctr r4 + addi r6,r6,3 +8: stbu r5,1(r6) + bdnz 8b + blr +99: li r3,-EFAULT + blr +.section __ex_table,"a" + .align 2 + .long 11b,99b + .long 1b,99b + .long 8b,99b +.text + + .globl __strncpy_from_user +__strncpy_from_user: + addi r6,r3,-1 + addi r4,r4,-1 + cmpwi 0,r5,0 + beq 2f + mtctr r5 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + stbu r0,1(r6) + bdnzf 2,1b /* dec ctr, branch if ctr != 0 && !cr0.eq */ + beq 3f +2: addi r6,r6,1 +3: subf r3,r3,r6 + blr +99: li r3,-EFAULT + blr +.section __ex_table,"a" + .align 2 + .long 1b,99b +.text + + .globl strlen_user +strlen_user: + addi r4,r3,-1 +1: lbzu r0,1(r4) + cmpwi 0,r0,0 + bne 1b + subf r3,r3,r4 + addi r3,r3,1 + blr +99: li r3,0 + blr +.section __ex_table,"a" + .align 2 + .long 1b,99b diff --git a/arch/ppc/mkdiff b/arch/ppc/mkdiff new file mode 100644 index 000000000..304eb9428 --- /dev/null +++ b/arch/ppc/mkdiff @@ -0,0 +1,8 @@ +#!/bin/bash + +N=`basename $PWD` +date=`date +'%y%m%d'` +cd ../ +mkdir -p dist +echo Diff of: $N against $N.ORIG '->' $N-$date-ppc.patch +diff -uNr -X $N/arch/ppc/ignore $N.ORIG $N > dist/$N-$date-ppc.patch diff --git a/arch/ppc/mkdist b/arch/ppc/mkdist new file mode 100644 index 000000000..80408b741 --- /dev/null +++ b/arch/ppc/mkdist @@ -0,0 +1,13 @@ +#!/bin/bash + +V=`egrep ^VERSION Makefile | awk '{print $3}'` +P=`egrep ^PATCHLEVEL Makefile | awk '{print $3}'` +S=`egrep ^SUBLEVEL Makefile | awk '{print $3}'` +date=`date +'%y%m%d'` + +echo zImage-$V.$P.$S-$date +echo System.map-$V.$P.$S-$date + +rcp zImage charon:ppc/dist/kernel-images/zImage-$V.$P.$S-$date +rcp System.map charon:ppc/dist/kernel-images/System.map-$V.$P.$S-$date + diff --git a/arch/ppc/mktar b/arch/ppc/mktar new file mode 100644 index 000000000..a50112ff6 --- /dev/null +++ b/arch/ppc/mktar @@ -0,0 +1,9 @@ +#!/bin/bash + +V=`egrep ^VERSION Makefile | awk '{print $3}'` +P=`egrep ^PATCHLEVEL Makefile | awk '{print $3}'` +S=`egrep ^SUBLEVEL Makefile | awk '{print $3}'` +date=`date +'%y%m%d'` +cd ../ +mkdir -p dist +tar -vzcf ppclinux-$V.$P.$S-$date.tar.gz -X linux/arch/ppc/ignore linux diff --git a/arch/ppc/mm/Makefile b/arch/ppc/mm/Makefile index 13aeab6ca..2b07c7227 100644 --- a/arch/ppc/mm/Makefile +++ b/arch/ppc/mm/Makefile @@ -7,28 +7,7 @@ # # Note 2! The CFLAGS definition is now in the main makefile... -.c.o: - $(CC) $(CFLAGS) -c $< -.s.o: - $(AS) -o $*.o $< -.c.s: - $(CC) $(CFLAGS) -S $< +O_TARGET := mm.o +O_OBJS = fault.o init.o extable.o -OBJS = fault.o init.o extable.o - -mm.o: $(OBJS) - $(LD) -r -o mm.o $(OBJS) - -modules: - -dep: - -fastdep: - $(TOPDIR)/scripts/mkdep *.[Sch] > .depend - -# -# include a dependency file if one exists -# -ifeq (.depend,$(wildcard .depend)) -include .depend -endif +include $(TOPDIR)/Rules.make diff --git a/arch/ppc/mm/extable.c b/arch/ppc/mm/extable.c new file mode 100644 index 000000000..77e3171bc --- /dev/null +++ b/arch/ppc/mm/extable.c @@ -0,0 +1,65 @@ +/* + * linux/arch/ppc/mm/extable.c + * + * from linux/arch/i386/mm/extable.c + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <asm/uaccess.h> + +extern const struct exception_table_entry __start___ex_table[]; +extern const struct exception_table_entry __stop___ex_table[]; + +static inline unsigned long +search_one_table(const struct exception_table_entry *first, + const struct exception_table_entry *last, + unsigned long value) +{ + const struct exception_table_entry *mid; + for ( mid = first; mid < last; mid++) + { + if ( mid->insn == value ) + return mid->fixup; + } + return 0; +#if 0 + while (first <= last) { + const struct exception_table_entry *mid; + long diff; + mid = (last - first) / 2 + first; + diff = mid->insn - value; + if (diff == 0) + return mid->fixup; + else if (diff < 0) + first = mid+1; + else + last = mid-1; + } +#endif + return 0; +} + +unsigned long +search_exception_table(unsigned long addr) +{ + unsigned long ret; + +#if 1 /*ndef CONFIG_MODULES*/ + /* There is only the kernel to search. */ + ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr); + if (ret) return ret; +#else + /* The kernel is the last "module" -- no need to treat it special. */ + struct module *mp; + for (mp = module_list; mp != NULL; mp = mp->next) { + if (mp->ex_table_start == NULL) + continue; + ret = search_one_table(mp->ex_table_start, + mp->ex_table_end - 1, addr); + if (ret) return ret; + } +#endif + + return 0; +} diff --git a/arch/ppc/mm/fault.c b/arch/ppc/mm/fault.c index fea722780..67b94d809 100644 --- a/arch/ppc/mm/fault.c +++ b/arch/ppc/mm/fault.c @@ -1,8 +1,12 @@ /* * arch/ppc/mm/fault.c * - * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds - * Ported to PPC by Gary Thomas + * PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/i386/mm/fault.c" + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * * Modified by Cort Dougan and Paul Mackerras. * * This program is free software; you can redistribute it and/or @@ -26,40 +30,42 @@ #include <asm/page.h> #include <asm/pgtable.h> +#include <asm/mmu.h> #include <asm/mmu_context.h> +#include <asm/system.h> +#include <asm/uaccess.h> -#ifdef CONFIG_PMAC +#ifdef CONFIG_XMON +extern void xmon(struct pt_regs *); extern void (*xmon_fault_handler)(void); -#endif - -/* the linux norm for the function name is show_regs() so - make it call dump_regs() on the mac -- Cort */ -#ifdef CONFIG_PMAC -#define show_regs dump_regs +extern int xmon_dabr_match(struct pt_regs *); +int xmon_kernel_faults; #endif extern void die_if_kernel(char *, struct pt_regs *, long); void bad_page_fault(struct pt_regs *, unsigned long); void do_page_fault(struct pt_regs *, unsigned long, unsigned long); -void print_pte(struct _PTE); -void do_page_fault(struct pt_regs *regs, unsigned long address, unsigned long error_code) +/* + * The error_code parameter is DSISR for a data fault, SRR1 for + * an instruction fault. + */ +void do_page_fault(struct pt_regs *regs, unsigned long address, + unsigned long error_code) { - struct task_struct *tsk = current; - extern unsigned _end[]; struct vm_area_struct * vma; struct mm_struct *mm = current->mm; - pgd_t *dir; - pmd_t *pmd; - pte_t *pte; - - /*printk("do_page_fault() %s/%d addr %x nip %x regs %x error %x\n", - current->comm,current->pid,address,regs->nip,regs,error_code);*/ -#ifdef CONFIG_PMAC + +#ifdef CONFIG_XMON if (xmon_fault_handler && regs->trap == 0x300) { xmon_fault_handler(); return; } + if (error_code & 0x00400000) { + /* DABR match */ + if (xmon_dabr_match(regs)) + return; + } #endif if (in_interrupt()) { static int complained; @@ -68,14 +74,18 @@ void do_page_fault(struct pt_regs *regs, unsigned long address, unsigned long er printk("page fault in interrupt handler, addr=%lx\n", address); show_regs(regs); +#ifdef CONFIG_XMON + if (xmon_kernel_faults) + xmon(regs); +#endif } } - if (current == NULL) - goto bad_area; - -do_page: + if (current == NULL) { + bad_page_fault(regs, address); + return; + } down(&mm->mmap_sem); - vma = find_vma(tsk->mm, address); + vma = find_vma(mm, address); if (!vma) goto bad_area; if (vma->vm_start <= address) @@ -84,7 +94,7 @@ do_page: goto bad_area; if (expand_stack(vma, address)) goto bad_area; - + good_area: if (error_code & 0xb5700000) /* an error such as lwarx to I/O controller space, @@ -98,67 +108,45 @@ good_area: /* a read */ } else { /* protection fault */ - if ( error_code & 0x08000000 ) + if (error_code & 0x08000000) goto bad_area; if (!(vma->vm_flags & (VM_READ | VM_EXEC))) goto bad_area; } handle_mm_fault(current, vma, address, error_code & 0x02000000); - up(&mm->mmap_sem); - /*printk("do_page_fault() return %s/%d addr %x msr %x\n", - current->comm,current->pid,address,regs->msr);*/ - /* not needed since flush_page_to_ram() works */ -#if 0 - flush_page(address); -#endif + up(&mm->mmap_sem); return; - + bad_area: - up(¤t->mm->mmap_sem); + up(&mm->mmap_sem); bad_page_fault(regs, address); } - void bad_page_fault(struct pt_regs *regs, unsigned long address) { - extern unsigned int probingmem; - struct task_struct *tsk = current; unsigned long fixup; - + + if (user_mode(regs)) { + force_sig(SIGSEGV, current); + return; + } /* Are we prepared to handle this fault? */ if ((fixup = search_exception_table(regs->nip)) != 0) { - if ( user_mode(regs) ) - printk("Exception from user mode\n"); -#if 0 - printk(KERN_DEBUG "Exception at %lx (%lx)\n", regs->nip, fixup); -#endif regs->nip = fixup; return; } - if ( user_mode(regs) ) - { - force_sig(SIGSEGV, tsk); - return; - } - -bad_kernel_access: - /* make sure it's not a bootup probe test */ - if ( probingmem ) - { - probingmem = 0; - return; - } /* kernel has accessed a bad area */ show_regs(regs); - print_backtrace( regs->gpr[1] ); -#ifdef CONFIG_PMAC - xmon(regs); + print_backtrace( (unsigned long *)regs->gpr[1] ); +#ifdef CONFIG_XMON + if (xmon_kernel_faults) + xmon(regs); #endif - panic("kernel access of bad area\n pc %x address %X tsk %s/%d", - regs->nip,address,tsk->comm,tsk->pid); + panic("kernel access of bad area pc %lx lr %lx address %lX tsk %s/%d", + regs->nip,regs->link,address,current->comm,current->pid); } unsigned long va_to_phys(unsigned long address) @@ -189,6 +177,10 @@ unsigned long va_to_phys(unsigned long address) return (0); } +#if 0 +/* + * Misc debugging functions. Please leave them here. -- Cort + */ void print_pte(struct _PTE p) { printk( @@ -210,7 +202,8 @@ unsigned long htab_phys_to_va(unsigned long address) for ( ptr = Hash ; ptr < Hash_end ; ptr++ ) { if ( ptr->rpn == (address>>12) ) - printk("phys %08X -> va ???\n", + printk("phys %08lX -> va ???\n", address); } } +#endif diff --git a/arch/ppc/mm/init.c b/arch/ppc/mm/init.c index d46975996..8503401b4 100644 --- a/arch/ppc/mm/init.c +++ b/arch/ppc/mm/init.c @@ -31,54 +31,58 @@ #include <linux/mm.h> #include <linux/swap.h> #include <linux/stddef.h> -#ifdef CONFIG_PMAC #include <asm/prom.h> -#endif #include <asm/io.h> #include <asm/mmu_context.h> #include <asm/pgtable.h> #include <asm/mmu.h> -#ifdef CONFIG_PREP #include <asm/residual.h> +#ifdef CONFIG_BLK_DEV_INITRD +#include <linux/blk.h> /* for initrd_* */ #endif +int prom_trashed; int next_mmu_context; +unsigned long _SDR1; +PTE *Hash, *Hash_end; +unsigned long Hash_size, Hash_mask; +unsigned long *end_of_DRAM; +int mem_init_done; extern pgd_t swapper_pg_dir[]; extern char _start[], _end[]; extern char etext[], _stext[]; -/* References to section boundaries */ extern char __init_begin, __init_end; +extern RESIDUAL res; -extern void die_if_kernel(char *,struct pt_regs *,long); -extern void show_net_buffers(void); -extern unsigned long *find_end_of_memory(void); +/* Hardwired MMU segments */ +#if defined(CONFIG_PREP) || defined(CONFIG_PMAC) +#define MMU_SEGMENT_1 0x80000000 +#define MMU_SEGMENT_2 0xc0000000 +#endif /* CONFIG_PREP || CONFIG_PMAC */ +#ifdef CONFIG_CHRP +#define MMU_SEGMENT_1 0xf0000000 /* LongTrail */ +#define MMU_SEGMENT_2 0xc0000000 +#endif /* CONFIG_CHRP */ -#undef MAP_RAM_WITH_SEGREGS 1 -#ifdef CONFIG_PMAC void *find_mem_piece(unsigned, unsigned); static void mapin_ram(void); static void inherit_prom_translations(void); -#endif -#ifdef CONFIG_PREP -inline void MMU_invalidate_page(struct mm_struct *mm, unsigned long va); -int inline MMU_hash_page(struct task_struct *,unsigned long,pte *); -#endif - static void hash_init(void); static void *MMU_get_page(void); -void map_page(struct thread_struct *, unsigned long va, +void map_page(struct task_struct *, unsigned long va, unsigned long pa, int flags); +extern void die_if_kernel(char *,struct pt_regs *,long); +extern void show_net_buffers(void); +extern unsigned long *find_end_of_memory(void); -PTE *Hash, *Hash_end; -unsigned long Hash_size, Hash_mask; -unsigned long *end_of_DRAM; -int mem_init_done; +/* + * this tells the prep system to map all of ram with the segregs + * instead of the bats. I'd like to get this to apply to the + * pmac as well then have everything use the bats -- Cort + */ +#undef MAP_RAM_WITH_SEGREGS 1 -#ifdef CONFIG_PREP -#ifdef HASHSTATS -extern unsigned long evicts; -#endif /* HASHSTATS */ /* * these are used to setup the initial page tables * They can waste up to an entire page since the @@ -88,8 +92,6 @@ extern unsigned long evicts; unsigned int probingmem = 0; unsigned int mmu_pages_count = 0; char mmu_pages[(MAX_MMU_PAGES+1)*PAGE_SIZE]; -unsigned long _TotalMemory; -#endif /* CONFIG_PREP */ /* * BAD_PAGE is the page that is used for page faults when linux @@ -120,7 +122,6 @@ pte_t __bad_page(void) return pte_mkdirty(mk_pte(empty_bad_page, PAGE_SHARED)); } -#ifdef CONFIG_PMAC #define MAX_MEM_REGIONS 32 phandle memory_pkg; @@ -128,7 +129,6 @@ struct mem_pieces { int n_regions; struct reg_property regions[MAX_MEM_REGIONS]; }; - struct mem_pieces phys_mem; struct mem_pieces phys_avail; struct mem_pieces prom_mem; @@ -138,7 +138,6 @@ static void remove_mem_piece(struct mem_pieces *, unsigned, unsigned, int); static void print_mem_pieces(struct mem_pieces *); unsigned long avail_start; -int prom_trashed; /* * Read in a property describing some pieces of memory. @@ -272,10 +271,10 @@ find_mem_piece(unsigned size, unsigned align) * Our text, data, bss use something over 1MB, starting at 0. * Open Firmware may be using 1MB at the 4MB point. */ -unsigned long *find_end_of_memory(void) +unsigned long *pmac_find_end_of_memory(void) { unsigned long a, total; - unsigned long h, kstart, ksize; + unsigned long kstart, ksize; extern char _stext[], _end[]; int i; @@ -325,20 +324,6 @@ unsigned long *find_end_of_memory(void) remove_mem_piece(&phys_avail, kstart, ksize, 0); remove_mem_piece(&prom_mem, kstart, ksize, 0); - /* - * Allow 64k of hash table for every 16MB of memory, - * up to a maximum of 2MB. - */ - for (h = 64<<10; h < total / 256 && h < 2<<20; h *= 2) - ; - Hash_size = h; - Hash_mask = (h >> 6) - 1; - - /* Find some memory for the hash table. */ - Hash = find_mem_piece(Hash_size, Hash_size); - printk("Total memory = %ldMB; using %ldkB for hash table (at %p)\n", - total >> 20, Hash_size >> 10, Hash); - return __va(total); } @@ -353,6 +338,9 @@ unsigned long find_available_memory(void) int i; unsigned long a, free; unsigned long start, end; + + if ( _machine != _MACH_Pmac ) + return 0; free = 0; for (i = 0; i < phys_avail.n_regions - 1; ++i) { @@ -366,7 +354,6 @@ unsigned long find_available_memory(void) avail_start = (unsigned long) __va(a); return avail_start; } -#endif /* CONFIG_PMAC */ void show_mem(void) { @@ -399,14 +386,21 @@ void show_mem(void) "Ctx", "Ctx<<4", "Last Sys", "pc", "task"); for_each_task(p) { - printk("%-8.8s %3d %3d %8d %8d %8d %c%08x %08x", + printk("%-8.8s %3d %3d %8ld %8ld %8ld %c%08lx %08lx ", p->comm,p->pid, p->mm->count,p->mm->context, p->mm->context<<4, p->tss.last_syscall, user_mode(p->tss.regs) ? 'u' : 'k', p->tss.regs->nip, - p); + (ulong)p); if ( p == current ) - printk(" current"); + printk("current"); + if ( p == last_task_used_math ) + { + if ( p == current ) + printk(","); + printk("last math"); + } + printk("\n"); } } @@ -434,76 +428,68 @@ unsigned long paging_init(unsigned long start_mem, unsigned long end_mem) void mem_init(unsigned long start_mem, unsigned long end_mem) { unsigned long addr; -#ifdef CONFIG_PMAC int i; - unsigned long lim; -#endif + unsigned long a, lim; int codepages = 0; int datapages = 0; int initpages = 0; end_mem &= PAGE_MASK; high_memory = (void *) end_mem; - num_physpages = max_mapnr = MAP_NR(high_memory); - - /* clear the zero-page */ - memset(empty_zero_page, 0, PAGE_SIZE); - + max_mapnr = MAP_NR(high_memory); + num_physpages = max_mapnr; /* RAM is assumed contiguous */ + /* mark usable pages in the mem_map[] */ start_mem = PAGE_ALIGN(start_mem); - -#ifdef CONFIG_PMAC - remove_mem_piece(&phys_avail, __pa(avail_start), - start_mem - avail_start, 1); - - for (a = KERNELBASE ; a < end_mem; a += PAGE_SIZE) - set_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); - - for (i = 0; i < phys_avail.n_regions; ++i) { - a = (unsigned long) __va(phys_avail.regions[i].address); - lim = a + phys_avail.regions[i].size; - a = PAGE_ALIGN(a); - for (; a < lim; a += PAGE_SIZE) { - clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); - mem_map[MAP_NR(a)].count = 1; - free_page(a); + + if ( _machine == _MACH_Pmac ) + { + remove_mem_piece(&phys_avail, __pa(avail_start), + start_mem - avail_start, 1); + + for (addr = KERNELBASE ; addr < end_mem; addr += PAGE_SIZE) + set_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags); + + for (i = 0; i < phys_avail.n_regions; ++i) { + a = (unsigned long) __va(phys_avail.regions[i].address); + lim = a + phys_avail.regions[i].size; + a = PAGE_ALIGN(a); + for (; a < lim; a += PAGE_SIZE) + clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); } - } - phys_avail.n_regions = 0; - - /* free the prom's memory */ - for (i = 0; i < prom_mem.n_regions; ++i) { - a = (unsigned long) __va(prom_mem.regions[i].address); - lim = a + prom_mem.regions[i].size; - a = PAGE_ALIGN(a); - for (; a < lim; a += PAGE_SIZE) { - clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); - mem_map[MAP_NR(a)].count = 1; - free_page(a); + phys_avail.n_regions = 0; + + /* free the prom's memory */ + for (i = 0; i < prom_mem.n_regions; ++i) { + a = (unsigned long) __va(prom_mem.regions[i].address); + lim = a + prom_mem.regions[i].size; + a = PAGE_ALIGN(a); + for (; a < lim; a += PAGE_SIZE) + clear_bit(PG_reserved, &mem_map[MAP_NR(a)].flags); } + prom_trashed = 1; } - prom_trashed = 1; -#endif /* CONFIG_PMAC */ - -#ifdef CONFIG_PREP - /* mark mem used by kernel as reserved, mark other unreserved */ - for (addr = PAGE_OFFSET ; addr < end_mem; addr += PAGE_SIZE) + else /* prep */ { - /* skip hash table gap */ - if ( (addr > (ulong)_end) && (addr < (ulong)Hash)) - continue; - if ( addr < (ulong) /*Hash_end*/ start_mem ) - set_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags); - else - clear_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags); + /* mark mem used by kernel as reserved, mark other unreserved */ + for (addr = PAGE_OFFSET ; addr < end_mem; addr += PAGE_SIZE) + { + /* skip hash table gap */ + if ( (addr > (ulong)_end) && (addr < (ulong)Hash)) + continue; + if ( addr < (ulong) /*Hash_end*/ start_mem ) + set_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags); + else + clear_bit(PG_reserved, &mem_map[MAP_NR(addr)].flags); + } } for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) { - if(PageReserved(mem_map + MAP_NR(addr))) { + if (PageReserved(mem_map + MAP_NR(addr))) { if (addr < (ulong) etext) codepages++; - /*else if((addr >= (unsigned long)&__init_begin && addr < (unsigned long)&__init_end)) - initpages++;*/ + else if((addr >= (unsigned long)&__init_begin && addr < (unsigned long)&__init_end)) + initpages++; else if (addr < (ulong) start_mem) datapages++; continue; @@ -515,9 +501,8 @@ void mem_init(unsigned long start_mem, unsigned long end_mem) #endif /* CONFIG_BLK_DEV_INITRD */ free_page(addr); } - -#endif /* CONFIG_PREP */ - printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%08lx,%08lx]\n", + + printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%08x,%08lx]\n", (unsigned long) nr_free_pages << (PAGE_SHIFT-10), codepages << (PAGE_SHIFT-10), datapages << (PAGE_SHIFT-10), @@ -534,7 +519,6 @@ void mem_init(unsigned long start_mem, unsigned long end_mem) */ void free_initmem(void) { - unsigned long addr; unsigned long a; unsigned long num_freed_pages = 0; @@ -548,16 +532,14 @@ void free_initmem(void) num_freed_pages++; } -#if 0 - addr = (unsigned long)(&__init_begin); - for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { - num_freed_pages++; - mem_map[MAP_NR(addr)].flags &= ~(1 << PG_reserved); - mem_map[MAP_NR(addr)].count = 1; - free_page(addr); + a = (unsigned long)(&__init_begin); + for (; a < (unsigned long)(&__init_end); a += PAGE_SIZE) { + mem_map[MAP_NR(a)].flags &= ~(1 << PG_reserved); + atomic_set(&mem_map[MAP_NR(a)].count, 1); + free_page(a); } -#endif - printk ("Freeing unused kernel memory: %dk freed\n", + + printk ("Freeing unused kernel memory: %ldk freed\n", (num_freed_pages * PAGE_SIZE) >> 10); } @@ -576,29 +558,23 @@ void si_meminfo(struct sysinfo *val) val->totalram++; if (!atomic_read(&mem_map[i].count)) continue; - val->sharedram += atomic_read(&mem_map[i].count)-1; + val->sharedram += atomic_read(&mem_map[i].count) - 1; } val->totalram <<= PAGE_SHIFT; val->sharedram <<= PAGE_SHIFT; return; } -/* Kernel MMU setup & lowest level hardware support */ - -unsigned long _SDR1; /* Hardware SDR1 image */ - -#ifdef CONFIG_PREP - BAT BAT0 = { { - 0x80000000>>17, /* bepi */ + MMU_SEGMENT_1>>17, /* bepi */ BL_256M, /* bl */ 1, /* vs -- supervisor mode valid */ 1, /* vp -- user mode valid */ }, { - 0x80000000>>17, /* brpn */ + MMU_SEGMENT_1>>17, /* brpn */ 1, /* write-through */ 1, /* cache-inhibited */ 0, /* memory coherence */ @@ -609,13 +585,13 @@ BAT BAT0 = BAT BAT1 = { { - 0xC0000000>>17, /* bepi */ + MMU_SEGMENT_2>>17, /* bepi */ BL_256M, /* bl */ 1, /* vs */ 1, /* vp */ }, { - 0xC0000000>>17, /* brpn */ + MMU_SEGMENT_2>>17, /* brpn */ 1, /* w */ 1, /* i (cache disabled) */ 0, /* m */ @@ -635,7 +611,11 @@ BAT BAT2 = 0x00000000>>17, /* brpn */ 0, /* w */ 0, /* i */ +#ifdef __SMP__ 1, /* m */ +#else + 0, /* m */ +#endif 0, /* g */ BPP_RW /* pp */ } @@ -676,13 +656,13 @@ P601_BAT BAT0_601 = P601_BAT BAT1_601 = { { - 0xC0000000>>17, /* bepi */ + MMU_SEGMENT_2>>17, /* bepi */ 1,1,0, /* wim */ 1, 0, /* vs, vp */ BPP_RW, /* pp */ }, { - 0xC0000000>>17, /* brpn */ + MMU_SEGMENT_2>>17, /* brpn */ 1, /* v */ BL_8M, /* bl */ } @@ -724,69 +704,24 @@ P601_BAT BAT3_601 = * this will likely stay seperate from the pmac. * -- Cort */ -unsigned long *find_end_of_memory(void) +unsigned long *prep_find_end_of_memory(void) { - extern RESIDUAL res; - extern unsigned long resptr; - int i, p; - unsigned long h; - - /* copy residual data */ - if ( resptr ) - memcpy( &res, (void *)(resptr+KERNELBASE), sizeof(RESIDUAL) ); - else - /* clearing bss probably clears this but... */ - memset( &res, sizeof(RESIDUAL), 0 ); - _TotalMemory = res.TotalMemory; + int i; - /* this really has nothing to do with the mmu_init() but is - necessary for early setup -- Cort */ - if (!strncmp(res.VitalProductData.PrintableModel,"IBM",3)) - { - _machine = _MACH_IBM; - } - else - _machine = _MACH_Motorola; - - /* setup the hash table */ - if (_TotalMemory == 0 ) + if (res.TotalMemory == 0 ) { /* * I need a way to probe the amount of memory if the residual * data doesn't contain it. -- Cort */ printk("Ramsize from residual data was 0 -- Probing for value\n"); - _TotalMemory = 0x03000000; - printk("Ramsize default to be %dM\n", _TotalMemory>>20); + res.TotalMemory = 0x03000000; + printk("Ramsize default to be %ldM\n", res.TotalMemory>>20); } - -#if 0 - /* linux has trouble with > 64M ram -- Cort */ - if ( _TotalMemory > 0x04000000 /* 64M */ ) - { - printk("Only using first 64M of ram.\n"); - _TotalMemory = 0x04000000; - } -#endif - - /* setup the bat2 mapping to cover physical ram */ - BAT2.batu.bl = 0x1; /* 256k mapping */ - for ( h = 256*1024 /* 256k */ ; (h <= _TotalMemory) && (h <= 256*1024*1024); - h *= 2 ) - BAT2.batu.bl = (BAT2.batu.bl << 1) | BAT2.batu.bl; - /* - * Allow 64k of hash table for every 16MB of memory, - * up to a maximum of 2MB. - */ - for (h = 64<<10; h < _TotalMemory / 256 && h < 2<<20; h *= 2) - ; - Hash_size = h; - Hash_mask = (h >> 6) - 1; - - /* align htab on a Hash_size boundry above _end[] */ - Hash = (PTE *)_ALIGN( (unsigned long)&_end, Hash_size); - memset(Hash, Hash_size, 0 ); + /* NOTE: everything below here is moving to mapin_ram() */ + + /* * if this is a 601, we can only map sizes of 8M with the BAT's * so we have to map what we can't map with the bats with the segregs @@ -799,11 +734,11 @@ unsigned long *find_end_of_memory(void) if ( _get_PVR() == 1 ) { /* map in rest of ram with seg regs */ - if ( _TotalMemory > 0x01000000 /* 16M */) + if ( res.TotalMemory > 0x01000000 /* 16M */) { for (i = KERNELBASE+0x01000000; - i < KERNELBASE+_TotalMemory; i += PAGE_SIZE) - map_page(&init_task.tss, i, __pa(i), + i < KERNELBASE+res.TotalMemory; i += PAGE_SIZE) + map_page(&init_task, i, __pa(i), _PAGE_PRESENT| _PAGE_RW|_PAGE_DIRTY|_PAGE_ACCESSED); } } @@ -815,25 +750,21 @@ unsigned long *find_end_of_memory(void) memset(&BAT2_601, sizeof(BAT2), 0); /* in case we're on a 601 */ memset(&BAT3_601, sizeof(BAT2), 0); /* map all of ram for kernel with segregs */ - for (i = KERNELBASE; i < KERNELBASE+_TotalMemory; i += PAGE_SIZE) + for (i = KERNELBASE; i < KERNELBASE+res.TotalMemory; i += PAGE_SIZE) { if ( i < (unsigned long)etext ) - map_page(&init_task.tss, i, __pa(i), + map_page(&init_task, i, __pa(i), _PAGE_PRESENT/*| _PAGE_RW*/|_PAGE_DIRTY|_PAGE_ACCESSED); else - map_page(&init_task.tss, i, __pa(i), + map_page(&init_task, i, __pa(i), _PAGE_PRESENT| _PAGE_RW|_PAGE_DIRTY|_PAGE_ACCESSED); } #endif /* MAP_RAM_WITH_SEGREGS */ - printk("Total memory = %ldMB; using %ldkB for hash table (at %p)\n", - _TotalMemory >> 20, Hash_size >> 10, Hash); - return ((unsigned long *)_TotalMemory); + return (__va(res.TotalMemory)); } -#endif /* CONFIG_PREP */ -#ifdef CONFIG_PMAC /* * Map in all of physical memory starting at KERNELBASE. */ @@ -847,17 +778,39 @@ static void mapin_ram() int i; unsigned long v, p, s, f; - v = KERNELBASE; - for (i = 0; i < phys_mem.n_regions; ++i) { - p = phys_mem.regions[i].address; - for (s = 0; s < phys_mem.regions[i].size; s += PAGE_SIZE) { - f = _PAGE_PRESENT | _PAGE_ACCESSED; - if ((char *) v < _stext || (char *) v >= etext) - f |= _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE; - map_page(&init_task.tss, v, p, f); - v += PAGE_SIZE; - p += PAGE_SIZE; - } + if ( _machine == _MACH_Pmac ) + { + v = KERNELBASE; + for (i = 0; i < phys_mem.n_regions; ++i) { + p = phys_mem.regions[i].address; + for (s = 0; s < phys_mem.regions[i].size; s += PAGE_SIZE) { + f = _PAGE_PRESENT | _PAGE_ACCESSED; + if ((char *) v < _stext || (char *) v >= etext) + f |= _PAGE_RW | _PAGE_DIRTY | _PAGE_HWWRITE; + else + /* On the powerpc, no user access forces R/W kernel access */ + f |= _PAGE_USER; + map_page(&init_task, v, p, f); + v += PAGE_SIZE; + p += PAGE_SIZE; + } + } + } + else /* prep */ + { + /* setup the bat2 mapping to cover physical ram */ + BAT2.batu.bl = 0x1; /* 256k mapping */ + for ( f = 256*1024 /* 256k */ ; + (f <= res.TotalMemory) && (f <= 256*1024*1024); + f *= 2 ) + BAT2.batu.bl = (BAT2.batu.bl << 1) | BAT2.batu.bl; + /* + * let ibm get to the device mem from user mode since + * the X for them needs it right now -- Cort + */ + if ( _machine == _MACH_IBM ) + BAT0.batu.vp = BAT1.batu.vp = 1; + } } @@ -889,7 +842,7 @@ static void inherit_prom_translations() for (tp = prom_translations, i = 0; i < n_translations; ++i, ++tp) { /* ignore stuff mapped down low */ - if (tp->virt < 0x10000000) + if (tp->virt < 0x10000000 && tp->phys < 0x10000000) continue; /* map PPC mmu flags to linux mm flags */ f = (tp->flags & (_PAGE_NO_CACHE | _PAGE_WRITETHRU @@ -900,13 +853,12 @@ static void inherit_prom_translations() p = tp->phys; n = tp->size; for (; n != 0; n -= PAGE_SIZE) { - map_page(&init_task.tss, v, p, f); + map_page(&init_task, v, p, f); v += PAGE_SIZE; p += PAGE_SIZE; } } } -#endif /* * Initialize the hash table and patch the instructions in head.S. @@ -914,10 +866,29 @@ static void inherit_prom_translations() static void hash_init(void) { int Hash_bits; + unsigned long h; extern unsigned int hash_page_patch_A[], hash_page_patch_B[], hash_page_patch_C[]; + /* + * Allow 64k of hash table for every 16MB of memory, + * up to a maximum of 2MB. + */ + for (h = 64<<10; h < (ulong)__pa(end_of_DRAM) / 256 && h < 2<<20; h *= 2) + ; + Hash_size = h; + Hash_mask = (h >> 6) - 1; + + /* Find some memory for the hash table. */ + if ( is_prep ) + /* align htab on a Hash_size boundry above _end[] */ + Hash = (PTE *)_ALIGN( (unsigned long)&_end, Hash_size); + else /* pmac */ + Hash = find_mem_piece(Hash_size, Hash_size); + + printk("Total memory = %ldMB; using %ldkB for hash table (at %p)\n", + __pa(end_of_DRAM) >> 20, Hash_size >> 10, Hash); memset(Hash, 0, Hash_size); Hash_end = (PTE *) ((unsigned long)Hash + Hash_size); @@ -943,8 +914,8 @@ static void hash_init(void) * out from the data cache and invalidated in the instruction * cache, on those machines with split caches. */ - store_cache_range((unsigned long) hash_page_patch_A, - (unsigned long) (hash_page_patch_C + 1)); + flush_icache_range((unsigned long) hash_page_patch_A, + (unsigned long) (hash_page_patch_C + 1)); } @@ -958,71 +929,93 @@ static void hash_init(void) void MMU_init(void) { - end_of_DRAM = find_end_of_memory(); + if ( _machine == _MACH_Pmac ) + end_of_DRAM = pmac_find_end_of_memory(); + else /* prep and chrp */ + end_of_DRAM = prep_find_end_of_memory(); + hash_init(); _SDR1 = __pa(Hash) | (Hash_mask >> 10); -#ifdef CONFIG_PMAC - /* Force initial page tables */ - /* this done by INIT_TSS in processor.h on prep -- Cort */ - init_task.tss.pg_tables = (unsigned long *)swapper_pg_dir; /* Map in all of RAM starting at KERNELBASE */ mapin_ram(); - /* Copy mappings from the prom */ - inherit_prom_translations(); -#endif /* CONFIG_PMAC */ + if ( _machine == _MACH_Pmac ) + /* Copy mappings from the prom */ + inherit_prom_translations(); } static void * MMU_get_page() { - void *p; - - if (mem_init_done) { - p = (void *) __get_free_page(GFP_KERNEL); - if (p == 0) - panic("couldn't get a page in MMU_get_page"); - } else { -#ifdef CONFIG_PREP - mmu_pages_count++; - if ( mmu_pages_count > MAX_MMU_PAGES ) - printk("out of mmu pages!\n"); - p = (pte *)(PAGE_ALIGN((unsigned long)mmu_pages)+ - (mmu_pages_count+PAGE_SIZE)); -#endif -#ifdef CONFIG_PMAC - p = find_mem_piece(PAGE_SIZE, PAGE_SIZE); -#endif - } - memset(p, 0, PAGE_SIZE); - return p; + void *p; + + if (mem_init_done) { + p = (void *) __get_free_page(GFP_KERNEL); + if (p == 0) + panic("couldn't get a page in MMU_get_page"); + } else { + if ( is_prep || (_machine == _MACH_chrp) ) + { + mmu_pages_count++; + if ( mmu_pages_count > MAX_MMU_PAGES ) + printk("out of mmu pages!\n"); + p = (pte *)(PAGE_ALIGN((unsigned long)mmu_pages)+ + (mmu_pages_count+PAGE_SIZE)); + } + else /* pmac */ + { + p = find_mem_piece(PAGE_SIZE, PAGE_SIZE); + } + } + memset(p, 0, PAGE_SIZE); + return p; } -#ifdef CONFIG_PMAC void * ioremap(unsigned long addr, unsigned long size) { unsigned long p, end = addr + size; + /* + * BAT mappings on prep cover this already so don't waste + * space with it. -- Cort + */ + if ( is_prep ) + if ( ((addr >= 0xc0000000) && (end < (0xc0000000+(256<<20)))) || + ((addr >= 0x80000000) && (end < (0x80000000+(256<<20)))) ) + return (void *)addr; for (p = addr & PAGE_MASK; p < end; p += PAGE_SIZE) - map_page(&init_task.tss, p, p, pgprot_val(PAGE_KERNEL_CI) | _PAGE_GUARDED); + map_page(&init_task, p, p, pgprot_val(PAGE_KERNEL_CI) | _PAGE_GUARDED); return (void *) addr; } -#endif + +extern void iounmap(unsigned long *addr) +{ + /* + * BAT mappings on prep cover this already so don't waste + * space with it. -- Cort + */ + if ( is_prep ) + if ( (((unsigned long)addr >= 0xc0000000) && ((unsigned long)addr < (0xc0000000+(256<<20)))) || + (((unsigned long)addr >= 0x80000000) && ((unsigned long)addr < (0x80000000+(256<<20)))) ) + return; + /* else unmap it */ +} void -map_page(struct thread_struct *tss, unsigned long va, +map_page(struct task_struct *tsk, unsigned long va, unsigned long pa, int flags) { pmd_t *pd; pte_t *pg; + - if (tss->pg_tables == NULL) { + if (tsk->mm->pgd == NULL) { /* Allocate upper level page map */ - tss->pg_tables = (unsigned long *) MMU_get_page(); + tsk->mm->pgd = (pgd_t *) MMU_get_page(); } /* Use upper 10 bits of VA to index the first level map */ - pd = (pmd_t *) (tss->pg_tables + (va >> PGDIR_SHIFT)); + pd = (pmd_t *) (tsk->mm->pgd + (va >> PGDIR_SHIFT)); if (pmd_none(*pd)) { /* Need to allocate second-level table */ pg = (pte_t *) MMU_get_page(); @@ -1084,26 +1077,6 @@ flush_tlb_mm(struct mm_struct *mm) } } - -void -flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr) -{ - unsigned vsid; - - if ( vmaddr < TASK_SIZE) { - /*vsid = (vma->vm_mm->context << 4) | (vmaddr >> 28);*/ - flush_hash_page(vma->vm_mm->context/*vsid*/, vmaddr); - /* this is needed on prep at the moment -- don't know why - -- Cort*/ - MMU_invalidate_page(vma->vm_mm,vmaddr); - } - else - { - /*printk("flush_tlb_page() vmaddr > TASK_SIZE %08x\n", vmaddr);*/ - } -} - - /* for each page addr in the range, call MMU_invalidate_page() if the range is very large and the hash table is small it might be faster to do a search of the hash table and just invalidate pages that are in the range @@ -1113,15 +1086,10 @@ flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr) void flush_tlb_range(struct mm_struct *mm, unsigned long start, unsigned long end) { - start &= PAGE_MASK; + start &= PAGE_MASK; for (; start < end && start < TASK_SIZE; start += PAGE_SIZE) { - /*flush_hash_page(VSID_FROM_CONTEXT( start>>28, mm->context), - start );*/ flush_hash_page(mm->context, start); - /* this is needed on prep at the moment -- don't know why - -- Cort*/ - MMU_invalidate_page(mm,start); } } @@ -1135,78 +1103,18 @@ void mmu_context_overflow(void) { struct task_struct *tsk; - int nr; - printk(KERN_INFO "mmu_context_overflow\n"); - for (nr = 0; nr < NR_TASKS; ++nr) { - tsk = task[nr]; - if (tsk && tsk->mm) + printk(KERN_DEBUG "mmu_context_overflow\n"); + read_lock(&tasklist_lock); + for_each_task(tsk) { + if (tsk->mm) tsk->mm->context = NO_CONTEXT; } + read_unlock(&tasklist_lock); flush_hash_segments(0x10, 0xffffff); - _tlbia(); next_mmu_context = 0; + /* make sure current always has a context */ + current->mm->context = MUNGE_CONTEXT(++next_mmu_context); + set_context(current->mm->context); } -#ifdef CONFIG_PREP -/* - * it's not a simple matter to get rid of these and switch to the - * ones paul is using. it will take some time and thought -- Cort - */ -inline void MMU_invalidate_page(struct mm_struct *mm, unsigned long va) -{ - int hash, page_index, segment, i, h, _h, api, vsid, perms; - PTE *_pte, *slot; - int flags = 0; - page_index = ((int)va & 0x0FFFF000) >> 12; - segment = (unsigned int)va >> 28; - api = page_index >> 10; - vsid = VSID_FROM_CONTEXT(segment,mm->context); - for (_h = 0; _h < 2; _h++) - { - hash = page_index ^ vsid; - if (_h) - { - hash = ~hash; /* Secondary hash uses ones-complement */ - } - hash &= 0x3FF | (Hash_mask /*<< 10*/); - hash *= 8; /* Eight entries / hash bucket */ - _pte = &Hash[hash]; - for (i = 0; i < 8; i++, _pte++) - { - if (_pte->v && _pte->vsid == vsid && _pte->h == _h && _pte->api == api) - { /* Found it! */ - _tlbie(va); /* Clear TLB */ - if (_pte->r) flags |= _PAGE_ACCESSED; - if (_pte->c) flags |= _PAGE_DIRTY; - _pte->v = 0; - return /*(flags)*/; - } - } - } - _tlbie(va); - return /*(flags)*/; -} -#endif - -#include <asm/mmu.h> -void print_mm_info(void) -{ - struct _SEGREG s; - long a; - struct _BATU bu; - struct _BATL bl; - unsigned long i; - - for ( i = 0x70000000 ; i <= 0x90000000 ; i+= 0x10000000 ) - { - a = get_SR(i); - memcpy(&s,&a,4); - printk("sr %2d t:%1d ks:%d kp:%d n:%d vsid:%x %x\n", - i>>28, s.t, s.ks, s.kp, s.n, s.vsid, a); - } - - asm("mfspr %0,532; mfspr %1, 533\n" : "=r" (bu), "=r" (bl)); - printk("bat2 bepi: %0x vs: %1x vp: %1x wimg: %x%x%x%x pp: %1x\n", - bu.bepi<<17, bu.vs, bu.vp, bl.w, bl.i, bl.m, bl.g, bl.pp); -} diff --git a/arch/ppc/pmac_defconfig b/arch/ppc/pmac_defconfig new file mode 100644 index 000000000..5f34efcd2 --- /dev/null +++ b/arch/ppc/pmac_defconfig @@ -0,0 +1,274 @@ +# +# Automatically generated make config: don't edit +# + +# +# Platform support +# +CONFIG_NATIVE=y +CONFIG_PMAC=y +# CONFIG_PREP is not set +CONFIG_MCOMMON=y +# CONFIG_M601 is not set +# CONFIG_M603 is not set +# CONFIG_M604 is not set + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_MODULES=y +# CONFIG_MODVERSIONS is not set +CONFIG_KERNELD=y +CONFIG_PCI=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +CONFIG_BINFMT_MISC=m +CONFIG_BINFMT_JAVA=m +CONFIG_PMAC_CONSOLE=y +CONFIG_MAC_KEYBOARD=y +CONFIG_MAC_FLOPPY=y +CONFIG_PROC_DEVICETREE=y +CONFIG_XMON=y +CONFIG_ATY_VIDEO=y +CONFIG_IMSTT_VIDEO=y + +# +# Plug and Play support +# +# CONFIG_PNP is not set + +# +# Floppy, IDE, and other block devices +# +# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_HD_IDE is not set +CONFIG_BLK_DEV_IDEDISK=y +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_RZ1000 is not set +# CONFIG_BLK_DEV_TRITON is not set +# CONFIG_IDE_CHIPSETS is not set + +# +# Additional Block Devices +# +CONFIG_BLK_DEV_LOOP=m +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_BLK_DEV_XD is not set +# CONFIG_BLK_DEV_EZ is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +CONFIG_SCSI=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_ST=y +CONFIG_BLK_DEV_SR=y +CONFIG_BLK_DEV_SR_VENDOR=y +CONFIG_CHR_DEV_SG=y + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_CONSTANTS=y + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AHA1740 is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA_DMA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_NCR53C7xx is not set +# CONFIG_SCSI_NCR53C8XX is not set +# CONFIG_SCSI_PPA is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_QLOGIC_ISP is not set +# CONFIG_SCSI_SEAGATE is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_ULTRASTOR is not set +CONFIG_SCSI_MESH=y +CONFIG_SCSI_MESH_SYNC_RATE=10 +CONFIG_SCSI_MAC53C94=y +CONFIG_SCSI_QLOGIC_PMAC=m + +# +# Network device support +# + +# +# Networking options +# +# CONFIG_NETLINK is not set +# CONFIG_FIREWALL is not set +CONFIG_NET_ALIAS=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ACCT is not set +# CONFIG_IP_ROUTER is not set +CONFIG_NET_IPIP=m +# CONFIG_IP_MROUTE is not set +CONFIG_IP_ALIAS=y +# CONFIG_SYN_COOKIES is not set + +# +# (it is safe to leave these untouched) +# +# CONFIG_INET_PCTCP is not set +CONFIG_INET_RARP=y +CONFIG_PATH_MTU_DISCOVERY=y +CONFIG_IP_NOSR=y +CONFIG_SKB_LARGE=y +# CONFIG_IPV6 is not set + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_AX25 is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_BRIDGE is not set +# CONFIG_LLC is not set +# CONFIG_WAN_ROUTER is not set +CONFIG_NETDEVICES=y +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_NET_ETHERNET=y +CONFIG_MACE=y +CONFIG_DEC_ELCP=m +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_ISA is not set +# CONFIG_NET_EISA is not set +# CONFIG_NET_POCKET is not set +# CONFIG_FDDI is not set +# CONFIG_DLCI is not set +# CONFIG_PLIP is not set +CONFIG_PPP=m + +# +# CCP compressors for PPP are only built as modules. +# +# CONFIG_NET_RADIO is not set +# CONFIG_SLIP is not set +# CONFIG_TR is not set +# CONFIG_SHAPER is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# CD-ROM drivers (not for SCSI or IDE/ATAPI drives) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Filesystems +# +# CONFIG_QUOTA is not set +CONFIG_MINIX_FS=m +CONFIG_EXT2_FS=y +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_UMSDOS_FS is not set +CONFIG_PROC_FS=y +CONFIG_NFS_FS=y +# CONFIG_ROOT_NFS is not set +# CONFIG_NFSD is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +CONFIG_ISO9660_FS=y +# CONFIG_HPFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_AUTOFS_FS=y +# CONFIG_UFS_FS is not set +CONFIG_MAC_PARTITION=y + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_SERIAL=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_PRINTER is not set +# CONFIG_MOUSE is not set +# CONFIG_UMISC is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_FTAPE is not set +# CONFIG_APM is not set +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +CONFIG_NVRAM=y +# CONFIG_JOYSTICK is not set + +# +# Sound +# +CONFIG_SOUND=m +# CONFIG_PAS is not set +# CONFIG_SB is not set +# CONFIG_ADLIB is not set +# CONFIG_GUS is not set +# CONFIG_MPU401 is not set +# CONFIG_PSS is not set +# CONFIG_GUS16 is not set +# CONFIG_GUSMAX is not set +# CONFIG_MSS is not set +# CONFIG_SSCAPE is not set +# CONFIG_TRIX is not set +# CONFIG_MAD16 is not set +# CONFIG_CS4232 is not set +# CONFIG_MAUI is not set +# CONFIG_YM3812 is not set +CONFIG_LOWLEVEL_SOUND=y +# CONFIG_ACI_MIXER is not set +# CONFIG_AWE32_SYNTH is not set +# CONFIG_AEDSP16 is not set +CONFIG_AWACS=y diff --git a/arch/ppc/prep_defconfig b/arch/ppc/prep_defconfig new file mode 100644 index 000000000..ea39e83ea --- /dev/null +++ b/arch/ppc/prep_defconfig @@ -0,0 +1,250 @@ +# +# Automatically generated by make menuconfig: don't edit +# + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_NATIVE=y +# CONFIG_PMAC is not set +CONFIG_PREP=y +CONFIG_MCOMMON=y + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_MODULES=y +CONFIG_MODVERSIONS=y +CONFIG_KERNELD=y +CONFIG_PCI=y +CONFIG_PCI_OPTIMIZE=y +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_BINFMT_JAVA is not set +CONFIG_VGA_CONSOLE=y + +# +# Plug and Play support +# +# CONFIG_PNP is not set + +# +# Floppy, IDE, and other block devices +# +CONFIG_BLK_DEV_FD=y +CONFIG_BLK_DEV_IDE=y +# CONFIG_BLK_DEV_HD_IDE is not set +CONFIG_BLK_DEV_IDEDISK=y +CONFIG_BLK_DEV_IDECD=y +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_BLK_DEV_CMD640 is not set +# CONFIG_BLK_DEV_RZ1000 is not set +# CONFIG_BLK_DEV_TRITON is not set +# CONFIG_IDE_CHIPSETS is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_BLK_DEV_XD is not set +# CONFIG_BLK_DEV_EZ is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_ST=y +CONFIG_BLK_DEV_SR=y +CONFIG_BLK_DEV_SR_VENDOR=y +# CONFIG_CHR_DEV_SG is not set +# CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_CONSTANTS=y + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_7000FASST is not set +# CONFIG_SCSI_AHA152X is not set +# CONFIG_SCSI_AHA1542 is not set +# CONFIG_SCSI_AHA1740 is not set +# CONFIG_SCSI_AIC7XXX is not set +# CONFIG_SCSI_ADVANSYS is not set +# CONFIG_SCSI_IN2000 is not set +# CONFIG_SCSI_AM53C974 is not set +# CONFIG_SCSI_BUSLOGIC is not set +# CONFIG_SCSI_DTC3280 is not set +# CONFIG_SCSI_EATA_DMA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_EATA is not set +# CONFIG_SCSI_FUTURE_DOMAIN is not set +# CONFIG_SCSI_GENERIC_NCR5380 is not set +# CONFIG_SCSI_NCR53C406A is not set +# CONFIG_SCSI_NCR53C7xx is not set +CONFIG_SCSI_NCR53C8XX=y +# CONFIG_SCSI_NCR53C8XX_NVRAM_DETECT is not set +CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE=y +CONFIG_SCSI_NCR53C8XX_IOMAPPED=y +CONFIG_SCSI_NCR53C8XX_MAX_TAGS=4 +CONFIG_SCSI_NCR53C8XX_SYNC=5 +# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set +# CONFIG_SCSI_PPA is not set +# CONFIG_SCSI_PAS16 is not set +# CONFIG_SCSI_QLOGIC_FAS is not set +# CONFIG_SCSI_QLOGIC_ISP is not set +# CONFIG_SCSI_SEAGATE is not set +# CONFIG_SCSI_DC390T is not set +# CONFIG_SCSI_T128 is not set +# CONFIG_SCSI_U14_34F is not set +# CONFIG_SCSI_ULTRASTOR is not set +# CONFIG_SCSI_MESH is not set +# CONFIG_SCSI_MAC53C94 is not set + +# +# Network device support +# + +# +# Networking options +# +# CONFIG_NETLINK is not set +# CONFIG_FIREWALL is not set +# CONFIG_NET_ALIAS is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ACCT is not set +# CONFIG_IP_ROUTER is not set +# CONFIG_NET_IPIP is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_XTP is not set +# CONFIG_INET_PCTCP is not set +# CONFIG_INET_RARP is not set +CONFIG_PATH_MTU_DISCOVERY=y +# CONFIG_IP_NOSR is not set +CONFIG_SKB_LARGE=y +# CONFIG_IPV6 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_AX25 is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_BRIDGE is not set +# CONFIG_LLC is not set +# CONFIG_WAN_ROUTER is not set +CONFIG_NETDEVICES=y +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_EQUALIZER is not set +CONFIG_NET_ETHERNET=y +CONFIG_NET_VENDOR_3COM=y +# CONFIG_EL1 is not set +# CONFIG_EL2 is not set +# CONFIG_ELPLUS is not set +# CONFIG_EL16 is not set +CONFIG_EL3=y +# CONFIG_VORTEX is not set +CONFIG_LANCE=y +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_NET_ISA is not set +CONFIG_NET_EISA=y +CONFIG_PCNET32=y +# CONFIG_AC3200 is not set +# CONFIG_APRICOT is not set +# CONFIG_CS89x0 is not set +CONFIG_DE4X5=y +# CONFIG_DEC_ELCP is not set +# CONFIG_DGRS is not set +# CONFIG_EEXPRESS_PRO100 is not set +# CONFIG_TLAN is not set +# CONFIG_ES3210 is not set +# CONFIG_ZNET is not set +# CONFIG_NET_POCKET is not set +# CONFIG_FDDI is not set +# CONFIG_DLCI is not set +# CONFIG_PLIP is not set +CONFIG_PPP=y +# CONFIG_NET_RADIO is not set +# CONFIG_SLIP is not set +# CONFIG_TR is not set +# CONFIG_SHAPER is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# CD-ROM drivers (not for SCSI or IDE/ATAPI drives) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Filesystems +# +# CONFIG_QUOTA is not set +# CONFIG_MINIX_FS is not set +CONFIG_EXT2_FS=y +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +# CONFIG_VFAT_FS is not set +# CONFIG_UMSDOS_FS is not set +CONFIG_PROC_FS=y +CONFIG_NFS_FS=y +# CONFIG_ROOT_NFS is not set +CONFIG_NFSD=y +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +CONFIG_ISO9660_FS=y +# CONFIG_HPFS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_UFS_FS is not set +CONFIG_MAC_PARTITION=y + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +# CONFIG_SOFTCURSOR is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_EXTENDED=y +# CONFIG_SERIAL_MANY_PORTS is not set +# CONFIG_SERIAL_SHARE_IRQ is not set +# CONFIG_SERIAL_MULTIPORT is not set +# CONFIG_HUB6 is not set +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_PRINTER is not set +CONFIG_MOUSE=y +# CONFIG_ATIXL_BUSMOUSE is not set +# CONFIG_BUSMOUSE is not set +# CONFIG_MS_BUSMOUSE is not set +CONFIG_PSMOUSE=y +# CONFIG_82C710_MOUSE is not set +# CONFIG_PC110_PAD is not set +# CONFIG_UMISC is not set +# CONFIG_QIC02_TAPE is not set +# CONFIG_FTAPE is not set +# CONFIG_APM is not set +# CONFIG_WATCHDOG is not set +# CONFIG_RTC is not set +# CONFIG_NVRAM is not set +# CONFIG_JOYSTICK is not set + +# +# Sound +# +# CONFIG_SOUND is not set diff --git a/arch/ppc/vmlinux.lds b/arch/ppc/vmlinux.lds new file mode 100644 index 000000000..1a51a4a7e --- /dev/null +++ b/arch/ppc/vmlinux.lds @@ -0,0 +1,85 @@ +OUTPUT_ARCH(powerpc) +SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/local/powerpc-any-elf/lib); +/* Do we need any of these for elf? + __DYNAMIC = 0; */ +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + . = + SIZEOF_HEADERS; + .interp : { *(.interp) } + .hash : { *(.hash) } + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + .rel.text : { *(.rel.text) } + .rela.text : { *(.rela.text) } + .rel.data : { *(.rel.data) } + .rela.data : { *(.rela.data) } + .rel.rodata : { *(.rel.rodata) } + .rela.rodata : { *(.rela.rodata) } + .rel.got : { *(.rel.got) } + .rela.got : { *(.rela.got) } + .rel.ctors : { *(.rel.ctors) } + .rela.ctors : { *(.rela.ctors) } + .rel.dtors : { *(.rel.dtors) } + .rela.dtors : { *(.rela.dtors) } + .rel.bss : { *(.rel.bss) } + .rela.bss : { *(.rela.bss) } + .rel.plt : { *(.rel.plt) } + .rela.plt : { *(.rela.plt) } +/* .init : { *(.init) } =0*/ + .plt : { *(.plt) } + .text : + { + *(.text) + *(.fixup) + *(.got1) + } + _etext = .; + PROVIDE (etext = .); + .rodata : + { + *(.rodata) + *(.rodata1) + } + .fini : { *(.fini) } =0 + .ctors : { *(.ctors) } + .dtors : { *(.dtors) } + /* Read-write section, merged into data segment: */ + . = (. + 0x0FFF) & 0xFFFFF000; + .data : + { + *(.data) + *(.data1) + *(.sdata) + *(.sdata2) + *(.got.plt) *(.got) + *(.dynamic) + CONSTRUCTORS + } + _edata = .; + PROVIDE (edata = .); + + .fixup : { *(.fixup) } + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + . = ALIGN(4096); + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(4096); + __init_end = .; + + __bss_start = .; + .bss : + { + *(.sbss) *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + } + _end = . ; + PROVIDE (end = .); +} + |