diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-08-25 09:12:35 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-08-25 09:12:35 +0000 |
commit | c7fc24dc4420057f103afe8fc64524ebc25c5d37 (patch) | |
tree | 3682407a599b8f9f03fc096298134cafba1c9b2f /arch/m68k/kernel | |
parent | 1d793fade8b063fde3cf275bf1a5c2d381292cd9 (diff) |
o Merge with Linux 2.1.116.
o New Newport console code.
o New G364 console code.
Diffstat (limited to 'arch/m68k/kernel')
-rw-r--r-- | arch/m68k/kernel/bios32.c | 50 | ||||
-rw-r--r-- | arch/m68k/kernel/entry.S | 10 | ||||
-rw-r--r-- | arch/m68k/kernel/head.S | 199 | ||||
-rw-r--r-- | arch/m68k/kernel/ints.c | 3 | ||||
-rw-r--r-- | arch/m68k/kernel/kgdb.c | 1194 | ||||
-rw-r--r-- | arch/m68k/kernel/m68k_defs.h | 8 | ||||
-rw-r--r-- | arch/m68k/kernel/m68k_ksyms.c | 8 | ||||
-rw-r--r-- | arch/m68k/kernel/process.c | 3 | ||||
-rw-r--r-- | arch/m68k/kernel/ptrace.c | 7 | ||||
-rw-r--r-- | arch/m68k/kernel/setup.c | 14 | ||||
-rw-r--r-- | arch/m68k/kernel/signal.c | 400 | ||||
-rw-r--r-- | arch/m68k/kernel/sys_m68k.c | 13 | ||||
-rw-r--r-- | arch/m68k/kernel/time.c | 2 |
13 files changed, 1707 insertions, 204 deletions
diff --git a/arch/m68k/kernel/bios32.c b/arch/m68k/kernel/bios32.c index a1dc77f33..b1322261a 100644 --- a/arch/m68k/kernel/bios32.c +++ b/arch/m68k/kernel/bios32.c @@ -33,7 +33,6 @@ * been removed in this version. */ -#include <linux/bios32.h> #include <linux/pci.h> #include <linux/malloc.h> #include <linux/mm.h> @@ -49,7 +48,7 @@ #define GB (1024*MB) #define MAJOR_REV 0 -#define MINOR_REV 0 +#define MINOR_REV 1 /* * Base addresses of the PCI memory and I/O areas on the Hades. @@ -264,12 +263,14 @@ __initfunc(static void disable_dev(struct pci_dev *dev)) #define MAX(val1, val2) ( ((val1) > (val2)) ? val1 : val2) -__initfunc(static void layout_dev(struct pci_dev *dev)) +__initfunc(static void layout_dev(struct pci_dev *dev, unsigned long pci_mem_base, + unsigned long pci_io_base)) { struct pci_bus *bus; unsigned short cmd; unsigned int base, mask, size, reg; unsigned int alignto; + int i; /* * Skip video cards for the time being. @@ -283,7 +284,7 @@ __initfunc(static void layout_dev(struct pci_dev *dev)) 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) + for (reg = PCI_BASE_ADDRESS_0, i = 0; reg <= PCI_BASE_ADDRESS_5; reg += 4, i++) { /* * Figure out how much space and of what type this @@ -297,6 +298,7 @@ __initfunc(static void layout_dev(struct pci_dev *dev)) if (!base) { /* this base-address register is unused */ + dev->base_address[i] = 0; continue; } @@ -322,6 +324,7 @@ __initfunc(static void layout_dev(struct pci_dev *dev)) io_base = base + size; pcibios_write_config_dword(bus->number, dev->devfn, reg, base | 0x1); + dev->base_address[i] = (pci_io_base + base) | 1; DBG_DEVS(("layout_dev: IO address: %lX\n", base)); } else @@ -369,6 +372,7 @@ __initfunc(static void layout_dev(struct pci_dev *dev)) mem_base = base + size; pcibios_write_config_dword(bus->number, dev->devfn, reg, base); + dev->base_address[i] = pci_mem_base + base; } } @@ -396,7 +400,8 @@ __initfunc(static void layout_dev(struct pci_dev *dev)) bus->number, PCI_SLOT(dev->devfn), dev->vendor, dev->device, dev->class)); } -__initfunc(static void layout_bus(struct pci_bus *bus)) +__initfunc(static void layout_bus(struct pci_bus *bus, unsigned long pci_mem_base, + unsigned long pci_io_base)) { struct pci_dev *dev; @@ -438,7 +443,7 @@ __initfunc(static void layout_bus(struct pci_bus *bus)) for (dev = bus->devices; dev; dev = dev->sibling) { if (dev->class >> 16 != PCI_BASE_CLASS_BRIDGE) - layout_dev(dev); + layout_dev(dev, pci_mem_base, pci_io_base); } } @@ -507,8 +512,7 @@ int pcibios_present(void) return 0; } -__initfunc(unsigned long pcibios_init(unsigned long mem_start, - unsigned long mem_end)) +__initfunc(void pcibios_init(void)) { printk("Linux/m68k PCI BIOS32 revision %x.%02x\n", MAJOR_REV, MINOR_REV); @@ -516,7 +520,8 @@ __initfunc(unsigned long pcibios_init(unsigned long mem_start, printk("...NOT modifying existing PCI configuration\n"); #endif - return mem_start; + pci_mem_base = 0x80000000; + pci_io_base = 0xB0000000; } /* @@ -555,26 +560,39 @@ __initfunc(static inline void hades_fixup(void)) } } -__initfunc(unsigned long pcibios_fixup(unsigned long mem_start, - unsigned long mem_end)) +__initfunc(void pcibios_fixup(void)) { #if PCI_MODIFY + unsigned long orig_mem_base, orig_io_base; + + orig_mem_base = pci_mem_base; + orig_io_base = pci_io_base; + pci_mem_base = 0; + pci_io_base = 0; + /* * Scan the tree, allocating PCI memory and I/O space. */ - layout_bus(&pci_root); -#endif + layout_bus(&pci_root, orig_mem_base, orig_io_base); - pci_mem_base = 0x80000000; - pci_io_base = 0xB0000000; + pci_mem_base = orig_mem_base; + pci_io_base = orig_io_base; +#endif /* * Now is the time to do all those dirty little deeds... */ hades_fixup(); +} + +__initfunc(void pcibios_fixup_bus(struct pci_bus *bus)) +{ +} - return mem_start; +__initfunc(char *pcibios_setup(char *str)) +{ + return str; } #endif /* CONFIG_PCI */ diff --git a/arch/m68k/kernel/entry.S b/arch/m68k/kernel/entry.S index 1c157c26a..eba9842cf 100644 --- a/arch/m68k/kernel/entry.S +++ b/arch/m68k/kernel/entry.S @@ -114,7 +114,7 @@ SYMBOL_NAME_LABEL(ret_from_exception) | kernel stack, otherwise stack overflow can occur during | heavy interupt load andw #ALLOWINT,%sr - tstl SYMBOL_NAME(need_resched) + tstl %curptr@(LTASK_NEEDRESCHED) jne SYMBOL_NAME(reschedule) cmpl #SYMBOL_NAME(task),%curptr | task[0] cannot have signals jeq 2f @@ -579,6 +579,14 @@ SYMBOL_NAME_LABEL(sys_call_table) .long SYMBOL_NAME(sys_pread) /* 180 */ .long SYMBOL_NAME(sys_pwrite) .long SYMBOL_NAME(sys_lchown); + .long SYMBOL_NAME(sys_getcwd) + .long SYMBOL_NAME(sys_capget) + .long SYMBOL_NAME(sys_capset) /* 185 */ + .long SYMBOL_NAME(sys_sigaltstack) + .long SYMBOL_NAME(sys_sendfile) + .long SYMBOL_NAME(sys_ni_syscall) /* streams1 */ + .long SYMBOL_NAME(sys_ni_syscall) /* streams2 */ + .rept NR_syscalls-(.-SYMBOL_NAME(sys_call_table))/4 .long SYMBOL_NAME(sys_ni_syscall) .endr diff --git a/arch/m68k/kernel/head.S b/arch/m68k/kernel/head.S index 1f3795a7c..e98f39fa8 100644 --- a/arch/m68k/kernel/head.S +++ b/arch/m68k/kernel/head.S @@ -17,6 +17,7 @@ ** 95/11/18 Richard Hirst: Added MVME166 support ** 96/04/26 Guenther Kelleter: fixed identity mapping for Falcon with ** Magnum- and FX-alternate ram +** 98/04/25 Phil Blundell: added HP300 support ** ** This file is subject to the terms and conditions of the GNU General Public ** License. See the file README.legal in the main directory of this archive @@ -73,10 +74,25 @@ #include <asm/pgtable.h> .globl SYMBOL_NAME(kernel_pg_dir), SYMBOL_NAME(kpt) -.globl SYMBOL_NAME(availmem), SYMBOL_NAME(mvme_bdid_ptr) +.globl SYMBOL_NAME(availmem) .globl SYMBOL_NAME(m68k_pgtable_cachemode) .globl SYMBOL_NAME(kernel_pmd_table), SYMBOL_NAME(swapper_pg_dir) +#if defined(CONFIG_MVME16x) +.globl SYMBOL_NAME(mvme_bdid_ptr) +#endif + +/* + * Added m68k_supervisor_cachemode for 68060 boards where some drivers + * need writethrough caching for supervisor accesses. Drivers known to + * be effected are 53c7xx.c and apricot.c (when used on VME boards). + * Richard Hirst. + */ + +#ifdef CONFIG_060_WRITETHROUGH +.globl SYMBOL_NAME(m68k_supervisor_cachemode) +#endif + D6B_0460 = 16 /* indicates 680[46]0 in d6 */ D6B_060 = 17 /* indicates 68060 in d6 */ D6F_040 = 1<<D6B_0460 @@ -146,12 +162,23 @@ TABLENR_16MB = 64 /* same for 16 MB */ #define is_not_amiga(lab) moveq &MACH_AMIGA,%d7; cmpl %d4,%d7; jne lab #define is_not_atari(lab) moveq &MACH_ATARI,%d7; cmpl %d4,%d7; jne lab #define is_not_mvme16x(lab) moveq &MACH_MVME16x,%d7; cmpl %d4,%d7; jne lab +#define is_not_bvme6000(lab) moveq &MACH_BVME6000,%d7; cmpl %d4,%d7; jne lab +#define is_not_hp300(lab) moveq &MACH_HP300,%d7 ; cmpl %d4,%d7; jne lab #define is_040_or_060(lab) btst &D6B_0460,%d6; jne lab #define is_not_040_or_060(lab) btst &D6B_0460,%d6; jeq lab #define is_060(lab) btst &D6B_060,%d6; jne lab #define is_not_060(lab) btst &D6B_060,%d6; jeq lab +/* On the HP300 we use the on-board LEDs for debug output before + the console is running. Writing a 1 bit turns the corresponding LED + _off_ - on the 340 bit 7 is towards the back panel of the machine. */ +#ifdef CONFIG_HP300 +#define leds(x) is_not_hp300(42f) ; moveb #(x),%d7 ; jbsr Lset_leds; 42: +#else +#define leds(x) +#endif + .text ENTRY(_stext) /* @@ -164,6 +191,7 @@ ENTRY(_stext) .long MACH_AMIGA, AMIGA_BOOTI_VERSION .long MACH_ATARI, ATARI_BOOTI_VERSION .long MACH_MVME16x, MVME16x_BOOTI_VERSION + .long MACH_BVME6000, BVME6000_BOOTI_VERSION .long 0 1: jra SYMBOL_NAME(_start) @@ -224,6 +252,22 @@ ENTRY(_start) movew %d6,%d0 movel %d0,%a0@ /* save cache mode for page tables */ + /* + * If this is a 68060 board using drivers with cache coherency + * problems, then supervisor memory accesses need to be write-through + * also; otherwise, we want copyback. + */ + +#if defined(CONFIG_060_WRITETHROUGH) + is_not_060(Lset_norm) + jra 1f +Lset_norm: + move.w #_PAGE_CACHE040,%d0 +1: + lea %pc@(SYMBOL_NAME(m68k_supervisor_cachemode)),%a0 + movel %d0,%a0@ +#endif + /* * raise interrupt level */ @@ -386,7 +430,12 @@ Lnotypetest: movel %a3,%a0 movel %d5,%a1 - addw #_PAGE_GLOBAL040+_PAGE_CACHE040+_PAGE_PRESENT+_PAGE_ACCESSED,%a1 +#if defined(CONFIG_060_WRITETHROUGH) + addw #_PAGE_GLOBAL040+_PAGE_PRESENT+_PAGE_ACCESSED,%a1 + addl m68k_supervisor_cachemode,%a1 +#else + addw #_PAGE_GLOBAL040+_PAGE_CACHE040+_PAGE_PRESENT+_PAGE_ACCESSED,%a1 +#endif movew #(PAGE_TABLE_SIZE*TABLENR_4MB)-1,%d1 movel #PAGESIZE,%d2 1: movel %a1,%a0@+ @@ -424,6 +473,9 @@ Lnotypetest: * of %a2 are forgotten. */ Lnot040: + + leds(0x4) + /* * Do any machine specific page table initializations. */ @@ -615,6 +667,21 @@ Lspata68040: Lnotatari: #endif +#ifdef CONFIG_HP300 + is_not_hp300(Lnothp300) + +/* On the HP300, we map the ROM, INTIO and DIO regions (phys. 0x00xxxxxx) + by mapping 32MB from 0xf0xxxxxx -> 0x00xxxxxx) using an 030 early + termination page descriptor. The ROM mapping is needed because the LEDs + are mapped there too. */ + + movel #_PAGE_NOCACHE030+_PAGE_PRESENT+_PAGE_ACCESSED,%d0 + movel %d0,%a5@(0x78<<2) + +Lnothp300: + +#endif + #if defined(CONFIG_MVME16x) is_not_mvme16x(Lnot16x) @@ -639,6 +706,25 @@ Lnotatari: Lnot16x: #endif +#if defined(CONFIG_BVME6000) + is_not_bvme6000(Lnotbvm) + + /* + * On BVME6000 we have already created kernel page tables for + * 4MB of RAM at address 0, so now need to do a transparent + * mapping of the top of memory space. Make it 0.5GByte for now. + */ + + movel #0xe01f0000,%d2 /* logical address base */ + orw #0xa040,%d2 /* add in magic bits */ + .long 0x4e7b2005 /* movec d2,ittr1 */ + .long 0x4e7b2007 /* movec d2,dttr1 */ + .long 0x4e7b2004 /* movec d2,ittr0 */ + .long 0x4e7b2006 /* movec d2,dttr0 */ + +Lnotbvm: +#endif + /* * Setup a transparent mapping of the physical memory we are executing in. * @@ -647,6 +733,7 @@ Lnot16x: */ Lmapphys: putc('J') + leds(0x8) #ifdef CONFIG_AMIGA is_not_amiga(Lmapphysnotamiga) @@ -853,6 +940,80 @@ Lmapphysnot16x: #endif +#if defined(CONFIG_HP300) + is_not_hp300(Lmapphysnothp300) + +/* + * Physical RAM is at 0xff000000. We want to map the kernel at 0x00000000. + * In order to avoid disaster when we enable the MMU we need to make a + * transparent mapping of the RAM we're executing out of as well. + */ + /* + * save physaddr of phys mem in register a3 + */ + + .chip 68030 + lea %pc@(Lmmu),%a3 + movel %d5,%d0 + andl #0xff000000,%d0 /* logical address base */ + orw #TTR_ENABLE+TTR_CI+TTR_RWM+TTR_FCB2+TTR_FCM1+TTR_FCM0,%d0 + movel %d0,%a3@ + pmove %a3@,%tt0 + /* no limit, 4byte descriptors */ + movel #0x80000002,%a3@ + movel %a5,%a3@(4) + pmove %a3@,%srp + pmove %a3@,%crp + pflusha + /* + * enable,super root enable,4096 byte pages,7 bit root index, + * 7 bit pointer index, 6 bit page table index. + */ + movel #0x82c07760,%a3@ + pmove %a3@,%tc /* enable the MMU */ + jmp 1f +1: + .chip 68k + + /* + * Fix up the custom register to point to the new location of the LEDs. + */ + lea %pc@(Lcustom),%a1 + movel #0xf0000000,%a1@ + + /* + * Energise the FPU and caches. + */ + orl #0x64, 0xf05f400c + +Lmapphysnothp300: + +#endif + +#if defined(CONFIG_BVME6000) + is_not_bvme6000(Lmapphysnotbvm) + /* + * save physaddr of phys mem in register a3 + */ + moveq #'L',%d7 + jbsr Lserial_putc + + .word 0xf4d8 /* CINVA I/D */ + .word 0xf518 /* pflusha */ + .long 0x4e7bd807 /* movec a5,srp */ + .long 0x4e7bd806 /* movec a5,urp */ + movel #(TC_ENABLE+TC_PAGE4K),%d0 + /* + * this value is also ok for the 68060, we don`t use the cache + * mode/protection defaults + */ + .long 0x4e7b0003 /* movec d0,tc (enable the MMU) */ + jra LdoneMMUenable /* branch to continuation of startup */ + +Lmapphysnotbvm: + +#endif + LdoneMMUenable: /* @@ -861,6 +1022,7 @@ LdoneMMUenable: */ putc('M') + leds(0x10) /* * d5 contains physaddr of kernel start @@ -917,6 +1079,7 @@ Lcache68060: /* jump to the kernel start */ putr() + leds(0x55) subl %a6,%a6 /* clear a6 for gdb */ jbsr SYMBOL_NAME(start_kernel) @@ -998,6 +1161,11 @@ LMFP_UDR = 0xfffa2f #endif #endif +#if defined (CONFIG_BVME6000) +BVME_SCC_CTRL_A = 0xffb0000b +BVME_SCC_DATA_A = 0xffb0000f +#endif + /* * Serial port output support. */ @@ -1062,6 +1230,17 @@ Lserial_init: 9: rts +#ifdef CONFIG_HP300 +/* Set LEDs to %d7 */ + .even +Lset_leds: + moveml %a0/%a1,%sp@- + movel %pc@(Lcustom),%a1 + moveb %d7,%a1@(0x1ffff) + moveml %sp@+,%a0/%a1 + rts +#endif + /* * Output character in d7 on serial port. * d7 thrashed. @@ -1073,6 +1252,16 @@ Lserial_putc: jne 2f moveb %d7,%sp@- .long 0x4e4f0020 + jra 9f +2: +#endif +#ifdef CONFIG_BVME6000 + cmpil #MACH_BVME6000,%d4 + jne 2f +1: btst #2,BVME_SCC_CTRL_A + jeq 1b + moveb %d7,BVME_SCC_DATA_A + jra 9f 2: #endif #ifdef CONFIG_AMIGA @@ -1199,5 +1388,11 @@ SYMBOL_NAME_LABEL(availmem) .long 0 SYMBOL_NAME_LABEL(m68k_pgtable_cachemode) .long 0 +#ifdef CONFIG_060_WRITETHROUGH +SYMBOL_NAME_LABEL(m68k_supervisor_cachemode) + .long 0 +#endif +#if defined(CONFIG_MVME16x) SYMBOL_NAME_LABEL(mvme_bdid_ptr) .long 0 +#endif diff --git a/arch/m68k/kernel/ints.c b/arch/m68k/kernel/ints.c index a9109ab0a..20e39f17f 100644 --- a/arch/m68k/kernel/ints.c +++ b/arch/m68k/kernel/ints.c @@ -31,6 +31,7 @@ #include <linux/errno.h> #include <linux/init.h> +#include <asm/setup.h> #include <asm/system.h> #include <asm/irq.h> #include <asm/traps.h> @@ -212,7 +213,7 @@ static void dummy_free_irq(unsigned int irq, void *dev_id) asmlinkage void process_int(unsigned long vec, struct pt_regs *fp) { - if (vec >= VEC_INT1 && vec <= VEC_INT7) { + if (vec >= VEC_INT1 && vec <= VEC_INT7 && !MACH_IS_BVME6000) { vec -= VEC_SPUR; kstat.irqs[0][vec]++; irq_list[vec].handler(vec, irq_list[vec].dev_id, fp); diff --git a/arch/m68k/kernel/kgdb.c b/arch/m68k/kernel/kgdb.c new file mode 100644 index 000000000..cdbd250cd --- /dev/null +++ b/arch/m68k/kernel/kgdb.c @@ -0,0 +1,1194 @@ +/* + * arch/m68k/kernel/kgdb.c -- Stub for GDB remote debugging protocol + * + * Originally written by Glenn Engel, Lake Stevens Instrument Division + * + * Contributed by HP Systems + * + * Modified for SPARC by Stu Grossman, Cygnus Support. + * Modified for Linux/MIPS (and MIPS in general) by Andreas Busse + * Modified and extended for Linux/68k by Roman Hodek + * + * Send complaints, suggestions etc. to + * <Roman.Hodek@informatik.uni-erlangen.de> + * + * Copyright (C) 1996-97 Roman Hodek + */ + +/* + * kgdb usage notes: + * ----------------- + * + * If you select CONFIG_KGDB in the configuration, the kernel will be built + * with different gccc flags: "-g" is added to get debug infos, and + * "-fomit-frame-pointer" is omitted to make debugging easier. Since the + * resulting kernel will be quite big (approx. > 7 MB), it will be stripped + * before compresion. Such a kernel will behave just as usually, except if + * given a "debug=<device>" command line option. (Only serial devices are + * allowed for <device>, i.e. no printers or the like; possible values are + * machine depedend and are the same as for the usual debug device, the one + * for logging kernel messages.) If that option is given and the device can be + * initialized, the kernel will connect to the remote gdb in trap_init(). The + * serial parameters are fixed to 8N1 and 9600bps, for easyness of + * implementation. + * + * Of course, you need a remote machine a suitable gdb there. I.e., it must + * have support for a m68k-linux target built in. If the remote machine + * doesn't run Linux/68k itself, you have to build a cross gdb. This is done + * by + * ./configure --target=m68k-linux + * in the gdb source directory. Until gdb comes with m68k-linux support by + * default, you have to apply some patches before. The remote debugging + * protocol itself is always built into gdb anyway, so you don't have to take + * special care about it. + * + * To start a debugging session, start that gdb with the debugging kernel + * image (the one with the symbols, vmlinux.debug) named on the command line. + * This file will be used by gdb to get symbol and debugging infos about the + * kernel. Next, select remote debug mode by + * target remote <device> + * where <device> is the name of the serial device over which the debugged + * machine is connected. Maybe you have to adjust the baud rate by + * set remotebaud <rate> + * or also other parameters with stty: + * shell stty ... </dev/... + * If the kernel to debug has already booted, it waited for gdb and now + * connects, and you'll see a breakpoint being reported. If the kernel isn't + * running yet, start it now. The order of gdb and the kernel doesn't matter. + * Another thing worth knowing about in the getting-started phase is how to + * debug the remote protocol itself. This is activated with + * set remotedebug 1 + * gdb will then print out each packet sent or received. You'll also get some + * messages about the gdb stub on the console of the debugged machine. + * + * If all that works, you can use lots of the usual debugging techniques on + * the kernel, e.g. inspecting and changing variables/memory, setting + * breakpoints, single stepping and so on. It's also possible to interrupt the + * debugged kernel by pressing C-c in gdb. Have fun! :-) + * + * The gdb stub is entered (and thus the remote gdb gets control) in the + * following situations: + * + * - If breakpoint() is called. This is just after kgdb initialization, or if + * a breakpoint() call has been put somewhere into the kernel source. + * (Breakpoints can of course also be set the usual way in gdb.) + * + * - If there is a kernel exception, i.e. bad_super_trap() or die_if_kernel() + * are entered. All the CPU exceptions are mapped to (more or less..., see + * the hard_trap_info array below) appropriate signal, which are reported + * to gdb. die_if_kernel() is usually called after some kind of access + * error and thus is reported as SIGSEGV. + * + * - When panic() is called. This is reported as SIGABRT. + * + * - If C-c is received over the serial line, which is treated as + * SIGINT. + * + * Of course, all these signals are just faked for gdb, since there is no + * signal concept as such for the kernel. It also isn't possible --obviously-- + * to set signal handlers from inside gdb, or restart the kernel with a + * signal. + * + * Current limitations: + * + * - While the kernel is stopped, interrupts are disabled for safety reasons + * (i.e., variables not changing magically or the like). But this also + * means that the clock isn't running anymore, and that interrupts from the + * hardware may get lost/not be served in time. This can cause some device + * errors... + * + * - When single-stepping, only one instruction of the current thread is + * executed, but interrupts are allowed for that time and will be serviced + * if pending. Be prepared for that. + * + * - All debugging happens in kernel virtual address space. There's no way to + * access physical memory not mapped in kernel space, or to access user + * space. A way to work around this is using get_user_long & Co. in gdb + * expressions, but only for the current process. + * + * - Interrupting the kernel only works if interrupts are currently allowed, + * and the interrupt of the serial line isn't blocked by some other means + * (IPL too high, disabled, ...) + * + * - The gdb stub is currently not reentrant, i.e. errors that happen therein + * (e.g. accesing invalid memory) may not be caught correctly. This could + * be removed in future by introducing a stack of struct registers. + * + */ + +/* + * To enable debugger support, two things need to happen. One, a + * call to kgdb_init() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * (Linux/68k note: Due to the current design, kgdb has to be initialized + * after traps and interrupts.) + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing a TRAP #15 instruction. + * + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * bBB..BB Set baud rate to BB..BB OK or BNN, then sets + * baud rate + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $<packet info>#<checksum>. + * + * where + * <packet info> :: <characters representing the command or response> + * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>> + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + */ + +#include <linux/config.h> +#include <linux/string.h> +#include <linux/signal.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/linkage.h> + +#include <asm/setup.h> +#include <asm/ptrace.h> +#include <asm/traps.h> +#include <asm/machdep.h> +#include <asm/kgdb.h> +#ifdef CONFIG_ATARI +#include <asm/atarihw.h> +#include <asm/atariints.h> +#endif +#ifdef CONFIG_AMIGA +#include <asm/amigahw.h> +#include <asm/amigaints.h> +#endif + + +#undef DEBUG + +/* + * global variable: register structure + */ + +struct gdb_regs kgdb_registers; + +/* + * serial i/o functions + */ + +static int (*serial_out)( unsigned char c ); +static unsigned char (*serial_in)( void ); +static unsigned char (*serial_intr)( void ); + +#define putDebugChar(c) serial_out(c) +#define getDebugChar() serial_in() + + +/***************************** Prototypes *****************************/ + +static int hex( unsigned char ch); +static void getpacket( char *buffer); +static void putpacket( char *buffer, int expect_ack); +static inline unsigned long *get_vbr( void ); +static int protected_read( char *p, unsigned long *vbr ); +static int protected_write( char *p, char val, unsigned long *vbr ); +static unsigned char *mem2hex( char *mem, char *buf, int count, int + may_fault); +static char *hex2mem( char *buf, char *mem, int count, int may_fault); +static int computeSignal( int tt); +static int hexToInt( char **ptr, int *intValue); +extern asmlinkage void kgdb_intr( int intno, void *data, struct pt_regs *fp ); +static asmlinkage void handle_exception( void ); +static void show_gdbregs( void ); +#ifdef CONFIG_ATARI +static int atari_mfp_out( unsigned char c ); +static unsigned char atari_mfp_in( void ); +static unsigned char atari_mfp_intr( void ); +static int atari_scc_out( unsigned char c ); +static unsigned char atari_scc_in( void ); +static unsigned char atari_scc_intr( void ); +#endif +#ifdef CONFIG_AMIGA +extern int amiga_ser_out( unsigned char c ); +extern unsigned char amiga_ser_in( void ); +#endif + +/************************* End of Prototypes **************************/ + + +int kgdb_initialized = 0; /* !0 means we've been initialized */ + +/* + * BUFMAX defines the maximum number of characters in inbound/outbound buffers + * at least NUMREGBYTES*2 are needed for register packets + */ +#define BUFMAX 2048 + +static char input_buffer[BUFMAX]; +static char output_buffer[BUFMAX]; +static const char hexchars[]="0123456789abcdef"; +/* debug > 0 prints ill-formed commands in valid packets & checksum errors */ +static int remote_debug = 0; + +/* sizes (in bytes) of CPU stack frames */ +static int frame_sizes[16] = { + 8, 8, 12, 12, /* $0..$3 */ + 16, 8, 8, 60, /* $4..$7 */ + 8, 20, 32, 92, /* $8..$B */ + 12, 4, 4, 4 /* $C..$F */ +}; + +/* + * Convert ch from a hex digit to an int + */ +static int hex(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'f') + return ch-'a'+10; + if (ch >= '0' && ch <= '9') + return ch-'0'; + if (ch >= 'A' && ch <= 'F') + return ch-'A'+10; + return -1; +} + +/* + * scan for the sequence $<data>#<checksum> + */ +static void getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + unsigned char ch; + + do { + /* + * wait around for the start character, + * ignore all other characters + */ + while ((ch = (getDebugChar() & 0x7f)) != '$') ; + + checksum = 0; + xmitcsum = -1; + count = 0; + + /* + * now, read until a # or end of buffer is found + */ + while (count < BUFMAX) { + ch = getDebugChar() & 0x7f; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + + if (count >= BUFMAX) + continue; + + buffer[count] = 0; +#ifdef DEBUG + printk( "kgdb: received packet %s\n", buffer ); +#endif + + if (ch == '#') { + xmitcsum = hex(getDebugChar() & 0x7f) << 4; + xmitcsum |= hex(getDebugChar() & 0x7f); + + if (checksum != xmitcsum) { + if (remote_debug) + printk( "kgdb: bad checksum. count = 0x%x sent=0x%x " + "buf=%s\n", checksum, xmitcsum, buffer ); + putDebugChar('-'); /* failed checksum */ + } + else { + putDebugChar('+'); /* successful transfer */ + + /* + * if a sequence char is present, + * reply the sequence ID + */ + if (buffer[2] == ':') { + putDebugChar(buffer[0]); + putDebugChar(buffer[1]); + + /* + * remove sequence chars from buffer + */ + count = strlen(buffer); + for (i=3; i <= count; i++) + buffer[i-3] = buffer[i]; + } + } + } + } + while (checksum != xmitcsum); +} + +/* + * send the packet in buffer. + */ +static void putpacket(char *buffer, int expect_ack) +{ + unsigned char checksum; + int count; + unsigned char ch; + + /* + * $<packet info>#<checksum>. + */ + +#ifdef DEBUG + printk( "kgdb: sending packet %s\n", buffer ); +#endif + do { + putDebugChar('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count]) != 0) { + if (!(putDebugChar(ch))) + return; + checksum += ch; + count += 1; + } + + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum & 0xf]); + + } + while (expect_ack && (getDebugChar() & 0x7f) != '+'); +} + + +static inline unsigned long *get_vbr( void ) + +{ unsigned long *vbr; + + __asm__ __volatile__ ( "movec %/vbr,%0" : "=d" (vbr) : ); + return( vbr ); +} + +static int protected_read( char *p, unsigned long *vbr ) + +{ unsigned char val; + int rv; + + __asm__ __volatile__ + ( "movel %3@(8),%/a0\n\t" + "movel #Lberr1,%3@(8)\n\t" + "movel %/sp,%/a1\n\t" + "moveq #1,%1\n\t" + "moveb %2@,%0\n" + "nop \n\t" + "moveq #0,%1\n\t" + "Lberr1:\t" + "movel %/a1,%/sp\n\t" + "movel %/a0,%3@(8)" + : "=&d" (val), "=&r" (rv) + : "a" (p), "a" (vbr) + : "a0", "a1" ); + + return( rv ? -1 : val ); +} + +static int protected_write( char *p, char val, unsigned long *vbr ) + +{ int rv; + + __asm__ __volatile__ + ( "movel %3@(8),%/a0\n\t" + "movel #Lberr2,%3@(8)\n\t" + "movel %/sp,%/a1\n\t" + "moveq #1,%0\n\t" + "moveb %2,%1@\n" + "nop \n\t" + "moveq #0,%0\n\t" + "Lberr2:\t" + "movel %/a1,%/sp\n\t" + "movel %/a0,%3@(8)" + : "=&r" (rv) + : "a" (p), "d" (val), "a" (vbr) + : "a0", "a1" ); + + return( rv ); +} + +/* + * Convert the memory pointed to by mem into hex, placing result in buf. + * Return a pointer to the last char put in buf (null), in case of mem fault, + * return 0. + * If MAY_FAULT is non-zero, then we will handle memory faults by returning + * a 0, else treat a fault like any other fault in the stub. + */ +static unsigned char *mem2hex(char *mem, char *buf, int count, int may_fault) +{ + int ch; + unsigned long *vbr = get_vbr(); + + for( ; count-- > 0; ++mem ) { + if ((ch = protected_read( mem, vbr )) < 0) { + /* bus error happened */ + if (may_fault) + return 0; + else { + /* ignore, but print a warning */ + printk( "Bus error on read from %p\n", mem ); + ch = 0; + } + } + *buf++ = hexchars[(ch >> 4) & 0xf]; + *buf++ = hexchars[ch & 0xf]; + } + + *buf = 0; + + return buf; +} + +/* + * convert the hex array pointed to by buf into binary to be placed in mem + * return a pointer to the character AFTER the last byte written + */ +static char *hex2mem(char *buf, char *mem, int count, int may_fault) +{ + int i; + unsigned char ch; + unsigned long *vbr = get_vbr(); + + for( i = 0; i < count; i++, mem++ ) { + ch = hex(*buf++) << 4; + ch |= hex(*buf++); + if (protected_write( mem, ch, vbr )) { + /* bus error happened */ + if (may_fault) + return 0; + else + /* ignore, but print a warning */ + printk( "Bus error on write to %p\n", mem ); + } + } + + return mem; +} + +/* + * This table contains the mapping between SPARC hardware trap types, and + * signals, which are primarily what GDB understands. It also indicates + * which hardware traps we need to commandeer when initializing the stub. + */ +static struct hard_trap_info +{ + unsigned char tt; /* Trap type code for MIPS R3xxx and R4xxx */ + unsigned char signo; /* Signal that we map this trap into */ +} hard_trap_info[] = { + { 1, SIGINT }, /* excep. 1 is used to fake SIGINT */ + { VEC_BUSERR, SIGSEGV }, /* bus/access error */ + { VEC_ADDRERR, SIGBUS }, /* address error */ + { VEC_ILLEGAL, SIGILL }, /* illegal insn */ + { VEC_ZERODIV, SIGFPE }, /* (integer) divison by zero */ + { VEC_CHK, SIGILL }, /* CHK insn */ + { VEC_TRAP, SIGFPE }, /* [F]TRAPcc insn */ + { VEC_PRIV, SIGILL }, /* priviledge violation (cannot happen) */ + { VEC_TRACE, SIGTRAP }, /* trace trap (single-stepping) */ + { VEC_LINE10, SIGILL }, /* A-line insn */ + { VEC_LINE11, SIGILL }, /* F-line insn */ + { VEC_COPROC, SIGIOT }, /* coprocessor protocol error */ + { VEC_FORMAT, SIGIOT }, /* frame format error */ + { VEC_UNINT, SIGIOT }, /* uninitialized intr. (should not happen) */ + { VEC_SYS, SIGILL }, /* TRAP #0 = system call (illegal in kernel) */ + { VEC_TRAP1, SIGILL }, /* TRAP #1 */ + { VEC_TRAP2, SIGILL }, /* TRAP #2 */ + { VEC_TRAP3, SIGILL }, /* TRAP #3 */ + { VEC_TRAP4, SIGILL }, /* TRAP #4 */ + { VEC_TRAP5, SIGILL }, /* TRAP #5 */ + { VEC_TRAP6, SIGILL }, /* TRAP #6 */ + { VEC_TRAP7, SIGILL }, /* TRAP #7 */ + { VEC_TRAP8, SIGILL }, /* TRAP #8 */ + { VEC_TRAP9, SIGILL }, /* TRAP #9 */ + { VEC_TRAP10, SIGILL }, /* TRAP #10 */ + { VEC_TRAP11, SIGILL }, /* TRAP #11 */ + { VEC_TRAP12, SIGILL }, /* TRAP #12 */ + { VEC_TRAP13, SIGILL }, /* TRAP #13 */ + { VEC_TRAP14, SIGABRT }, /* TRAP #14 (used by kgdb_abort) */ + { VEC_TRAP15, SIGTRAP }, /* TRAP #15 (breakpoint) */ + { VEC_FPBRUC, SIGFPE }, /* FPU */ + { VEC_FPIR, SIGFPE }, /* FPU */ + { VEC_FPDIVZ, SIGFPE }, /* FPU */ + { VEC_FPUNDER, SIGFPE }, /* FPU */ + { VEC_FPOE, SIGFPE }, /* FPU */ + { VEC_FPOVER, SIGFPE }, /* FPU */ + { VEC_FPNAN, SIGFPE }, /* FPU */ + { VEC_FPUNSUP, SIGFPE }, /* FPU */ + { VEC_UNIMPEA, SIGILL }, /* unimpl. effective address */ + { VEC_UNIMPII, SIGILL }, /* unimpl. integer insn */ + + { 0, 0 } /* Must be last */ +}; + + +/* + * Set up exception handlers for tracing and breakpoints + */ +void kgdb_init(void) +{ + extern char m68k_debug_device[]; + + /* fake usage to avoid gcc warnings about unused stuff (they're used in + * assembler code) The local variables will be optimized away... */ + void (*fake1)(void) = handle_exception; + int *fake2 = frame_sizes; + (void)fake1; (void)fake2; + + /* We don't modify the real exception vectors here for the m68k. + * handle_exception() will be called from bad_kernel_trap() or + * die_if_kernel() as needed. */ + + /* + * Initialize the serial port (name in 'm68k_debug_device') + */ + + serial_in = NULL; + serial_out = NULL; + serial_intr = NULL; + +#ifdef CONFIG_ATARI + if (MACH_IS_ATARI) { + if (!strcmp( m68k_debug_device, "ser" )) { + /* defaults to ser2 for a Falcon and ser1 otherwise */ + strcpy( m68k_debug_device, + ((atari_mch_cookie>>16) == ATARI_MCH_FALCON) ? + "ser2" : "ser1" ); + } + + if (!strcmp( m68k_debug_device, "ser1" )) { + /* ST-MFP Modem1 serial port init */ + mfp.trn_stat &= ~0x01; /* disable TX */ + mfp.rcv_stat &= ~0x01; /* disable RX */ + mfp.usart_ctr = 0x88; /* clk 1:16, 8N1 */ + mfp.tim_ct_cd &= 0x70; /* stop timer D */ + mfp.tim_dt_d = 2; /* 9600 bps */ + mfp.tim_ct_cd |= 0x01; /* start timer D, 1:4 */ + mfp.trn_stat |= 0x01; /* enable TX */ + mfp.rcv_stat |= 0x01; /* enable RX */ + + /* set function pointers */ + serial_in = atari_mfp_in; + serial_out = atari_mfp_out; + serial_intr = atari_mfp_intr; + + /* allocate interrupt */ + request_irq( IRQ_MFP_RECFULL, kgdb_intr, IRQ_TYPE_FAST, "kgdb", + NULL ); + } + else if (!strcmp( m68k_debug_device, "ser2" )) { + extern int atari_SCC_reset_done; + + /* SCC Modem2 serial port init */ + static unsigned char *p, scc_table[] = { + 9, 0xc0, /* Reset */ + 4, 0x44, /* x16, 1 stopbit, no parity */ + 3, 0xc0, /* receiver: 8 bpc */ + 5, 0xe2, /* transmitter: 8 bpc, assert dtr/rts */ + 2, 0x60, /* base int vector */ + 9, 0x09, /* int enab, with status low */ + 10, 0, /* NRZ */ + 11, 0x50, /* use baud rate generator */ + 12, 24, 13, 0, /* 9600 baud */ + 14, 2, 14, 3, /* use master clock for BRG, enable */ + 3, 0xc1, /* enable receiver */ + 5, 0xea, /* enable transmitter */ + 15, 0, /* no stat ints */ + 1, 0x10, /* Rx int every char, other ints off */ + 0 + }; + + (void)scc.cha_b_ctrl; /* reset reg pointer */ + MFPDELAY(); + for( p = scc_table; *p != 0; ) { + scc.cha_b_ctrl = *p++; + MFPDELAY(); + scc.cha_b_ctrl = *p++; + MFPDELAY(); + if (p[-2] == 9) + udelay(40); /* extra delay after WR9 access */ + } + /* avoid that atari_SCC.c resets the whole SCC again */ + atari_SCC_reset_done = 1; + + /* set function pointers */ + serial_in = atari_scc_in; + serial_out = atari_scc_out; + serial_intr = atari_scc_intr; + + /* allocate rx and spcond ints */ + request_irq( IRQ_SCCB_RX, kgdb_intr, IRQ_TYPE_FAST, "kgdb", NULL ); + request_irq( IRQ_SCCB_SPCOND, kgdb_intr, IRQ_TYPE_FAST, "kgdb", + NULL ); + } + } +#endif + +#ifdef CONFIG_AMIGA + if (MACH_IS_AMIGA) { + /* always use built-in serial port, no init required */ + serial_in = amiga_ser_in; + serial_out = amiga_ser_out; + } +#endif + +#ifdef CONFIG_ATARI + if (!serial_in || !serial_out) { + if (*m68k_debug_device) + printk( "kgdb_init failed: no valid serial device!\n" ); + else + printk( "kgdb not enabled\n" ); + return; + } +#endif + + /* + * In case GDB is started before us, ack any packets + * (presumably "$?#xx") sitting there. + */ + + putDebugChar ('+'); + kgdb_initialized = 1; + printk( KERN_INFO "kgdb initialized.\n" ); +} + + +/* + * Convert the MIPS hardware trap type code to a unix signal number. + */ +static int computeSignal(int tt) +{ + struct hard_trap_info *ht; + + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + if (ht->tt == tt) + return ht->signo; + + return SIGHUP; /* default for things we don't know about */ +} + +/* + * While we find nice hex chars, build an int. + * Return number of chars processed. + */ +static int hexToInt(char **ptr, int *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0; + + while (**ptr) + { + hexValue = hex(**ptr); + if (hexValue < 0) + break; + + *intValue = (*intValue << 4) | hexValue; + numChars ++; + + (*ptr)++; + } + + return (numChars); +} + + +/* + * This assembler stuff copies a struct frame (passed as argument) into struct + * gdb_regs registers and then calls handle_exception. After return from + * there, register and the like are restored from 'registers', the stack is + * set up and execution is continued where registers->pc tells us. + */ + +/* offsets in struct frame */ +#define FRAMEOFF_D1 "0" /* d1..d5 */ +#define FRAMEOFF_A0 "5*4" /* a0..a2 */ +#define FRAMEOFF_D0 "8*4" +#define FRAMEOFF_SR "11*4" +#define FRAMEOFF_PC "11*4+2" +#define FRAMEOFF_VECTOR "12*4+2" + +/* offsets in struct gdb_regs */ +#define GDBOFF_D0 "0" +#define GDBOFF_D1 "1*4" +#define GDBOFF_D6 "6*4" +#define GDBOFF_A0 "8*4" +#define GDBOFF_A3 "11*4" +#define GDBOFF_A7 "15*4" +#define GDBOFF_VECTOR "16*4" +#define GDBOFF_SR "16*4+2" +#define GDBOFF_PC "17*4" +#define GDBOFF_FP0 "18*4" +#define GDBOFF_FPCTL "42*4" + +__asm__ +( " .globl " SYMBOL_NAME_STR(enter_kgdb) "\n" + SYMBOL_NAME_STR(enter_kgdb) ":\n" + /* return if not initialized */ + " tstl "SYMBOL_NAME_STR(kgdb_initialized)"\n" + " bne 1f\n" + " rts \n" + "1: orw #0x700,%sr\n" /* disable interrupts while in stub */ + " tstl %sp@+\n" /* pop off return address */ + " movel %sp@+,%a0\n" /* get pointer to fp->ptregs (param) */ + " movel #"SYMBOL_NAME_STR(kgdb_registers)",%a1\n" /* destination */ + /* copy d0-d5/a0-a1 into gdb_regs */ + " movel %a0@("FRAMEOFF_D0"),%a1@("GDBOFF_D0")\n" + " moveml %a0@("FRAMEOFF_D1"),%d1-%d5\n" + " moveml %d1-%d5,%a1@("GDBOFF_D1")\n" + " moveml %a0@("FRAMEOFF_A0"),%d0-%d2\n" + " moveml %d0-%d2,%a1@("GDBOFF_A0")\n" + /* copy sr and pc */ + " movel %a0@("FRAMEOFF_PC"),%a1@("GDBOFF_PC")\n" + " movew %a0@("FRAMEOFF_SR"),%a1@("GDBOFF_SR")\n" + /* copy format/vector word */ + " movew %a0@("FRAMEOFF_VECTOR"),%a1@("GDBOFF_VECTOR")\n" + /* save FPU regs */ + " fmovemx %fp0-%fp7,%a1@("GDBOFF_FP0")\n" + " fmoveml %fpcr/%fpsr/%fpiar,%a1@("GDBOFF_FPCTL")\n" + + /* set stack to CPU frame */ + " addl #"FRAMEOFF_SR",%a0\n" + " movel %a0,%sp\n" + " movew %sp@(6),%d0\n" + " andl #0xf000,%d0\n" + " lsrl #8,%d0\n" + " lsrl #2,%d0\n" /* get frame format << 2 */ + " lea "SYMBOL_NAME_STR(frame_sizes)",%a2\n" + " addl %a2@(%d0),%sp\n" + " movel %sp,%a1@("GDBOFF_A7")\n" /* save a7 now */ + + /* call handle_exception() now that the stack is set up */ + "Lcall_handle_excp:" + " jsr "SYMBOL_NAME_STR(handle_exception)"\n" + + /* after return, first restore FPU registers */ + " movel #"SYMBOL_NAME_STR(kgdb_registers)",%a0\n" /* source */ + " fmovemx %a0@("GDBOFF_FP0"),%fp0-%fp7\n" + " fmoveml %a0@("GDBOFF_FPCTL"),%fpcr/%fpsr/%fpiar\n" + /* set new stack pointer */ + " movel %a0@("GDBOFF_A7"),%sp\n" + " clrw %sp@-\n" /* fake format $0 frame */ + " movel %a0@("GDBOFF_PC"),%sp@-\n" /* new PC into frame */ + " movew %a0@("GDBOFF_SR"),%sp@-\n" /* new SR into frame */ + /* restore general registers */ + " moveml %a0@("GDBOFF_D0"),%d0-%d7/%a0-%a6\n" + /* and jump to new PC */ + " rte" + ); + + +/* + * This is the entry point for the serial interrupt handler. It calls the + * machine specific function pointer 'serial_intr' to get the char that + * interrupted. If that was C-c, the stub is entered as above, but based on + * just a struct intframe, not a struct frame. + */ + +__asm__ +( SYMBOL_NAME_STR(kgdb_intr) ":\n" + /* return if not initialized */ + " tstl "SYMBOL_NAME_STR(kgdb_initialized)"\n" + " bne 1f\n" + "2: rts \n" + "1: movel "SYMBOL_NAME_STR(serial_intr)",%a0\n" + " jsr (%a0)\n" /* get char from serial */ + " cmpb #3,%d0\n" /* is it C-c ? */ + " bne 2b\n" /* no -> just ignore */ + " orw #0x700,%sr\n" /* disable interrupts */ + " subql #1,"SYMBOL_NAME_STR(local_irq_count)"\n" + " movel %sp@(12),%sp\n" /* revert stack to where 'inthandler' set + * it up */ + /* restore regs from frame */ + " moveml %sp@+,%d1-%d5/%a0-%a2\n" + " movel %sp@+,%d0\n" + " addql #8,%sp\n" /* throw away orig_d0 and stkadj */ + /* save them into 'registers' */ + " moveml %d0-%d7/%a0-%a6,"SYMBOL_NAME_STR(kgdb_registers)"\n" + " movel #"SYMBOL_NAME_STR(kgdb_registers)",%a1\n" /* destination */ + /* copy sr and pc */ + " movel %sp@(2),%a1@("GDBOFF_PC")\n" + " movew %sp@,%a1@("GDBOFF_SR")\n" + /* fake format 0 and vector 1 (translated to SIGINT) */ + " movew #4,%a1@("GDBOFF_VECTOR")\n" + /* save FPU regs */ + " fmovemx %fp0-%fp7,%a1@("GDBOFF_FP0")\n" + " fmoveml %fpcr/%fpsr/%fpiar,%a1@("GDBOFF_FPCTL")\n" + /* pop off the CPU stack frame */ + " addql #8,%sp\n" + " movel %sp,%a1@("GDBOFF_A7")\n" /* save a7 now */ + /* proceed as in enter_kgdb */ + " jbra Lcall_handle_excp\n" + ); + +/* + * This function does all command processing for interfacing to gdb. It + * returns 1 if you should skip the instruction at the trap address, 0 + * otherwise. + */ +static asmlinkage void handle_exception( void ) +{ + int trap; /* Trap type */ + int sigval; + int addr; + int length; + char *ptr; + + trap = kgdb_registers.vector >> 2; + sigval = computeSignal(trap); + /* clear upper half of vector/sr word */ + kgdb_registers.vector = 0; + kgdb_registers.format = 0; + +#ifndef DEBUG + if (remote_debug) { +#endif + printk("in handle_exception() trap=%d sigval=%d\n", trap, sigval ); + show_gdbregs(); +#ifndef DEBUG + } +#endif + + /* + * reply to host that an exception has occurred + */ + ptr = output_buffer; + + /* + * Send trap type (converted to signal) + */ + *ptr++ = 'T'; + *ptr++ = hexchars[sigval >> 4]; + *ptr++ = hexchars[sigval & 0xf]; + + /* + * Send Error PC + */ + *ptr++ = hexchars[GDBREG_PC >> 4]; + *ptr++ = hexchars[GDBREG_PC & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((char *)&kgdb_registers.pc, ptr, 4, 0); + *ptr++ = ';'; + + /* + * Send frame pointer + */ + *ptr++ = hexchars[GDBREG_A6 >> 4]; + *ptr++ = hexchars[GDBREG_A6 & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((char *)&kgdb_registers.regs[GDBREG_A6], ptr, 4, 0); + *ptr++ = ';'; + + /* + * Send stack pointer + */ + *ptr++ = hexchars[GDBREG_SP >> 4]; + *ptr++ = hexchars[GDBREG_SP & 0xf]; + *ptr++ = ':'; + ptr = mem2hex((char *)&kgdb_registers.regs[GDBREG_SP], ptr, 4, 0); + *ptr++ = ';'; + + *ptr++ = 0; + putpacket(output_buffer,1); /* send it off... */ + + /* + * Wait for input from remote GDB + */ + for(;;) { + output_buffer[0] = 0; + getpacket(input_buffer); + + switch (input_buffer[0]) { + case '?': + output_buffer[0] = 'S'; + output_buffer[1] = hexchars[sigval >> 4]; + output_buffer[2] = hexchars[sigval & 0xf]; + output_buffer[3] = 0; + break; + + case 'd': + /* toggle debug flag */ + remote_debug = !remote_debug; + break; + + /* + * Return the value of the CPU registers + */ + case 'g': + ptr = output_buffer; + ptr = mem2hex((char *)&kgdb_registers, ptr, NUMREGSBYTES, 0); + break; + + /* + * set the value of the CPU registers - return OK + */ + case 'G': + ptr = &input_buffer[1]; + ptr = hex2mem(ptr, (char *)&kgdb_registers, NUMREGSBYTES, 0); + strcpy(output_buffer,"OK"); + break; + + /* + * Pn...=r... Write register n + */ + case 'P': + ptr = &input_buffer[1]; + if (hexToInt(&ptr, &addr) && *ptr++ == '=') { + if (addr >= 0 && addr <= GDBREG_PC) + hex2mem(ptr, (char *)&kgdb_registers.regs[addr], 4, 0); + else if (addr >= GDBREG_FP0 && addr <= GDBREG_FP7) + hex2mem(ptr, (char *)&kgdb_registers.fpregs[addr-GDBREG_FP0], + 12, 0); + else if (addr >= GDBREG_FPCR && addr <= GDBREG_FPIAR) + hex2mem(ptr, (char *)&kgdb_registers.fpcntl[addr-GDBREG_FPCR], + 4, 0); + } + else + strcpy(output_buffer,"E01"); + break; + + /* + * mAA..AA,LLLL Read LLLL bytes at address AA..AA + */ + case 'm': + ptr = &input_buffer[1]; + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length)) { + if (mem2hex((char *)addr, output_buffer, length, 1)) + break; + strcpy (output_buffer, "E03"); + } else + strcpy(output_buffer,"E01"); + break; + + /* + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK + */ + case 'M': + ptr = &input_buffer[1]; + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length) + && *ptr++ == ':') { + if (hex2mem(ptr, (char *)addr, length, 1)) + strcpy(output_buffer, "OK"); + else + strcpy(output_buffer, "E03"); + } + else + strcpy(output_buffer, "E02"); + break; + + /* + * cAA..AA Continue at address AA..AA(optional) + * sAA..AA Step one instruction from AA..AA(optional) + */ + case 'c': + case 's': + /* try to read optional parameter, pc unchanged if no parm */ + + ptr = &input_buffer[1]; + if (hexToInt(&ptr, &addr)) + kgdb_registers.pc = addr; + + kgdb_registers.sr &= 0x7fff; /* clear Trace bit */ + if (input_buffer[0] == 's') + kgdb_registers.sr |= 0x8000; /* set it if step command */ + + if (remote_debug) + printk( "cont; new PC=0x%08lx SR=0x%04x\n", + kgdb_registers.pc, kgdb_registers.sr ); + + /* + * Need to flush the instruction cache here, as we may + * have deposited a breakpoint, and the icache probably + * has no way of knowing that a data ref to some location + * may have changed something that is in the instruction + * cache. + */ + + if (m68k_is040or060) + __asm__ __volatile__ + ( ".word 0xf4f8\n\t" /* CPUSHA I/D */ + ".word 0xf498" /* CINVA I */ + ); + else + __asm__ __volatile__ + ( "movec %/cacr,%/d0\n\t" + "oriw #0x0008,%/d0\n\t" + "movec %/d0,%/cacr" + : : : "d0" ); + + return; + + /* + * kill the program means reset the machine + */ + case 'k' : + case 'r': + if (mach_reset) { + /* reply OK before actual reset */ + strcpy(output_buffer,"OK"); + putpacket(output_buffer,0); + mach_reset(); + } + else + strcpy(output_buffer,"E01"); + break; + + /* + * Set baud rate (bBB) + * FIXME: Needs to be written (in gdb, too...) + */ + case 'b': + strcpy(output_buffer,"E01"); + break; + + } /* switch */ + + /* + * reply to the request + */ + + putpacket(output_buffer,1); + + } +} + + +/* + * Print registers (on target console) + * Used only to debug the stub... + */ +static void show_gdbregs( void ) +{ + printk( "d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n", + kgdb_registers.regs[0], kgdb_registers.regs[1], + kgdb_registers.regs[2], kgdb_registers.regs[3] ); + printk( "d4: %08lx d5: %08lx d6: %08lx d7: %08lx\n", + kgdb_registers.regs[4], kgdb_registers.regs[5], + kgdb_registers.regs[6], kgdb_registers.regs[7] ); + printk( "a0: %08lx a1: %08lx a2: %08lx a3: %08lx\n", + kgdb_registers.regs[8], kgdb_registers.regs[9], + kgdb_registers.regs[10], kgdb_registers.regs[11] ); + printk( "a4: %08lx a5: %08lx a6: %08lx a7: %08lx\n", + kgdb_registers.regs[12], kgdb_registers.regs[13], + kgdb_registers.regs[14], kgdb_registers.regs[15] ); + printk( "pc: %08lx sr: %04x\n", kgdb_registers.pc, kgdb_registers.sr ); +} + + +/* -------------------- Atari serial I/O -------------------- */ + +#ifdef CONFIG_ATARI + +static int atari_mfp_out( unsigned char c ) + +{ + while( !(mfp.trn_stat & 0x80) ) /* wait for tx buf empty */ + barrier(); + mfp.usart_dta = c; + return( 1 ); +} + + +static unsigned char atari_mfp_in( void ) + +{ + while( !(mfp.rcv_stat & 0x80) ) /* wait for rx buf filled */ + barrier(); + return( mfp.usart_dta ); +} + + +static unsigned char atari_mfp_intr( void ) + +{ + return( mfp.usart_dta ); +} + + +static int atari_scc_out( unsigned char c ) + +{ + do { + MFPDELAY(); + } while( !(scc.cha_b_ctrl & 0x04) ); /* wait for tx buf empty */ + MFPDELAY(); + scc.cha_b_data = c; + return( 1 ); +} + + +static unsigned char atari_scc_in( void ) + +{ + do { + MFPDELAY(); + } while( !(scc.cha_b_ctrl & 0x01) ); /* wait for rx buf filled */ + MFPDELAY(); + return( scc.cha_b_data ); +} + + +static unsigned char atari_scc_intr( void ) + +{ unsigned char c, stat; + + MFPDELAY(); + scc.cha_b_ctrl = 1; /* RR1 */ + MFPDELAY(); + stat = scc.cha_b_ctrl; + MFPDELAY(); + c = scc.cha_b_data; + MFPDELAY(); + if (stat & 0x30) { + scc.cha_b_ctrl = 0x30; /* error reset for overrun and parity */ + MFPDELAY(); + } + scc.cha_b_ctrl = 0x38; /* reset highest IUS */ + MFPDELAY(); + return( c ); +} + +#endif diff --git a/arch/m68k/kernel/m68k_defs.h b/arch/m68k/kernel/m68k_defs.h new file mode 100644 index 000000000..992d390c7 --- /dev/null +++ b/arch/m68k/kernel/m68k_defs.h @@ -0,0 +1,8 @@ +/* + * WARNING! This file is automatically generated - DO NOT EDIT! + */ + +#define TS_MAGICKEY 0x5a5a5a5a +#define TS_TSS 478 +#define TS_ESP0 498 +#define TS_FPU 502 diff --git a/arch/m68k/kernel/m68k_ksyms.c b/arch/m68k/kernel/m68k_ksyms.c index 025e11c0f..ee73b3ff2 100644 --- a/arch/m68k/kernel/m68k_ksyms.c +++ b/arch/m68k/kernel/m68k_ksyms.c @@ -1,4 +1,3 @@ -#include <linux/config.h> #include <linux/module.h> #include <linux/linkage.h> #include <linux/sched.h> @@ -8,7 +7,6 @@ #include <linux/elfcore.h> #include <linux/in6.h> #include <linux/interrupt.h> -#include <linux/pci.h> #include <asm/setup.h> #include <asm/machdep.h> @@ -44,6 +42,8 @@ EXPORT_SYMBOL(strrchr); EXPORT_SYMBOL(strstr); EXPORT_SYMBOL(local_irq_count); EXPORT_SYMBOL(local_bh_count); +EXPORT_SYMBOL(enable_irq); +EXPORT_SYMBOL(disable_irq); /* Networking helper routines. */ EXPORT_SYMBOL(csum_partial_copy); @@ -60,7 +60,3 @@ EXPORT_SYMBOL_NOVERS(memcmp); EXPORT_SYMBOL_NOVERS(__down_failed); EXPORT_SYMBOL_NOVERS(__down_failed_interruptible); EXPORT_SYMBOL_NOVERS(__up_wakeup); - -#ifdef CONFIG_PCI -EXPORT_SYMBOL(pci_devices); -#endif diff --git a/arch/m68k/kernel/process.c b/arch/m68k/kernel/process.c index a6fbd5718..dac2bfcc8 100644 --- a/arch/m68k/kernel/process.c +++ b/arch/m68k/kernel/process.c @@ -66,13 +66,14 @@ asmlinkage int sys_idle(void) current->priority = -100; current->counter = -100; for (;;){ - if (!need_resched) + if (!current->need_resched) #if defined(CONFIG_ATARI) && !defined(CONFIG_AMIGA) && !defined(CONFIG_MAC) /* block out HSYNC on the atari (falcon) */ __asm__("stop #0x2200" : : : "cc"); #else /* portable version */ __asm__("stop #0x2000" : : : "cc"); #endif /* machine compilation types */ + check_pgt_cache(); run_task_queue(&tq_scheduler); schedule(); } diff --git a/arch/m68k/kernel/ptrace.c b/arch/m68k/kernel/ptrace.c index 4a2a95f4b..da91d32de 100644 --- a/arch/m68k/kernel/ptrace.c +++ b/arch/m68k/kernel/ptrace.c @@ -347,9 +347,13 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) goto out; child->flags |= PF_PTRACED; if (child->p_pptr != current) { + unsigned long flags; + + write_lock_irqsave(&tasklist_lock, flags); REMOVE_LINKS(child); child->p_pptr = current; SET_LINKS(child); + write_unlock_irqrestore(&tasklist_lock, flags); } send_sig(SIGSTOP, child, 1); ret = 0; @@ -491,6 +495,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) } case PTRACE_DETACH: { /* detach a process that was attached. */ + unsigned long flags; long tmp; ret = -EIO; @@ -499,9 +504,11 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) child->flags &= ~(PF_PTRACED|PF_TRACESYS); wake_up_process(child); child->exit_code = data; + write_lock_irqsave(&tasklist_lock, flags); REMOVE_LINKS(child); child->p_pptr = child->p_opptr; SET_LINKS(child); + write_unlock_irqrestore(&tasklist_lock, flags); /* make sure the single step bit is not set. */ tmp = get_reg(child, PT_SR) & ~(TRACE_BITS << 16); put_reg(child, PT_SR, tmp); diff --git a/arch/m68k/kernel/setup.c b/arch/m68k/kernel/setup.c index fccf8b4cf..6df005fb4 100644 --- a/arch/m68k/kernel/setup.c +++ b/arch/m68k/kernel/setup.c @@ -61,6 +61,8 @@ void (*mach_sched_init) (void (*handler)(int, void *, struct pt_regs *)) __initd int (*mach_keyb_init) (void) __initdata; int (*mach_kbdrate) (struct kbd_repeat *) = NULL; void (*mach_kbd_leds) (unsigned int) = NULL; +/* machine dependent "kbd-reset" setup function */ +void (*kbd_reset_setup) (char *, int) __initdata = NULL; /* machine dependent irq functions */ void (*mach_init_IRQ) (void) __initdata; void (*(*mach_default_handler)[]) (int, void *, struct pt_regs *) = NULL; @@ -102,6 +104,8 @@ extern void config_mac(void); extern void config_sun3(void); extern void config_apollo(void); extern void config_mvme16x(void); +extern void config_bvme6000(void); +extern void config_hp300(void); #define MASK_256K 0xfffc0000 @@ -250,6 +254,16 @@ __initfunc(void setup_arch(char **cmdline_p, unsigned long * memory_start_p, config_mvme16x(); break; #endif +#ifdef CONFIG_BVME6000 + case MACH_BVME6000: + config_bvme6000(); + break; +#endif +#ifdef CONFIG_HP300 + case MACH_HP300: + config_hp300(); + break; +#endif default: panic ("No configuration setup"); } diff --git a/arch/m68k/kernel/signal.c b/arch/m68k/kernel/signal.c index 97809d382..03214a42f 100644 --- a/arch/m68k/kernel/signal.c +++ b/arch/m68k/kernel/signal.c @@ -147,6 +147,12 @@ sys_sigaction(int sig, const struct old_sigaction *act, return ret; } +asmlinkage int +sys_sigaltstack(const stack_t *uss, stack_t *uoss) +{ + return do_sigaltstack(uss, uoss, rdusp()); +} + /* * Do a signal return; undo the signal stack. @@ -177,31 +183,33 @@ struct rt_sigframe static unsigned char fpu_version = 0; /* version number of fpu, set by setup_frame */ -static inline void restore_fpu_state(struct sigcontext *sc) +static inline int restore_fpu_state(struct sigcontext *sc) { + int err = 1; + if (CPU_IS_060 ? sc->sc_fpstate[2] : sc->sc_fpstate[0]) { /* Verify the frame format. */ if (!CPU_IS_060 && (sc->sc_fpstate[0] != fpu_version)) - goto badframe; + goto out; if (CPU_IS_020_OR_030) { if (m68k_fputype & FPU_68881 && !(sc->sc_fpstate[1] == 0x18 || sc->sc_fpstate[1] == 0xb4)) - goto badframe; + goto out; if (m68k_fputype & FPU_68882 && !(sc->sc_fpstate[1] == 0x38 || sc->sc_fpstate[1] == 0xd4)) - goto badframe; + goto out; } else if (CPU_IS_040) { if (!(sc->sc_fpstate[1] == 0x00 || sc->sc_fpstate[1] == 0x28 || sc->sc_fpstate[1] == 0x60)) - goto badframe; + goto out; } else if (CPU_IS_060) { if (!(sc->sc_fpstate[3] == 0x00 || sc->sc_fpstate[3] == 0x60 || sc->sc_fpstate[3] == 0xe0)) - goto badframe; + goto out; } else - goto badframe; + goto out; __asm__ volatile (".chip 68k/68881\n\t" "fmovemx %0,%/fp0-%/fp1\n\t" @@ -213,10 +221,10 @@ static inline void restore_fpu_state(struct sigcontext *sc) __asm__ volatile (".chip 68k/68881\n\t" "frestore %0\n\t" ".chip 68k" : : "m" (*sc->sc_fpstate)); - return; + err = 0; -badframe: - do_exit(SIGSEGV); +out: + return err; } #define FPCONTEXT_SIZE 216 @@ -224,42 +232,43 @@ badframe: #define uc_formatvec uc_filler[FPCONTEXT_SIZE/4] #define uc_extra uc_filler[FPCONTEXT_SIZE/4+1] -static inline void rt_restore_fpu_state(struct ucontext *uc) +static inline int rt_restore_fpu_state(struct ucontext *uc) { unsigned char fpstate[FPCONTEXT_SIZE]; int context_size = CPU_IS_060 ? 8 : 0; fpregset_t fpregs; + int err = 1; if (__get_user(*(long *)fpstate, (long *)&uc->uc_fpstate)) - goto badframe; + goto out; if (CPU_IS_060 ? fpstate[2] : fpstate[0]) { if (!CPU_IS_060) context_size = fpstate[1]; /* Verify the frame format. */ if (!CPU_IS_060 && (fpstate[0] != fpu_version)) - goto badframe; + goto out; if (CPU_IS_020_OR_030) { if (m68k_fputype & FPU_68881 && !(context_size == 0x18 || context_size == 0xb4)) - goto badframe; + goto out; if (m68k_fputype & FPU_68882 && !(context_size == 0x38 || context_size == 0xd4)) - goto badframe; + goto out; } else if (CPU_IS_040) { if (!(context_size == 0x00 || context_size == 0x28 || context_size == 0x60)) - goto badframe; + goto out; } else if (CPU_IS_060) { if (!(fpstate[3] == 0x00 || fpstate[3] == 0x60 || fpstate[3] == 0xe0)) - goto badframe; + goto out; } else - goto badframe; + goto out; if (__copy_from_user(&fpregs, &uc->uc_mcontext.fpregs, sizeof(fpregs))) - goto badframe; + goto out; __asm__ volatile (".chip 68k/68881\n\t" "fmovemx %0,%/fp0-%/fp7\n\t" "fmoveml %1,%/fpcr/%/fpsr/%/fpiar\n\t" @@ -271,21 +280,23 @@ static inline void rt_restore_fpu_state(struct ucontext *uc) if (context_size && __copy_from_user(fpstate + 4, (long *)&uc->uc_fpstate + 1, context_size)) - goto badframe; + goto out; __asm__ volatile (".chip 68k/68881\n\t" "frestore %0\n\t" ".chip 68k" : : "m" (*fpstate)); - return; + err = 0; -badframe: - do_exit(SIGSEGV); +out: + return err; } static inline int -restore_sigcontext(struct pt_regs *regs, struct sigcontext *usc, void *fp) +restore_sigcontext(struct pt_regs *regs, struct sigcontext *usc, void *fp, + int *pd0) { int fsize, formatvec; struct sigcontext context; + int err; /* get previous context */ if (copy_from_user(&context, usc, sizeof(context))) @@ -303,7 +314,7 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext *usc, void *fp) regs->format = formatvec >> 12; regs->vector = formatvec & 0xfff; - restore_fpu_state(&context); + err = restore_fpu_state(&context); fsize = frame_extra_sizes[regs->format]; if (fsize < 0) { @@ -356,49 +367,55 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext *usc, void *fp) goto badframe; } - return context.sc_d0; + *pd0 = context.sc_d0; + return err; badframe: - do_exit(SIGSEGV); + return 1; } static inline int rt_restore_ucontext(struct pt_regs *regs, struct switch_stack *sw, - struct ucontext *uc) + struct ucontext *uc, int *pd0) { int fsize, temp; greg_t *gregs = uc->uc_mcontext.gregs; + unsigned long usp; + int err; - __get_user(temp, &uc->uc_mcontext.version); + err = __get_user(temp, &uc->uc_mcontext.version); if (temp != MCONTEXT_VERSION) goto badframe; /* restore passed registers */ - __get_user(regs->d0, &gregs[0]); - __get_user(regs->d1, &gregs[1]); - __get_user(regs->d2, &gregs[2]); - __get_user(regs->d3, &gregs[3]); - __get_user(regs->d4, &gregs[4]); - __get_user(regs->d5, &gregs[5]); - __get_user(sw->d6, &gregs[6]); - __get_user(sw->d7, &gregs[7]); - __get_user(regs->a0, &gregs[8]); - __get_user(regs->a1, &gregs[9]); - __get_user(regs->a2, &gregs[10]); - __get_user(sw->a3, &gregs[11]); - __get_user(sw->a4, &gregs[12]); - __get_user(sw->a5, &gregs[13]); - __get_user(sw->a6, &gregs[14]); - __get_user(temp, &gregs[15]); - wrusp(temp); - __get_user(regs->pc, &gregs[16]); - __get_user(temp, &gregs[17]); + err |= __get_user(regs->d0, &gregs[0]); + err |= __get_user(regs->d1, &gregs[1]); + err |= __get_user(regs->d2, &gregs[2]); + err |= __get_user(regs->d3, &gregs[3]); + err |= __get_user(regs->d4, &gregs[4]); + err |= __get_user(regs->d5, &gregs[5]); + err |= __get_user(sw->d6, &gregs[6]); + err |= __get_user(sw->d7, &gregs[7]); + err |= __get_user(regs->a0, &gregs[8]); + err |= __get_user(regs->a1, &gregs[9]); + err |= __get_user(regs->a2, &gregs[10]); + err |= __get_user(sw->a3, &gregs[11]); + err |= __get_user(sw->a4, &gregs[12]); + err |= __get_user(sw->a5, &gregs[13]); + err |= __get_user(sw->a6, &gregs[14]); + err |= __get_user(usp, &gregs[15]); + wrusp(usp); + err |= __get_user(regs->pc, &gregs[16]); + err |= __get_user(temp, &gregs[17]); regs->sr = (regs->sr & 0xff00) | (temp & 0xff); regs->orig_d0 = -1; /* disable syscall checks */ - __get_user(temp, &uc->uc_formatvec); + err |= __get_user(temp, &uc->uc_formatvec); regs->format = temp >> 12; regs->vector = temp & 0xfff; - rt_restore_fpu_state(uc); + err |= rt_restore_fpu_state(uc); + + if (do_sigaltstack(&uc->uc_stack, NULL, usp) == -EFAULT) + goto badframe; fsize = frame_extra_sizes[regs->format]; if (fsize < 0) { @@ -449,10 +466,11 @@ rt_restore_ucontext(struct pt_regs *regs, struct switch_stack *sw, goto badframe; } - return regs->d0; + *pd0 = regs->d0; + return err; badframe: - do_exit(SIGSEGV); + return 1; } asmlinkage int do_sigreturn(unsigned long __unused) @@ -462,6 +480,7 @@ asmlinkage int do_sigreturn(unsigned long __unused) unsigned long usp = rdusp(); struct sigframe *frame = (struct sigframe *)(usp - 24); sigset_t set; + int d0; if (verify_area(VERIFY_READ, frame, sizeof(*frame))) goto badframe; @@ -475,10 +494,13 @@ asmlinkage int do_sigreturn(unsigned long __unused) current->blocked = set; recalc_sigpending(current); - return restore_sigcontext(regs, &frame->sc, frame + 1); + if (restore_sigcontext(regs, &frame->sc, frame + 1, &d0)) + goto badframe; + return d0; badframe: - do_exit(SIGSEGV); + force_sig(SIGSEGV, current); + return 0; } asmlinkage int do_rt_sigreturn(unsigned long __unused) @@ -488,6 +510,7 @@ asmlinkage int do_rt_sigreturn(unsigned long __unused) unsigned long usp = rdusp(); struct rt_sigframe *frame = (struct rt_sigframe *)(usp - 4); sigset_t set; + int d0; if (verify_area(VERIFY_READ, frame, sizeof(*frame))) goto badframe; @@ -498,10 +521,13 @@ asmlinkage int do_rt_sigreturn(unsigned long __unused) current->blocked = set; recalc_sigpending(current); - return rt_restore_ucontext(regs, sw, &frame->uc); + if (rt_restore_ucontext(regs, sw, &frame->uc, &d0)) + goto badframe; + return d0; badframe: - do_exit(SIGSEGV); + force_sig(SIGSEGV, current); + return 0; } /* @@ -535,17 +561,18 @@ static inline void save_fpu_state(struct sigcontext *sc, struct pt_regs *regs) } } -static inline void rt_save_fpu_state(struct ucontext *uc, struct pt_regs *regs) +static inline int rt_save_fpu_state(struct ucontext *uc, struct pt_regs *regs) { unsigned char fpstate[FPCONTEXT_SIZE]; int context_size = CPU_IS_060 ? 8 : 0; + int err = 0; __asm__ volatile (".chip 68k/68881\n\t" "fsave %0\n\t" ".chip 68k" : : "m" (*fpstate) : "memory"); - __put_user(*(long *)fpstate, (long *)&uc->uc_fpstate); + err |= __put_user(*(long *)fpstate, (long *)&uc->uc_fpstate); if (CPU_IS_060 ? fpstate[2] : fpstate[0]) { fpregset_t fpregs; if (!CPU_IS_060) @@ -566,11 +593,13 @@ static inline void rt_save_fpu_state(struct ucontext *uc, struct pt_regs *regs) : "m" (*fpregs.f_fpregs), "m" (fpregs.f_pcr) : "memory"); - copy_to_user(&uc->uc_mcontext.fpregs, &fpregs, sizeof(fpregs)); + err |= copy_to_user(&uc->uc_mcontext.fpregs, &fpregs, + sizeof(fpregs)); } if (context_size) - copy_to_user((long *)&uc->uc_fpstate + 1, fpstate + 4, - context_size); + err |= copy_to_user((long *)&uc->uc_fpstate + 1, fpstate + 4, + context_size); + return err; } static void setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs, @@ -588,32 +617,34 @@ static void setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs, save_fpu_state(sc, regs); } -static inline void rt_setup_ucontext(struct ucontext *uc, struct pt_regs *regs) +static inline int rt_setup_ucontext(struct ucontext *uc, struct pt_regs *regs) { struct switch_stack *sw = (struct switch_stack *)regs - 1; greg_t *gregs = uc->uc_mcontext.gregs; - - __put_user(MCONTEXT_VERSION, &uc->uc_mcontext.version); - __put_user(regs->d0, &gregs[0]); - __put_user(regs->d1, &gregs[1]); - __put_user(regs->d2, &gregs[2]); - __put_user(regs->d3, &gregs[3]); - __put_user(regs->d4, &gregs[4]); - __put_user(regs->d5, &gregs[5]); - __put_user(sw->d6, &gregs[6]); - __put_user(sw->d7, &gregs[7]); - __put_user(regs->a0, &gregs[8]); - __put_user(regs->a1, &gregs[9]); - __put_user(regs->a2, &gregs[10]); - __put_user(sw->a3, &gregs[11]); - __put_user(sw->a4, &gregs[12]); - __put_user(sw->a5, &gregs[13]); - __put_user(sw->a6, &gregs[14]); - __put_user(rdusp(), &gregs[15]); - __put_user(regs->pc, &gregs[16]); - __put_user(regs->sr, &gregs[17]); - __put_user((regs->format << 12) | regs->vector, &uc->uc_formatvec); - rt_save_fpu_state(uc, regs); + int err = 0; + + err |= __put_user(MCONTEXT_VERSION, &uc->uc_mcontext.version); + err |= __put_user(regs->d0, &gregs[0]); + err |= __put_user(regs->d1, &gregs[1]); + err |= __put_user(regs->d2, &gregs[2]); + err |= __put_user(regs->d3, &gregs[3]); + err |= __put_user(regs->d4, &gregs[4]); + err |= __put_user(regs->d5, &gregs[5]); + err |= __put_user(sw->d6, &gregs[6]); + err |= __put_user(sw->d7, &gregs[7]); + err |= __put_user(regs->a0, &gregs[8]); + err |= __put_user(regs->a1, &gregs[9]); + err |= __put_user(regs->a2, &gregs[10]); + err |= __put_user(sw->a3, &gregs[11]); + err |= __put_user(sw->a4, &gregs[12]); + err |= __put_user(sw->a5, &gregs[13]); + err |= __put_user(sw->a6, &gregs[14]); + err |= __put_user(rdusp(), &gregs[15]); + err |= __put_user(regs->pc, &gregs[16]); + err |= __put_user(regs->sr, &gregs[17]); + err |= __put_user((regs->format << 12) | regs->vector, &uc->uc_formatvec); + err |= rt_save_fpu_state(uc, regs); + return err; } static inline void push_cache (unsigned long vaddr) @@ -708,60 +739,72 @@ static inline void push_cache (unsigned long vaddr) } } +static inline void * +get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size) +{ + unsigned long usp; + + /* Default to using normal stack. */ + usp = rdusp(); + + /* This is the X/Open sanctioned signal stack switching. */ + if (ka->sa.sa_flags & SA_ONSTACK) { + if (!on_sig_stack(usp)) + usp = current->sas_ss_sp + current->sas_ss_size; + } + return (void *)((usp - frame_size) & -8UL); +} + static void setup_frame (int sig, struct k_sigaction *ka, sigset_t *set, struct pt_regs *regs) { struct sigframe *frame; int fsize = frame_extra_sizes[regs->format]; struct sigcontext context; + int err = 0; if (fsize < 0) { #ifdef DEBUG printk ("setup_frame: Unknown frame format %#x\n", regs->format); #endif - goto segv_and_exit; + goto give_sigsegv; } - frame = (struct sigframe *)((rdusp() - sizeof(*frame) - fsize) & -8); - - if (!(current->flags & PF_ONSIGSTK) && (ka->sa.sa_flags & SA_ONSTACK)) { - frame = (struct sigframe *)(((unsigned long)ka->sa.sa_restorer - - sizeof(*frame) - fsize) & -8); - current->flags |= PF_ONSIGSTK; - } + frame = get_sigframe(ka, regs, sizeof(*frame) + fsize); if (fsize) { - if (copy_to_user (frame + 1, regs + 1, fsize)) - goto segv_and_exit; + err |= copy_to_user (frame + 1, regs + 1, fsize); regs->stkadj = fsize; } - __put_user((current->exec_domain - && current->exec_domain->signal_invmap - && sig < 32 - ? current->exec_domain->signal_invmap[sig] - : sig), - &frame->sig); + err |= __put_user((current->exec_domain + && current->exec_domain->signal_invmap + && sig < 32 + ? current->exec_domain->signal_invmap[sig] + : sig), + &frame->sig); - __put_user(regs->vector, &frame->code); - __put_user(&frame->sc, &frame->psc); + err |= __put_user(regs->vector, &frame->code); + err |= __put_user(&frame->sc, &frame->psc); if (_NSIG_WORDS > 1) - copy_to_user(frame->extramask, &set->sig[1], - sizeof(frame->extramask)); + err |= copy_to_user(frame->extramask, &set->sig[1], + sizeof(frame->extramask)); setup_sigcontext(&context, regs, set->sig[0]); - if (copy_to_user (&frame->sc, &context, sizeof(context))) - goto segv_and_exit; + err |= copy_to_user (&frame->sc, &context, sizeof(context)); /* Set up to return from userspace. */ - __put_user(frame->retcode, &frame->pretcode); + err |= __put_user(frame->retcode, &frame->pretcode); /* addaw #20,sp */ - __put_user(0xdefc0014, (long *)(frame->retcode + 0)); + err |= __put_user(0xdefc0014, (long *)(frame->retcode + 0)); /* moveq #,d0; trap #0 */ - __put_user(0x70004e40 + (__NR_sigreturn << 16), - (long *)(frame->retcode + 4)); + err |= __put_user(0x70004e40 + (__NR_sigreturn << 16), + (long *)(frame->retcode + 4)); + + if (err) + goto give_sigsegv; push_cache ((unsigned long) &frame->retcode); @@ -791,8 +834,10 @@ static void setup_frame (int sig, struct k_sigaction *ka, } return; -segv_and_exit: - do_exit(SIGSEGV); +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); } static void setup_rt_frame (int sig, struct k_sigaction *ka, siginfo_t *info, @@ -800,48 +845,53 @@ static void setup_rt_frame (int sig, struct k_sigaction *ka, siginfo_t *info, { struct rt_sigframe *frame; int fsize = frame_extra_sizes[regs->format]; + int err = 0; if (fsize < 0) { #ifdef DEBUG printk ("setup_frame: Unknown frame format %#x\n", regs->format); #endif - goto segv_and_exit; + goto give_sigsegv; } - frame = (struct rt_sigframe *)((rdusp() - sizeof(*frame)) & -8); - - /* XXX: Check here if we need to switch stacks.. */ + frame = get_sigframe(ka, regs, sizeof(*frame)); if (fsize) { - if (copy_to_user (&frame->uc.uc_extra, regs + 1, fsize)) - goto segv_and_exit; + err |= copy_to_user (&frame->uc.uc_extra, regs + 1, fsize); regs->stkadj = fsize; } - __put_user((current->exec_domain - && current->exec_domain->signal_invmap - && sig < 32 - ? current->exec_domain->signal_invmap[sig] - : sig), - &frame->sig); - __put_user(&frame->info, &frame->pinfo); - __put_user(&frame->uc, &frame->puc); - __copy_to_user(&frame->info, info, sizeof(*info)); - - /* Clear all the bits of the ucontext we don't use. */ - clear_user(&frame->uc, offsetof(struct ucontext, uc_mcontext)); - - rt_setup_ucontext(&frame->uc, regs); - if (copy_to_user (&frame->uc.uc_sigmask, set, sizeof(*set))) - goto segv_and_exit; + err |= __put_user((current->exec_domain + && current->exec_domain->signal_invmap + && sig < 32 + ? current->exec_domain->signal_invmap[sig] + : sig), + &frame->sig); + err |= __put_user(&frame->info, &frame->pinfo); + err |= __put_user(&frame->uc, &frame->puc); + err |= __copy_to_user(&frame->info, info, sizeof(*info)); + + /* Create the ucontext. */ + err |= __put_user(0, &frame->uc.uc_flags); + err |= __put_user(0, &frame->uc.uc_link); + err |= __put_user((void *)current->sas_ss_sp, + &frame->uc.uc_stack.ss_sp); + err |= __put_user(sas_ss_flags(rdusp()), + &frame->uc.uc_stack.ss_flags); + err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); + err |= rt_setup_ucontext(&frame->uc, regs); + err |= copy_to_user (&frame->uc.uc_sigmask, set, sizeof(*set)); /* Set up to return from userspace. */ - __put_user(frame->retcode, &frame->pretcode); + err |= __put_user(frame->retcode, &frame->pretcode); /* movel #,d0; trap #0 */ - __put_user(0x203c, (short *)(frame->retcode + 0)); - __put_user(__NR_rt_sigreturn, (long *)(frame->retcode + 2)); - __put_user(0x4e40, (short *)(frame->retcode + 6)); + err |= __put_user(0x203c, (short *)(frame->retcode + 0)); + err |= __put_user(__NR_rt_sigreturn, (long *)(frame->retcode + 2)); + err |= __put_user(0x4e40, (short *)(frame->retcode + 6)); + + if (err) + goto give_sigsegv; push_cache ((unsigned long) &frame->retcode); @@ -871,8 +921,34 @@ static void setup_rt_frame (int sig, struct k_sigaction *ka, siginfo_t *info, } return; -segv_and_exit: - do_exit(SIGSEGV); +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); +} + +static inline void +handle_restart(struct pt_regs *regs, struct k_sigaction *ka, int has_handler) +{ + switch (regs->d0) { + case -ERESTARTNOHAND: + if (!has_handler) + goto do_restart; + regs->d0 = -EINTR; + break; + + case -ERESTARTSYS: + if (has_handler && !(ka->sa.sa_flags & SA_RESTART)) { + regs->d0 = -EINTR; + break; + } + /* fallthrough */ + case -ERESTARTNOINTR: + do_restart: + regs->d0 = regs->orig_d0; + regs->pc -= 2; + break; + } } /* @@ -883,24 +959,9 @@ handle_signal(int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *oldset, struct pt_regs *regs) { /* are we from a system call? */ - if (regs->orig_d0 >= 0) { + if (regs->orig_d0 >= 0) /* If so, check system call restarting.. */ - switch (regs->d0) { - case -ERESTARTNOHAND: - regs->d0 = -EINTR; - break; - - case -ERESTARTSYS: - if (!(ka->sa.sa_flags & SA_RESTART)) { - regs->d0 = -EINTR; - break; - } - /* fallthrough */ - case -ERESTARTNOINTR: - regs->d0 = regs->orig_d0; - regs->pc -= 2; - } - } + handle_restart(regs, ka, 1); /* set up the stack frame */ if (ka->sa.sa_flags & SA_SIGINFO) @@ -952,13 +1013,14 @@ asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs) /* Did we come from a system call? */ if (regs->orig_d0 >= 0) { - /* Restart the system call */ - if (regs->d0 == -ERESTARTNOHAND || - regs->d0 == -ERESTARTSYS || - regs->d0 == -ERESTARTNOINTR) { - regs->d0 = regs->orig_d0; - regs->pc -= 2; - } + /* Restart the system call the same way as + if the process were not traced. */ + struct k_sigaction *ka = + ¤t->sig->action[signr-1]; + int has_handler = + (ka->sa.sa_handler != SIG_IGN && + ka->sa.sa_handler != SIG_DFL); + handle_restart(regs, ka, has_handler); } notify_parent(current, SIGCHLD); schedule(); @@ -1052,15 +1114,9 @@ asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs) } /* Did we come from a system call? */ - if (regs->orig_d0 >= 0) { + if (regs->orig_d0 >= 0) /* Restart the system call - no handlers present */ - if (regs->d0 == -ERESTARTNOHAND || - regs->d0 == -ERESTARTSYS || - regs->d0 == -ERESTARTNOINTR) { - regs->d0 = regs->orig_d0; - regs->pc -= 2; - } - } + handle_restart(regs, NULL, 0); /* If we are about to discard some frame stuff we must copy over the remaining frame. */ diff --git a/arch/m68k/kernel/sys_m68k.c b/arch/m68k/kernel/sys_m68k.c index 50d564134..75da52541 100644 --- a/arch/m68k/kernel/sys_m68k.c +++ b/arch/m68k/kernel/sys_m68k.c @@ -66,20 +66,25 @@ asmlinkage int old_mmap(struct mmap_arg_struct *arg) struct file * file = NULL; struct mmap_arg_struct a; - lock_kernel(); - error = -EFAULT; if (copy_from_user(&a, arg, sizeof(a))) - goto out; + return -EFAULT; + down(¤t->mm->mmap_sem); + lock_kernel(); if (!(a.flags & MAP_ANONYMOUS)) { error = -EBADF; - if (a.fd >= NR_OPEN || !(file = current->files->fd[a.fd])) + file = fget(a.fd); + if (!file) goto out; } a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + error = do_mmap(file, a.addr, a.len, a.prot, a.flags, a.offset); + if (file) + fput(file); out: unlock_kernel(); + up(¤t->mm->mmap_sem); return error; } diff --git a/arch/m68k/kernel/time.c b/arch/m68k/kernel/time.c index 51359f046..8f11a00d8 100644 --- a/arch/m68k/kernel/time.c +++ b/arch/m68k/kernel/time.c @@ -38,7 +38,7 @@ static inline void do_profile (unsigned long pc) ++prof_buffer[pc]; else /* - * Dont ignore out-of-bounds PC values silently, + * Don't ignore out-of-bounds PC values silently, * put them into the last histogram slot, so if * present, they will show up as a sharp peak. */ |