diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1995-11-14 08:00:00 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1995-11-14 08:00:00 +0000 |
commit | e7c2a72e2680827d6a733931273a93461c0d8d1b (patch) | |
tree | c9abeda78ef7504062bb2e816bcf3e3c9d680112 /arch/alpha/kernel | |
parent | ec6044459060a8c9ce7f64405c465d141898548c (diff) |
Import of Linux/MIPS 1.3.0
Diffstat (limited to 'arch/alpha/kernel')
-rw-r--r-- | arch/alpha/kernel/Makefile | 47 | ||||
-rw-r--r-- | arch/alpha/kernel/bios32.c | 478 | ||||
-rw-r--r-- | arch/alpha/kernel/entry.S | 554 | ||||
-rw-r--r-- | arch/alpha/kernel/head.S | 97 | ||||
-rw-r--r-- | arch/alpha/kernel/irq.c | 414 | ||||
-rw-r--r-- | arch/alpha/kernel/lca.c | 304 | ||||
-rw-r--r-- | arch/alpha/kernel/osf_sys.c | 494 | ||||
-rw-r--r-- | arch/alpha/kernel/process.c | 186 | ||||
-rw-r--r-- | arch/alpha/kernel/setup.c | 172 | ||||
-rw-r--r-- | arch/alpha/kernel/signal.c | 319 | ||||
-rw-r--r-- | arch/alpha/kernel/traps.c | 159 |
11 files changed, 3224 insertions, 0 deletions
diff --git a/arch/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile new file mode 100644 index 000000000..2843d4481 --- /dev/null +++ b/arch/alpha/kernel/Makefile @@ -0,0 +1,47 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +.c.s: + $(CC) $(CFLAGS) -S $< +.s.o: + $(AS) -o $*.o $< +.c.o: + $(CC) $(CFLAGS) -c $< +.S.s: + $(CPP) -D__ASSEMBLY__ -traditional $< -o $*.s +.S.o: + $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o + +OBJS = entry.o traps.o process.o osf_sys.o irq.o signal.o setup.o \ + lca.o bios32.o + +all: kernel.o head.o + +head.o: head.s + +head.s: head.S $(TOPDIR)/include/asm-alpha/system.h + $(CPP) -traditional -o $*.s $< + +kernel.o: $(OBJS) + $(LD) -r -o kernel.o $(OBJS) + sync + +dep: + $(CPP) -M *.c > .depend + +dummy: + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif + + diff --git a/arch/alpha/kernel/bios32.c b/arch/alpha/kernel/bios32.c new file mode 100644 index 000000000..37c566fb1 --- /dev/null +++ b/arch/alpha/kernel/bios32.c @@ -0,0 +1,478 @@ +#define DEBUG +/* + * bios32.c - PCI BIOS functions for Alpha systems not using BIOS + * emulation code. + * + * Written by Dave Rusling (david.rusling@reo.mts.dec.com) + * + * Adapted to 64-bit kernel and then rewritten by David Mosberger + * (davidm@cs.arizona.edu) + * + * For more information, please consult + * + * PCI BIOS Specification Revision + * PCI Local Bus Specification + * PCI System Design Guide + * + * PCI Special Interest Group + * M/S HF3-15A + * 5200 N.E. Elam Young Parkway + * Hillsboro, Oregon 97124-6497 + * +1 (503) 696-2000 + * +1 (800) 433-5177 + * + * Manuals are $25 each or $50 for all three, plus $7 shipping + * within the United States, $35 abroad. + */ +#include <linux/config.h> + +#ifndef CONFIG_PCI + +int pcibios_present(void) +{ + return 0; +} + +#else /* CONFIG_PCI */ + +#include <linux/kernel.h> +#include <linux/bios32.h> +#include <linux/pci.h> +#include <linux/malloc.h> +#include <linux/mm.h> + +#include <asm/hwrpb.h> +#include <asm/io.h> + + +#define KB 1024 +#define MB (1024*KB) +#define GB (1024*MB) + +#define MAJOR_REV 0 +#define MINOR_REV 2 + +/* + * Align VAL to ALIGN, which must be a power of two. + */ +#define ALIGN(val,align) (((val) + ((align) - 1)) & ~((align) - 1)) + + +/* + * Temporary internal macro. If this 0, then do not write to any of + * the PCI registers, merely read them (i.e., use configuration as + * determined by SRM). The SRM seem do be doing a less than perfect + * job in configuring PCI devices, so for now we do it ourselves. + * Reconfiguring PCI devices breaks console (RPB) callbacks, but + * those don't work properly with 64 bit addresses anyways. + * + * The accepted convention seems to be that the console (POST + * software) should fully configure boot devices and configure the + * interrupt routing of *all* devices. In particular, the base + * addresses of non-boot devices need not be initialized. For + * example, on the AXPpci33 board, the base address a #9 GXE PCI + * graphics card reads as zero (this may, however, be due to a bug in + * the graphics card---there have been some rumor that the #9 BIOS + * incorrectly resets that address to 0...). + */ +#define PCI_MODIFY 1 + + +extern struct hwrpb_struct *hwrpb; + + +#if PCI_MODIFY + +static unsigned int io_base = 64*KB; /* <64KB are (E)ISA ports */ +static unsigned int mem_base = 16*MB; /* <16MB is ISA memory */ + + +/* + * Layout memory and I/O for a device: + */ +static void layout_dev(struct pci_dev *dev) +{ + struct pci_bus *bus; + unsigned short cmd; + unsigned int base, mask, size, reg; + + bus = dev->bus; + pcibios_read_config_word(bus->number, dev->devfn, PCI_COMMAND, &cmd); + + for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) { + /* + * Figure out how much space and of what type this + * device wants. + */ + pcibios_write_config_dword(bus->number, dev->devfn, reg, + 0xffffffff); + pcibios_read_config_dword(bus->number, dev->devfn, reg, &base); + if (!base) { + break; /* done with this device */ + } + /* + * We've read the base address register back after + * writing all ones and so now we must decode it. + */ + if (base & PCI_BASE_ADDRESS_SPACE_IO) { + /* + * I/O space base address register. + */ + cmd |= PCI_COMMAND_IO; + + base &= PCI_BASE_ADDRESS_IO_MASK; + mask = (~base << 1) | 0x1; + size = (mask & base) & 0xffffffff; + base = ALIGN(io_base, size); + io_base = base + size; + pcibios_write_config_dword(bus->number, dev->devfn, + reg, base | 0x1); + } else { + unsigned int type; + /* + * Memory space base address register. + */ + cmd |= PCI_COMMAND_MEMORY; + + type = base & PCI_BASE_ADDRESS_MEM_TYPE_MASK; + base &= PCI_BASE_ADDRESS_MEM_MASK; + mask = (~base << 1) | 0x1; + size = (mask & base) & 0xffffffff; + switch (type) { + case PCI_BASE_ADDRESS_MEM_TYPE_32: + break; + + case PCI_BASE_ADDRESS_MEM_TYPE_64: + printk("bios32 WARNING: " + "ignoring 64-bit device in " + "slot %d, function %d: \n", + PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn)); + reg += 4; /* skip extra 4 bytes */ + continue; + + case PCI_BASE_ADDRESS_MEM_TYPE_1M: + /* + * Allocating memory below 1MB is *very* + * tricky, as there may be all kinds of + * ISA devices lurking that we don't know + * about. For now, we just cross fingers + * and hope nobody tries to do this on an + * Alpha (or that the console has set it + * up properly). + */ + printk("bios32 WARNING: slot %d, function %d " + "requests memory below 1MB---don't " + "know how to do that.\n", + PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn)); + continue; + } + /* + * The following holds at least for the Low Cost + * Alpha implementation of the PCI interface: + * + * In sparse memory address space, the first + * octant (16MB) of every 128MB segment is + * aliased to the the very first 16MB of the + * address space (i.e., it aliases the ISA + * memory address space). Thus, we try to + * avoid allocating PCI devices in that range. + * Can be allocated in 2nd-7th octant only. + * Devices that need more than 112MB of + * address space must be accessed through + * dense memory space only! + */ + base = ALIGN(mem_base, size); + if (size > 7 * 16*MB) { + printk("bios32 WARNING: slot %d, function %d " + "requests %dB of contiguous address " + " space---don't use sparse memory " + " accesses on this device!!\n", + PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), size); + } else { + if (((base / 16*MB) & 0x7) == 0) { + base &= ~(128*MB - 1); + base += 16*MB; + base = ALIGN(base, size); + } + if (base / 128*MB != (base + size) / 128*MB) { + base &= ~(128*MB - 1); + base += (128 + 16)*MB; + base = ALIGN(base, size); + } + } + mem_base = base + size; + pcibios_write_config_dword(bus->number, dev->devfn, + reg, base); + } + } + /* enable device: */ + pcibios_write_config_word(bus->number, dev->devfn, PCI_COMMAND, + cmd | PCI_COMMAND_MASTER); +} + + +static void layout_bus(struct pci_bus *bus) +{ + unsigned int l, tio, bio, tmem, bmem; + struct pci_bus *child; + struct pci_dev *dev; + + if (!bus->devices && !bus->children) + return; + + /* + * Align the current bases on appropriate boundaries (4K for + * IO and 1MB for memory). + */ + bio = io_base = ALIGN(io_base, 4*KB); + bmem = mem_base = ALIGN(mem_base, 1*MB); + + /* + * Allocate space to each device: + */ + for (dev = bus->devices; dev; dev = dev->sibling) { + if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) { + layout_dev(dev); + } + } + /* + * Recursively allocate space for all of the sub-buses: + */ + for (child = bus->children; child; child = child->next) { + layout_bus(child); + } + /* + * Align the current bases on 4K and 1MB boundaries: + */ + tio = io_base = ALIGN(io_base, 4*KB); + tmem = mem_base = ALIGN(mem_base, 1*MB); + + if (bus->self) { + struct pci_dev *bridge = bus->self; + /* + * Set up the top and bottom of the I/O memory segment + * for this bus. + */ + pcibios_read_config_dword(bridge->bus->number, bridge->devfn, + 0x1c, &l); + l = l | (bio >> 8) | ((tio - 1) & 0xf000); + pcibios_write_config_dword(bridge->bus->number, bridge->devfn, + 0x1c, l); + + l = ((bmem & 0xfff00000) >> 16) | ((tmem - 1) & 0xfff00000); + pcibios_write_config_dword(bridge->bus->number, bridge->devfn, + 0x20, l); + /* + * Turn off downstream PF memory address range: + */ + pcibios_write_config_dword(bridge->bus->number, bridge->devfn, + 0x24, 0x0000ffff); + /* + * Tell bridge that there is an ISA bus in the system: + */ + pcibios_write_config_dword(bridge->bus->number, bridge->devfn, + 0x3c, 0x00040000); + /* + * Clear status bits, enable I/O (for downstream I/O), + * turn on master enable (for upstream I/O), turn on + * memory enable (for downstream memory), turn on + * master enable (for upstream memory and I/O). + */ + pcibios_write_config_dword(bridge->bus->number, bridge->devfn, + 0x4, 0xffff0007); + } +} + +#endif /* !PCI_MODIFY */ + + +/* + * Given the vendor and device ids, find the n'th instance of that device + * in the system. + */ +int pcibios_find_device (unsigned short vendor, unsigned short device_id, + unsigned short index, unsigned char *bus, + unsigned char *devfn) +{ + unsigned int current = 0; + struct pci_dev *dev; + + for (dev = pci_devices; dev; dev = dev->next) { + if (dev->vendor == vendor && dev->device == device_id) { + if (current == index) { + *devfn = dev->devfn; + *bus = dev->bus->number; + return PCIBIOS_SUCCESSFUL; + } + ++current; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + + +/* + * Given the class, find the n'th instance of that device + * in the system. + */ +int pcibios_find_class (unsigned int class_code, unsigned short index, + unsigned char *bus, unsigned char *devfn) +{ + unsigned int current = 0; + struct pci_dev *dev; + + for (dev = pci_devices; dev; dev = dev->next) { + if (dev->class == class_code) { + if (current == index) { + *devfn = dev->devfn; + *bus = dev->bus->number; + return PCIBIOS_SUCCESSFUL; + } + ++current; + } + } + return PCIBIOS_DEVICE_NOT_FOUND; +} + + +int pcibios_present(void) +{ + return 1; +} + + +unsigned long pcibios_init(unsigned long mem_start, + unsigned long mem_end) +{ + printk("Alpha PCI BIOS32 revision %x.%02x\n", MAJOR_REV, MINOR_REV); + +#if !PCI_MODIFY + printk("...NOT modifying existing (SRM) PCI configuration\n"); +#endif + return mem_start; +} + + +/* + * Fixup configuration for Noname boards (AXPpci33). + */ +static void noname_fixup(void) +{ + struct pci_dev *dev; + + /* + * The Noname board has 5 PCI slots with each of the 4 + * interrupt pins routed to different pins on the PCI/ISA + * bridge (PIRQ0-PIRQ3). I don't have any information yet as + * to how INTB, INTC, and INTD get routed (4/12/95, + * davidm@cs.arizona.edu). + */ + static const char pirq_tab[5][4] = { + { 3, -1, -1, -1}, /* slot 6 (53c810) */ + {-1, -1, -1, -1}, /* slot 7 (PCI/ISA bridge) */ + { 2, -1, -1, -1}, /* slot 8 (slot closest to ISA) */ + { 1, -1, -1, -1}, /* slot 9 (middle slot) */ + { 0, -1, -1, -1}, /* slot 10 (slot furthest from ISA) */ + }; + /* + * route_tab selects irq routing in PCI/ISA bridge so that: + * PIRQ0 -> irq 15 + * PIRQ1 -> irq 9 + * PIRQ2 -> irq 10 + * PIRQ3 -> irq 11 + */ + const unsigned int route_tab = 0x0b0a090f; + unsigned char pin; + int pirq; + + pcibios_write_config_dword(0, PCI_DEVFN(7, 0), 0x60, route_tab); + + /* ensure irq 9, 10, 11, and 15 are level sensitive: */ + outb((1<<(9-8)) | (1<<(10-8)) | (1<<(11-8)) | (1<<(15-8)), 0x4d1); + + /* + * Go through all devices, fixing up irqs as we see fit: + */ + for (dev = pci_devices; dev; dev = dev->next) { + dev->irq = 0; + if (dev->bus->number != 0 || + PCI_SLOT(dev->devfn) < 6 || PCI_SLOT(dev->devfn) > 10) + { + printk("noname_set_irq: no dev on bus %d, slot %d!!\n", + dev->bus->number, PCI_SLOT(dev->devfn)); + continue; + } + + pcibios_read_config_byte(dev->bus->number, dev->devfn, + PCI_INTERRUPT_PIN, &pin); + if (!pin) { + if (dev->vendor == PCI_VENDOR_ID_S3 && + (dev->device == PCI_DEVICE_ID_S3_864_1 || + dev->device == PCI_DEVICE_ID_S3_864_2)) + { + pin = 1; + } else { + continue; /* no interrupt line */ + } + } + pirq = pirq_tab[PCI_SLOT(dev->devfn) - 6][pin - 1]; + if (pirq < 0) { + continue; + } + dev->irq = (route_tab >> (8 * pirq)) & 0xff; +#if PCI_MODIFY + /* tell the device: */ + pcibios_write_config_byte(dev->bus->number, dev->devfn, + PCI_INTERRUPT_LINE, dev->irq); +#endif + } + +#if PCI_MODIFY + { + unsigned char hostid; + /* + * SRM console version X3.9 seems to reset the SCSI + * host-id to 0 no matter what console environment + * variable pka0_host_id is set to. Thus, if the + * host-id reads out as a zero, we set it to 7. The + * SCSI controller is on the motherboard on bus 0, + * slot 6 + */ + if (pcibios_read_config_byte(0, PCI_DEVFN(6, 0), 0x84, &hostid) + == PCIBIOS_SUCCESSFUL && (hostid == 0)) + { + pcibios_write_config_byte(0, PCI_DEVFN(6, 0), + 0x84, 7); + } + } +#endif /* !PCI_MODIFY */ +} + + +unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end) +{ +#if PCI_MODIFY + /* + * Scan the tree, allocating PCI memory and I/O space. + */ + layout_bus(&pci_root); +#endif + + /* + * Now is the time to do all those dirty little deeds... + */ + switch (hwrpb->sys_type) { + case ST_DEC_AXPPCI_33: noname_fixup(); break; + + default: + printk("pcibios_fixup: don't know how to fixup sys type %ld\n", + hwrpb->sys_type); + break; + } + return mem_start; +} + +#endif /* CONFIG_PCI */ diff --git a/arch/alpha/kernel/entry.S b/arch/alpha/kernel/entry.S new file mode 100644 index 000000000..b54ea220b --- /dev/null +++ b/arch/alpha/kernel/entry.S @@ -0,0 +1,554 @@ +/* + * alpha/entry.S + * + * kernel entry-points + */ + +#include <asm/system.h> + +#define halt .long PAL_halt +#define rti .long PAL_rti + +#define NR_SYSCALLS 310 +#define osf_vfork sys_fork + +/* + * These offsets must match with "struct hae" in io.h: + */ +#define HAE_CACHE 0 +#define HAE_REG 8 + +/* + * stack offsets + */ +#define SP_OFF 160 + +#define SWITCH_STACK_SIZE 320 + +/* + * task structure offsets + */ +#define TASK_STATE 0 +#define TASK_COUNTER 8 +#define TASK_PRIORITY 16 +#define TASK_SIGNAL 24 +#define TASK_BLOCKED 32 +#define TASK_FLAGS 40 + +/* + * This defines the normal kernel pt-regs layout. + * + * regs 9-15 preserved by C code + * regs 16-18 saved by PAL-code + * regs 29-30 saved and set up by PAL-code + */ +#define SAVE_ALL \ + subq $30,160,$30; \ + stq $0,0($30); \ + stq $1,8($30); \ + stq $2,16($30); \ + stq $3,24($30); \ + stq $4,32($30); \ + stq $5,40($30); \ + stq $6,48($30); \ + stq $7,56($30); \ + stq $8,64($30); \ + stq $19,72($30); \ + stq $20,80($30); \ + stq $21,88($30); \ + stq $22,96($30); \ + stq $23,104($30); \ + stq $24,112($30); \ + stq $25,120($30); \ + stq $26,128($30); \ + stq $27,136($30); \ + stq $28,144($30); \ + lda $2,hae; \ + ldq $2,HAE_CACHE($2); \ + stq $2,152($30) + +#define RESTORE_ALL \ + lda $8,hae; \ + ldq $7,HAE_CACHE($8); \ + ldq $6,152($30); \ + subq $7,$6,$5; \ + beq $5,99f; \ + ldq $7,HAE_REG($8); \ + addq $31,7,$16; \ + call_pal PAL_swpipl; \ + stq $6,HAE_CACHE($8); \ + stq $6,0($7); \ + mb; \ + bis $0,$0,$16; \ + call_pal PAL_swpipl; \ +99:; \ + ldq $0,0($30); \ + ldq $1,8($30); \ + ldq $2,16($30); \ + ldq $3,24($30); \ + ldq $4,32($30); \ + ldq $5,40($30); \ + ldq $6,48($30); \ + ldq $7,56($30); \ + ldq $8,64($30); \ + ldq $19,72($30); \ + ldq $20,80($30); \ + ldq $21,88($30); \ + ldq $22,96($30); \ + ldq $23,104($30); \ + ldq $24,112($30); \ + ldq $25,120($30); \ + ldq $26,128($30); \ + ldq $27,136($30); \ + ldq $28,144($30); \ + addq $30,160,$30 + +.text +.set noat + +.align 3 +.globl entInt +.ent entInt +entInt: + SAVE_ALL +/* start atomic operation with respect to software interrupts */ + lda $0,intr_count + ldq $1,0($0) + addq $1,1,$1 + stq $1,0($0) +/* set up the arguments to the C interrupt handler */ + lda $27,do_entInt + jsr $26,($27),do_entInt +/* ok, check if we need to do software interrupts */ +1: lda $0,intr_count + ldq $1,0($0) + subq $1,1,$1 + bne $1,2f /* interrupt within interrupt: return now */ + lda $2,bh_active + ldq $3,0($2) + lda $2,bh_mask + ldq $2,0($2) + and $2,$3,$2 + bne $2,3f + stq $1,0($0) + br $31,ret_from_sys_call +.align 3 +2: stq $1,0($0) + br $31,restore_all +.align 3 +3: lda $27,do_bottom_half + jsr $26,($27),do_bottom_half + br $31,1b +.end entInt + +.align 3 +.globl entMM +.ent entMM +entMM: + SAVE_ALL + lda $27,do_page_fault + lda $26,ret_from_sys_call + jsr $31,($27),do_page_fault +.end entMM + +.align 3 +.globl entArith +.ent entArith +entArith: + SAVE_ALL + lda $27,do_entArith + lda $26,ret_from_sys_call + jsr $31,($27),do_entArith +.end entArith + +.align 3 +.globl entIF +.ent entIF +entIF: + SAVE_ALL + lda $27,do_entIF + lda $26,ret_from_sys_call + jsr $31,($27),do_entIF +.end entIF + +/* + * Fork() is one of the special system calls: it needs to + * save the callee-saved regs so that the regs can be found + * for the new process.. We save them in the "context switch" + * stack format (see arch/alpha/kernel/process.c). + * + * Also, for the kernel fork, we need to fake the system call + * stack buildup, as we can't do system calls from kernel space. + */ +.align 3 +.globl kernel_fork +.ent kernel_fork +kernel_fork: + subq $30,6*8,$30 + stq $31,0($30) + stq $26,8($30) + stq $29,16($30) + stq $16,24($30) + stq $17,32($30) + stq $18,40($30) + SAVE_ALL + lda $27,sys_fork + jsr $26,($27),sys_fork + br ret_from_sys_call +.end kernel_fork + +.align 3 +.ent do_switch_stack +do_switch_stack: + lda $30,-SWITCH_STACK_SIZE($30) + stq $9,0($30) + stq $10,8($30) + stq $11,16($30) + stq $12,24($30) + stq $13,32($30) + stq $14,40($30) + stq $15,48($30) + stq $26,56($30) + stt $f0,64($30) + stt $f1,72($30) + stt $f2,80($30) + stt $f3,88($30) + stt $f4,96($30) + stt $f5,104($30) + stt $f6,112($30) + stt $f7,120($30) + stt $f8,128($30) + stt $f9,136($30) + stt $f10,144($30) + stt $f11,152($30) + stt $f12,160($30) + stt $f13,168($30) + stt $f14,176($30) + stt $f15,184($30) + stt $f16,192($30) + stt $f17,200($30) + stt $f18,208($30) + stt $f19,216($30) + stt $f20,224($30) + stt $f21,232($30) + stt $f22,240($30) + stt $f23,248($30) + stt $f24,256($30) + stt $f25,264($30) + stt $f26,272($30) + stt $f27,280($30) + stt $f28,288($30) + stt $f29,296($30) + stt $f30,304($30) + ret $31,($0),1 +.end do_switch_stack + +.align 3 +.ent undo_switch_stack +undo_switch_stack: + ldq $9,0($30) + ldq $10,8($30) + ldq $11,16($30) + ldq $12,24($30) + ldq $13,32($30) + ldq $14,40($30) + ldq $15,48($30) + ldq $26,56($30) + ldt $f0,64($30) + ldt $f1,72($30) + ldt $f2,80($30) + ldt $f3,88($30) + ldt $f4,96($30) + ldt $f5,104($30) + ldt $f6,112($30) + ldt $f7,120($30) + ldt $f8,128($30) + ldt $f9,136($30) + ldt $f10,144($30) + ldt $f11,152($30) + ldt $f12,160($30) + ldt $f13,168($30) + ldt $f14,176($30) + ldt $f15,184($30) + ldt $f16,192($30) + ldt $f17,200($30) + ldt $f18,208($30) + ldt $f19,216($30) + ldt $f20,224($30) + ldt $f21,232($30) + ldt $f22,240($30) + ldt $f23,248($30) + ldt $f24,256($30) + ldt $f25,264($30) + ldt $f26,272($30) + ldt $f27,280($30) + ldt $f28,288($30) + ldt $f29,296($30) + ldt $f30,304($30) + lda $30,SWITCH_STACK_SIZE($30) + ret $31,($0),1 +.end undo_switch_stack + +.align 3 +.globl entUna +.ent entUna +entUna: + lda $30,-256($30) + stq $0,0($30) + stq $1,8($30) + stq $2,16($30) + stq $3,24($30) + stq $4,32($30) + stq $5,40($30) + stq $6,48($30) + stq $7,56($30) + stq $8,64($30) + stq $9,72($30) + stq $10,80($30) + stq $11,88($30) + stq $12,96($30) + stq $13,104($30) + stq $14,112($30) + stq $15,120($30) + /* 16-18 PAL-saved */ + stq $19,152($30) + stq $20,160($30) + stq $21,168($30) + stq $22,176($30) + stq $23,184($30) + stq $24,192($30) + stq $25,200($30) + stq $26,208($30) + stq $27,216($30) + stq $28,224($30) + stq $29,232($30) + stq $30,240($30) + stq $31,248($30) + lda $27,do_entUna + jsr $26,($27),do_entUna + ldq $0,0($30) + ldq $1,8($30) + ldq $2,16($30) + ldq $3,24($30) + ldq $4,32($30) + ldq $5,40($30) + ldq $6,48($30) + ldq $7,56($30) + ldq $8,64($30) + ldq $9,72($30) + ldq $10,80($30) + ldq $11,88($30) + ldq $12,96($30) + ldq $13,104($30) + ldq $14,112($30) + ldq $15,120($30) + /* 16-18 PAL-saved */ + ldq $19,152($30) + ldq $20,160($30) + ldq $21,168($30) + ldq $22,176($30) + ldq $23,184($30) + ldq $24,192($30) + ldq $25,200($30) + ldq $26,208($30) + ldq $27,216($30) + ldq $28,224($30) + ldq $29,232($30) + ldq $30,240($30) + lda $30,256($30) + rti +.end entUna + +.align 3 +.globl sys_fork +.ent sys_fork +sys_fork: + br $0,do_switch_stack + bis $30,$30,$16 + lda $27,alpha_fork + jsr $26,($27),alpha_fork + br $0,undo_switch_stack + ldq $0,0($30) + ret $31,($26),1 +.end sys_fork + +.align 3 +.globl alpha_switch_to +.ent alpha_switch_to +alpha_switch_to: + br $0,do_switch_stack + call_pal PAL_swpctx + br $0,undo_switch_stack + ret $31,($26),1 +.end alpha_switch_to + +/* + * Oh, well.. Disassembling OSF/1 binaries to find out how the + * system calls work isn't much fun. + * + * entSys is special in that the PAL-code doesn't save a0-a2, so + * we start off by doing that by hand. + */ +.align 3 +.globl entSys +.globl ret_from_sys_call +.ent entSys +entSys: + stq $16,24($30) + stq $17,32($30) + stq $18,40($30) + SAVE_ALL + lda $1,NR_SYSCALLS($31) + lda $2,sys_call_table + lda $27,do_entSys + cmpult $0,$1,$1 + s8addq $0,$2,$2 + beq $1,1f + ldq $27,0($2) +1: jsr $26,($27),do_entSys + bis $31,$31,$1 + bge $0,2f + bis $31,$31,$26 /* tell "ret_from_sys_call" that we can restart */ + ldq $19,0($30) /* .. with this syscall nr */ + ldq $20,72($30) /* .. and this a3 */ + addq $31,1,$1 /* set a3 for errno return */ + subq $31,$0,$0 /* with error in v0 */ +2: stq $0,0($30) + stq $1,72($30) /* a3 for return */ +.align 3 +ret_from_sys_call: + ldq $0,SP_OFF($30) + cmovne $26,0,$19 + and $0,8,$0 + beq $0,restore_all +ret_from_reschedule: + lda $0,need_resched + lda $1,current + ldl $2,0($0) + lda $4,init_task + ldq $3,0($1) + bne $2,reschedule + subq $4,$3,$4 + beq $4,restore_all + ldq $4,TASK_SIGNAL($3) + ldq $16,TASK_BLOCKED($3) + bic $4,$16,$4 + bne $4,signal_return +restore_all: + RESTORE_ALL + rti +.align 3 +signal_return: + bis $30,$30,$17 + br $0,do_switch_stack + bis $30,$30,$18 + lda $27,do_signal + jsr $26,($27),do_signal + lda $30,SWITCH_STACK_SIZE($30) + br $31,restore_all +.end entSys + +.align 3 +.ent reschedule +reschedule: + subq $30,16,$30 + stq $19,0($30) + stq $20,8($30) + lda $27,schedule + jsr $26,($27),schedule + ldq $19,0($30) + ldq $20,8($30) + addq $30,16,$30 + br $31,ret_from_reschedule +.end reschedule + +.align 3 +.ent sys_sigreturn +sys_sigreturn: + bis $30,$30,$17 + lda $30,-SWITCH_STACK_SIZE($30) + bis $30,$30,$18 + lda $27,do_sigreturn + jsr $26,($27),do_sigreturn + br $0,undo_switch_stack + br $31,ret_from_sys_call +.end sys_sigreturn + +.align 3 +.ent sys_sigsuspend +sys_sigsuspend: + bis $30,$30,$17 + br $0,do_switch_stack + bis $30,$30,$18 + lda $27,do_sigsuspend + jsr $26,($27),do_sigsuspend + lda $30,SWITCH_STACK_SIZE($30) + br $31,ret_from_sys_call +.end sys_sigreturn + + .align 3 + .globl sys_call_table +sys_call_table: +/*0*/ .quad do_entSys, sys_exit, sys_fork, sys_read, sys_write + .quad do_entSys, sys_close, sys_wait4, do_entSys, sys_link + .quad sys_unlink, do_entSys, sys_chdir, sys_fchdir, sys_mknod + .quad sys_chmod, sys_chown, sys_brk, do_entSys, sys_lseek + .quad sys_getxpid, osf_mount, osf_umount, sys_setuid, sys_getxuid + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, sys_access, do_entSys + .quad do_entSys, sys_sync, sys_kill, do_entSys, sys_setpgid + .quad do_entSys, sys_dup, sys_pipe, do_entSys, do_entSys + .quad sys_open, do_entSys, sys_getxgid, osf_sigprocmask, do_entSys +/*50*/ .quad do_entSys, do_entSys, do_entSys, do_entSys, sys_ioctl + .quad do_entSys, do_entSys, sys_symlink, sys_readlink, sys_execve + .quad sys_umask, do_entSys, do_entSys, sys_getpgrp, sys_getpagesize + .quad do_entSys, osf_vfork, sys_newstat, sys_newlstat, do_entSys + .quad do_entSys, osf_mmap, do_entSys, sys_munmap, sys_mprotect + .quad sys_madvise, do_entSys, do_entSys, do_entSys, sys_getgroups + .quad do_entSys, do_entSys, do_entSys, sys_setitimer, do_entSys + .quad do_entSys, sys_getitimer, sys_gethostname, do_entSys, sys_getdtablesize + .quad sys_dup2, sys_newfstat, sys_fcntl, sys_select, do_entSys + .quad sys_fsync, do_entSys, sys_socket, do_entSys, do_entSys +/*100*/ .quad do_entSys, do_entSys, do_entSys, sys_sigreturn, sys_bind + .quad do_entSys, sys_listen, do_entSys, do_entSys, do_entSys + .quad do_entSys, sys_sigsuspend, do_entSys, do_entSys, do_entSys + .quad do_entSys, sys_gettimeofday, sys_getrusage, do_entSys, do_entSys + .quad do_entSys, do_entSys, sys_settimeofday, sys_fchown, sys_fchmod + .quad do_entSys, sys_setreuid, sys_setregid, sys_rename, sys_truncate + .quad sys_ftruncate, do_entSys, sys_setgid, do_entSys, do_entSys + .quad do_entSys, sys_mkdir, sys_rmdir, sys_utimes, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, sys_getrlimit + .quad sys_setrlimit, do_entSys, sys_setsid, do_entSys, do_entSys +/*150*/ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, sys_sigaction, do_entSys, do_entSys, osf_getdirentries + .quad osf_statfs, osf_fstatfs, do_entSys, do_entSys, do_entSys + .quad osf_getdomainname, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, osf_swapon +/*200*/ .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, osf_utsname, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys +/*250*/ .quad do_entSys, osf_usleep_thread, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys +/* linux-specific system calls start at 300 */ +/*300*/ .quad sys_bdflush, sys_sethae, do_entSys, do_entSys, do_entSys + .quad do_entSys, do_entSys, do_entSys, do_entSys, do_entSys diff --git a/arch/alpha/kernel/head.S b/arch/alpha/kernel/head.S new file mode 100644 index 000000000..2cff1159a --- /dev/null +++ b/arch/alpha/kernel/head.S @@ -0,0 +1,97 @@ +/* + * alpha/boot/head.S + * + * initial boot stuff.. At this point, the bootloader has already + * switched into OSF/1 PAL-code, and loaded us at the correct address + * (START_ADDR). So there isn't much left for us to do: just set up + * the kernel global pointer and jump to the kernel entry-point. + */ + +#define __ASSEMBLY__ +#include <asm/system.h> +#include <linux/fd.h> + +#define halt .long PAL_halt + +.globl swapper_pg_dir +swapper_pg_dir=SWAPPER_PGD + + .set noreorder + .globl __start + .ent __start +__start: + br $27,1f +1: ldgp $29,0($27) + lda $27,start_kernel + jsr $26,($27),start_kernel + halt + .end __start + + .align 3 + .globl wrent + .ent wrent +wrent: + .long PAL_wrent + ret ($26) + .end wrent + + .align 3 + .globl wrkgp + .ent wrkgp +wrkgp: + .long PAL_wrkgp + ret ($26) + .end wrkgp + + .align 3 + .globl wrusp + .ent wrusp +wrusp: + .long PAL_wrusp + ret ($26) + .end wrusp + + .align 3 + .globl rdusp + .ent rdusp +rdusp: + .long PAL_rdusp + ret ($26) + .end rdusp + + .align 3 + .globl tbi + .ent tbi +tbi: + .long PAL_tbi + ret ($26) + .end tbi + + .align 3 + .globl imb + .ent imb +imb: + .long PAL_imb + ret ($26) + .end imb + + .align 3 + .globl rdmces + .ent rdmces +rdmces: + call_pal PAL_rdmces + ret ($26) + .end rdmces + + .align 3 + .globl wrmces + .ent wrmces +wrmces: + call_pal PAL_wrmces + ret ($26) + .end wrmces + +.align 9 +.globl floppy_track_buffer +floppy_track_buffer: + .space 512*2*MAX_BUFFER_SECTORS,1 diff --git a/arch/alpha/kernel/irq.c b/arch/alpha/kernel/irq.c new file mode 100644 index 000000000..a59077539 --- /dev/null +++ b/arch/alpha/kernel/irq.c @@ -0,0 +1,414 @@ +/* + * linux/arch/alpha/kernel/irq.c + * + * Copyright (C) 1995 Linus Torvalds + * + * This file contains the code used by various IRQ handling routines: + * asking for different IRQ's should be done through these routines + * instead of just grabbing them. Thus setups with different IRQ numbers + * shouldn't result in any weird surprises, and installing new handlers + * should be easier. + */ + +#include <linux/config.h> +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/kernel_stat.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/interrupt.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/bitops.h> +#include <asm/dma.h> + +static unsigned char cache_21 = 0xff; +static unsigned char cache_A1 = 0xff; + +void disable_irq(unsigned int irq_nr) +{ + unsigned long flags; + unsigned char mask; + + mask = 1 << (irq_nr & 7); + save_flags(flags); + if (irq_nr < 8) { + cli(); + cache_21 |= mask; + outb(cache_21,0x21); + restore_flags(flags); + return; + } + cli(); + cache_A1 |= mask; + outb(cache_A1,0xA1); + restore_flags(flags); +} + +void enable_irq(unsigned int irq_nr) +{ + unsigned long flags; + unsigned char mask; + + mask = ~(1 << (irq_nr & 7)); + save_flags(flags); + if (irq_nr < 8) { + cli(); + cache_21 &= mask; + outb(cache_21,0x21); + restore_flags(flags); + return; + } + cli(); + cache_A1 &= mask; + outb(cache_A1,0xA1); + restore_flags(flags); +} + +/* + * Initial irq handlers. + */ +struct irqaction { + void (*handler)(int, struct pt_regs *); + unsigned long flags; + unsigned long mask; + const char *name; +}; + +static struct irqaction irq_action[16] = { + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }, + { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL } +}; + +int get_irq_list(char *buf) +{ + int i, len = 0; + struct irqaction * action = irq_action; + + for (i = 0 ; i < 16 ; i++, action++) { + if (!action->handler) + continue; + len += sprintf(buf+len, "%2d: %8d %c %s\n", + i, kstat.interrupts[i], + (action->flags & SA_INTERRUPT) ? '+' : ' ', + action->name); + } + return len; +} + +static inline void ack_irq(int irq) +{ + /* ACK the interrupt making it the lowest priority */ + /* First the slave .. */ + if (irq > 7) { + outb(0xE0 | (irq - 8), 0xa0); + irq = 2; + } + /* .. then the master */ + outb(0xE0 | irq, 0x20); +} + +static inline void mask_irq(int irq) +{ + if (irq < 8) { + cache_21 |= 1 << irq; + outb(cache_21, 0x21); + } else { + cache_A1 |= 1 << (irq - 8); + outb(cache_A1, 0xA1); + } +} + +static inline void unmask_irq(unsigned long irq) +{ + if (irq < 8) { + cache_21 &= ~(1 << irq); + outb(cache_21, 0x21); + } else { + cache_A1 &= ~(1 << (irq - 8)); + outb(cache_A1, 0xA1); + } +} + +int request_irq(unsigned int irq, void (*handler)(int, struct pt_regs *), + unsigned long irqflags, const char * devname) +{ + struct irqaction * action; + unsigned long flags; + + if (irq > 15) + return -EINVAL; + action = irq + irq_action; + if (action->handler) + return -EBUSY; + if (!handler) + return -EINVAL; + save_flags(flags); + cli(); + action->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + if (irq < 8) { + if (irq) { + cache_21 &= ~(1<<irq); + outb(cache_21,0x21); + } + } else { + cache_21 &= ~(1<<2); + cache_A1 &= ~(1<<(irq-8)); + outb(cache_21,0x21); + outb(cache_A1,0xA1); + } + restore_flags(flags); + return 0; +} + +void free_irq(unsigned int irq) +{ + struct irqaction * action = irq + irq_action; + unsigned long flags; + + if (irq > 15) { + printk("Trying to free IRQ%d\n", irq); + return; + } + if (!action->handler) { + printk("Trying to free free IRQ%d\n", irq); + return; + } + save_flags(flags); + cli(); + mask_irq(irq); + action->handler = NULL; + action->flags = 0; + action->mask = 0; + action->name = NULL; + restore_flags(flags); +} + +static void handle_nmi(struct pt_regs * regs) +{ + printk("Whee.. NMI received. Probable hardware error\n"); + printk("61=%02x, 461=%02x\n", inb(0x61), inb(0x461)); +} + +static void unexpected_irq(int irq, struct pt_regs * regs) +{ + int i; + + printk("IO device interrupt, irq = %d\n", irq); + printk("PC = %016lx PS=%04lx\n", regs->pc, regs->ps); + printk("Expecting: "); + for (i = 0; i < 16; i++) + if (irq_action[i].handler) + printk("[%s:%d] ", irq_action[i].name, i); + printk("\n"); + printk("64=%02x, 60=%02x, 3fa=%02x 2fa=%02x\n", + inb(0x64), inb(0x60), inb(0x3fa), inb(0x2fa)); + outb(0x0c, 0x3fc); + outb(0x0c, 0x2fc); + outb(0,0x61); + outb(0,0x461); +} + +static inline void handle_irq(int irq, struct pt_regs * regs) +{ + struct irqaction * action = irq + irq_action; + + kstat.interrupts[irq]++; + if (!action->handler) { + unexpected_irq(irq, regs); + return; + } + action->handler(irq, regs); +} + +static void local_device_interrupt(unsigned long vector, struct pt_regs * regs) +{ + switch (vector) { + /* com1: map to irq 4 */ + case 0x900: + handle_irq(4, regs); + return; + + /* com2: map to irq 3 */ + case 0x920: + handle_irq(3, regs); + return; + + /* keyboard: map to irq 1 */ + case 0x980: + handle_irq(1, regs); + return; + + /* mouse: map to irq 9 */ + case 0x990: + handle_irq(9, regs); + return; + default: + printk("Unknown local interrupt %lx\n", vector); + } +} + +/* + * The vector is 0x8X0 for EISA interrupt X, and 0x9X0 for the local + * motherboard interrupts.. This is for the Jensen. + * + * 0x660 - NMI + * + * 0x800 - IRQ0 interval timer (not used, as we use the RTC timer) + * 0x810 - IRQ1 line printer (duh..) + * 0x860 - IRQ6 floppy disk + * 0x8E0 - IRQ14 SCSI controller + * + * 0x900 - COM1 + * 0x920 - COM2 + * 0x980 - keyboard + * 0x990 - mouse + * + * The PCI version is more sane: it doesn't have the local interrupts at + * all, and has only normal PCI interrupts from devices. Happily it's easy + * enough to do a sane mapping from the Jensen.. Note that this means + * that we may have to do a hardware "ack" to a different interrupt than + * we report to the rest of the world.. + */ +static void device_interrupt(unsigned long vector, struct pt_regs * regs) +{ + int irq, ack; + struct irqaction * action; + + if (vector == 0x660) { + handle_nmi(regs); + return; + } + + ack = irq = (vector - 0x800) >> 4; +#ifndef CONFIG_PCI + if (vector >= 0x900) { + local_device_interrupt(vector, regs); + return; + } + /* irq1 is supposed to be the keyboard, silly Jensen */ + if (irq == 1) + irq = 7; +#endif + kstat.interrupts[irq]++; + action = irq_action + irq; + /* quick interrupts get executed with no extra overhead */ + if (action->flags & SA_INTERRUPT) { + action->handler(irq, regs); + ack_irq(ack); + return; + } + /* + * For normal interrupts, we mask it out, and then ACK it. + * This way another (more timing-critical) interrupt can + * come through while we're doing this one. + * + * Note! A irq without a handler gets masked and acked, but + * never unmasked. The autoirq stuff depends on this (it looks + * at the masks before and after doing the probing). + */ + mask_irq(ack); + ack_irq(ack); + if (!action->handler) + return; + action->handler(irq, regs); + unmask_irq(ack); +} + +/* + * Start listening for interrupts.. + */ +unsigned int probe_irq_on(void) +{ + unsigned int i, irqs = 0, irqmask; + unsigned long delay; + + for (i = 15; i > 0; i--) { + if (!irq_action[i].handler) { + enable_irq(i); + irqs |= (1 << i); + } + } + + /* wait for spurious interrupts to mask themselves out again */ + for (delay = jiffies + HZ/10; delay > jiffies; ) + /* about 100 ms delay */; + + /* now filter out any obviously spurious interrupts */ + irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int) cache_21; + irqs &= ~irqmask; + return irqs; +} + +/* + * Get the result of the IRQ probe.. A negative result means that + * we have several candidates (but we return the lowest-numbered + * one). + */ +int probe_irq_off(unsigned int irqs) +{ + unsigned int i, irqmask; + + irqmask = (((unsigned int)cache_A1)<<8) | (unsigned int)cache_21; + irqs &= irqmask; + if (!irqs) + return 0; + i = ffz(~irqs); + if (irqs != (1 << i)) + i = -i; + return i; +} + +static void machine_check(unsigned long vector, unsigned long la_ptr, struct pt_regs * regs) +{ + printk("Machine check\n"); +} + +asmlinkage void do_entInt(unsigned long type, unsigned long vector, unsigned long la_ptr, + unsigned long a3, unsigned long a4, unsigned long a5, + struct pt_regs regs) +{ + switch (type) { + case 0: + printk("Interprocessor interrupt? You must be kidding\n"); + break; + case 1: + /* timer interrupt.. */ + handle_irq(0, ®s); + return; + case 2: + machine_check(vector, la_ptr, ®s); + break; + case 3: + device_interrupt(vector, ®s); + return; + case 4: + printk("Performance counter interrupt\n"); + break;; + default: + printk("Hardware intr %ld %lx? Huh?\n", type, vector); + } + printk("PC = %016lx PS=%04lx\n", regs.pc, regs.ps); +} + +extern asmlinkage void entInt(void); + +void init_IRQ(void) +{ + wrent(entInt, 0); + dma_outb(0, DMA1_RESET_REG); + dma_outb(0, DMA2_RESET_REG); + dma_outb(0, DMA1_CLR_MASK_REG); + dma_outb(0, DMA2_CLR_MASK_REG); +} diff --git a/arch/alpha/kernel/lca.c b/arch/alpha/kernel/lca.c new file mode 100644 index 000000000..c32c308fb --- /dev/null +++ b/arch/alpha/kernel/lca.c @@ -0,0 +1,304 @@ +/* + * Code common to all LCA chips. + * + * Written by David Mosberger (davidm@cs.arizona.edu) with some code + * taken from Dave Rusling's (david.rusling@reo.mts.dec.com) 32-bit + * bios code. + */ +#include <linux/kernel.h> +#include <linux/config.h> +#include <linux/types.h> +#include <linux/bios32.h> +#include <linux/pci.h> + +#include <asm/system.h> +#include <asm/io.h> + +/* + * BIOS32-style PCI interface: + */ + +#ifdef CONFIG_PCI + +#define vulp volatile unsigned long * + +/* + * Given a bus, device, and function number, compute resulting + * configuration space address and setup the LCA_IOC_CONF register + * accordingly. It is therefore not safe to have concurrent + * invocations to configuration space access routines, but there + * really shouldn't be any need for this. + * + * Type 0: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | | | | | | | | | | | | | | | | | | | | | | |F|F|F|R|R|R|R|R|R|0|0| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:11 Device select bit. + * 10:8 Function number + * 7:2 Register number + * + * Type 1: + * + * 3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 + * 3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 31:24 reserved + * 23:16 bus number (8 bits = 128 possible buses) + * 15:11 Device number (5 bits) + * 10:8 function number + * 7:2 register number + * + * Notes: + * The function number selects which function of a multi-function device + * (e.g., scsi and ethernet). + * + * The register selects a DWORD (32 bit) register offset. Hence it + * doesn't get shifted by 2 bits as we want to "drop" the bottom two + * bits. + */ +static int mk_conf_addr(unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned long *pci_addr) +{ + unsigned long addr; + + if (bus == 0) { + int device = device_fn >> 3; + int func = device_fn & 0x7; + + /* type 0 configuration cycle: */ + + if (device > 12) { + return -1; + } /* if */ + + *((volatile unsigned long*) LCA_IOC_CONF) = 0; + addr = (1 << (11 + device)) | (func << 8) | where; + } else { + /* type 1 configuration cycle: */ + *((volatile unsigned long*) LCA_IOC_CONF) = 1; + addr = (bus << 16) | (device_fn << 8) | where; + } /* if */ + *pci_addr = addr; + + return 0; +} + + +static unsigned int conf_read(unsigned long addr) +{ + unsigned long old_ipl, code, stat0; + unsigned int value; + + old_ipl = swpipl(7); /* avoid getting hit by machine check */ + + /* reset status register to avoid loosing errors: */ + stat0 = *((volatile unsigned long*)LCA_IOC_STAT0); + *((volatile unsigned long*)LCA_IOC_STAT0) = stat0; + mb(); + + /* access configuration space: */ + + value = *((volatile unsigned int*)addr); + draina(); + + stat0 = *((unsigned long*)LCA_IOC_STAT0); + if (stat0 & LCA_IOC_STAT0_ERR) { + code = ((stat0 >> LCA_IOC_STAT0_CODE_SHIFT) + & LCA_IOC_STAT0_CODE_MASK); + if (code != 1) { + printk("lca.c:conf_read: got stat0=%lx\n", stat0); + } + + /* reset error status: */ + *((volatile unsigned long*)LCA_IOC_STAT0) = stat0; + mb(); + wrmces(0x7); /* reset machine check */ + + value = 0xffffffff; + } + swpipl(old_ipl); + + return value; +} + + +static void conf_write(unsigned long addr, unsigned int value) +{ + unsigned long old_ipl, code, stat0; + + old_ipl = swpipl(7); /* avoid getting hit by machine check */ + + /* reset status register to avoid loosing errors: */ + stat0 = *((volatile unsigned long*)LCA_IOC_STAT0); + *((volatile unsigned long*)LCA_IOC_STAT0) = stat0; + mb(); + + /* access configuration space: */ + + *((volatile unsigned int*)addr) = value; + draina(); + + stat0 = *((unsigned long*)LCA_IOC_STAT0); + if (stat0 & LCA_IOC_STAT0_ERR) { + code = ((stat0 >> LCA_IOC_STAT0_CODE_SHIFT) + & LCA_IOC_STAT0_CODE_MASK); + if (code != 1) { + printk("lca.c:conf_write: got stat0=%lx\n", stat0); + } + + /* reset error status: */ + *((volatile unsigned long*)LCA_IOC_STAT0) = stat0; + mb(); + wrmces(0x7); /* reset machine check */ + } + swpipl(old_ipl); +} + + +int pcibios_read_config_byte (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned char *value) +{ + unsigned long addr = LCA_CONF; + unsigned long pci_addr; + + *value = 0xff; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr) < 0) { + return PCIBIOS_SUCCESSFUL; + } /* if */ + + addr |= (pci_addr << 5) + 0x00; + + *value = conf_read(addr) >> ((where & 3) * 8); + + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_read_config_word (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned short *value) +{ + unsigned long addr = LCA_CONF; + unsigned long pci_addr; + + *value = 0xffff; + + if (where & 0x1) { + return PCIBIOS_BAD_REGISTER_NUMBER; + } /* if */ + + if (mk_conf_addr(bus, device_fn, where, &pci_addr)) { + return PCIBIOS_SUCCESSFUL; + } /* if */ + + addr |= (pci_addr << 5) + 0x08; + + *value = conf_read(addr) >> ((where & 3) * 8); + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_read_config_dword (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned int *value) +{ + unsigned long addr = LCA_CONF; + unsigned long pci_addr; + + *value = 0xffffffff; + + if (where & 0x3) { + return PCIBIOS_BAD_REGISTER_NUMBER; + } /* if */ + + if (mk_conf_addr(bus, device_fn, where, &pci_addr)) { + return PCIBIOS_SUCCESSFUL; + } /* if */ + + addr |= (pci_addr << 5) + 0x18; + + *value = conf_read(addr); + + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_write_config_byte (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned char value) +{ + unsigned long addr = LCA_CONF; + unsigned long pci_addr; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr) < 0) { + return PCIBIOS_SUCCESSFUL; + } /* if */ + + addr |= (pci_addr << 5) + 0x00; + + conf_write(addr, value << ((where & 3) * 8)); + + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_write_config_word (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned short value) +{ + unsigned long addr = LCA_CONF; + unsigned long pci_addr; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr) < 0) { + return PCIBIOS_SUCCESSFUL; + } /* if */ + + addr |= (pci_addr << 5) + 0x08; + + conf_write(addr, value << ((where & 3) * 8)); + + return PCIBIOS_SUCCESSFUL; +} + + +int pcibios_write_config_dword (unsigned char bus, unsigned char device_fn, + unsigned char where, unsigned int value) +{ + unsigned long addr = LCA_CONF; + unsigned long pci_addr; + + if (mk_conf_addr(bus, device_fn, where, &pci_addr) < 0) { + return PCIBIOS_SUCCESSFUL; + } /* if */ + + addr |= (pci_addr << 5) + 0x18; + + conf_write(addr, value << ((where & 3) * 8)); + + return PCIBIOS_SUCCESSFUL; +} + + +unsigned long lca_init(unsigned long mem_start, unsigned long mem_end) +{ + /* + * Set up the PCI->physical memory translation windows. + * For now, window 1 is disabled. In the future, we may + * want to use it to do scatter/gather DMA. Window 0 + * goes at 1 GB and is 1 GB large. + */ + *(vulp)LCA_IOC_W_BASE1 = 0UL<<33; + *(vulp)LCA_IOC_W_BASE0 = 1UL<<33 | LCA_DMA_WIN_BASE; + *(vulp)LCA_IOC_W_MASK0 = LCA_DMA_WIN_SIZE - 1; + *(vulp)LCA_IOC_T_BASE0 = 0; + + return mem_start; +} + +#endif /* CONFIG_PCI */ + + /*** end of lca.c ***/ diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c new file mode 100644 index 000000000..ddf090283 --- /dev/null +++ b/arch/alpha/kernel/osf_sys.c @@ -0,0 +1,494 @@ +/* + * linux/arch/alpha/kernel/osf_sys.c + * + * Copyright (C) 1995 Linus Torvalds + */ + +/* + * This file handles some of the stranger OSF/1 system call interfaces. + * Some of the system calls expect a non-C calling standard, others have + * special parameter blocks.. + */ + +#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/ldt.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/utsname.h> +#include <linux/time.h> +#include <linux/major.h> +#include <linux/stat.h> +#include <linux/mman.h> + +#include <asm/segment.h> +#include <asm/system.h> +#include <asm/io.h> + +extern int do_mount(dev_t, const char *, char *, int, void *); +extern int do_pipe(int *); + +extern struct file_operations * get_blkfops(unsigned int); +extern struct file_operations * get_chrfops(unsigned int); + +extern dev_t get_unnamed_dev(void); +extern void put_unnamed_dev(dev_t); + +extern asmlinkage int sys_umount(char *); +extern asmlinkage int sys_swapon(const char *specialfile); + +/* + * OSF/1 directory handling functions... + * + * The "getdents()" interface is much more sane: the "basep" stuff is + * braindamage (it can't really handle filesystems where the directory + * offset differences aren't the same as "d_reclen"). + */ +#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) +#define ROUND_UP(x) (((x)+7) & ~7) + +struct osf_dirent { + unsigned int d_ino; + unsigned short d_reclen; + unsigned short d_namlen; + char d_name[1]; +}; + +struct osf_dirent_callback { + struct osf_dirent * dirent; + long *basep; + int count; + int error; +}; + +static int osf_filldir(void * __buf, char * name, int namlen, off_t offset, ino_t ino) +{ + struct osf_dirent * dirent; + struct osf_dirent_callback * buf = (struct osf_dirent_callback *) __buf; + int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1); + + buf->error = -EINVAL; /* unly used if we fail */ + if (reclen > buf->count) + return -EINVAL; + if (buf->basep) { + put_user(offset, buf->basep); + buf->basep = NULL; + } + dirent = buf->dirent; + put_user(ino, &dirent->d_ino); + put_user(namlen, &dirent->d_namlen); + put_user(reclen, &dirent->d_reclen); + memcpy_tofs(dirent->d_name, name, namlen); + put_fs_byte(0, dirent->d_name + namlen); + ((char *) dirent) += reclen; + buf->dirent = dirent; + buf->count -= reclen; + return 0; +} + +asmlinkage int osf_getdirentries(unsigned int fd, struct osf_dirent * dirent, + unsigned int count, long *basep) +{ + int error; + struct file * file; + struct osf_dirent_callback buf; + + if (fd >= NR_OPEN || !(file = current->files->fd[fd])) + return -EBADF; + if (!file->f_op || !file->f_op->readdir) + return -ENOTDIR; + error = verify_area(VERIFY_WRITE, dirent, count); + if (error) + return error; + if (basep) { + error = verify_area(VERIFY_WRITE, basep, sizeof(long)); + if (error) + return error; + } + buf.dirent = dirent; + buf.basep = basep; + buf.count = count; + buf.error = 0; + error = file->f_op->readdir(file->f_inode, file, dirent, osf_filldir); + if (error < 0) + return error; + if (count == buf.count) + return buf.error; + return count - buf.count; +} + +/* + * Heh. As documented by DEC.. + */ +asmlinkage unsigned long sys_madvise(void) +{ + return 0; +} + +asmlinkage unsigned long sys_getxuid(int a0, int a1, int a2, int a3, int a4, int a5, + struct pt_regs regs) +{ + (®s)->r20 = current->euid; + return current->uid; +} + +asmlinkage unsigned long sys_getxgid(int a0, int a1, int a2, int a3, int a4, int a5, + struct pt_regs regs) +{ + (®s)->r20 = current->egid; + return current->gid; +} + +asmlinkage unsigned long sys_getxpid(int a0, int a1, int a2, int a3, int a4, int a5, + struct pt_regs regs) +{ + (®s)->r20 = current->p_opptr->pid; + return current->pid; +} + +#define OSF_MAP_ANONYMOUS 0x0010 +#define OSF_MAP_FIXED 0x0100 +#define OSF_MAP_HASSEMAPHORE 0x0200 +#define OSF_MAP_INHERIT 0x0400 +#define OSF_MAP_UNALIGNED 0x0800 + +asmlinkage unsigned long osf_mmap(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long osf_flags, unsigned long fd, + unsigned long off) +{ + struct file * file = NULL; + unsigned long flags = osf_flags & 0x0f; + + if (osf_flags & (OSF_MAP_HASSEMAPHORE | OSF_MAP_INHERIT | OSF_MAP_UNALIGNED)) + printk("%s: unimplemented OSF mmap flags %04lx\n", current->comm, osf_flags); + if (osf_flags & OSF_MAP_FIXED) + flags |= MAP_FIXED; + if (osf_flags & OSF_MAP_ANONYMOUS) + flags |= MAP_ANONYMOUS; + else { + if (fd >= NR_OPEN || !(file = current->files->fd[fd])) + return -EBADF; + } + return do_mmap(file, addr, len, prot, flags, off); +} + +asmlinkage int osf_statfs(char * path, struct statfs * buffer, unsigned long bufsiz) +{ + struct inode * inode; + int retval; + + if (bufsiz > sizeof(struct statfs)) + bufsiz = sizeof(struct statfs); + retval = verify_area(VERIFY_WRITE, buffer, bufsiz); + if (retval) + return retval; + retval = namei(path, &inode); + if (retval) + return retval; + if (!inode->i_sb->s_op->statfs) { + iput(inode); + return -ENOSYS; + } + inode->i_sb->s_op->statfs(inode->i_sb, buffer, bufsiz); + iput(inode); + return 0; +} + +asmlinkage int osf_fstatfs(unsigned long fd, struct statfs * buffer, unsigned long bufsiz) +{ + struct file * file; + struct inode * inode; + int retval; + + retval = verify_area(VERIFY_WRITE, buffer, bufsiz); + if (retval) + return retval; + if (bufsiz > sizeof(struct statfs)) + bufsiz = sizeof(struct statfs); + if (fd >= NR_OPEN || !(file = current->files->fd[fd])) + return -EBADF; + if (!(inode = file->f_inode)) + return -ENOENT; + if (!inode->i_sb->s_op->statfs) + return -ENOSYS; + inode->i_sb->s_op->statfs(inode->i_sb, buffer, bufsiz); + return 0; +} + +/* + * Uhh.. OSF/1 mount parameters aren't exactly obvious.. + * + * Although to be frank, neither are the native Linux/i386 ones.. + */ +struct ufs_args { + char * devname; + int flags; + uid_t exroot; +}; + +struct cdfs_args { + char * devname; + int flags; + uid_t exroot; +/* + * this has lots more here, which linux handles with the option block + * but I'm too lazy to do the translation into ascii.. + */ +}; + +struct procfs_args { + char * devname; + int flags; + uid_t exroot; +}; + +static int getdev(const char * name, int rdonly, struct inode ** ino) +{ + dev_t dev; + struct inode * inode; + struct file_operations * fops; + int retval; + + retval = namei(name, &inode); + if (retval) + return retval; + if (!S_ISBLK(inode->i_mode)) { + iput(inode); + return -ENOTBLK; + } + if (IS_NODEV(inode)) { + iput(inode); + return -EACCES; + } + dev = inode->i_rdev; + if (MAJOR(dev) >= MAX_BLKDEV) { + iput(inode); + return -ENXIO; + } + fops = get_blkfops(MAJOR(dev)); + if (!fops) { + iput(inode); + return -ENODEV; + } + if (fops->open) { + struct file dummy; + memset(&dummy, 0, sizeof(dummy)); + dummy.f_inode = inode; + dummy.f_mode = rdonly ? 1 : 3; + retval = fops->open(inode, &dummy); + if (retval) { + iput(inode); + return retval; + } + } + *ino = inode; + return 0; +} + +static void putdev(struct inode * inode) +{ + struct file_operations * fops; + + fops = get_blkfops(MAJOR(inode->i_rdev)); + if (fops->release) + fops->release(inode, NULL); +} + +/* + * We can't actually handle ufs yet, so we translate UFS mounts to + * ext2fs mounts... I wouldn't mind a USF filesystem, but the UFS + * layout is so braindead it's a major headache doing it.. + */ +static int osf_ufs_mount(char * dirname, struct ufs_args * args, int flags) +{ + int retval; + struct inode * inode; + struct cdfs_args tmp; + + retval = verify_area(VERIFY_READ, args, sizeof(*args)); + if (retval) + return retval; + memcpy_fromfs(&tmp, args, sizeof(tmp)); + retval = getdev(tmp.devname, 0, &inode); + if (retval) + return retval; + retval = do_mount(inode->i_rdev, dirname, "ext2", flags, NULL); + if (retval) + putdev(inode); + iput(inode); + return retval; +} + +static int osf_cdfs_mount(char * dirname, struct cdfs_args * args, int flags) +{ + int retval; + struct inode * inode; + struct cdfs_args tmp; + + retval = verify_area(VERIFY_READ, args, sizeof(*args)); + if (retval) + return retval; + memcpy_fromfs(&tmp, args, sizeof(tmp)); + retval = getdev(tmp.devname, 1, &inode); + if (retval) + return retval; + retval = do_mount(inode->i_rdev, dirname, "iso9660", flags, NULL); + if (retval) + putdev(inode); + iput(inode); + return retval; +} + +static int osf_procfs_mount(char * dirname, struct procfs_args * args, int flags) +{ + dev_t dev; + int retval; + struct procfs_args tmp; + + retval = verify_area(VERIFY_READ, args, sizeof(*args)); + if (retval) + return retval; + memcpy_fromfs(&tmp, args, sizeof(tmp)); + dev = get_unnamed_dev(); + if (!dev) + return -ENODEV; + retval = do_mount(dev, dirname, "proc", flags, NULL); + if (retval) + put_unnamed_dev(dev); + return retval; +} + +asmlinkage int osf_mount(unsigned long typenr, char * path, int flag, void * data) +{ + int retval; + + retval = -EINVAL; + switch (typenr) { + case 1: + retval = osf_ufs_mount(path, (struct ufs_args *) data, flag); + break; + case 6: + retval = osf_cdfs_mount(path, (struct cdfs_args *) data, flag); + break; + case 9: + retval = osf_procfs_mount(path, (struct procfs_args *) data, flag); + break; + default: + printk("osf_mount(%ld, %x)\n", typenr, flag); + } + return retval; +} + +asmlinkage int osf_umount(char * path, int flag) +{ + return sys_umount(path); +} + +/* + * I don't know what the parameters are: the first one + * seems to be a timeval pointer, and I suspect the second + * one is the time remaining.. Ho humm.. No documentation. + */ +asmlinkage int osf_usleep_thread(struct timeval * sleep, struct timeval * remain) +{ + struct timeval tmp; + unsigned long ticks; + int retval; + + retval = verify_area(VERIFY_READ, sleep, sizeof(*sleep)); + if (retval) + return retval; + if (remain && (retval = verify_area(VERIFY_WRITE, remain, sizeof(*remain)))) + return retval; + memcpy_fromfs(&tmp, sleep, sizeof(*sleep)); + ticks = tmp.tv_usec; + ticks = (ticks + (1000000 / HZ) - 1) / (1000000 / HZ); + ticks += tmp.tv_sec * HZ; + current->timeout = ticks + jiffies; + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (!remain) + return 0; + ticks = jiffies; + if (ticks < current->timeout) + ticks = current->timeout - ticks; + else + ticks = 0; + current->timeout = 0; + tmp.tv_sec = ticks / HZ; + tmp.tv_usec = ticks % HZ; + memcpy_tofs(remain, &tmp, sizeof(*remain)); + return 0; +} + +asmlinkage int osf_utsname(char * name) +{ + int error = verify_area(VERIFY_WRITE, name, 5*32); + if (error) + return error; + memcpy_tofs(name + 0, system_utsname.sysname, 32); + memcpy_tofs(name + 32, system_utsname.nodename, 32); + memcpy_tofs(name + 64, system_utsname.release, 32); + memcpy_tofs(name + 96, system_utsname.version, 32); + memcpy_tofs(name + 128, system_utsname.machine, 32); + return 0; +} + +asmlinkage int osf_swapon(const char * path, int flags, int lowat, int hiwat) +{ + /* for now, simply ignore flags, lowat and hiwat... */ + return sys_swapon(path); +} + +asmlinkage unsigned long sys_getpagesize(void) +{ + return PAGE_SIZE; +} + +asmlinkage unsigned long sys_getdtablesize(void) +{ + return NR_OPEN; +} + +asmlinkage int sys_pipe(int a0, int a1, int a2, int a3, int a4, int a5, + struct pt_regs regs) +{ + int fd[2]; + int error; + + error = do_pipe(fd); + if (error) + return error; + (®s)->r20 = fd[1]; + return fd[0]; +} + +/* + * For compatibility with OSF/1 only. Use utsname(2) instead. + */ +asmlinkage int osf_getdomainname(char *name, int namelen) +{ + unsigned len; + int i, error; + + error = verify_area(VERIFY_WRITE, name, namelen); + if (error) + return error; + + len = namelen; + if (namelen > 32) + len = 32; + + for (i = 0; i < len; ++i) { + put_user(system_utsname.domainname[i], name + i); + if (system_utsname.domainname[i] == '\0') + break; + } + return 0; +} diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c new file mode 100644 index 000000000..257f431a5 --- /dev/null +++ b/arch/alpha/kernel/process.c @@ -0,0 +1,186 @@ +/* + * linux/arch/alpha/kernel/process.c + * + * Copyright (C) 1995 Linus Torvalds + */ + +/* + * This file handles the architecture-dependent parts of process handling.. + */ + +#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/ldt.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/utsname.h> +#include <linux/time.h> +#include <linux/major.h> +#include <linux/stat.h> +#include <linux/mman.h> + +#include <asm/segment.h> +#include <asm/system.h> +#include <asm/io.h> + +asmlinkage int sys_sethae(unsigned long hae, unsigned long a1, unsigned long a2, + unsigned long a3, unsigned long a4, unsigned long a5, + struct pt_regs regs) +{ + (®s)->hae = hae; + return 0; +} + +asmlinkage int sys_idle(void) +{ + if (current->pid != 0) + return -EPERM; + + /* endless idle loop with no priority at all */ + current->counter = -100; + for (;;) { + schedule(); + } +} + +void hard_reset_now(void) +{ + halt(); +} + +void show_regs(struct pt_regs * regs) +{ + printk("\nps: %04lx pc: %016lx\n", regs->ps, regs->pc); + printk("rp: %016lx sp: %p\n", regs->r26, regs+1); + printk(" r0: %016lx r1: %016lx r2: %016lx r3: %016lx\n", + regs->r0, regs->r1, regs->r2, regs->r3); + printk(" r4: %016lx r5: %016lx r6: %016lx r7: %016lx\n", + regs->r4, regs->r5, regs->r6, regs->r7); + printk(" r8: %016lx r16: %016lx r17: %016lx r18: %016lx\n", + regs->r8, regs->r16, regs->r17, regs->r18); + printk("r19: %016lx r20: %016lx r21: %016lx r22: %016lx\n", + regs->r19, regs->r20, regs->r21, regs->r22); + printk("r23: %016lx r24: %016lx r25: %016lx r26: %016lx\n", + regs->r23, regs->r24, regs->r25, regs->r26); + printk("r27: %016lx r28: %016lx r29: %016lx hae: %016lx\n", + regs->r27, regs->r28, regs->gp, regs->hae); +} + +/* + * Free current thread data structures etc.. + */ +void exit_thread(void) +{ +} + +void flush_thread(void) +{ +} + +/* + * "alpha_fork()".. By the time we get here, the + * non-volatile registers have also been saved on the + * stack. We do some ugly pointer stuff here.. (see + * also copy_thread) + */ +int alpha_fork(struct switch_stack * swstack) +{ + return do_fork(COPYVM | SIGCHLD, + rdusp(), + (struct pt_regs *) (swstack+1)); +} + +extern void ret_from_sys_call(void); +/* + * Copy an alpha thread.. + * + * Note the "stack_offset" stuff: when returning to kernel mode, we need + * to have some extra stack-space for the kernel stack that still exists + * after the "ret_from_sys_call". When returning to user mode, we only + * want the space needed by the syscall stack frame (ie "struct pt_regs"). + * Use the passed "regs" pointer to determine how much space we need + * for a kernel fork(). + */ +void copy_thread(int nr, unsigned long clone_flags, unsigned long usp, + struct task_struct * p, struct pt_regs * regs) +{ + struct pt_regs * childregs; + struct switch_stack * childstack, *stack; + unsigned long stack_offset; + + stack_offset = PAGE_SIZE - sizeof(struct pt_regs); + if (!(regs->ps & 8)) + stack_offset = (PAGE_SIZE-1) & (unsigned long) regs; + childregs = (struct pt_regs *) (p->kernel_stack_page + stack_offset); + + *childregs = *regs; + childregs->r0 = 0; + childregs->r19 = 0; + childregs->r20 = 1; /* OSF/1 has some strange fork() semantics.. */ + regs->r0 = p->pid; + regs->r20 = 0; + stack = ((struct switch_stack *) regs) - 1; + childstack = ((struct switch_stack *) childregs) - 1; + *childstack = *stack; + childstack->r26 = (unsigned long) ret_from_sys_call; + p->tss.usp = usp; + p->tss.ksp = (unsigned long) childstack; + p->tss.flags = 1; +} + +/* + * fill in the user structure for a core dump.. + */ +void dump_thread(struct pt_regs * regs, struct user * dump) +{ +} + +/* + * sys_execve() executes a new program. + * + * This works due to the alpha calling sequence: the first 6 args + * are gotten from registers, while the rest is on the stack, so + * we get a0-a5 for free, and then magically find "struct pt_regs" + * on the stack for us.. + * + * Don't do this at home. + */ +asmlinkage int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2, + unsigned long a3, unsigned long a4, unsigned long a5, + struct pt_regs regs) +{ + int error; + char * filename; + + error = getname((char *) a0, &filename); + if (error) + return error; + error = do_execve(filename, (char **) a1, (char **) a2, ®s); + putname(filename); + return error; +} + +/* + * This doesn't actually work correctly like this: we need to do the + * same stack setups that fork() does first. + */ +asmlinkage int sys_clone(unsigned long a0, unsigned long a1, unsigned long a2, + unsigned long a3, unsigned long a4, unsigned long a5, + struct pt_regs regs) +{ + unsigned long clone_flags = a0; + unsigned long newsp; + + newsp = rdusp(); + if (newsp == a1 || !a1) + clone_flags |= COPYVM; + else + newsp = a1; + return do_fork(clone_flags, newsp, ®s); +} diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c new file mode 100644 index 000000000..23a2efd65 --- /dev/null +++ b/arch/alpha/kernel/setup.c @@ -0,0 +1,172 @@ +/* + * linux/arch/alpha/kernel/setup.c + * + * Copyright (C) 1995 Linus Torvalds + */ + +/* + * bootup setup stuff.. + */ + +#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/ldt.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/tty.h> +#include <linux/delay.h> + +#include <asm/segment.h> +#include <asm/system.h> +#include <asm/hwrpb.h> +#include <asm/dma.h> +#include <asm/io.h> + +struct hae hae = { + 0, + (unsigned long*) HAE_ADDRESS +}; + +struct hwrpb_struct *hwrpb; + +unsigned char aux_device_present; + +/* + * This is setup by the secondary bootstrap loader. Because + * the zero page is zeroed out as soon as the vm system is + * initialized, we need to copy things out into a more permanent + * place. + */ +#define PARAM ZERO_PGE +#define COMMAND_LINE ((char*)(PARAM + 0x0000)) +#define COMMAND_LINE_SIZE 256 + +static char command_line[COMMAND_LINE_SIZE] = { 0, }; + +/* + * The format of "screen_info" is strange, and due to early + * i386-setup code. This is just enough to make the console + * code think we're on a EGA+ colour display. + */ +struct screen_info screen_info = { + 0, 0, /* 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 */ +}; + +static unsigned long find_end_memory(void) +{ + int i; + unsigned long high = 0; + struct memclust_struct * cluster; + struct memdesc_struct * memdesc; + + memdesc = (struct memdesc_struct *) (INIT_HWRPB->mddt_offset + (unsigned long) INIT_HWRPB); + cluster = memdesc->cluster; + for (i = memdesc->numclusters ; i > 0; i--, cluster++) { + unsigned long tmp; + tmp = (cluster->start_pfn + cluster->numpages) << PAGE_SHIFT; + if (tmp > high) + high = tmp; + } + /* round it up to an even number of pages.. */ + high = (high + PAGE_SIZE) & (PAGE_MASK*2); + return PAGE_OFFSET + high; +} + +void setup_arch(char **cmdline_p, + unsigned long * memory_start_p, unsigned long * memory_end_p) +{ + extern int _end; + + hwrpb = (struct hwrpb_struct*)(IDENT_ADDR + INIT_HWRPB->phys_addr); + + set_hae(hae.cache); /* sync HAE register w/hae_cache */ + + ROOT_DEV = 0x0802; /* sda2 */ +#ifndef CONFIG_PCI + aux_device_present = 0xaa; +#else + aux_device_present = 0x00; +#endif + command_line[COMMAND_LINE_SIZE - 1] = '\0'; + strcpy(command_line, COMMAND_LINE); + + *cmdline_p = command_line; + *memory_start_p = (unsigned long) &_end; + *memory_end_p = find_end_memory(); + +#ifdef CONFIG_PCI + *memory_start_p = lca_init(*memory_start_p, *memory_end_p); +#endif +} + +asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on) +{ + return -EIO; +} + + +/* + * BUFFER is PAGE_SIZE bytes long. + */ +int get_cpuinfo(char *buffer) +{ + const char *cpu_name[] = { + "EV3", "EV4", "Unknown 1", "LCA4", "EV5", "EV45" + }; + const char *systype_name[] = { + "ADU", "Cobra", "Ruby", "Flamingo", "Unknown 1", "Jensen", + "Pelican", "Unknown 2", "Sable", "AXPvme", "Noname", + "Turbolaser", "Avanti", "Mustang", "Alcor", "Unknown 3", + "Mikasa", "Unknown3", "EB66", "EB64+" + }; + struct percpu_struct *cpu; + unsigned int cpu_index, system_index; +# define N(a) (sizeof(a)/sizeof(a[0])) + + cpu = (struct percpu_struct*)((char*)hwrpb + hwrpb->processor_offset); + cpu_index = (unsigned) (cpu->type - 1); + system_index = (unsigned) (hwrpb->sys_type - 1); + + return sprintf(buffer, + "cpu\t\t\t: Alpha\n" + "cpu model\t\t: %s\n" + "cpu variation\t\t: %ld\n" + "cpu revision\t\t: %ld\n" + "cpu serial number\t: %s\n" + "system type\t\t: %s\n" + "system variation\t: %ld\n" + "system revision\t\t: %ld\n" + "system serial number\t: %s\n" + "cycle frequency [Hz]\t: %lu\n" + "timer frequency [Hz]\t: %lu.%02lu\n" + "page size [bytes]\t: %ld\n" + "phys. address bits\t: %ld\n" + "max. addr. space #\t: %ld\n" + "BogoMIPS\t\t: %lu.%02lu\n", + + (cpu_index < N(cpu_name) ? cpu_name[cpu_index] : "Unknown"), + cpu->variation, cpu->revision, (char*)cpu->serial_no, + (system_index < N(systype_name) ? systype_name[system_index] : "Unknown"), + hwrpb->sys_variation, hwrpb->sys_revision, + (char*)hwrpb->ssn, + hwrpb->cycle_freq, + hwrpb->intr_freq / 4096, + (100 * hwrpb->intr_freq / 4096) % 100, + hwrpb->pagesize, + hwrpb->pa_bits, + hwrpb->max_asn, + loops_per_sec / 500000, (loops_per_sec / 5000) % 100); +# undef N +} diff --git a/arch/alpha/kernel/signal.c b/arch/alpha/kernel/signal.c new file mode 100644 index 000000000..cc7069728 --- /dev/null +++ b/arch/alpha/kernel/signal.c @@ -0,0 +1,319 @@ +/* + * linux/arch/alpha/kernel/signal.c + * + * Copyright (C) 1995 Linus Torvalds + */ + +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/wait.h> +#include <linux/ptrace.h> +#include <linux/unistd.h> +#include <linux/mm.h> + +#include <asm/bitops.h> +#include <asm/segment.h> + +#define _S(nr) (1<<((nr)-1)) +#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP))) + +asmlinkage int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options); +asmlinkage void ret_from_sys_call(void); +asmlinkage int do_signal(unsigned long, struct pt_regs *, struct switch_stack *, + unsigned long, unsigned long); +asmlinkage void imb(void); + +/* + * The OSF/1 sigprocmask calling sequence is different from the + * C sigprocmask() sequence.. + */ +asmlinkage unsigned long osf_sigprocmask(int how, unsigned long newmask) +{ + unsigned long oldmask = current->blocked; + + newmask &= _BLOCKABLE; + switch (how) { + case SIG_BLOCK: + current->blocked |= newmask; + return oldmask; + case SIG_UNBLOCK: + current->blocked &= ~newmask; + return oldmask; + case SIG_SETMASK: + current->blocked = newmask; + return oldmask; + } + return -EINVAL; +} + +/* + * atomically swap in the new signal mask, and wait for a signal. + */ +asmlinkage int do_sigsuspend(unsigned long mask, struct pt_regs * regs, struct switch_stack * sw) +{ + unsigned long oldmask = current->blocked; + current->blocked = mask & _BLOCKABLE; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(oldmask,regs, sw, 0, 0)) + return -EINTR; + } +} + +/* + * Do a signal return; undo the signal stack. + */ +asmlinkage void do_sigreturn(struct sigcontext_struct * sc, + struct pt_regs * regs, struct switch_stack * sw) +{ + unsigned long mask; + int i; + + /* verify that it's a good sigcontext before using it */ + if (verify_area(VERIFY_READ, sc, sizeof(*sc))) + do_exit(SIGSEGV); + if (get_fs_quad(&sc->sc_ps) != 8) + do_exit(SIGSEGV); + mask = get_fs_quad(&sc->sc_mask); + if (mask & ~_BLOCKABLE) + do_exit(SIGSEGV); + + /* ok, looks fine, start restoring */ + wrusp(get_fs_quad(sc->sc_regs+30)); + regs->pc = get_fs_quad(&sc->sc_pc); + sw->r26 = (unsigned long) ret_from_sys_call; + current->blocked = mask; + + regs->r0 = get_fs_quad(sc->sc_regs+0); + regs->r1 = get_fs_quad(sc->sc_regs+1); + regs->r2 = get_fs_quad(sc->sc_regs+2); + regs->r3 = get_fs_quad(sc->sc_regs+3); + regs->r4 = get_fs_quad(sc->sc_regs+4); + regs->r5 = get_fs_quad(sc->sc_regs+5); + regs->r6 = get_fs_quad(sc->sc_regs+6); + regs->r7 = get_fs_quad(sc->sc_regs+7); + regs->r8 = get_fs_quad(sc->sc_regs+8); + sw->r9 = get_fs_quad(sc->sc_regs+9); + sw->r10 = get_fs_quad(sc->sc_regs+10); + sw->r11 = get_fs_quad(sc->sc_regs+11); + sw->r12 = get_fs_quad(sc->sc_regs+12); + sw->r13 = get_fs_quad(sc->sc_regs+13); + sw->r14 = get_fs_quad(sc->sc_regs+14); + sw->r15 = get_fs_quad(sc->sc_regs+15); + regs->r16 = get_fs_quad(sc->sc_regs+16); + regs->r17 = get_fs_quad(sc->sc_regs+17); + regs->r18 = get_fs_quad(sc->sc_regs+18); + regs->r19 = get_fs_quad(sc->sc_regs+19); + regs->r20 = get_fs_quad(sc->sc_regs+20); + regs->r21 = get_fs_quad(sc->sc_regs+21); + regs->r22 = get_fs_quad(sc->sc_regs+22); + regs->r23 = get_fs_quad(sc->sc_regs+23); + regs->r24 = get_fs_quad(sc->sc_regs+24); + regs->r25 = get_fs_quad(sc->sc_regs+25); + regs->r26 = get_fs_quad(sc->sc_regs+26); + regs->r27 = get_fs_quad(sc->sc_regs+27); + regs->r28 = get_fs_quad(sc->sc_regs+28); + regs->gp = get_fs_quad(sc->sc_regs+29); + for (i = 0; i < 31; i++) + sw->fp[i] = get_fs_quad(sc->sc_fpregs+i); +} + +/* + * Set up a signal frame... + */ +static void setup_frame(struct sigaction * sa, struct sigcontext_struct ** fp, unsigned long pc, + struct pt_regs * regs, struct switch_stack * sw, int signr, unsigned long oldmask) +{ + int i; + struct sigcontext_struct * sc; + + sc = *fp; + /* check here if we would need to switch stacks.. */ + sc--; + if (verify_area(VERIFY_WRITE, sc, sizeof(*sc))) + do_exit(SIGSEGV); + + put_fs_quad(oldmask, &sc->sc_mask); + put_fs_quad(8, &sc->sc_ps); + put_fs_quad(pc, &sc->sc_pc); + put_fs_quad((unsigned long)*fp, sc->sc_regs+30); + + put_fs_quad(regs->r0 , sc->sc_regs+0); + put_fs_quad(regs->r1 , sc->sc_regs+1); + put_fs_quad(regs->r2 , sc->sc_regs+2); + put_fs_quad(regs->r3 , sc->sc_regs+3); + put_fs_quad(regs->r4 , sc->sc_regs+4); + put_fs_quad(regs->r5 , sc->sc_regs+5); + put_fs_quad(regs->r6 , sc->sc_regs+6); + put_fs_quad(regs->r7 , sc->sc_regs+7); + put_fs_quad(regs->r8 , sc->sc_regs+8); + put_fs_quad(sw->r9 , sc->sc_regs+9); + put_fs_quad(sw->r10 , sc->sc_regs+10); + put_fs_quad(sw->r11 , sc->sc_regs+11); + put_fs_quad(sw->r12 , sc->sc_regs+12); + put_fs_quad(sw->r13 , sc->sc_regs+13); + put_fs_quad(sw->r14 , sc->sc_regs+14); + put_fs_quad(sw->r15 , sc->sc_regs+15); + put_fs_quad(regs->r16, sc->sc_regs+16); + put_fs_quad(regs->r17, sc->sc_regs+17); + put_fs_quad(regs->r18, sc->sc_regs+18); + put_fs_quad(regs->r19, sc->sc_regs+19); + put_fs_quad(regs->r20, sc->sc_regs+20); + put_fs_quad(regs->r21, sc->sc_regs+21); + put_fs_quad(regs->r22, sc->sc_regs+22); + put_fs_quad(regs->r23, sc->sc_regs+23); + put_fs_quad(regs->r24, sc->sc_regs+24); + put_fs_quad(regs->r25, sc->sc_regs+25); + put_fs_quad(regs->r26, sc->sc_regs+26); + put_fs_quad(regs->r27, sc->sc_regs+27); + put_fs_quad(regs->r28, sc->sc_regs+28); + put_fs_quad(regs->gp , sc->sc_regs+29); + for (i = 0; i < 31; i++) + put_fs_quad(sw->fp[i], sc->sc_fpregs+i); + + /* + * The following is: + * + * bis $30,$30,$16 + * addq $31,0x67,$0 + * call_pal callsys + * + * ie, "sigreturn(stack-pointer)" + */ + put_fs_quad(0x43ecf40047de0410, sc->sc_retcode+0); + put_fs_quad(0x0000000000000083, sc->sc_retcode+1); + regs->r26 = (unsigned long) sc->sc_retcode; + regs->r16 = signr; + *fp = sc; +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + * + * Note that we go through the signals twice: once to check the signals that + * the kernel can handle, and then we build all the user-level signal handling + * stack-frames in one go after that. + * + * "r0" and "r19" are the registers we need to restore for system call + * restart. "r0" is also used as an indicator whether we can restart at + * all (if we get here from anything but a syscall return, it will be 0) + */ +asmlinkage int do_signal(unsigned long oldmask, + struct pt_regs * regs, + struct switch_stack * sw, + unsigned long r0, unsigned long r19) +{ + unsigned long mask = ~current->blocked; + unsigned long handler_signal = 0; + struct sigcontext_struct *frame = NULL; + unsigned long pc = 0; + unsigned long signr; + struct sigaction * sa; + + while ((signr = current->signal & mask) != 0) { + signr = ffz(~signr); + clear_bit(signr, ¤t->signal); + sa = current->sigaction + signr; + signr++; + if ((current->flags & PF_PTRACED) && signr != SIGKILL) { + current->exit_code = signr; + current->state = TASK_STOPPED; + notify_parent(current); + schedule(); + if (!(signr = current->exit_code)) + continue; + current->exit_code = 0; + if (signr == SIGSTOP) + continue; + if (_S(signr) & current->blocked) { + current->signal |= _S(signr); + continue; + } + sa = current->sigaction + signr - 1; + } + if (sa->sa_handler == SIG_IGN) { + if (signr != SIGCHLD) + continue; + /* check for SIGCHLD: it's special */ + while (sys_waitpid(-1,NULL,WNOHANG) > 0) + /* nothing */; + continue; + } + if (sa->sa_handler == SIG_DFL) { + if (current->pid == 1) + continue; + switch (signr) { + case SIGCONT: case SIGCHLD: case SIGWINCH: + continue; + + case SIGSTOP: case SIGTSTP: case SIGTTIN: case SIGTTOU: + if (current->flags & PF_PTRACED) + continue; + current->state = TASK_STOPPED; + current->exit_code = signr; + if (!(current->p_pptr->sigaction[SIGCHLD-1].sa_flags & + SA_NOCLDSTOP)) + notify_parent(current); + schedule(); + continue; + + case SIGQUIT: case SIGILL: case SIGTRAP: + case SIGABRT: case SIGFPE: case SIGSEGV: + if (current->binfmt && current->binfmt->core_dump) { + if (current->binfmt->core_dump(signr, regs)) + signr |= 0x80; + } + /* fall through */ + default: + current->signal |= _S(signr & 0x7f); + do_exit(signr); + } + } + /* + * OK, we're invoking a handler + */ + if (r0) { + if (regs->r0 == ERESTARTNOHAND || + (regs->r0 == ERESTARTSYS && !(sa->sa_flags & SA_RESTART))) + regs->r0 = EINTR; + } + handler_signal |= 1 << (signr-1); + mask &= ~sa->sa_mask; + } + if (r0 && + (regs->r0 == ERESTARTNOHAND || + regs->r0 == ERESTARTSYS || + regs->r0 == ERESTARTNOINTR)) { + regs->r0 = r0; + regs->r19 = r19; + regs->pc -= 4; + } + if (!handler_signal) /* no handler will be called - return 0 */ + return 0; + pc = regs->pc; + frame = (struct sigcontext_struct *) rdusp(); + signr = 1; + sa = current->sigaction; + for (mask = 1 ; mask ; sa++,signr++,mask += mask) { + if (mask > handler_signal) + break; + if (!(mask & handler_signal)) + continue; + setup_frame(sa,&frame,pc,regs,sw,signr,oldmask); + pc = (unsigned long) sa->sa_handler; + regs->r27 = pc; + if (sa->sa_flags & SA_ONESHOT) + sa->sa_handler = NULL; + current->blocked |= sa->sa_mask; + oldmask |= sa->sa_mask; + } + imb(); + wrusp((unsigned long) frame); + regs->pc = pc; /* "return" to the first handler */ + return 1; +} diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c new file mode 100644 index 000000000..4e3512ddd --- /dev/null +++ b/arch/alpha/kernel/traps.c @@ -0,0 +1,159 @@ +/* + * kernel/traps.c + * + * (C) Copyright 1994 Linus Torvalds + */ + +/* + * This file initializes the trap entry points + */ + +#include <linux/sched.h> +#include <linux/tty.h> + +#include <asm/unaligned.h> + +void die_if_kernel(char * str, struct pt_regs * regs, long err) +{ + long i; + unsigned long sp; + unsigned int * pc; + + printk("%s(%d): %s %ld\n", current->comm, current->pid, str, err); + sp = (unsigned long) (regs+1); + if (regs->ps & 8) + sp = rdusp(); + printk("pc = %lx ps = %04lx\n", regs->pc, regs->ps); + printk("rp = %lx sp = %lx\n", regs->r26, sp); + printk("r0=%lx r1=%lx r2=%lx r3=%lx\n", + regs->r0, regs->r1, regs->r2, regs->r3); + printk("r8=%lx\n", regs->r8); + printk("r16=%lx r17=%lx r18=%lx r19=%lx\n", + regs->r16, regs->r17, regs->r18, regs->r19); + printk("r20=%lx r21=%lx r22=%lx r23=%lx\n", + regs->r20, regs->r21, regs->r22, regs->r23); + printk("r24=%lx r25=%lx r26=%lx r27=%lx\n", + regs->r24, regs->r25, regs->r26, regs->r27); + printk("r28=%lx r29=%lx r30=%lx\n", + regs->r28, regs->gp, sp); + if (regs->ps & 8) + return; + printk("Code:"); + pc = (unsigned int *) regs->pc; + for (i = -3; i < 6; i++) + printk("%c%08x%c",i?' ':'<',pc[i],i?' ':'>'); + printk("\n"); + for (i = 0 ; i < 5000000000 ; i++) + /* pause */; + halt(); +} + +asmlinkage void do_entArith(unsigned long summary, unsigned long write_mask, + unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, + struct pt_regs regs) +{ + printk("Arithmetic trap: %02lx %016lx\n", summary, write_mask); + die_if_kernel("Arithmetic fault", ®s, 0); +} + +asmlinkage void do_entIF(unsigned long type, unsigned long a1, unsigned long a2, + unsigned long a3, unsigned long a4, unsigned long a5, + struct pt_regs regs) +{ + die_if_kernel("Instruction fault", ®s, type); +} + +/* + * entUna has a different register layout to be reasonably simple. It + * needs access to all the integer registers (the kernel doesn't use + * fp-regs), and it needs to have them in order for simpler access. + * + * Due to the non-standard register layout (and because we don't want + * to handle floating-point regs), we disallow user-mode unaligned + * accesses (we'd need to do "verify_area()" checking, as well as + * do a full "ret_from_sys_call" return). + * + * Oh, btw, we don't handle the "gp" register correctly, but if we fault + * on a gp-register unaligned load/store, something is _very_ wrong + * in the kernel anyway.. + */ +struct allregs { + unsigned long regs[32]; + unsigned long ps, pc, gp, a0, a1, a2; +}; + +asmlinkage void do_entUna(void * va, unsigned long opcode, unsigned long reg, + unsigned long a3, unsigned long a4, unsigned long a5, + struct allregs regs) +{ + static int cnt = 0; + + if (regs.ps & 8) + do_exit(SIGSEGV); + if (++cnt < 5) + printk("Unaligned trap at %016lx: %p %lx %ld\n", + regs.pc, va, opcode, reg); + /* $16-$18 are PAL-saved, and are offset by 19 entries */ + if (reg >= 16 && reg <= 18) + reg += 19; + switch (opcode) { + case 0x28: /* ldl */ + *(reg+regs.regs) = (int) ldl_u(va); + return; + case 0x29: /* ldq */ + *(reg+regs.regs) = ldq_u(va); + return; + case 0x2c: /* stl */ + stl_u(*(reg+regs.regs), va); + return; + case 0x2d: /* stq */ + stq_u(*(reg+regs.regs), va); + return; + } + printk("Bad unaligned kernel access at %016lx: %p %lx %ld\n", + regs.pc, va, opcode, reg); + do_exit(SIGSEGV); +} + +/* + * DEC means people to use the "retsys" instruction for return from + * a system call, but they are clearly misguided about this. We use + * "rti" in all cases, and fill in the stack with the return values. + * That should make signal handling etc much cleaner. + * + * Even more horribly, DEC doesn't allow system calls from kernel mode. + * "Security" features letting the user do something the kernel can't + * are a thinko. DEC palcode is strange. The PAL-code designers probably + * got terminally tainted by VMS at some point. + */ +asmlinkage long do_entSys(unsigned long a0, unsigned long a1, unsigned long a2, + unsigned long a3, unsigned long a4, unsigned long a5, struct pt_regs regs) +{ + printk("<sc %ld(%lx,%lx,%lx)>", regs.r0, a0, a1, a2); + return -1; +} + +extern asmlinkage void entMM(void); +extern asmlinkage void entIF(void); +extern asmlinkage void entArith(void); +extern asmlinkage void entUna(void); +extern asmlinkage void entSys(void); + +void trap_init(void) +{ + unsigned long gptr; + + /* + * Tell PAL-code what global pointer we want in the kernel.. + */ + __asm__("br %0,___tmp\n" + "___tmp:\tldgp %0,0(%0)" + : "=r" (gptr)); + wrkgp(gptr); + + wrent(entArith, 1); + wrent(entMM, 2); + wrent(entIF, 3); + wrent(entUna, 4); + wrent(entSys, 5); +} |