S: Finland
N: Jonathan Naylor
-E: jsn@cs.nott.ac.uk
+E: g4klx@g4klx.demon.co.uk
E: g4klx@amsat.org
+W: http://zone.pspt.fi/~jsn/
D: AX.25, NET/ROM and ROSE amateur radio protocol suites
D: CCITT X.25 PLP and LAPB.
S: 24 Castle View Drive
Kernel Support for miscellaneous (your favourite) Binary Formats v1.1
- ====================================================================
+ =====================================================================
This Kernel feature allows to invoke almost (for restrictions see below) every
program by simply typing it's name in the shell.
- enable Java(TM)-support (like binfmt_java):
echo ":Java:M::\xca\xfe\xba\xbe::/usr/local/bin/java:" > register
echo :Applet:M::\<\!--applet::/usr/local/bin/appletviewer: > register
+
- enable support for em86 (like binfmt_em86, for Alpha AXP only):
echo ":i386:M::\x7fELF\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03:\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfb\xff\xff:/bin/em86:" > register
echo ":i486:M::\x7fELF\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x06:\xff\xff\xff\xff\xff\xfe\xfe\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfb\xff\xff:/bin/em86:" > register
+
- enable support for packed DOS applications (pre-configured dosemu hdimages):
echo ":DEXE:M::\x0eDEX::/usr/bin/dosexec:" > register
+
- enable support for DOS/Windows executables (using mzloader and dosemu/wine):
echo ":DOSWin:M::MZ::/usr/sbin/mzloader:" > register
echo ":DOS:E::com::/usr/sbin/mzloader:" > register
-
+ echo ":DOS2:E::exe::/usr/sbin/mzloader:" > register
You can enable/disable binfmt_misc or one binary type by echoing 0 (to disable)
or 1 (to enable) to /proc/sys/fs/binfmt_misc/status or /proc/.../the_name.
HINTS:
======
-If your interpreter does not look at the PATH to determine the full name of the
-program, you need to invoke a wrapper-script (like the following for java) first:
+If you want to pass special arguments to your interpreter, you can
+write a wrapper script for it.
-#!/bin/sh
-FOO=`which $1` || exit 1
-shift
-/usr/local/bin/java $FOO ${1+$@}
+Your interpreter should NOT look in the PATH for the filename; the
+kernel passes it the full filename to use. Using the PATH can cause
+unexpected behaviour and be a security hazard.
There is a web page about binfmt_misc at
- Linux/m68k Amiga Bootstrap version 5.5
+ Linux/m68k Amiga Bootstrap version 5.6
--------------------------------------
Maintained by Geert Uytterhoeven (Geert.Uytterhoeven@cs.kuleuven.ac.be)
-Last revised: March 27, 1997
+Last revised: June 12, 1997
0. Introduction
first. Although the Installation Guide is getting a bit outdated, it's still a
good starting point.
-Amiboot 5.5 is meant for Linux/m68k 2.0.x, 2.1.x or higher (kernel bootinfo
+Amiboot 5.6 is meant for Linux/m68k 2.0.x, 2.1.x or higher (kernel bootinfo
interface versions 1.x and 2.x). Please use an older version for older kernels.
To use the amateur radio protocols within Linux you will need to get a
suitable copy of the AX.25 Utilities. More detailed information about these
-and associated programs can be found on http://www.cs.nott.ac.uk/~jsn/.
+and associated programs can be found on http://zone.pspt.fi/~jsn/.
For more information about the AX.25, NET/ROM and ROSE protocol stacks, see
the AX25-HOWTO written by Terry Dawson <terry@perf.no.itg.telstra.com.au>
Jonathan G4KLX
-jsn@cs.nott.ac.uk
+g4klx@g4klx.demon.co.uk
Jonathan
-jsn@cs.nott.ac.uk
g4klx@g4klx.demon.co.uk
# CONFIG_IP_ACCT is not set
# CONFIG_IP_ROUTER is not set
# CONFIG_NET_IPIP is not set
+# CONFIG_SYN_COOKIES is not set
#
# (it is safe to leave these untouched)
# Filesystems
#
# CONFIG_QUOTA is not set
+# CONFIG_DCACHE_PRELOAD is not set
+# CONFIG_OMIRR is not set
+# CONFIG_TRANS_NAMES is not set
# CONFIG_MINIX_FS is not set
CONFIG_EXT2_FS=y
-CONFIG_FAT_FS=y
-CONFIG_MSDOS_FS=y
+# CONFIG_FAT_FS is not set
+# CONFIG_MSDOS_FS is not set
# CONFIG_VFAT_FS is not set
# CONFIG_UMSDOS_FS is not set
CONFIG_PROC_FS=y
-CONFIG_NFS_FS=y
-# CONFIG_ROOT_NFS is not set
+# CONFIG_NFS_FS is not set
# CONFIG_NFSD is not set
-CONFIG_SUNRPC=y
-CONFIG_LOCKD=y
+# CONFIG_SUNRPC is not set
+# CONFIG_LOCKD is not set
# CONFIG_SMB_FS is not set
-CONFIG_ISO9660_FS=y
+# CONFIG_ISO9660_FS is not set
# CONFIG_HPFS_FS is not set
# CONFIG_SYSV_FS is not set
# CONFIG_AFFS_FS is not set
# Kernel hacking
#
# CONFIG_PROFILE is not set
+# CONFIG_MAGIC_SYSRQ is not set
retval = verify_area(VERIFY_WRITE, buffer, bufsiz);
if (retval)
goto out;
- retval = namei(NAM_FOLLOW_LINK, path, &inode);
+ retval = namei(path, &inode);
if (retval)
goto out;
retval = -ENOSYS;
struct file_operations *fops;
int retval;
- retval = namei(NAM_FOLLOW_LINK, name, &inode);
+ retval = namei(name, &inode);
if (retval)
return retval;
if (!S_ISBLK(inode->i_mode)) {
return -EOPNOTSUPP;
}
+/* Dummy functions for now */
+#define wrfpcr(x) do { } while (0)
+#define rdfpcr() 0
asmlinkage unsigned long osf_setsysinfo(unsigned long op, void *buffer,
unsigned long nbytes,
# CONFIG_DCACHE_PRELOAD is not set
# CONFIG_OMIRR is not set
# CONFIG_TRANS_NAMES is not set
-CONFIG_MINIX_FS=y
+# CONFIG_MINIX_FS is not set
CONFIG_EXT2_FS=y
-CONFIG_FAT_FS=y
-CONFIG_MSDOS_FS=y
-CONFIG_VFAT_FS=y
+# CONFIG_FAT_FS is not set
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
# CONFIG_UMSDOS_FS is not set
CONFIG_PROC_FS=y
CONFIG_NFS_FS=y
CONFIG_SUNRPC=y
CONFIG_LOCKD=y
# CONFIG_SMB_FS is not set
-CONFIG_ISO9660_FS=y
+# CONFIG_ISO9660_FS is not set
# CONFIG_HPFS_FS is not set
# CONFIG_SYSV_FS is not set
# CONFIG_AFFS_FS is not set
math_error();
}
-static struct irqaction irq13 = { math_error_irq, 0, 0, "math error", NULL, NULL };
+static struct irqaction irq13 = { math_error_irq, 0, 0, "fpu", NULL, NULL };
/*
* IRQ2 is cascade interrupt to second interrupt controller
request_region(0x40,0x20,"timer");
request_region(0x80,0x10,"dma page reg");
request_region(0xc0,0x20,"dma2");
- request_region(0xf0,0x10,"npu");
+ request_region(0xf0,0x10,"fpu");
}
static const char * i486model(unsigned int nr)
*/
extern unsigned short ami_intena_vals[];
-extern void amiga_init_sound(void);
/*
* Support for Graphics Boards
int err, tag, i;
u_long chipptr;
- /*
- * Our beloved beeper
- */
- amiga_init_sound();
-
/*
* Check for a Graphics Board
*/
static void amiga_debug_init(void);
extern void amiga_video_setup(char *, int *);
+extern void amiga_init_sound(void);
static struct console amiga_console_driver = {
NULL, NULL, amiga_wait_key
__initfunc(static void amiga_debug_init(void))
{
- if (!strcmp( m68k_debug_device, "ser" )) {
- /* no initialization required (?) */
- amiga_console_driver.write = amiga_serial_console_write;
- } else if (!strcmp( m68k_debug_device, "mem" )) {
- amiga_savekmsg_init();
- amiga_console_driver.write = amiga_mem_console_write;
- }
- register_console(&amiga_console_driver);
+ if (!strcmp( m68k_debug_device, "ser" )) {
+ /* no initialization required (?) */
+ amiga_console_driver.write = amiga_serial_console_write;
+ } else if (!strcmp( m68k_debug_device, "mem" )) {
+ amiga_savekmsg_init();
+ amiga_console_driver.write = amiga_mem_console_write;
+ }
+ register_console(&amiga_console_driver);
+
+ /* our beloved beeper */
+ if (AMIGAHW_PRESENT(AMI_AUDIO))
+ amiga_init_sound();
}
* Rectangle Fill Solid
*/
void Cyber_RectFill (u_short x, u_short y, u_short width, u_short height,
- u_short mode, u_short color)
+ u_short mode, u_short fcolor)
{
u_short blitcmd = S3_FILLEDRECT;
* break_flag...
* */
int keyval = plain_map[scancode], keytyp;
-
+
set_bit( scancode, broken_keys );
self_test_last_rcv = jiffies;
keyval = plain_map[scancode];
#include <stdarg.h>
#include <string.h>
#include <sys/file.h>
-#include <sys/types.h>
#include <unistd.h>
/* required Linux/m68k include files */
+#define __KERNEL_STRICT_NAMES /* This is ugly, I know */
+#define _LINUX_POSIX_TYPES_H
+#include <asm/posix_types.h>
#include <linux/a.out.h>
#include <linux/elf.h>
#include <asm/amigahw.h>
* for more details.
*
* History:
+ * 11 Jun 1997 Fix for unpadded gzipped ramdisks with bootinfo interface
+ * version 1.0
* 27 Mar 1997 FPU-less machines couldn't boot kernels that use bootinfo
* interface version 1.0 (Geert)
- * 03 Feb 1997 Implemented kernel decompression (Geert, based on Roman's
+ * 3 Feb 1997 Implemented kernel decompression (Geert, based on Roman's
* code for ataboot)
* 30 Dec 1996 Reverted the CPU detection to the old scheme
* New boot parameter override scheme (Geert)
#include <stddef.h>
#include <string.h>
#include <errno.h>
-#include <sys/types.h>
#include <linux/a.out.h>
#include <linux/elf.h>
#undef custom
#define custom ((*(volatile struct CUSTOM *)(CUSTOM_PHYSADDR)))
+/* a.out linkage conventions */
+#undef SYMBOL_NAME_STR
+#define SYMBOL_NAME_STR(X) "_"#X
+
/* temporary stack size */
#define TEMP_STACKSIZE (256)
static int add_bi_string(u_short tag, const u_char *s);
static int check_bootinfo_version(const char *memptr);
static void start_kernel(void (*startfunc)(), char *stackp, char *memptr,
- u_long start_mem, u_long mem_size, u_long rd_size,
- u_long kernel_size) __attribute__ ((noreturn));
+ u_long start_mem, u_long kernel_size, u_long rd_dest,
+ u_long rd_size) __attribute__ ((noreturn));
asmlinkage u_long maprommed(void);
-asmlinkage u_long check346(void);
#ifdef ZKERNEL
static int load_zkernel(int fd);
static int KRead(int fd, void *buf, int cnt);
if (debugflag) {
if (bi.ramdisk.size)
Printf("RAM disk at 0x%08lx, size is %ldK\n",
- (u_long)memptr+kernel_size, bi.ramdisk.size>>10);
+ (u_long)memptr+kernel_size+bi_size, bi.ramdisk.size>>10);
if (elf_kernel) {
PutChar('\n');
Printf("\nKernel entry is 0x%08lx\n", elf_kernel ? kexec_elf.e_entry :
kexec.a_entry);
- Printf("ramdisk dest top is 0x%08lx\n", start_mem+mem_size);
+ Printf("ramdisk dest is 0x%08lx\n", bi.ramdisk.addr);
Printf("ramdisk lower limit is 0x%08lx\n",
- (u_long)(memptr+kernel_size));
+ (u_long)memptr+kernel_size+bi_size);
Printf("ramdisk src top is 0x%08lx\n",
- (u_long)(memptr+kernel_size)+rd_size);
+ (u_long)memptr+kernel_size+bi_size+rd_size);
Puts("\nType a key to continue the Linux/m68k boot...");
GetChar();
/* execute the copy-and-go code (from CHIP RAM) */
start_kernel(startfunc, (char *)stack+TEMP_STACKSIZE, memptr, start_mem,
- mem_size, rd_size, kernel_size);
+ kernel_size, bi.ramdisk.addr, rd_size);
/* Clean up and exit in case of a failure */
Fail:
compat_bootinfo.memory[i].size = bi.memory[i].size;
}
if (bi.ramdisk.size) {
+ bi.ramdisk.addr &= 0xfffffc00;
compat_bootinfo.ramdisk_size = (bi.ramdisk.size+1023)/1024;
compat_bootinfo.ramdisk_addr = bi.ramdisk.addr;
} else {
*/
static void start_kernel(void (*startfunc)(), char *stackp, char *memptr,
- u_long start_mem, u_long mem_size, u_long rd_size,
- u_long kernel_size)
+ u_long start_mem, u_long kernel_size, u_long rd_dest,
+ u_long rd_size)
{
register void (*a0)() __asm("a0") = startfunc;
register char *a2 __asm("a2") = stackp;
register char *a3 __asm("a3") = memptr;
register u_long a4 __asm("a4") = start_mem;
- register u_long d0 __asm("d0") = mem_size;
+ register u_long d0 __asm("d0") = rd_dest;
register u_long d1 __asm("d1") = rd_size;
register u_long d2 __asm("d2") = kernel_size;
register u_long d3 __asm("d3") = bi_size;
*
* a3 = memptr
* a4 = start_mem
- * d0 = mem_size
+ * d0 = rd_dest
* d1 = rd_size
* d2 = kernel_size
* d3 = bi_size
dbra d7,1b | *dest++ = *src++
| /* copy the ramdisk to the top of memory */
- | /* (from back to front) */
- movel a4,a1 | dest = (u_long *)(start_mem+mem_size);
- addl d0,a1
- movel a3,a2 | limit = (u_long *)(memptr+kernel_size +
- addl d2,a2 | bi_size);
- addl d3,a2
- movel a2,a0 | src = (u_long *)((u_long)limit+rd_size);
- addl d1,a0
+ movel a3,a0 | src = (u_long *)(memptr+kernel_size+bi_size);
+ addl d2,a0
+ addl d3,a0
+ movel d0,a1 | dest = (u_long *)rd_dest;
+ movel a0,a2 | limit = (u_long *)(memptr+kernel_size+
+ addl d1,a2 | bi_size+rd_size);
1: cmpl a0,a2
- beqs 2f | while (src > limit)
- moveb a0@-,a1@- | *--dest = *--src;
- bras 1b
+ jeq 2f | while (src > limit)
+ moveb a0@+,a1@+ | *dest++ = *src++;
+ jra 1b
2:
| /* jump to start of kernel */
movel a4,a0 | jump_to (start_mem);
bool 'Sysctl support' CONFIG_SYSCTL
tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT
tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC
+fi
+
if [ "$CONFIG_AMIGA" = "y" ]; then
bool 'Amiga AutoConfig Identification' CONFIG_ZORRO
bool 'Amiga OCS chipset support' CONFIG_AMIFB_OCS
bool 'A4000T SCSI support' CONFIG_A4000T_SCSI
bool 'A4091 SCSI support' CONFIG_A4091_SCSI
bool 'WarpEngine SCSI support' CONFIG_WARPENGINE_SCSI
+ bool 'GVP Turbo 040/060 SCSI support' CONFIG_GVP_TURBO_SCSI
fi
fi
if [ "$CONFIG_ATARI" = "y" ]; then
if [ "$CONFIG_PROFILE" = "y" ]; then
int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
fi
+bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ
bool 'Remote debugging support' CONFIG_KGDB
endmenu
#undef CONFIG_FBCON_CYBER
#undef CONFIG_FBCON_RETINAZ3
-
/* Monochrome is default */
#define CONFIG_FBCON_MONO
int count, int yy, int xx);
static void rev_char_cyber(struct display *p, int xx, int yy);
-extern void Cyber_WaitQueue(u_short fifo);
+extern void Cyber_WaitQueue(unsigned short fifo);
extern void Cyber_WaitBlit(void);
-extern void Cyber_BitBLT(u_short curx, u_short cury, u_short destx,
- u_short desty, u_short width, u_short height,
- u_short mode);
-extern void Cyber_RectFill(u_short xx, u_short yy, u_short width, u_short height,
- u_short mode, u_short color);
-extern void Cyber_MoveCursor(u_short xx, u_short yy);
+extern void Cyber_BitBLT(unsigned short curx, unsigned short cury,
+ unsigned short destx, unsigned short desty,
+ unsigned short width, unsigned short height,
+ unsigned short mode);
+extern void Cyber_RectFill(unsigned short xx, unsigned short yy,
+ unsigned short width, unsigned short
+ height, unsigned short mode,
+ unsigned short fcolor);
+extern void Cyber_MoveCursor(unsigned short xx, unsigned short yy);
#endif /* CONFIG_FBCON_CYBER */
#ifdef CONFIG_FBCON_RETINAZ3
c &= 0xff;
- dest = p->screen_base+y*p->fontheight*p->next_line+8*x;
+ dest = p->screen_base + yy * p->fontheight * p->next_line + 8 * xx;
cdat = p->fontdata+(c*p->fontheight);
fg = disp->fgcol;
bg = disp->bgcol;
u_char c, d;
u_char fg, bg;
- dest0 = p->screen_base+y*p->fontheight*p->next_line+8*x;
+ dest0 = p->screen_base + yy * p->fontheight * p->next_line + 8 * xx;
fg = disp->fgcol;
bg = disp->bgcol;
revs = conp->vc_reverse;
fg = disp->fgcol;
bg = disp->bgcol;
- dest = p->screen_base+y*p->fontheight*p->next_line+8*x;
+ dest = p->screen_base + yy * p->fontheight * p->next_line + 8 * xx;
Cyber_WaitBlit();
for (rows = p->fontheight; rows--; dest += p->next_line) {
*dest = (*dest == fg) ? bg : fg;
# Filesystems
#
# CONFIG_QUOTA is not set
+# CONFIG_DCACHE_PRELOAD is not set
+# CONFIG_OMIRR is not set
+# CONFIG_TRANS_NAMES is not set
CONFIG_MINIX_FS=y
CONFIG_EXT2_FS=y
CONFIG_FAT_FS=y
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
+#include <linux/console.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
void poke_blanked_console(void);
void do_blank_screen(int);
+#if 0
+/* Make sure there are no references left to this variables. */
unsigned long video_num_lines;
unsigned long video_num_columns;
unsigned long video_size_row;
+#endif
static int printable = 0; /* Is console ready for printing? */
unsigned long video_font_height; /* Height of current screen font */
int tmp;
/*
- * cwe need special treatment for the first page, in case it
- * is not page-aligned.
+ * We need special treatment for the first page, in case it
+ * is not page-aligned. Page align the addresses to work
+ * around bug I17 in the 68060.
*/
if ((tmp = -paddr & (PAGE_SIZE - 1))) {
- pushcl040(paddr);
+ pushcl040(paddr & PAGE_MASK);
if ((len -= tmp) <= 0)
return;
paddr += tmp;
}
tmp = PAGE_SIZE;
+ paddr &= PAGE_MASK;
while ((len -= tmp) >= 0) {
clear040(paddr);
paddr += tmp;
* the '060!
*/
len += paddr & (PAGE_SIZE - 1);
+
+ /*
+ * Work around bug I17 in the 68060 affecting some instruction
+ * lines not being invalidated properly.
+ */
+ paddr &= PAGE_MASK;
+
do {
pushcli040(paddr);
paddr += tmp;
/* on 68040, push cache lines for pages in the range */
len += vaddr & (PAGE_SIZE - 1);
+
+ /*
+ * Work around bug I17 in the 68060 affecting some instruction
+ * lines not being invalidated properly.
+ */
+ vaddr &= PAGE_MASK;
+
do {
pushv040(vaddr);
vaddr += tmp;
#
-# $Id: Makefile,v 1.5 1997/06/16 00:34:01 ralf Exp $
-#
# arch/mips/Makefile
#
# This file is included by the global makefile so that you can add your own
# Copyright (C) 1994, 1995, 1996 by Ralf Baechle
# DECStation modifications by Paul M. Antoine, 1996
#
+# $Id: Makefile,v 1.7 1997/06/30 15:52:03 ralf Exp $
+#
+#
+# Select the object file format to substitute into the linker script.
+#
ifdef CONFIG_CPU_LITTLE_ENDIAN
-CROSS_COMPILE = mipsel-
+CROSS_COMPILE = mipsel-linux-
ifdef CONFIG_MIPS_ECOFF
-oformat = ecoff-littlemips
+oformat = ecoff-littlemips
else
-oformat = a.out-mips-big-linux
+oformat = elf32-littlemips
endif
else
-CROSS_COMPILE = mips-
+CROSS_COMPILE = mips-linux-
ifdef CONFIG_MIPS_ECOFF
-oformat = ecoff-bigmips
+oformat = ecoff-bigmips
else
-oformat = a.out-mips-big-linux
-endif
+oformat = elf32-bigmips
endif
-
-ifdef CONFIG_EXTRA_ELF_COMPILER
-CROSS_COMPILE := $(CROSS_COMPILE)linuxelf-
-else
-CROSS_COMPILE := $(CROSS_COMPILE)linux-
endif
LINKFLAGS = -static -N
SUBDIRS += arch/mips/algor
#LOADADDR += 0x80000000
endif
-ifdef CONFIG_ACER_PICA_61
-CORE_FILES += arch/mips/jazz/jazz.o
-SUBDIRS += arch/mips/jazz
-LOADADDR += 0x80000000
-endif
ifdef CONFIG_DECSTATION
CORE_FILES += arch/mips/dec/dec.o
SUBDIRS += arch/mips/dec
SUBDIRS += arch/mips/deskstation
LOADADDR += 0x80000000
endif
-ifdef CONFIG_MIPS_MAGNUM_3000
-LOADADDR += 0x80000000
-endif
-ifdef CONFIG_MIPS_MAGNUM_4000
+#
+# Acer PICA 61, Mips Magnum 4000 and Olivetti M700.
+#
+ifdef CONFIG_MIPS_JAZZ
CORE_FILES += arch/mips/jazz/jazz.o
SUBDIRS += arch/mips/jazz
LOADADDR += 0x80000000
endif
-ifdef CONFIG_OLIVETTI_M700
-CORE_FILES += arch/mips/jazz/jazz.o
-SUBDIRS += arch/mips/jazz
+ifdef CONFIG_MIPS_MAGNUM_3000
LOADADDR += 0x80000000
endif
ifdef CONFIG_SNI_RM200_PCI
# Filesystems
#
# CONFIG_QUOTA is not set
+# CONFIG_DCACHE_PRELOAD is not set
+# CONFIG_OMIRR is not set
+# CONFIG_TRANS_NAMES is not set
# CONFIG_MINIX_FS is not set
CONFIG_EXT2_FS=y
# CONFIG_FAT_FS is not set
#include <linux/major.h>
#include <linux/mm.h>
#include <linux/ioport.h>
+#include <linux/kbd_kern.h>
+#include <linux/vt_kern.h>
+#include <linux/consolemap.h>
+#include <linux/selection.h>
+#include <linux/console_struct.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/bootinfo.h>
#include <asm/types.h>
-#include "../../../drivers/char/kbd_kern.h"
-#include "../../../drivers/char/vt_kern.h"
-#include "../../../drivers/char/consolemap.h"
-#include "../../../drivers/char/selection.h"
-#include "../../../drivers/char/console_struct.h"
-
extern void register_console(void (*proc)(const char *));
extern void console_print(const char *);
unsigned video_res_x;
* Send complaints, suggestions etc. to <andy@waldorf-gmbh.de>
*
* Copyright (C) 1995 Andreas Busse
+ *
+ * $Id: gdb-stub.c,v 1.4 1997/06/30 15:52:25 ralf Exp $
*/
/*
unsigned long flags;
unsigned char c;
- save_flags(flags); cli();
+ save_and_cli(flags);
for (ht = hard_trap_info; ht->tt && ht->signo; ht++)
set_except_vector(ht->tt, trap_low);
jal prom_init /* prom_init(argc, argv, envp); */
nop
+ jal sgi_sysinit
+ nop
#endif
/* Get the very one tags we need early in the boot process */
nop
mfc0 t0, CP0_STATUS
li t1, ~(ST0_CU1|ST0_CU2|ST0_CU3|ST0_KX|ST0_SX)
and t0, t1
- li t1, ST0_CU0
or t0, ST0_CU0
mtc0 t0, CP0_STATUS
goto losing;
old_fs = get_fs(); set_fs(get_ds());
- retval = namei(*name, interpreter_inode);
+ retval = namei(NAM_FOLLOW_LINK, *name, interpreter_inode);
set_fs(old_fs);
if(retval < 0)
goto losing;
*/
static int dump_write(struct file *file, const void *addr, int nr)
{
+ file->f_inode->i_status |= ST_MODIFIED;
return file->f_op->write(file->f_inode, file, addr, nr) == nr;
}
* should be easier.
*
* Mips support by Ralf Baechle and Andreas Busse
+ *
+ * $Id: irq.c,v 1.6 1997/06/30 15:52:34 ralf Exp $
*/
#include <linux/config.h>
#include <linux/errno.h>
{
unsigned long flags;
- save_flags(flags);
- cli();
+ save_and_cli(flags);
mask_irq(irq_nr);
restore_flags(flags);
}
void enable_irq(unsigned int irq_nr)
{
unsigned long flags;
- save_flags(flags);
- cli();
+ save_and_cli(flags);
unmask_irq(irq_nr);
restore_flags(flags);
}
if (new->flags & SA_SAMPLE_RANDOM)
rand_initialize_irq(irq);
- save_flags(flags);
- cli();
+ save_and_cli(flags);
*p = new;
if (!shared) {
continue;
/* Found it - now free it */
- save_flags(flags);
- cli();
+ save_and_cli(flags);
*p = action->next;
if (!irq[irq_action]) {
mask_irq(irq);
-/* $Id: r2300_fpu.S,v 1.1 1997/06/06 09:32:55 ralf Exp $
+/*
* r2300_fpu.S: Save/restore floating point context for signal handlers.
*
* This file is subject to the terms and conditions of the GNU General Public
*
* Multi-arch abstraction and asm macros for easier reading:
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ *
+ * $Id: r2300_fpu.S,v 1.2 1997/06/25 16:57:15 ralf Exp $
*/
#include <asm/asm.h>
#include <asm/fpregdef.h>
#include <asm/mipsregs.h>
+#include <asm/offset.h>
#include <asm/regdef.h>
-#include <asm/sigcontext.h>
.set mips3
.set noreorder
-/* $Id: r4k_fpu.S,v 1.1 1997/06/06 09:33:04 ralf Exp $
+/*
* r4k_fpu.S: Save/restore floating point context for signal handlers.
*
* This file is subject to the terms and conditions of the GNU General Public
*
* Multi-arch abstraction and asm macros for easier reading:
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ *
+ * $Id: r4k_fpu.S,v 1.2 1997/06/25 16:57:18 ralf Exp $
*/
#include <asm/asm.h>
#include <asm/fpregdef.h>
#include <asm/mipsregs.h>
+#include <asm/offset.h>
#include <asm/regdef.h>
-#include <asm/sigcontext.h>
.set noreorder
.set mips3
-/* $Id: r6000_fpu.S,v 1.1 1997/06/06 09:33:14 ralf Exp $
+/*
* r6000_fpu.S: Save/restore floating point context for signal handlers.
*
* This file is subject to the terms and conditions of the GNU General Public
*
* Multi-arch abstraction and asm macros for easier reading:
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ *
+ * $Id: r6000_fpu.S,v 1.2 1997/06/25 16:57:19 ralf Exp $
*/
#include <asm/asm.h>
#include <asm/fpregdef.h>
#include <asm/mipsregs.h>
+#include <asm/offset.h>
#include <asm/regdef.h>
-#include <asm/sigcontext.h>
.set noreorder
/* Save floating point context */
*
* Copyright (C) 1991, 1992 Linus Torvalds
* Copyright (C) 1994, 1995, 1996 Ralf Baechle
+ *
+ * $Id: signal.c,v 1.7 1997/06/25 19:25:08 ralf Exp $
*/
#include <linux/config.h>
#include <linux/sched.h>
(regs->regs[29] & (SZREG - 1)))
goto badframe;
- current->blocked = context->sc_sigset & _BLOCKABLE;
- regs->cp0_epc = context->sc_pc;
+ current->blocked = context->sc_sigset & _BLOCKABLE; /* XXX */
+ regs->cp0_epc = context->sc_pc; /* XXX */
+/*
+ * Disabled because we only use the lower 32 bit of the registers.
+ */
+#if 0
/*
* We only allow user processes in 64bit mode (n32, 64 bit ABI) to
* restore the upper half of registers.
*/
- if (read_32bit_cp0_register(CP0_STATUS) & ST0_UX)
+ if (read_32bit_cp0_register(CP0_STATUS) & ST0_UX) {
for(i = 31;i >= 0;i--)
__get_user(regs->regs[i], &context->sc_regs[i]);
- else
+ __get_user(regs->hi, &context->sc_mdhi);
+ __get_user(regs->lo, &context->sc_mdlo);
+ } else
+#endif
+ {
+ long long reg;
for(i = 31;i >= 0;i--) {
- __get_user(regs->regs[i], &context->sc_regs[i]);
- regs->regs[i] = (int) regs->regs[i];
+ __get_user(reg, &context->sc_regs[i]);
+ regs->regs[i] = (int) reg;
}
+ __get_user(reg, &context->sc_mdhi);
+ regs->hi = (int) reg;
+ __get_user(reg, &context->sc_mdlo);
+ regs->lo = (int) reg;
+ }
- __get_user(regs->hi, &context->sc_mdhi);
- __get_user(regs->lo, &context->sc_mdlo);
restore_fp_context(context);
/*
* for more details.
*
* Copyright (C) 1995, 1996 by Ralf Baechle
+ *
+ * $Id: syscalls.h,v 1.5 1997/06/25 17:08:35 ralf Exp $
*/
/*
SYS(sys_munlock, 2) /* 4155 */
SYS(sys_mlockall, 1)
SYS(sys_munlockall, 0)
-SYS(sys_nfsservctl, 3)
SYS(sys_sched_setparam,2)
SYS(sys_sched_getparam,2)
SYS(sys_sched_setscheduler,3) /* 4160 */
-/* $Id: sysirix.c,v 1.1 1997/06/06 09:33:25 ralf Exp $
+/* $Id: sysirix.c,v 1.2 1997/06/17 15:24:26 ralf Exp $
* sysirix.c: IRIX system call emulation.
*
* Copyright (C) 1996 David S. Miller
error = verify_area(VERIFY_WRITE, buf, sizeof(struct irix_statfs));
if (error)
goto out;
- error = namei(path,&inode);
+ error = namei(NAM_FOLLOW_LINK, path, &inode);
if (error)
goto out;
if (!inode->i_sb->s_op->statfs) {
error = verify_area(VERIFY_WRITE, buf, sizeof(struct irix_statvfs));
if(error)
goto out;
- error = namei(fname, &inode);
+ error = namei(NAM_FOLLOW_LINK, fname, &inode);
if(error)
goto out;
if(!inode->i_sb->s_op->statfs) {
return error;
}
-#define NOFOLLOW_LINKS 0
-#define FOLLOW_LINKS 1
+#define NOFOLLOW_LINKS NAM_FOLLOW_TRAILSLASH
+#define FOLLOW_LINKS NAM_FOLLOW_LINK
static inline int chown_common(char *filename, uid_t user, gid_t group, int follow)
{
int error;
struct iattr newattrs;
- if(follow == NOFOLLOW_LINKS)
- error = lnamei(filename,&inode);
- else
- error = namei(filename,&inode);
+ error = namei(follow, filename,&inode);
if (error)
return error;
if (IS_RDONLY(inode)) {
error = verify_area(VERIFY_WRITE, buf, sizeof(struct irix_statvfs));
if(error)
goto out;
- error = namei(fname, &inode);
+ error = namei(NAM_FOLLOW_LINK, fname, &inode);
if(error)
goto out;
if(!inode->i_sb->s_op->statfs) {
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
- * Copyright (C) 1995 by Ralf Baechle
+ * Copyright (C) 1995, 1996, 1997 by Ralf Baechle
+ *
+ * $Id: sysmips.c,v 1.4 1997/06/30 15:52:37 ralf Exp $
*/
#include <linux/errno.h>
#include <linux/linkage.h>
retval = verify_area(VERIFY_WRITE, p, sizeof(*p));
if (retval)
goto out;
- save_flags(flags);
- cli();
+ save_and_cli(flags);
retval = *p;
*p = arg2;
restore_flags(flags);
*
* This file contains the time handling details for PC-style clocks as
* found in some MIPS systems.
+ *
+ * $Id: time.c,v 1.4 1997/06/30 15:52:40 ralf Exp $
*/
#include <linux/errno.h>
#include <linux/init.h>
{
unsigned long flags;
- save_flags(flags);
- cli();
+ save_and_cli(flags);
*tv = xtime;
tv->tv_usec += do_gettimeoffset();
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
+ *
+ * $Id: checksum.c,v 1.4 1997/07/03 09:43:16 ralf Exp $
*/
#include <net/checksum.h>
+#include <linux/types.h>
+#include <asm/byteorder.h>
#include <asm/string.h>
#include <asm/uaccess.h>
goto out;
odd = 1 & (unsigned long) buff;
if (odd) {
- result = *buff;
+ result = be16_to_cpu(*buff);
len--;
buff++;
}
}
}
if (len & 1)
- result += (*buff << 8);
+ result += le16_to_cpu(*buff);
result = from32to16(result);
if (odd)
result = ((result >> 8) & 0xff) | ((result & 0xff) << 8);
-/* $Id: r2300.c,v 1.1 1997/06/06 09:35:14 ralf Exp $
+/*
* r2300.c: R2000 and R3000 specific mmu/cache code.
*
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ *
+ * $Id: r2300.c,v 1.2 1997/06/30 15:52:51 ralf Exp $
*/
#include <linux/kernel.h>
unsigned long flags;
int entry;
- save_flags(flags); cli();
+ save_and_cli(flags);
write_32bit_cp0_register(CP0_ENTRYLO0, 0);
for(entry = 0; entry < mips_tlb_entries; entry++) {
write_32bit_cp0_register(CP0_INDEX, entry);
{
unsigned long flags;
- save_flags(flags); cli();
+ save_and_cli(flags);
write_32bit_cp0_register(CP0_ENTRYHI, TLB_ROOT);
write_32bit_cp0_register(CP0_INDEX, 0);
write_32bit_cp0_register(CP0_ENTRYLO0, ((pg_dir >> 6) | 0x00e0));
* r4xx0.c: R4000 processor variant specific MMU/Cache routines.
*
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ *
+ * $Id: r4xx0.c,v 1.4 1997/06/30 15:52:53 ralf Exp $
*/
#include <linux/config.h>
}
+/*
+ * This flavour of r4k_clear_page is for the R4600 V1.x. Cite from the
+ * IDT R4600 V1.7 errata:
+ *
+ * 18. The CACHE instructions Hit_Writeback_Invalidate_D, Hit_Writeback_D,
+ * Hit_Invalidate_D and Create_Dirty_Exclusive_D should only be
+ * executed if there is no other dcache activity. If the dcache is
+ * accessed for another instruction immeidately preceding when these
+ * cache instructions are executing, it is possible that the dcache
+ * tag match outputs used by these cache instructions will be
+ * incorrect. These cache instructions should be preceded by at least
+ * four instructions that are not any kind of load or store
+ * instruction.
+ *
+ * This is not allowed: lw
+ * nop
+ * nop
+ * nop
+ * cache Hit_Writeback_Invalidate_D
+ *
+ * This is allowed: lw
+ * nop
+ * nop
+ * nop
+ * nop
+ * cache Hit_Writeback_Invalidate_D
+ */
+static void r4k_clear_page_r4600_v1(unsigned long page)
+{
+ __asm__ __volatile__(
+ ".set\tnoreorder\n\t"
+ ".set\tnoat\n\t"
+ ".set\tmips3\n\t"
+ "daddiu\t$1,%0,%2\n"
+ "1:\tnop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "cache\t%3,(%0)\n\t"
+ "sd\t$0,(%0)\n\t"
+ "sd\t$0,8(%0)\n\t"
+ "sd\t$0,16(%0)\n\t"
+ "sd\t$0,24(%0)\n\t"
+ "daddiu\t%0,64\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "cache\t%3,-32(%0)\n\t"
+ "sd\t$0,-32(%0)\n\t"
+ "sd\t$0,-24(%0)\n\t"
+ "sd\t$0,-16(%0)\n\t"
+ "bne\t$1,%0,1b\n\t"
+ "sd\t$0,-8(%0)\n\t"
+ ".set\tmips0\n\t"
+ ".set\tat\n\t"
+ ".set\treorder"
+ :"=r" (page)
+ :"0" (page),
+ "I" (PAGE_SIZE),
+ "i" (Create_Dirty_Excl_D)
+ :"$1","memory");
+}
+
/*
* This is still inefficient. We only can do better if we know the
* virtual address where the copy will be accessed.
"i" (Create_Dirty_Excl_D));
}
+/*
+ * Again a special version for the R4600 V1.x
+ */
+static void r4k_copy_page_r4600_v1(unsigned long to, unsigned long from)
+{
+ unsigned long dummy1, dummy2;
+ unsigned long reg1, reg2, reg3, reg4;
+
+ __asm__ __volatile__(
+ ".set\tnoreorder\n\t"
+ ".set\tnoat\n\t"
+ ".set\tmips3\n\t"
+ "daddiu\t$1,%0,%8\n"
+ "1:\tnop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "\tcache\t%9,(%0)\n\t"
+ "lw\t%2,(%1)\n\t"
+ "lw\t%3,4(%1)\n\t"
+ "lw\t%4,8(%1)\n\t"
+ "lw\t%5,12(%1)\n\t"
+ "sw\t%2,(%0)\n\t"
+ "sw\t%3,4(%0)\n\t"
+ "sw\t%4,8(%0)\n\t"
+ "sw\t%5,12(%0)\n\t"
+ "lw\t%2,16(%1)\n\t"
+ "lw\t%3,20(%1)\n\t"
+ "lw\t%4,24(%1)\n\t"
+ "lw\t%5,28(%1)\n\t"
+ "sw\t%2,16(%0)\n\t"
+ "sw\t%3,20(%0)\n\t"
+ "sw\t%4,24(%0)\n\t"
+ "sw\t%5,28(%0)\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "cache\t%9,32(%0)\n\t"
+ "daddiu\t%0,64\n\t"
+ "daddiu\t%1,64\n\t"
+ "lw\t%2,-32(%1)\n\t"
+ "lw\t%3,-28(%1)\n\t"
+ "lw\t%4,-24(%1)\n\t"
+ "lw\t%5,-20(%1)\n\t"
+ "sw\t%2,-32(%0)\n\t"
+ "sw\t%3,-28(%0)\n\t"
+ "sw\t%4,-24(%0)\n\t"
+ "sw\t%5,-20(%0)\n\t"
+ "lw\t%2,-16(%1)\n\t"
+ "lw\t%3,-12(%1)\n\t"
+ "lw\t%4,-8(%1)\n\t"
+ "lw\t%5,-4(%1)\n\t"
+ "sw\t%2,-16(%0)\n\t"
+ "sw\t%3,-12(%0)\n\t"
+ "sw\t%4,-8(%0)\n\t"
+ "bne\t$1,%0,1b\n\t"
+ "sw\t%5,-4(%0)\n\t"
+ ".set\tmips0\n\t"
+ ".set\tat\n\t"
+ ".set\treorder"
+ :"=r" (dummy1), "=r" (dummy2),
+ "=&r" (reg1), "=&r" (reg2), "=&r" (reg3), "=&r" (reg4)
+ :"0" (to), "1" (from),
+ "I" (PAGE_SIZE),
+ "i" (Create_Dirty_Excl_D));
+}
+
/*
* If you think for one second that this stuff coming up is a lot
* of bulky code eating too many kernel cache lines. Think _again_.
{
unsigned long flags;
- save_flags(flags); cli();
+ save_and_cli(flags);
blast_dcache16(); blast_icache16(); blast_scache16();
restore_flags(flags);
}
{
unsigned long flags;
- save_flags(flags); cli();
+ save_and_cli(flags);
blast_dcache16(); blast_icache16(); blast_scache32();
restore_flags(flags);
}
{
unsigned long flags;
- save_flags(flags); cli();
+ save_and_cli(flags);
blast_dcache16(); blast_icache16(); blast_scache64();
restore_flags(flags);
}
{
unsigned long flags;
- save_flags(flags); cli();
+ save_and_cli(flags);
blast_dcache16(); blast_icache16(); blast_scache128();
restore_flags(flags);
}
{
unsigned long flags;
- save_flags(flags); cli();
+ save_and_cli(flags);
blast_dcache32(); blast_icache32(); blast_scache16();
restore_flags(flags);
}
{
unsigned long flags;
- save_flags(flags); cli();
+ save_and_cli(flags);
blast_dcache32(); blast_icache32(); blast_scache32();
restore_flags(flags);
}
{
unsigned long flags;
- save_flags(flags); cli();
+ save_and_cli(flags);
blast_dcache32(); blast_icache32(); blast_scache64();
restore_flags(flags);
}
{
unsigned long flags;
- save_flags(flags); cli();
+ save_and_cli(flags);
blast_dcache32(); blast_icache32(); blast_scache128();
restore_flags(flags);
}
{
unsigned long flags;
- save_flags(flags); cli();
+ save_and_cli(flags);
blast_dcache16(); blast_icache16();
restore_flags(flags);
}
{
unsigned long flags;
- save_flags(flags); cli();
+ save_and_cli(flags);
blast_dcache32(); blast_icache32();
restore_flags(flags);
}
pte_t *pte;
int text;
- save_flags(flags); cli();
+ save_and_cli(flags);
text = vma->vm_flags & VM_EXEC;
while(start < end) {
pgd = pgd_offset(mm, start);
pte_t *pte;
int text;
- save_flags(flags); cli();
+ save_and_cli(flags);
text = vma->vm_flags & VM_EXEC;
while(start < end) {
pgd = pgd_offset(mm, start);
pte_t *pte;
int text;
- save_flags(flags); cli();
+ save_and_cli(flags);
text = vma->vm_flags & VM_EXEC;
while(start < end) {
pgd = pgd_offset(mm, start);
pte_t *pte;
int text;
- save_flags(flags); cli();
+ save_and_cli(flags);
text = vma->vm_flags & VM_EXEC;
while(start < end) {
pgd = pgd_offset(mm, start);
pte_t *pte;
int text;
- save_flags(flags); cli();
+ save_and_cli(flags);
text = vma->vm_flags & VM_EXEC;
while(start < end) {
pgd = pgd_offset(mm, start);
pte_t *pte;
int text;
- save_flags(flags); cli();
+ save_and_cli(flags);
text = vma->vm_flags & VM_EXEC;
while(start < end) {
pgd = pgd_offset(mm, start);
pte_t *pte;
int text;
- save_flags(flags); cli();
+ save_and_cli(flags);
text = vma->vm_flags & VM_EXEC;
while(start < end) {
pgd = pgd_offset(mm, start);
pte_t *pte;
int text;
- save_flags(flags); cli();
+ save_and_cli(flags);
text = vma->vm_flags & VM_EXEC;
while(start < end) {
pgd = pgd_offset(mm, start);
#ifdef DEBUG_CACHE
printk("crange[%d,%08lx,%08lx]", (int)mm->context, start, end);
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
blast_dcache16(); blast_icache16();
restore_flags(flags);
}
#ifdef DEBUG_CACHE
printk("crange[%d,%08lx,%08lx]", (int)mm->context, start, end);
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
blast_dcache32(); blast_icache32();
restore_flags(flags);
}
#ifdef DEBUG_CACHE
printk("cpage[%d,%08lx]", (int)mm->context, page);
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
page &= PAGE_MASK;
pgdp = pgd_offset(mm, page);
pmdp = pmd_offset(pgdp, page);
#ifdef DEBUG_CACHE
printk("cpage[%d,%08lx]", (int)mm->context, page);
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
page &= PAGE_MASK;
pgdp = pgd_offset(mm, page);
pmdp = pmd_offset(pgdp, page);
#ifdef DEBUG_CACHE
printk("cpage[%d,%08lx]", (int)mm->context, page);
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
page &= PAGE_MASK;
pgdp = pgd_offset(mm, page);
pmdp = pmd_offset(pgdp, page);
#ifdef DEBUG_CACHE
printk("cpage[%d,%08lx]", (int)mm->context, page);
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
page &= PAGE_MASK;
pgdp = pgd_offset(mm, page);
pmdp = pmd_offset(pgdp, page);
#ifdef DEBUG_CACHE
printk("cpage[%d,%08lx]", (int)mm->context, page);
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
page &= PAGE_MASK;
pgdp = pgd_offset(mm, page);
pmdp = pmd_offset(pgdp, page);
#ifdef DEBUG_CACHE
printk("cpage[%d,%08lx]", (int)mm->context, page);
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
page &= PAGE_MASK;
pgdp = pgd_offset(mm, page);
pmdp = pmd_offset(pgdp, page);
#ifdef DEBUG_CACHE
printk("cpage[%d,%08lx]", (int)mm->context, page);
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
page &= PAGE_MASK;
pgdp = pgd_offset(mm, page);
pmdp = pmd_offset(pgdp, page);
#ifdef DEBUG_CACHE
printk("cpage[%d,%08lx]", (int)mm->context, page);
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
page &= PAGE_MASK;
pgdp = pgd_offset(mm, page);
pmdp = pmd_offset(pgdp, page);
#ifdef DEBUG_CACHE
printk("cpage[%d,%08lx]", (int)mm->context, page);
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
page &= PAGE_MASK;
pgdp = pgd_offset(mm, page);
pmdp = pmd_offset(pgdp, page);
#ifdef DEBUG_CACHE
printk("cpage[%d,%08lx]", (int)mm->context, page);
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
page &= PAGE_MASK;
pgdp = pgd_offset(mm, page);
pmdp = pmd_offset(pgdp, page);
#ifdef DEBUG_CACHE
printk("cpage[%d,%08lx]", (int)mm->context, page);
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
page &= PAGE_MASK;
pgdp = pgd_offset(mm, page);
pmdp = pmd_offset(pgdp, page);
#ifdef DEBUG_CACHE
printk("cram[%08lx]", page);
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
blast_dcache16_page(page);
blast_scache16_page(page);
restore_flags(flags);
#ifdef DEBUG_CACHE
printk("cram[%08lx]", page);
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
blast_dcache16_page(page);
blast_scache32_page(page);
restore_flags(flags);
#ifdef DEBUG_CACHE
printk("cram[%08lx]", page);
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
blast_dcache16_page(page);
blast_scache64_page(page);
restore_flags(flags);
#ifdef DEBUG_CACHE
printk("cram[%08lx]", page);
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
blast_dcache16_page(page);
blast_scache128_page(page);
restore_flags(flags);
#ifdef DEBUG_CACHE
printk("cram[%08lx]", page);
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
blast_dcache32_page(page);
blast_scache16_page(page);
restore_flags(flags);
#ifdef DEBUG_CACHE
printk("cram[%08lx]", page);
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
blast_dcache32_page(page);
blast_scache32_page(page);
restore_flags(flags);
#ifdef DEBUG_CACHE
printk("cram[%08lx]", page);
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
blast_dcache32_page(page);
blast_scache64_page(page);
restore_flags(flags);
#ifdef DEBUG_CACHE
printk("cram[%08lx]", page);
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
blast_dcache32_page(page);
blast_scache128_page(page);
restore_flags(flags);
#ifdef DEBUG_CACHE
printk("cram[%08lx]", page);
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
blast_dcache16_page(page);
restore_flags(flags);
}
#ifdef DEBUG_CACHE
printk("cram[%08lx]", page);
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
blast_dcache32_page(page);
restore_flags(flags);
}
*/
*(volatile unsigned long *)KSEG1;
- save_flags(flags); cli();
+ save_and_cli(flags);
blast_dcache32_page(page);
blast_dcache32_page(page ^ 0x2000);
#ifdef CONFIG_SGI
daddu %0, 32
mtc0 $0, $12
nop; nop; nop; nop;
- mtc0 %3, $12
- nop; nop; nop; nop;
.set mips0
.set reorder"
: "=&r" (tmp1), "=&r" (tmp2),
- "=&r" (page), "=&r" (flags)
- : "2" (page & 0x0007f000), "3" (flags));
+ "=&r" (page)
+ : "2" (page & 0x0007f000));
}
#endif /* CONFIG_SGI */
+ restore_flags(flags);
}
}
static void r4k_flush_cache_sigtramp(unsigned long addr)
{
addr &= ~(dc_lsize - 1);
+ __asm__ __volatile__("nop;nop;nop;nop");
flush_dcache_line(addr);
flush_dcache_line(addr + dc_lsize);
flush_icache_line(addr);
printk("[tlball]");
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
/* Save old context and create impossible VPN2 value */
old_ctx = (get_entryhi() & 0xff);
set_entryhi(KSEG0);
#ifdef DEBUG_TLB
printk("[tlbmm<%d>]", mm->context);
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
get_new_mmu_context(mm, asid_cache);
if(mm == current->mm)
set_entryhi(mm->context & 0xff);
printk("[tlbrange<%02x,%08lx,%08lx>]", (mm->context & 0xff),
start, end);
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
size = (size + 1) >> 1;
if(size <= NTLB_ENTRIES_HALF) {
#endif
newpid = (vma->vm_mm->context & 0xff);
page &= (PAGE_MASK << 1);
- save_flags(flags); cli();
+ save_and_cli(flags);
oldpid = (get_entryhi() & 0xff);
set_entryhi(page | newpid);
BARRIER;
}
#endif
- save_flags(flags); cli();
+ save_and_cli(flags);
address &= (PAGE_MASK << 1);
set_entryhi(address | (pid));
pgdp = pgd_offset(vma->vm_mm, address);
pte_t *ptep;
int idx;
- save_flags(flags); cli();
+ save_and_cli(flags);
address &= (PAGE_MASK << 1);
set_entryhi(address | (get_entryhi() & 0xff));
pgdp = pgd_offset(vma->vm_mm, address);
__asm__ __volatile__("
.set noreorder
.set mips3
- li %0, 0x1
- dsll %0, 31
- lui %1, 0x9000
- dsll32 %1, 0
- or %0, %1, %0
mfc0 %2, $12
nop; nop; nop; nop;
li %1, 0x80
mtc0 %1, $12
nop; nop; nop; nop;
+ li %0, 0x1
+ dsll %0, 31
+ lui %1, 0x9000
+ dsll32 %1, 0
+ or %0, %1, %0
sb $0, 0(%0)
mtc0 $0, $12
nop; nop; nop; nop;
/* This is such a bitch, you'd think they would make it
* easy to do this. Away you daemons of stupidity!
*/
- save_flags(flags); cli();
+ save_and_cli(flags);
/* Fill each size-multiple cache line with a valid tag. */
pow2 = (64 * 1024);
flush_page_to_ram = r4k_flush_page_to_ram_d16i16;
break;
case 32:
- clear_page = r4k_clear_page_d32;
- copy_page = r4k_copy_page_d32;
+ if ((read_32bit_cp0_register(CP0_PRID) & 0xfff0) == 0x2010) {
+ clear_page = r4k_clear_page_r4600_v1;
+ copy_page = r4k_copy_page_r4600_v1;
+ } else {
+ clear_page = r4k_clear_page_d32;
+ copy_page = r4k_copy_page_d32;
+ }
flush_cache_all = r4k_flush_cache_all_d32i32;
flush_cache_mm = r4k_flush_cache_mm_d32i32;
flush_cache_range = r4k_flush_cache_range_d32i32;
-/* $Id: indy_int.c,v 1.1 1997/06/06 09:36:21 ralf Exp $
+/*
* indy_int.c: Routines for generic manipulation of the INT[23] ASIC
* found on INDY workstations..
*
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ *
+ * $Id: indy_int.c,v 1.2 1997/06/30 15:53:01 ralf Exp $
*/
#include <linux/config.h>
{
unsigned long flags;
- save_flags(flags);
- cli();
+ save_and_cli(flags);
switch(irq_nr) {
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
ioc_icontrol->imask0 &= ~(1 << irq_nr);
void enable_local_irq(unsigned int irq_nr)
{
unsigned long flags;
- save_flags(flags);
- cli();
+ save_and_cli(flags);
switch(irq_nr) {
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
ioc_icontrol->imask0 |= (1 << irq_nr);
continue;
/* Found it - now free it */
- save_flags(flags);
- cli();
+ save_and_cli(flags);
*p = action->next;
restore_flags(flags);
kfree(action);
-/* $Id: indy_timer.c,v 1.1 1997/06/06 09:36:28 ralf Exp $
+/*
* indy_timer.c: Setting up the clock on the INDY 8254 controller.
*
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ *
+ * $Id: indy_timer.c,v 1.2 1997/06/30 15:53:04 ralf Exp $
*/
#include <linux/errno.h>
{
unsigned long flags;
- save_flags(flags); cli();
+ save_and_cli(flags);
*tv = xtime;
restore_flags(flags);
}
-/* $Id: setup.c,v 1.1 1997/06/06 09:36:33 ralf Exp $
+/* $Id: setup.c,v 1.2 1997/06/30 15:26:24 ralf Exp $
* setup.c: SGI specific setup, including init of the feature struct.
*
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
/* register_console(sgi_print); */
- sgi_sysinit();
-
/* Init the INDY HPC I/O controller. Need to call this before
* fucking with the memory controller because it needs to know the
* boardID and whether this is a Guiness or a FullHouse machine.
-/* $Id: system.c,v 1.1 1997/06/06 09:36:36 ralf Exp $
+/*
* system.c: Probe the system type using ARCS prom interface library.
*
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ *
+ * $Id: system.c,v 1.2 1997/06/30 15:26:32 ralf Exp $
*/
#include <linux/kernel.h>
#include <linux/types.h>
return 0;
}
+/*
+ * We' call this early before loadmmu(). If we do the other way around
+ * the firmware will crash and burn.
+ */
void sgi_sysinit(void)
{
pcomponent *p, *toplev, *cpup = 0;
linefeed;
}
+void output_sc_defines(void)
+{
+ text("/* Linux sigcontext offsets. */");
+ offset("#define SC_REGMASK ", struct sigcontext, sc_regmask);
+ offset("#define SC_STATUS ", struct sigcontext, sc_status);
+ offset("#define SC_PC ", struct sigcontext, sc_pc);
+ offset("#define SC_REGS ", struct sigcontext, sc_regs);
+ offset("#define SC_FPREGS ", struct sigcontext, sc_fpregs);
+ offset("#define SC_OWNEDFP ", struct sigcontext, sc_ownedfp);
+ offset("#define SC_FPC_CSR ", struct sigcontext, sc_fpc_csr);
+ offset("#define SC_FPC_EIR ", struct sigcontext, sc_fpc_eir);
+ offset("#define SC_SSFLAGS ", struct sigcontext, sc_ssflags);
+ offset("#define SC_MDHI ", struct sigcontext, sc_mdhi);
+ offset("#define SC_MDLO ", struct sigcontext, sc_mdlo);
+ offset("#define SC_CAUSE ", struct sigcontext, sc_cause);
+ offset("#define SC_BADVADDR ", struct sigcontext, sc_badvaddr);
+ offset("#define SC_SIGSET ", struct sigcontext, sc_sigset);
+ linefeed;
+}
+
text("#endif /* !(_MIPS_OFFSET_H) */");
{ 0, 0, "Fujitsu MB86900/1A or LSI L64831 SparcKIT-40"},
/* borned STP1012PGA */
{ 0, 4, "Fujitsu MB86904"},
+ { 0, 5, "Fujitsu TurboSparc MB86907"},
/* SparcStation2, SparcServer 490 & 690 */
{ 1, 0, "LSI Logic Corporation - L64811"},
/* SparcStation2 */
-/* $Id: sparc_ksyms.c,v 1.59 1997/05/08 17:45:20 davem Exp $
+/* $Id: sparc_ksyms.c,v 1.60 1997/06/17 13:25:13 jj Exp $
* arch/sparc/kernel/ksyms.c: Sparc specific ksyms support.
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
extern void *__memscan_generic(void *, int, size_t);
extern int __memcmp(const void *, const void *, __kernel_size_t);
extern int __strncmp(const char *, const char *, __kernel_size_t);
+extern char saved_command_line[];
extern void bcopy (const char *, char *, int);
extern int __ashrdi3(int, int);
EXPORT_SYMBOL(prom_getproperty);
EXPORT_SYMBOL(prom_node_has_property);
EXPORT_SYMBOL(prom_setprop);
-EXPORT_SYMBOL(prom_getbootargs);
+EXPORT_SYMBOL(saved_command_line);
EXPORT_SYMBOL(prom_apply_obio_ranges);
EXPORT_SYMBOL(prom_getname);
EXPORT_SYMBOL(prom_feval);
-# $Id: Makefile,v 1.25 1997/05/03 05:09:11 davem Exp $
+# $Id: Makefile,v 1.26 1997/06/24 15:48:06 jj Exp $
# Makefile for the linux Sparc-specific parts of the memory manager.
#
# Note! Dependencies are done automagically by 'make dep', which also
O_TARGET := mm.o
O_OBJS := fault.o init.o sun4c.o srmmu.o hypersparc.o viking.o \
- tsunami.o loadmmu.o generic.o asyncd.o extable.o
+ tsunami.o loadmmu.o generic.o asyncd.o extable.o \
+ turbosparc.o
include $(TOPDIR)/Rules.make
hypersparc.o: hypersparc.S
$(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o hypersparc.o hypersparc.S
+turbosparc.o: turbosparc.S
+ $(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o turbosparc.o turbosparc.S
+
viking.o: viking.S
$(CC) -D__ASSEMBLY__ $(AFLAGS) -ansi -c -o viking.o viking.S
hypersparc.o: hypersparc.S
$(CC) -D__ASSEMBLY__ -ansi -c -o hypersparc.o hypersparc.S
+turbosparc.o: turbosparc.S
+ $(CC) -D__ASSEMBLY__ -ansi -c -o turbosparc.o turbosparc.S
+
viking.o: viking.S
$(CC) -D__ASSEMBLY__ -ansi -c -o viking.o viking.S
-/* $Id: srmmu.c,v 1.147 1997/05/20 07:58:42 jj Exp $
+/* $Id: srmmu.c,v 1.148 1997/06/24 15:48:02 jj Exp $
* srmmu.c: SRMMU specific routines for memory management.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
* Copyright (C) 1995 Peter A. Zaitcev (zaitcev@ithil.mcst.ru)
* Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
#include <linux/config.h>
#include <asm/ross.h>
#include <asm/tsunami.h>
#include <asm/swift.h>
+#include <asm/turbosparc.h>
enum mbus_module srmmu_modtype;
unsigned int hwbug_bitmask;
poke_srmmu = poke_swift;
}
+/* turbosparc.S */
+extern void turbosparc_flush_cache_all();
+extern void turbosparc_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr);
+
+static void poke_turbosparc(void)
+{
+ unsigned long mreg = srmmu_get_mmureg();
+ unsigned long ccreg;
+
+ /* Clear any crap from the cache or else... */
+ turbosparc_flush_cache_all();
+ mreg &= ~(TURBOSPARC_ICENABLE | TURBOSPARC_DCENABLE); /* Temporarily disable I & D caches */
+ mreg &= ~(TURBOSPARC_PCENABLE); /* Don't check parity */
+ srmmu_set_mmureg(mreg);
+
+ ccreg = turbosparc_get_ccreg();
+
+#ifdef TURBOSPARC_WRITEBACK
+ ccreg |= (TURBOSPARC_SNENABLE); /* Do DVMA snooping in Dcache */
+ ccreg &= ~(TURBOSPARC_uS2 | TURBOSPARC_WTENABLE);
+ /* Write-back D-cache, emulate VLSI
+ * abortion number three, not number one */
+#else
+ /* For now let's play safe, optimize later */
+ ccreg |= (TURBOSPARC_SNENABLE | TURBOSPARC_WTENABLE);
+ /* Do DVMA snooping in Dcache, Write-thru D-cache */
+ ccreg &= ~(TURBOSPARC_uS2);
+ /* Emulate VLSI abortion number three, not number one */
+#endif
+ switch (ccreg & 7) {
+ case 0: /* No SE cache */
+ case 7: /* Test mode */
+ break;
+ default:
+ ccreg |= (TURBOSPARC_SCENABLE);
+ }
+ turbosparc_set_ccreg (ccreg);
+
+ mreg |= (TURBOSPARC_ICENABLE | TURBOSPARC_DCENABLE); /* I & D caches on */
+ mreg |= (TURBOSPARC_ICSNOOP); /* Icache snooping on */
+ srmmu_set_mmureg(mreg);
+}
+
+__initfunc(static void init_turbosparc(void))
+{
+ srmmu_name = "Fujitsu TurboSparc";
+ srmmu_modtype = TurboSparc;
+
+ flush_cache_all = turbosparc_flush_cache_all;
+ flush_cache_mm = hypersparc_flush_cache_mm;
+ flush_cache_page = hypersparc_flush_cache_page;
+ flush_cache_range = hypersparc_flush_cache_range;
+
+ flush_tlb_all = hypersparc_flush_tlb_all;
+ flush_tlb_mm = hypersparc_flush_tlb_mm;
+ flush_tlb_page = hypersparc_flush_tlb_page;
+ flush_tlb_range = hypersparc_flush_tlb_range;
+
+#ifdef TURBOSPARC_WRITEBACK
+ flush_page_to_ram = hypersparc_flush_page_to_ram;
+ flush_chunk = hypersparc_flush_chunk;
+#else
+ flush_page_to_ram = swift_flush_page_to_ram;
+ flush_chunk = swift_flush_chunk;
+#endif
+
+ flush_sig_insns = turbosparc_flush_sig_insns;
+ flush_page_for_dma = hypersparc_flush_page_for_dma;
+
+ poke_srmmu = poke_turbosparc;
+}
+
static void poke_tsunami(void)
{
unsigned long mreg = srmmu_get_mmureg();
};
return;
}
+
+ /* Now Fujitsu TurboSparc. It might happen that it is
+ in Swift emulation mode, so we will check later... */
+ if (psr_typ == 0 && psr_vers == 5) {
+ init_turbosparc();
+ return;
+ }
/* Next check for Fujitsu Swift. */
if(psr_typ == 0 && psr_vers == 4) {
+ int cpunode;
+ char node_str[128];
+
+ /* Look if it is not a TurboSparc emulating Swift... */
+ cpunode = prom_getchild(prom_root_node);
+ while((cpunode = prom_getsibling(cpunode)) != 0) {
+ prom_getstring(cpunode, "device_type", node_str, sizeof(node_str));
+ if(!strcmp(node_str, "cpu")) {
+ if (!prom_getintdefault(cpunode, "psr-implementation", 1) &&
+ prom_getintdefault(cpunode, "psr-version", 1) == 5) {
+ init_turbosparc();
+ return;
+ }
+ break;
+ }
+ }
+
init_swift();
return;
}
--- /dev/null
+/* $Id: turbosparc.S,v 1.1 1997/06/24 15:48:05 jj Exp $
+ * turbosparc.S: High speed Hypersparc mmu/cache operations.
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <asm/ptrace.h>
+#include <asm/psr.h>
+#include <asm/asi.h>
+#include <asm/page.h>
+#include <asm/pgtsrmmu.h>
+
+#define WINDOW_FLUSH(tmp1, tmp2) \
+ mov 0, tmp1; \
+98: ld [%g6 + AOFF_task_tss + AOFF_thread_uwinmask], tmp2; \
+ orcc %g0, tmp2, %g0; \
+ add tmp1, 1, tmp1; \
+ bne 98b; \
+ save %sp, -64, %sp; \
+99: subcc tmp1, 1, tmp1; \
+ bne 99b; \
+ restore %g0, %g0, %g0;
+
+ .text
+ .align 4
+
+ .globl turbosparc_flush_cache_all
+ .globl turbosparc_flush_sig_insns
+
+turbosparc_flush_cache_all:
+ WINDOW_FLUSH(%g4, %g5)
+ sethi %hi(vac_cache_size), %g4
+ ld [%g4 + %lo(vac_cache_size)], %g5
+ sethi %hi(vac_line_size), %g1
+ ld [%g1 + %lo(vac_line_size)], %g2
+1:
+ subcc %g5, %g2, %g5
+ bne 1b
+ sta %g0, [%g5] ASI_M_DATAC_TAG
+ retl
+ sta %g0, [%g0] ASI_M_IC_FLCLEAR
+
+turbosparc_flush_sig_insns:
+ retl
+ nop
-/* $Id: bootstr.c,v 1.12 1996/12/18 06:46:54 tridge Exp $
+/* $Id: bootstr.c,v 1.14 1997/06/19 16:28:49 jj Exp $
* bootstr.c: Boot string/argument acquisition from the PROM.
*
* Copyright(C) 1995 David S. Miller (davem@caip.rutgers.edu)
#include <linux/config.h>
#include <linux/string.h>
#include <asm/oplib.h>
+#include <linux/init.h>
#define BARG_LEN 256
-static char barg_buf[BARG_LEN];
-static char fetched = 0;
+static char barg_buf[BARG_LEN] __initdata = { 0 };
+static char fetched __initdata = 0;
-char *
-prom_getbootargs(void)
+__initfunc(char *
+prom_getbootargs(void))
{
int iter;
char *cp, *arg;
-/* $Id: tree.c,v 1.18 1997/05/14 20:45:03 davem Exp $
+/* $Id: tree.c,v 1.19 1997/06/27 14:52:54 jj Exp $
* tree.c: Basic device tree traversal/scanning for the Linux
* prom library.
*
/* Return the first property type for node 'node'.
*/
-char * prom_firstprop(int node)
+char * prom_firstprop(int node, char *buffer)
{
unsigned long flags;
char *ret;
* at node 'node' . Returns NULL string if no more
* property types for this node.
*/
-char * prom_nextprop(int node, char *oprop)
+char * prom_nextprop(int node, char *oprop, char *buffer)
{
char *ret;
unsigned long flags;
char *current_property = "";
do {
- current_property = prom_nextprop(node, current_property);
+ current_property = prom_nextprop(node, current_property, NULL);
if(!strcmp(current_property, prop))
return 1;
} while (*current_property);
-# $Id: Makefile,v 1.16 1997/05/04 07:21:08 davem Exp $
+# $Id: Makefile,v 1.19 1997/06/26 12:48:45 jj Exp $
# sparc64/Makefile
#
# Makefile for the architecture dependent flags and dependencies on the
# debugging of the kernel to get the proper debugging information.
#CFLAGS := $(CFLAGS) -g -pipe -fcall-used-g5 -fcall-used-g7
-CFLAGS := $(CFLAGS) -pipe \
- -fcall-used-g5 -fcall-used-g7 -Wno-sign-compare
+CFLAGS := $(CFLAGS) -pipe -mno-fpu -mtune=ultrasparc -mmedlow \
+ -ffixed-g4 -fcall-used-g5 -fcall-used-g7 -Wno-sign-compare
LINKFLAGS = -T arch/sparc64/vmlinux.lds
-# $Id: config.in,v 1.7 1997/06/17 03:54:52 davem Exp $
+# $Id: config.in,v 1.9 1997/07/04 11:33:05 davem Exp $
# For a description of the syntax of this configuration file,
# see the Configure script.
#
bool 'System V IPC' CONFIG_SYSVIPC
bool 'Sysctl support' CONFIG_SYSCTL
bool 'Kernel support for Linux/Sparc 32bit binary compatibility' CONFIG_SPARC32_COMPAT
-tristate 'Kernel support for a.out binaries' CONFIG_BINFMT_AOUT
tristate 'Kernel support for 64-bit ELF binaries' CONFIG_BINFMT_ELF
if [ "$CONFIG_SPARC32_COMPAT" != "n" ]; then
tristate 'Kernel support for 32-bit ELF binaries' CONFIG_BINFMT_ELF32
+ bool 'Kernel support for 32-bit (ie. SunOS) a.out binaries' CONFIG_BINFMT_AOUT32
fi
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
tristate 'Kernel support for JAVA binaries' CONFIG_BINFMT_JAVA
if [ "$CONFIG_PROFILE" = "y" ]; then
int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
fi
+bool 'ECache flush trap support at ta 0x72' CONFIG_EC_FLUSH_TRAP
endmenu
#
# Code maturity level options
#
-# CONFIG_EXPERIMENTAL is not set
+CONFIG_EXPERIMENTAL=y
#
# Loadable module support
#
-# CONFIG_MODULES is not set
+CONFIG_MODULES=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_KERNELD is not set
#
# General setup
#
# Misc Linux/SPARC drivers
#
-# CONFIG_SUN_OPENPROMIO is not set
+CONFIG_SUN_OPENPROMIO=m
CONFIG_SUN_MOSTEK_RTC=y
-# CONFIG_SUN_OPENPROMFS is not set
+# CONFIG_SUN_BPP is not set
+# CONFIG_SUN_VIDEOPIX is not set
+CONFIG_SUN_OPENPROMFS=m
CONFIG_NET=y
CONFIG_SYSVIPC=y
CONFIG_SYSCTL=y
CONFIG_SPARC32_COMPAT=y
-CONFIG_BINFMT_AOUT=y
CONFIG_BINFMT_ELF=y
CONFIG_BINFMT_ELF32=y
+CONFIG_BINFMT_AOUT32=y
+CONFIG_BINFMT_JAVA=m
+CONFIG_BINFMT_MISC=m
#
# Floppy, IDE, and other block devices
#
# CONFIG_BLK_DEV_FD is not set
-# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_MD=y
+CONFIG_MD_LINEAR=m
+CONFIG_MD_STRIPED=m
CONFIG_BLK_DEV_RAM=y
-# CONFIG_BLK_DEV_INITRD is not set
-# CONFIG_BLK_DEV_LOOP is not set
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_BLK_DEV_LOOP=m
#
# Networking options
CONFIG_PATH_MTU_DISCOVERY=y
CONFIG_IP_NOSR=y
CONFIG_SKB_LARGE=y
+# CONFIG_IPV6 is not set
#
#
#
-CONFIG_IPX=y
+CONFIG_IPX=m
# CONFIG_IPX_INTERN is not set
-CONFIG_ATALK=y
+# CONFIG_IPX_PPROP_ROUTING is not set
+CONFIG_ATALK=m
# CONFIG_IPDDP is not set
# CONFIG_AX25 is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_LLC is not set
+# CONFIG_WAN_ROUTER is not set
#
# SCSI support
# CONFIG_CHR_DEV_ST is not set
CONFIG_BLK_DEV_SR=y
CONFIG_BLK_DEV_SR_VENDOR=y
-# CONFIG_CHR_DEV_SG is not set
+CONFIG_CHR_DEV_SG=m
#
# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
# SCSI low-level drivers
#
CONFIG_SCSI_SUNESP=y
-# CONFIG_SCSI_QLOGICPTI is not set
+CONFIG_SCSI_QLOGICPTI=m
#
# Network device support
# CONFIG_PPP is not set
# CONFIG_SLIP is not set
CONFIG_SUNLANCE=y
-# CONFIG_HAPPYMEAL is not set
-# CONFIG_SUNQE is not set
+CONFIG_HAPPYMEAL=m
+CONFIG_SUNQE=m
# CONFIG_MYRI_SBUS is not set
#
CONFIG_ROOT_NFS=y
CONFIG_RNFS_BOOTP=y
# CONFIG_RNFS_RARP is not set
-# CONFIG_NFSD is not set
+CONFIG_NFSD=m
CONFIG_SUNRPC=y
CONFIG_LOCKD=y
# CONFIG_SMB_FS is not set
# CONFIG_SYSV_FS is not set
# CONFIG_AFFS_FS is not set
# CONFIG_ROMFS_FS is not set
-# CONFIG_AUTOFS_FS is not set
+CONFIG_AUTOFS_FS=m
CONFIG_UFS_FS=y
CONFIG_BSD_DISKLABEL=y
CONFIG_SMD_DISKLABEL=y
# Kernel hacking
#
# CONFIG_PROFILE is not set
+# CONFIG_EC_FLUSH_TRAP is not set
-# $Id: Makefile,v 1.23 1997/06/06 10:56:20 jj Exp $
+# $Id: Makefile,v 1.28 1997/07/05 09:52:20 davem Exp $
# Makefile for the linux kernel.
#
# Note! Dependencies are done automagically by 'make dep', which also
all: kernel.o head.o init_task.o
O_TARGET := kernel.o
-O_OBJS := etrap.o rtrap.o hack.o process.o setup.o cpu.o idprom.o \
- systbls.o traps.o entry.o devices.o auxio.o ioport.o \
- irq.o ptrace.o time.o sys_sparc.o signal.o winfixup.o \
- unaligned.o
+O_OBJS := process.o setup.o cpu.o idprom.o \
+ systbls.o traps.o devices.o auxio.o ioport.o \
+ irq.o ptrace.o time.o sys_sparc.o signal.o \
+ unaligned.o sys_sunos32.o sunos_ioctl32.o
OX_OBJS := sparc64_ksyms.o
ifdef CONFIG_SPARC32_COMPAT
- O_OBJS += sys_sparc32.o signal32.o ioctl32.o
+ O_OBJS += sys32.o sys_sparc32.o signal32.o ioctl32.o
endif
ifdef CONFIG_BINFMT_ELF32
O_OBJS += binfmt_elf32.o
endif
-head.o: head.S ttable.S itlb_miss.S dtlb_miss.S dtlb_prot.S
+ifdef CONFIG_BINFMT_AOUT32
+ O_OBJS += binfmt_aout32.o
+endif
+
+head.o: head.S ttable.S itlb_miss.S dtlb_miss.S dtlb_prot.S etrap.S rtrap.S \
+ winfixup.S entry.S
$(CC) -D__ASSEMBLY__ -ansi -c $*.S -o $*.o
#
--- /dev/null
+/*
+ * linux/fs/binfmt_aout.c
+ *
+ * Copyright (C) 1991, 1992, 1996 Linus Torvalds
+ *
+ * Hacked a bit by DaveM to make it work with 32-bit SunOS
+ * binaries on the sparc64 port.
+ */
+
+#include <linux/module.h>
+
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/a.out.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+#include <linux/malloc.h>
+#include <linux/binfmts.h>
+#include <linux/personality.h>
+#include <linux/init.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+
+static int load_aout32_binary(struct linux_binprm *, struct pt_regs * regs);
+static int load_aout32_library(int fd);
+static int aout32_core_dump(long signr, struct pt_regs * regs);
+
+extern void dump_thread(struct pt_regs *, struct user *);
+
+static struct linux_binfmt aout32_format = {
+ NULL, NULL, load_aout32_binary, load_aout32_library, aout32_core_dump
+};
+
+static void set_brk(unsigned long start, unsigned long end)
+{
+ start = PAGE_ALIGN(start);
+ end = PAGE_ALIGN(end);
+ if (end <= start)
+ return;
+ do_mmap(NULL, start, end - start,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE, 0);
+}
+
+/*
+ * These are the only things you should do on a core-file: use only these
+ * macros to write out all the necessary info.
+ */
+#define DUMP_WRITE(addr,nr) \
+while (file.f_op->write(inode,&file,(char *)(addr),(nr)) != (nr)) goto close_coredump
+
+#define DUMP_SEEK(offset) \
+if (file.f_op->llseek) { \
+ if (file.f_op->llseek(inode,&file,(offset),0) != (offset)) \
+ goto close_coredump; \
+} else file.f_pos = (offset)
+
+/*
+ * Routine writes a core dump image in the current directory.
+ * Currently only a stub-function.
+ *
+ * Note that setuid/setgid files won't make a core-dump if the uid/gid
+ * changed due to the set[u|g]id. It's enforced by the "current->dumpable"
+ * field, which also makes sure the core-dumps won't be recursive if the
+ * dumping of the process results in another error..
+ */
+
+static inline int
+do_aout32_core_dump(long signr, struct pt_regs * regs)
+{
+ struct inode * inode = NULL;
+ struct file file;
+ unsigned short fs;
+ int has_dumped = 0;
+ char corefile[6+sizeof(current->comm)];
+ unsigned long dump_start, dump_size;
+ struct user dump;
+# define START_DATA(u) (u.u_tsize)
+# define START_STACK(u) ((regs->u_regs[UREG_FP]) & ~(PAGE_SIZE - 1))
+
+ if (!current->dumpable || current->mm->count != 1)
+ return 0;
+ current->dumpable = 0;
+
+/* See if we have enough room to write the upage. */
+ if (current->rlim[RLIMIT_CORE].rlim_cur < PAGE_SIZE)
+ return 0;
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ memcpy(corefile,"core.",5);
+#if 0
+ memcpy(corefile+5,current->comm,sizeof(current->comm));
+#else
+ corefile[4] = '\0';
+#endif
+ if (open_namei(corefile,O_CREAT | 2 | O_TRUNC,0600,&inode,NULL)) {
+ inode = NULL;
+ goto end_coredump;
+ }
+ if (!S_ISREG(inode->i_mode))
+ goto end_coredump;
+ if (!inode->i_op || !inode->i_op->default_file_ops)
+ goto end_coredump;
+ if (get_write_access(inode))
+ goto end_coredump;
+ file.f_mode = 3;
+ file.f_flags = 0;
+ file.f_count = 1;
+ file.f_inode = inode;
+ file.f_pos = 0;
+ file.f_reada = 0;
+ file.f_op = inode->i_op->default_file_ops;
+ if (file.f_op->open)
+ if (file.f_op->open(inode,&file))
+ goto done_coredump;
+ if (!file.f_op->write)
+ goto close_coredump;
+ has_dumped = 1;
+ current->flags |= PF_DUMPCORE;
+ strncpy(dump.u_comm, current->comm, sizeof(current->comm));
+ dump.signal = signr;
+ dump_thread(regs, &dump);
+
+/* If the size of the dump file exceeds the rlimit, then see what would happen
+ if we wrote the stack, but not the data area. */
+ if ((dump.u_dsize+dump.u_ssize) >
+ current->rlim[RLIMIT_CORE].rlim_cur)
+ dump.u_dsize = 0;
+
+/* Make sure we have enough room to write the stack and data areas. */
+ if ((dump.u_ssize) >
+ current->rlim[RLIMIT_CORE].rlim_cur)
+ dump.u_ssize = 0;
+
+/* make sure we actually have a data and stack area to dump */
+ set_fs(USER_DS);
+ if (verify_area(VERIFY_READ, (void *) START_DATA(dump), dump.u_dsize))
+ dump.u_dsize = 0;
+ if (verify_area(VERIFY_READ, (void *) START_STACK(dump), dump.u_ssize))
+ dump.u_ssize = 0;
+
+ set_fs(KERNEL_DS);
+/* struct user */
+ DUMP_WRITE(&dump,sizeof(dump));
+/* now we start writing out the user space info */
+ set_fs(USER_DS);
+/* Dump the data area */
+ if (dump.u_dsize != 0) {
+ dump_start = START_DATA(dump);
+ dump_size = dump.u_dsize;
+ DUMP_WRITE(dump_start,dump_size);
+ }
+/* Now prepare to dump the stack area */
+ if (dump.u_ssize != 0) {
+ dump_start = START_STACK(dump);
+ dump_size = dump.u_ssize;
+ DUMP_WRITE(dump_start,dump_size);
+ }
+/* Finally dump the task struct. Not be used by gdb, but could be useful */
+ set_fs(KERNEL_DS);
+ DUMP_WRITE(current,sizeof(*current));
+ inode->i_status |= ST_MODIFIED;
+close_coredump:
+ if (file.f_op->release)
+ file.f_op->release(inode,&file);
+done_coredump:
+ put_write_access(inode);
+end_coredump:
+ set_fs(fs);
+ iput(inode);
+ return has_dumped;
+}
+
+static int
+aout32_core_dump(long signr, struct pt_regs * regs)
+{
+ int retval;
+
+ MOD_INC_USE_COUNT;
+ retval = do_aout32_core_dump(signr, regs);
+ MOD_DEC_USE_COUNT;
+ return retval;
+}
+
+/*
+ * create_aout32_tables() parses the env- and arg-strings in new user
+ * memory and creates the pointer tables from them, and puts their
+ * addresses on the "stack", returning the new stack pointer value.
+ */
+#define A(x) ((unsigned long)x)
+static u32 *create_aout32_tables(char * p, struct linux_binprm * bprm)
+{
+ u32 *argv, *envp;
+ u32 *sp;
+ int argc = bprm->argc;
+ int envc = bprm->envc;
+
+ sp = (u32 *) ((-(unsigned long)sizeof(char *)) & (unsigned long) p);
+
+ /* This imposes the proper stack alignment for a new process. */
+ sp = (u32 *) (((unsigned long) sp) & ~7);
+ if ((envc+argc+3)&1)
+ --sp;
+
+ sp -= envc+1;
+ envp = (u32 *) sp;
+ sp -= argc+1;
+ argv = (u32 *) sp;
+ put_user(argc,--sp);
+ current->mm->arg_start = (unsigned long) p;
+ while (argc-->0) {
+ char c;
+ put_user(((u32)A(p)),argv++);
+ do {
+ get_user(c,p++);
+ } while (c);
+ }
+ put_user(NULL,argv);
+ current->mm->arg_end = current->mm->env_start = (unsigned long) p;
+ while (envc-->0) {
+ char c;
+ put_user(((u32)A(p)),envp++);
+ do {
+ get_user(c,p++);
+ } while (c);
+ }
+ put_user(NULL,envp);
+ current->mm->env_end = (unsigned long) p;
+ return sp;
+}
+
+/*
+ * These are the functions used to load a.out style executables and shared
+ * libraries. There is no binary dependent code anywhere else.
+ */
+
+static inline int do_load_aout32_binary(struct linux_binprm * bprm,
+ struct pt_regs * regs)
+{
+ struct exec ex;
+ struct file * file;
+ int fd;
+ unsigned long error;
+ unsigned long p = bprm->p;
+ unsigned long fd_offset;
+ unsigned long rlim;
+
+ ex = *((struct exec *) bprm->buf); /* exec-header */
+ if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != OMAGIC &&
+ N_MAGIC(ex) != QMAGIC && N_MAGIC(ex) != NMAGIC) ||
+ N_TRSIZE(ex) || N_DRSIZE(ex) ||
+ bprm->inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
+ return -ENOEXEC;
+ }
+
+ current->personality = PER_LINUX;
+ fd_offset = N_TXTOFF(ex);
+
+ /* Check initial limits. This avoids letting people circumvent
+ * size limits imposed on them by creating programs with large
+ * arrays in the data or bss.
+ */
+ rlim = current->rlim[RLIMIT_DATA].rlim_cur;
+ if (rlim >= RLIM_INFINITY)
+ rlim = ~0;
+ if (ex.a_data + ex.a_bss > rlim)
+ return -ENOMEM;
+
+ /* OK, This is the point of no return */
+ flush_old_exec(bprm);
+ memcpy(¤t->tss.core_exec, &ex, sizeof(struct exec));
+
+ current->mm->end_code = ex.a_text +
+ (current->mm->start_code = N_TXTADDR(ex));
+ current->mm->end_data = ex.a_data +
+ (current->mm->start_data = N_DATADDR(ex));
+ current->mm->brk = ex.a_bss +
+ (current->mm->start_brk = N_BSSADDR(ex));
+
+ current->mm->rss = 0;
+ current->mm->mmap = NULL;
+ current->suid = current->euid = current->fsuid = bprm->e_uid;
+ current->sgid = current->egid = current->fsgid = bprm->e_gid;
+ current->flags &= ~PF_FORKNOEXEC;
+ if (N_MAGIC(ex) == NMAGIC) {
+ /* Fuck me plenty... */
+ error = do_mmap(NULL, N_TXTADDR(ex), ex.a_text,
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_FIXED|MAP_PRIVATE, 0);
+ read_exec(bprm->inode, fd_offset, (char *) N_TXTADDR(ex),
+ ex.a_text, 0);
+ error = do_mmap(NULL, N_DATADDR(ex), ex.a_data,
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_FIXED|MAP_PRIVATE, 0);
+ read_exec(bprm->inode, fd_offset + ex.a_text, (char *) N_DATADDR(ex),
+ ex.a_data, 0);
+ goto beyond_if;
+ }
+
+ if (N_MAGIC(ex) == OMAGIC) {
+ do_mmap(NULL, N_TXTADDR(ex) & PAGE_MASK,
+ ex.a_text+ex.a_data + PAGE_SIZE - 1,
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_FIXED|MAP_PRIVATE, 0);
+ read_exec(bprm->inode, fd_offset, (char *) N_TXTADDR(ex),
+ ex.a_text+ex.a_data, 0);
+ } else {
+ if ((ex.a_text & 0xfff || ex.a_data & 0xfff) &&
+ (N_MAGIC(ex) != NMAGIC))
+ printk(KERN_NOTICE "executable not page aligned\n");
+
+ fd = open_inode(bprm->inode, O_RDONLY);
+
+ if (fd < 0)
+ return fd;
+ file = current->files->fd[fd];
+ if (!file->f_op || !file->f_op->mmap) {
+ sys_close(fd);
+ do_mmap(NULL, 0, ex.a_text+ex.a_data,
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_FIXED|MAP_PRIVATE, 0);
+ read_exec(bprm->inode, fd_offset,
+ (char *) N_TXTADDR(ex), ex.a_text+ex.a_data, 0);
+ goto beyond_if;
+ }
+
+ error = do_mmap(file, N_TXTADDR(ex), ex.a_text,
+ PROT_READ | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
+ fd_offset);
+
+ if (error != N_TXTADDR(ex)) {
+ sys_close(fd);
+ send_sig(SIGKILL, current, 0);
+ return error;
+ }
+
+ error = do_mmap(file, N_DATADDR(ex), ex.a_data,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE,
+ fd_offset + ex.a_text);
+ sys_close(fd);
+ if (error != N_DATADDR(ex)) {
+ send_sig(SIGKILL, current, 0);
+ return error;
+ }
+ }
+beyond_if:
+ if (current->exec_domain && current->exec_domain->module)
+ __MOD_DEC_USE_COUNT(current->exec_domain->module);
+ if (current->binfmt && current->binfmt->module)
+ __MOD_DEC_USE_COUNT(current->binfmt->module);
+ current->exec_domain = lookup_exec_domain(current->personality);
+ current->binfmt = &aout32_format;
+ if (current->exec_domain && current->exec_domain->module)
+ __MOD_INC_USE_COUNT(current->exec_domain->module);
+ if (current->binfmt && current->binfmt->module)
+ __MOD_INC_USE_COUNT(current->binfmt->module);
+
+ set_brk(current->mm->start_brk, current->mm->brk);
+
+ p = setup_arg_pages(p, bprm);
+
+ p = (unsigned long) create_aout32_tables((char *)p, bprm);
+ current->mm->start_stack = p;
+ start_thread32(regs, ex.a_entry, p);
+ if (current->flags & PF_PTRACED)
+ send_sig(SIGTRAP, current, 0);
+ return 0;
+}
+
+
+static int
+load_aout32_binary(struct linux_binprm * bprm, struct pt_regs * regs)
+{
+ int retval;
+
+ MOD_INC_USE_COUNT;
+ retval = do_load_aout32_binary(bprm, regs);
+ MOD_DEC_USE_COUNT;
+ return retval;
+}
+
+static inline int
+do_load_aout32_library(int fd)
+{
+ struct file * file;
+ struct exec ex;
+ struct inode * inode;
+ unsigned int len;
+ unsigned int bss;
+ unsigned int start_addr;
+ unsigned long error;
+
+ file = current->files->fd[fd];
+ inode = file->f_inode;
+
+ if (!file || !file->f_op)
+ return -EACCES;
+
+ /* Seek into the file */
+ if (file->f_op->llseek) {
+ if ((error = file->f_op->llseek(inode, file, 0, 0)) != 0)
+ return -ENOEXEC;
+ } else
+ file->f_pos = 0;
+
+ set_fs(KERNEL_DS);
+ error = file->f_op->read(inode, file, (char *) &ex, sizeof(ex));
+ set_fs(USER_DS);
+ if (error != sizeof(ex))
+ return -ENOEXEC;
+
+ /* We come in here for the regular a.out style of shared libraries */
+ if ((N_MAGIC(ex) != ZMAGIC && N_MAGIC(ex) != QMAGIC) || N_TRSIZE(ex) ||
+ N_DRSIZE(ex) || ((ex.a_entry & 0xfff) && N_MAGIC(ex) == ZMAGIC) ||
+ inode->i_size < ex.a_text+ex.a_data+N_SYMSIZE(ex)+N_TXTOFF(ex)) {
+ return -ENOEXEC;
+ }
+ if (N_MAGIC(ex) == ZMAGIC && N_TXTOFF(ex) &&
+ (N_TXTOFF(ex) < inode->i_sb->s_blocksize)) {
+ printk("N_TXTOFF < BLOCK_SIZE. Please convert library\n");
+ return -ENOEXEC;
+ }
+
+ if (N_FLAGS(ex)) return -ENOEXEC;
+
+ /* For QMAGIC, the starting address is 0x20 into the page. We mask
+ this off to get the starting address for the page */
+
+ start_addr = ex.a_entry & 0xfffff000;
+
+ /* Now use mmap to map the library into memory. */
+ error = do_mmap(file, start_addr, ex.a_text + ex.a_data,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE,
+ N_TXTOFF(ex));
+ if (error != start_addr)
+ return error;
+ len = PAGE_ALIGN(ex.a_text + ex.a_data);
+ bss = ex.a_text + ex.a_data + ex.a_bss;
+ if (bss > len) {
+ error = do_mmap(NULL, start_addr + len, bss-len,
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_PRIVATE|MAP_FIXED, 0);
+ if (error != start_addr + len)
+ return error;
+ }
+ return 0;
+}
+
+static int
+load_aout32_library(int fd)
+{
+ int retval;
+
+ MOD_INC_USE_COUNT;
+ retval = do_load_aout32_library(fd);
+ MOD_DEC_USE_COUNT;
+ return retval;
+}
+
+
+__initfunc(int init_aout32_binfmt(void))
+{
+ return register_binfmt(&aout32_format);
+}
#include <linux/kernel.h>
#include <linux/init.h>
+#include <asm/asi.h>
#include <asm/system.h>
+#include <asm/fpumacro.h>
struct cpu_iu_info {
short manuf;
int manuf, impl;
unsigned i, cpuid;
long ver, fpu_vers;
-
- cpuid = get_cpuid();
+ long fprs;
+#ifndef __SMP__
+ cpuid = 0;
+#else
+#error SMP not supported on sparc64 yet
+ /* cpuid = get_cpuid(); */
+#endif
+
+ fprs = fprs_read ();
+ fprs_write (FPRS_FEF);
__asm__ __volatile__ ("rdpr %%ver, %0; stx %%fsr, [%1]" : "=r" (ver) : "r" (&fpu_vers));
-
+ fprs_write (fprs);
+
manuf = ((ver >> 48)&0xffff);
impl = ((ver >> 32)&0xffff);
#include <linux/kernel.h>
#include <linux/tasks.h>
-#include <linux/config.h>
#include <linux/init.h>
#include <asm/page.h>
#include <asm/smp.h>
struct prom_cpuinfo linux_cpus[NCPUS];
-int linux_num_cpus;
+int linux_num_cpus = 0;
extern void cpu_probe(void);
};
if(cpu_ctr == 0) {
printk("No CPU nodes found, cannot continue.\n");
- halt();
+ prom_halt();
}
printk("Found %d CPU prom device tree node(s).\n", cpu_ctr);
};
-/* $Id: dtlb_miss.S,v 1.11 1997/04/10 01:59:35 davem Exp $
+/* $Id: dtlb_miss.S,v 1.12 1997/06/26 12:47:08 jj Exp $
* dtlb_miss.S: Data TLB miss code, this is included directly
* into the trap table.
*
* }
* goto longer_processing;
* } else {
- * if(fault_address >= KERNBASE &&
- * fault_address < VMALLOC_START) {
- * tlb_load(__pa(fault_address) | PAGE_KERNEL);
+ * if(fault_address >= PAGE_OFFSET) {
+ * pte_val = PAGE_KERNEL;
+ * if (fault_address & 0x10000000000)
+ * pte_val = PAGE_KERNEL_IO;
+ * tlb_load(__pa(fault_address) | pte_val);
* return_from_trap();
* } else {
* pgd = pgd_offset(swapper_pg_dir, fault_address);
* This is optimized for user TLB misses on purpose.
*/
-#define KERN_HIGHBITS (_PAGE_VALID | _PAGE_SZ4MB)
+#define KERN_HIGHBITS ((_PAGE_VALID | _PAGE_SZ4MB) ^ 0xfffff80000000000)
#define KERN_LOWBITS (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_W)
-#define KERN_LOWBITS_IO (_PAGE_E | _PAGE_P | _PAGE_W)
+#define KERN_LOWBITS_IO ((_PAGE_E | _PAGE_P | _PAGE_W) ^ KERN_LOWBITS)
/* ICACHE line 1 */
/*0x00*/ ldxa [%g0] ASI_DMMU, %g1 ! Get TAG_TARGET
1:/*0x3c*/ retry ! Trap return
3: /* ICACHE line 3 */
- /*0x40*/ sllx %g1, 43, %g5 ! This gets >= VMALLOC_START...
- /*0x44*/ brlz,pn %g5, 4f ! ...if now less than zero.
- /*0x48*/ andncc %g1, 0x3ff, %g0 ! Slick trick...
- /*0x4c*/ be,pn %xcc, 4f ! Yes, it is some PROM mapping
- /*0x50*/ srlx %g5, 21, %g5 ! This is now physical page
- /*0x54*/ sethi %uhi(KERN_HIGHBITS), %g1 ! Construct PTE
- /*0x58*/ sllx %g1, 32, %g1 ! Move priv bits up
- /*0x5c*/ or %g1, %g5, %g1 ! Or in the page
+ /*0x40*/ sllx %g1, 22, %g5 ! This is now physical page + PAGE_OFFSET
+ /*0x44*/ brgez,pn %g5, 4f ! If >= 0, then walk down page tables
+ /*0x48*/ sethi %uhi(KERN_HIGHBITS), %g1 ! Construct PTE ^ PAGE_OFFSET
+ /*0x4c*/ andcc %g3, 0x80, %g0 ! Slick trick...
+ /*0x50*/ sllx %g1, 32, %g1 ! Move high bits up
+ /*0x54*/ or %g1, (KERN_LOWBITS), %g1 ! Assume not IO
+ /*0x58*/ bne,a,pn %icc, 5f ! Is it an IO page?
+ /*0x5c*/ xor %g1, (KERN_LOWBITS_IO), %g1 ! Aha, it is IO...
/* ICACHE line 4 */
- /*0x60*/ or %g1, (KERN_LOWBITS), %g1 ! Set low priv bits
+5:/*0x60*/ xor %g1, %g5, %g1 ! Slick trick II...
/*0x64*/ stxa %g1, [%g0] ASI_DTLB_DATA_IN ! TLB load
/*0x68*/ retry ! Trap return
4:/*0x6c*/ ldxa [%g0] ASI_DMMU_TSB_8KB_PTR, %g1 ! For PTE offset
#undef KERN_HIGHBITS
#undef KERN_LOWBITS
+#undef KERN_LOWBITS_IO
-/* $Id: entry.S,v 1.31 1997/06/02 06:33:25 davem Exp $
+/* $Id: entry.S,v 1.45 1997/07/05 09:52:25 davem Exp $
* arch/sparc64/kernel/entry.S: Sparc64 trap low-level entry points.
*
- * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu)
* Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
* Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
* Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
-#include <linux/config.h>
#include <linux/errno.h>
#include <asm/head.h>
#define NR_SYSCALLS 256 /* Each OS is different... */
.text
- .align 4
.globl sparc64_dtlb_prot_catch, sparc64_dtlb_refbit_catch
.globl sparc64_itlb_refbit_catch
* to update the dirty bit) and since we left crap in the sfsr
* it will not get updated properly.
*/
+ .align 32
sparc64_dtlb_prot_catch:
wr %g0, ASI_DMMU, %asi
rdpr %pstate, %g1
wrpr %g1, PSTATE_AG|PSTATE_MG, %pstate
rdpr %tl, %g3
ldxa [%g0 + TLB_TAG_ACCESS] %asi, %g5
- ldxa [%g0 + TLB_SFSR] %asi, %g4
- cmp %g3, 1
stxa %g0, [%g0 + TLB_SFSR] %asi
+ membar #Sync
+ cmp %g3, 1
+
bgu,a,pn %icc, winfix_trampoline
rdpr %tpc, %g3
ba,pt %xcc, etrap
rd %pc, %g7
- b,a,pt %xcc, 1f
-
+ b,pt %xcc, 1f
+ mov 1, %o2
sparc64_dtlb_refbit_catch:
srlx %g5, 9, %g4
and %g4, ((_PAGE_PRESENT | _PAGE_READ) >> 9), %g4
+
cmp %g4, ((_PAGE_PRESENT | _PAGE_READ) >> 9)
be,a,pt %xcc, 2f
mov 1, %g4
wrpr %g1, PSTATE_AG|PSTATE_MG, %pstate
rdpr %tl, %g3
ldxa [%g0 + TLB_TAG_ACCESS] %asi, %g5
+
cmp %g3, 1
- clr %g4 ! sfsr not updated for tlb misses
- bgu,a,pn %icc, winfix_trampoline
+ bgu,pn %icc, winfix_trampoline
rdpr %tpc, %g3
- ba,pt %xcc, etrap
+ b,pt %xcc, etrap
rd %pc, %g7
-1:
- mov %l5, %o4 ! raw tag access
- mov %l4, %o5 ! raw sfsr
- srlx %l5, PAGE_SHIFT, %o3
- clr %o1 ! text_fault == 0
- sllx %o3, PAGE_SHIFT, %o3 ! address
- and %l4, 0x4, %o2 ! write == sfsr.W
+ clr %o2
+1: srlx %l5, PAGE_SHIFT, %o1
+ add %sp, STACK_BIAS + REGWIN_SZ, %o0
+
call do_sparc64_fault
- add %sp, STACK_BIAS + REGWIN_SZ, %o0 ! pt_regs ptr
- ba,pt %xcc, rtrap
+ sllx %o1, PAGE_SHIFT, %o1
+ b,pt %xcc, rtrap
clr %l6
+ nop
+ nop
+ nop
+ nop
sparc64_itlb_refbit_catch:
srlx %g5, 9, %g4
mov 1, %g4
rdpr %pstate, %g1
wrpr %g1, PSTATE_AG|PSTATE_MG, %pstate
- ba,pt %xcc, etrap
- rd %pc, %g7
-
- ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC], %o3
- mov 1, %o1 ! text_fault == 1
- clr %o2 ! write == 0
- clr %o4 ! tag access (N/A)
- clr %o5 ! raw sfsr (N/A)
- call do_sparc64_fault
- add %sp, STACK_BIAS + REGWIN_SZ, %o0 ! pt_regs ptr
- ba,pt %xcc, rtrap
- clr %l6
+ rdpr %tpc, %g5
-2:
- sllx %g4, 63, %g4 ! _PAGE_VALID
+ b,pt %xcc, etrap
+ rd %pc, %g7
+ b,pt %xcc, 1b
+ clr %o2
+2: sllx %g4, 63, %g4 ! _PAGE_VALID
or %g5, _PAGE_ACCESSED, %g5
or %g5, %g4, %g5
stxa %g5, [%g3 + %g1] ASI_PHYS_USE_EC ! store new PTE
+
stxa %g5, [%g0] ASI_DTLB_DATA_IN ! TLB load
retry
-
-3:
- sllx %g4, 63, %g4 ! _PAGE_VALID
+3: sllx %g4, 63, %g4 ! _PAGE_VALID
or %g5, _PAGE_ACCESSED, %g5
or %g5, %g4, %g5
stxa %g5, [%g3 + %g1] ASI_PHYS_USE_EC ! store new PTE
stxa %g5, [%g0] ASI_ITLB_DATA_IN ! TLB load
retry
+ /* This is trivial with the new code... */
+ .align 32
+ .globl do_fpdis
+do_fpdis:
+ wr %g0, FPRS_FEF, %fprs
+ ldx [%g6 + AOFF_task_flags], %g2
+ sethi %hi(0x00100000), %g4 ! XXX PF_USEDFPU
+ andcc %g2, %g4, %g0
+
+ bne,a,pt %xcc, fpload_fromkstk
+ sethi %hi((((PAGE_SIZE<<1)-((64*4)+(2*8))) & ~(64 - 1))), %g2
+ fzero %f0
+ fzero %f2
+ faddd %f0, %f2, %f4
+ fmuld %f0, %f2, %f6
+ faddd %f0, %f2, %f8
+ fmuld %f0, %f2, %f10
+
+ faddd %f0, %f2, %f12
+ fmuld %f0, %f2, %f14
+ faddd %f0, %f2, %f16
+ fmuld %f0, %f2, %f18
+ faddd %f0, %f2, %f20
+ fmuld %f0, %f2, %f22
+ faddd %f0, %f2, %f24
+ fmuld %f0, %f2, %f26
+
+ faddd %f0, %f2, %f28
+ fmuld %f0, %f2, %f30
+ faddd %f0, %f2, %f32
+ fmuld %f0, %f2, %f34
+ faddd %f0, %f2, %f36
+ fmuld %f0, %f2, %f38
+ faddd %f0, %f2, %f40
+ fmuld %f0, %f2, %f42
+
+ faddd %f0, %f2, %f44
+ fmuld %f0, %f2, %f46
+ ldx [%g6 + AOFF_task_flags], %g2
+ faddd %f0, %f2, %f48
+ fmuld %f0, %f2, %f50
+ or %g2, %g4, %g2
+ faddd %f0, %f2, %f52
+ fmuld %f0, %f2, %f54
+
+ stx %g2, [%g6 + AOFF_task_flags]
+ faddd %f0, %f2, %f56
+ sethi %hi(empty_zero_page), %g3
+ fmuld %f0, %f2, %f58
+
+ faddd %f0, %f2, %f60
+ ldx [%g3], %fsr ! wheee, empty_zero_page
+ b,pt %xcc, fpdis_exit
+ wr %g0, 0, %gsr
+
+fpload_fromkstk:
+ or %g2, %lo((((PAGE_SIZE<<1)-((64*4)+(2*8))) & ~(64 - 1))), %g2
+ add %g6, %g2, %g2
+ mov SECONDARY_CONTEXT, %g3
+ stxa %g0, [%g3] ASI_DMMU
+ flush %g2
+ wr %g0, ASI_BLK_S, %asi ! grrr, where is ASI_BLK_NUCLEUS 8-(
+ membar #StoreLoad | #LoadLoad
+
+ ldda [%g2 + 0x000] %asi, %f0
+ ldda [%g2 + 0x040] %asi, %f16
+ ldda [%g2 + 0x080] %asi, %f32
+ ldda [%g2 + 0x0c0] %asi, %f48
+ ldx [%g2 + 0x100], %fsr
+ ldx [%g2 + 0x108], %g2
+ membar #Sync
+ wr %g2, 0, %gsr
+fpdis_exit:
+ rdpr %tstate, %g3
+ sethi %hi(TSTATE_PEF), %g4
+ or %g3, %g4, %g3 ! anal...
+ wrpr %g3, %tstate
+ retry
+
/* Note check out head.h, this code isn't even used for UP,
* for SMP things will be different. In particular the data
* registers for cross calls will be:
* With this method we can do most of the cross-call tlb/cache
* flushing in very quickly.
*/
- .align 4
+ .align 32
.globl do_ivec
do_ivec:
ldxa [%g0] ASI_INTR_RECEIVE, %g1
mov 0x40, %g2
/* Load up Interrupt Vector Data 0 register. */
- sethi %uhi(ivector_to_mask), %g4
ldxa [%g2] ASI_UDB_INTR_R, %g3
- or %g4, %ulo(ivector_to_mask), %g4
- and %g3, 0x7ff, %g3
- sllx %g4, 32, %g4
sethi %hi(ivector_to_mask), %g5
+ and %g3, 0x7ff, %g3
+ orcc %g5, %lo(ivector_to_mask), %g5
sllx %g3, 3, %g3
- or %g5, %lo(ivector_to_mask), %g5
- add %g5, %g4, %g4
- ldx [%g4 + %g3], %g2
+ ldx [%g5 + %g3], %g2
brz,pn %g2, do_ivec_spurious
nop
do_ivec_spurious:
stxa %g0, [%g0] ASI_INTR_RECEIVE
+ membar #Sync
rdpr %pstate, %g1
wrpr %g1, PSTATE_IG | PSTATE_AG, %pstate
ba,pt %xcc, etrap
ba,pt %xcc, rtrap
clr %l6
- .globl do_mna
+ .globl getcc, setcc
+getcc:
+ ldx [%o0 + PT_V9_TSTATE], %o1
+ srlx %o1, 32, %o1
+ and %o1, 0xf, %o1
+ retl
+ stx %o1, [%o0 + PT_V9_G1]
+setcc:
+ ldx [%o0 + PT_V9_TSTATE], %o1
+ ldx [%o0 + PT_V9_G1], %o2
+ or %g0, %ulo(TSTATE_ICC), %o3
+ sllx %o3, 32, %o3
+ andn %o1, %o3, %o1
+ sllx %o2, 32, %o2
+ and %o2, %o3, %o2
+ or %o1, %o2, %o1
+ retl
+ stx %o1, [%o0 + PT_V9_TSTATE]
+
+ /* XXX Here is stuff we still need to write... -DaveM XXX */
+ .globl floppy_hardint, indirect_syscall, netbsd_syscall
+ .globl solaris_syscall
+floppy_hardint:
+indirect_syscall:
+netbsd_syscall:
+solaris_syscall:
+ retl
+ nop
+
+ .globl do_mna
do_mna:
rdpr %tl, %g3
cmp %g3, 1
ba,pt %xcc, rtrap
nop
- .globl sys_pipe, sys_execve, sys_sigpause, sys_nis_syscall
- .globl sys_sigsuspend, sys_sigreturn
- .globl sys32_execve, sys_ptrace
-
-sys_pipe:
- sethi %hi(sparc_pipe), %g1
- add %g1, %g4, %g1
- jmpl %g1 + %lo(sparc_pipe), %g0
- add %sp, STACK_BIAS + REGWIN_SZ, %o0
-
-sys_nis_syscall:
- sethi %hi(c_sys_nis_syscall), %g1
- add %g1, %g4, %g1
- jmpl %g1 + %lo(c_sys_nis_syscall), %g0
- add %sp, STACK_BIAS + REGWIN_SZ, %o0
-
-sys_execve:
- sethi %hi(sparc_execve), %g1
- add %g1, %g4, %g1
- jmpl %g1 + %lo(sparc_execve), %g0
- add %sp, STACK_BIAS + REGWIN_SZ, %o0
-
-sys32_execve:
- sethi %hi(sparc32_execve), %g1
- add %g1, %g4, %g1
- jmpl %g1 + %lo(sparc32_execve), %g0
- add %sp, STACK_BIAS + REGWIN_SZ, %o0
-
-sys_sigpause:
- /* NOTE: %o0 has a correct value already */
- call do_sigpause
- add %sp, STACK_BIAS + REGWIN_SZ, %o1
-
- ld [%curptr + AOFF_task_flags], %l5
- andcc %l5, 0x20, %g0
- be,pt %icc, rtrap
- clr %l6
- call syscall_trace
+ /* SunOS uses syscall zero as the 'indirect syscall' it looks
+ * like indir_syscall(scall_num, arg0, arg1, arg2...); etc.
+ * This is complete brain damage.
+ */
+ .globl sunos_indir
+sunos_indir:
+ srl %o0, 0, %o0
+ mov %o7, %l4
+ cmp %o0, NR_SYSCALLS
+ blu,a,pt %icc, 1f
+ sll %o0, 0x3, %o0
+ sethi %hi(sunos_nosys), %l6
+ b,pt %xcc, 2f
+ or %l6, %lo(sunos_nosys), %l6
+1: sethi %hi(sunos_sys_table), %l7
+ or %l7, %lo(sunos_sys_table), %l7
+ ldx [%l7 + %o0], %l6
+2: mov %o1, %o0
+ mov %o2, %o1
+ mov %o3, %o2
+ mov %o4, %o3
+ mov %o5, %o4
+ call %l6
+ mov %l4, %o7
+
+ .globl sunos_getpid
+sunos_getpid:
+ call sys_getppid
nop
- ba,pt %xcc, rtrap
- clr %l6
-
-sys_sigsuspend:
- call do_sigsuspend
- add %sp, STACK_BIAS + REGWIN_SZ, %o0
-
- ld [%curptr + AOFF_task_flags], %l5
- andcc %l5, 0x20, %g0
- be,pt %icc, rtrap
- clr %l6
- call syscall_trace
+ call sys_getpid
+ stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1]
+ b,pt %xcc, ret_sys_call
+ stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0]
+
+ /* SunOS getuid() returns uid in %o0 and euid in %o1 */
+ .globl sunos_getuid
+sunos_getuid:
+ call sys_geteuid
nop
- ba,pt %xcc, rtrap
- clr %l6
-
-sys_sigreturn:
- call do_sigreturn
- add %sp, STACK_BIAS + REGWIN_SZ, %o0
-
- ld [%curptr + AOFF_task_flags], %l5
- andcc %l5, 0x20, %g0
- be,pt %icc, rtrap
- clr %l6
- call syscall_trace
+ call sys_getuid
+ stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1]
+ b,pt %xcc, ret_sys_call
+ stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0]
+
+ /* SunOS getgid() returns gid in %o0 and egid in %o1 */
+ .globl sunos_getgid
+sunos_getgid:
+ call sys_getegid
nop
- ba,pt %xcc, rtrap
- clr %l6
+ call sys_getgid
+ stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1]
+ b,pt %xcc, ret_sys_call
+ stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0]
-sys_ptrace:
- call do_ptrace
- add %sp, STACK_BIAS + REGWIN_SZ, %o0
+ /* SunOS's execv() call only specifies the argv argument, the
+ * environment settings are the same as the calling processes.
+ */
+ .globl sunos_execv
+sunos_execv:
+ sethi %hi(sparc32_execve), %g1
+ stx %g0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I2]
+ jmpl %g1 + %lo(sparc32_execve), %g0
+ add %sp, STACK_BIAS + REGWIN_SZ, %o0
+
+ .globl sys_pipe, sys_execve, sys_sigpause, sys_nis_syscall
+ .globl sys_sigsuspend, sys_sigreturn
+ .globl sys32_execve, sys_ptrace
+ .align 32
+sys_pipe: sethi %hi(sparc_pipe), %g1
+ add %sp, STACK_BIAS + REGWIN_SZ, %o0
+ jmpl %g1 + %lo(sparc_pipe), %g0
+ nop
+sys_nis_syscall:sethi %hi(c_sys_nis_syscall), %g1
+ add %sp, STACK_BIAS + REGWIN_SZ, %o0
+ jmpl %g1 + %lo(c_sys_nis_syscall), %g0
+ nop
+
+sys_execve: sethi %hi(sparc_execve), %g1
+ add %sp, STACK_BIAS + REGWIN_SZ, %o0
+ jmpl %g1 + %lo(sparc_execve), %g0
+ nop
+sys32_execve: sethi %hi(sparc32_execve), %g1
+ add %sp, STACK_BIAS + REGWIN_SZ, %o0
+ jmpl %g1 + %lo(sparc32_execve), %g0
+ nop
+
+ /* NOTE: %o0 has a correct value already */
+sys_sigpause: call do_sigpause
+ add %sp, STACK_BIAS + REGWIN_SZ, %o1
+ ldx [%curptr + AOFF_task_flags], %l5
+ andcc %l5, 0x20, %g0
+ be,pt %icc, rtrap
+ clr %l6
+ call syscall_trace
+ nop
+
+ ba,pt %xcc, rtrap
+ clr %l6
+linux_sparc_ni_syscall:
+ sethi %hi(sys_ni_syscall), %l7
+ b,pt %xcc,syscall_is_too_hard
+ or %l7, %lo(sys_ni_syscall), %l7
+ nop
+
+ .align 32
+sys_sigsuspend: call do_sigsuspend
+ add %sp, STACK_BIAS + REGWIN_SZ, %o0
+ ldx [%curptr + AOFF_task_flags], %l5
+ andcc %l5, 0x20, %g0
+ be,pt %icc, rtrap
+ clr %l6
+ call syscall_trace
+ nop
+
+ ba,pt %xcc, rtrap
+ clr %l6
- ld [%curptr + AOFF_task_flags], %l5
- andcc %l5, 0x20, %g0
- be,pt %icc, rtrap
- clr %l6
- call syscall_trace
- nop
- ba,pt %xcc, rtrap
- clr %l6
+ .align 32
+sys_sigreturn: call do_sigreturn
+ add %sp, STACK_BIAS + REGWIN_SZ, %o0
+ ldx [%curptr + AOFF_task_flags], %l5
+ andcc %l5, 0x20, %g0
+ be,pt %icc, rtrap
+ clr %l6
+ call syscall_trace
+ nop
+
+ ba,pt %xcc, rtrap
+ clr %l6
+
+ .align 32
+sys_ptrace: call do_ptrace
+ add %sp, STACK_BIAS + REGWIN_SZ, %o0
+ ldx [%curptr + AOFF_task_flags], %l5
+ andcc %l5, 0x20, %g0
+ be,pt %icc, rtrap
+ clr %l6
+ call syscall_trace
+ nop
+
+ ba,pt %xcc, rtrap
+ clr %l6
/* This is how fork() was meant to be done, 12 instruction entry. -DaveM */
- .globl sys_fork, sys_vfork, sys_clone
+ .globl sys_fork, sys_vfork, sys_clone, ret_from_syscall
+ .align 32
sys_fork:
-sys_vfork:
- mov SIGCHLD, %o0
- clr %o1
-sys_clone:
- mov %o7, %l5
- save %sp, -REGWIN_SZ, %sp
- flushw
- restore %g0, %g0, %g0
- rdpr %cwp, %o4
- add %sp, STACK_BIAS + REGWIN_SZ, %o2
- movrz %o1, %fp, %o1
-
- /* Don't try this at home. */
- stx %o4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G0]
- call do_fork
- mov %l5, %o7
-
-linux_sparc_ni_syscall:
- sethi %hi(sys_ni_syscall), %l7
- or %l7, %lo(sys_ni_syscall), %l7
- ba,pt %xcc,syscall_is_too_hard
- add %l7, %g4, %l7
-
-linux_fast_syscall:
- andn %l7, 3, %l7
- mov %i0, %o0
- mov %i1, %o1
- mov %i2, %o2
- jmpl %l7 + %g0, %g0
- mov %i3, %o3
+sys_vfork: mov SIGCHLD, %o0
+ clr %o1
+sys_clone: mov %o7, %l5
+ save %sp, -REGWIN_SZ, %sp
+ flushw
+ restore %g0, %g0, %g0
+ rdpr %cwp, %o4
+ add %sp, STACK_BIAS + REGWIN_SZ, %o2
+
+ movrz %o1, %fp, %o1
+ stx %o4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G0]
+ call do_fork
+ mov %l5, %o7
+ret_from_syscall:b,pt %xcc, ret_sys_call
+ ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0], %o0
+ nop
+ nop
linux_syscall_trace:
- call syscall_trace
- nop
- mov %i0, %o0
- mov %i1, %o1
- mov %i2, %o2
- mov %i3, %o3
- ba,pt %xcc, 2f
- mov %i4, %o4
-
- .globl ret_from_syscall
-ret_from_syscall:
- ba,pt %xcc, ret_sys_call
- ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0], %o0
+ call syscall_trace
+ nop
+ mov %i0, %o0
+ mov %i1, %o1
+ mov %i2, %o2
+ mov %i3, %o3
+ b,pt %xcc, 2f
+ mov %i4, %o4
/* Linux native and SunOS system calls enter here... */
- .align 4
- .globl linux_sparc_syscall
+ .align 32
+ .globl linux_sparc_syscall, syscall_is_too_hard, ret_sys_call
linux_sparc_syscall:
/* Direct access to user regs, must faster. */
- cmp %g1, NR_SYSCALLS
- add %l7, %g4, %l7
- bgeu,pn %xcc, linux_sparc_ni_syscall
- sll %g1, 3, %l4
- ldx [%l7 + %l4], %l7
- andcc %l7, 1, %g0
- bne,pn %icc, linux_fast_syscall
- /* Just do the next insn in the delay slot */
-
- .globl syscall_is_too_hard
+ cmp %g1, NR_SYSCALLS ! IEU1 Group
+ bgeu,pn %xcc, linux_sparc_ni_syscall ! CTI
+ mov %i0, %o0 ! IEU0
+ sll %g1, 3, %l4 ! IEU0 Group
+ mov %i1, %o1 ! IEU1
+ ldx [%l7 + %l4], %l7 ! Load
syscall_is_too_hard:
-#ifdef SYSCALL_TRACING /* Debugging... */
- mov %g1, %o0 ! o0=scall, o1=ptregs
- call syscall_trace_entry
- add %sp, STACK_BIAS + REGWIN_SZ, %o1
-#endif
- mov %i0, %o0
- mov %i1, %o1
- mov %i2, %o2
-
- ldx [%curptr + AOFF_task_flags], %l5
- mov %i3, %o3
- mov %i4, %o4
- andcc %l5, 0x20, %g0
- bne,pn %icc, linux_syscall_trace
- mov %i0, %l5
-2:
- call %l7
- mov %i5, %o5
-
-#ifdef SYSCALL_TRACING /* Debugging... */
- call syscall_trace_exit ! o0=sysret, o1=ptregs
- add %sp, STACK_BIAS + REGWIN_SZ, %o1
-#endif
+ mov %i2, %o2 ! IEU0 Group
+ ldx [%curptr + AOFF_task_flags], %l5 ! Load
+
+ st %g0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_FPRS]
+ mov %i3, %o3 ! IEU1
+ mov %i4, %o4 ! IEU0 Group
+ andcc %l5, 0x20, %g0 ! IEU1 2 bubbles
+ bne,pn %icc, linux_syscall_trace ! CTI Group
+ mov %i0, %l5 ! IEU0
+2: call %l7 ! CTI Group brk forced
+ mov %i5, %o5 ! IEU0
stx %o0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0]
- .globl ret_sys_call
ret_sys_call:
ldx [%curptr + AOFF_task_flags], %l6
- ldx [%curptr + AOFF_task_tss + AOFF_thread_flags], %l2
+ sra %o0, 0, %o0
mov %ulo(TSTATE_XCARRY | TSTATE_ICARRY), %g2
- and %l2, SPARC_FLAG_32BIT, %l2
ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE], %g3
- brnz,a,pn %l2, 1f
- sra %o0, 0, %o0
-1:
cmp %o0, -ENOIOCTLCMD
sllx %g2, 32, %g2
bgeu,pn %xcc, 1f
/* System call success, clear Carry condition code. */
andn %g3, %g2, %g3
- clr %l6
stx %g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE]
bne,pn %icc, linux_syscall_trace2
ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1 ! pc = npc
add %l1, 0x4, %l2 !npc = npc+4
stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC]
- ba,pt %xcc, rtrap
+ b,pt %xcc, rtrap_clr_l6
stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC]
1:
/* System call failure, set Carry condition code.
bne,pn %icc, linux_syscall_trace2
ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %l1 ! pc = npc
add %l1, 0x4, %l2 !npc = npc+4
+
stx %l1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC]
- ba,pt %xcc, rtrap
+ b,pt %xcc, rtrap
stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC]
-
linux_syscall_trace2:
call syscall_trace
add %l1, 0x4, %l2 /* npc = npc+4 */
ba,pt %xcc, rtrap
stx %l2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC]
-/* End of entry.S */
+ .align 32
+ .globl __flushw_user
+__flushw_user:
+1: save %sp, -128, %sp
+ rdpr %otherwin, %g1
+ brnz,pt %g1, 1b
+ add %g2, 1, %g2
+1: sub %g2, 1, %g2
+ brnz,pt %g2, 1b
+ restore %g0, %g0, %g0
+2: retl
+ mov %g3, %o7
-/* $Id: etrap.S,v 1.22 1997/06/13 14:02:40 davem Exp $
+/* $Id: etrap.S,v 1.30 1997/06/30 10:31:37 jj Exp $
* etrap.S: Preparing for entry into the kernel on Sparc V9.
*
* Copyright (C) 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
#include <asm/spitfire.h>
#include <asm/head.h>
- /* We assume that pstate, when entering this, has AG and
- * IE bits set, MG and IG clear.
- *
- * We also guarentee for caller that AG %g4 and %g5 will have
- * their values preserved and left in %l4 and %l5 respectively
- * for him (fault handling needs this).
- */
+#define FPUREG_SZ ((64 * 4) + (2 * 8))
+#define TASK_REGOFF ((((PAGE_SIZE<<1)-FPUREG_SZ)&~(64-1)) - \
+ TRACEREG_SZ-REGWIN_SZ)
- .text
- .align 32
- .globl etrap, etrap_irq, etraptl1
-etrap:
- rdpr %pil, %g2
-etrap_irq:
- rdpr %tstate, %g1
- sllx %g2, 20, %g2
- or %g1, %g2, %g1
- andcc %g1, TSTATE_PRIV, %g0
- bne,a,pn %xcc, 1f
- sub %sp, REGWIN_SZ + TRACEREG_SZ - STACK_BIAS, %g2
- rd %pic, %g3
+ .text
+ .align 32
+ .globl etrap, etrap_irq, etraptl1
- sethi %hi((PAGE_SIZE<<1)-TRACEREG_SZ-REGWIN_SZ), %g2
- or %g2, %lo((PAGE_SIZE<<1)-TRACEREG_SZ-REGWIN_SZ), %g2
- add %g3, %g2, %g2
-1: stx %g1, [%g2 + REGWIN_SZ + PT_V9_TSTATE]
- rdpr %tpc, %g1
- rdpr %tnpc, %g3
- stx %g1, [%g2 + REGWIN_SZ + PT_V9_TPC]
- rd %y, %g1
+etrap: rdpr %pil, %g2
+etrap_irq: rdpr %tstate, %g1
+ sllx %g2, 20, %g2
+ or %g1, %g2, %g1
+ andcc %g1, TSTATE_PRIV, %g0
+ bne,pn %xcc, etrap_maybe_fpu
+ sub %sp, REGWIN_SZ + TRACEREG_SZ - STACK_BIAS, %g2
+ sethi %hi(TASK_REGOFF), %g2
- stx %g3, [%g2 + REGWIN_SZ + PT_V9_TNPC]
- stx %g1, [%g2 + REGWIN_SZ + PT_V9_Y]
- save %g2, -STACK_BIAS, %sp ! The ordering of these two instructions
- rdpr %pstate, %g1 ! is critical, see winfixup.S for details
- bne,pn %xcc, 2f
- rdpr %canrestore, %g3
- rdpr %wstate, %g6
- wrpr %g0, 7, %cleanwin
+ or %g2, %lo(TASK_REGOFF), %g2
+ add %g6, %g2, %g2
+etrap_maybe_fpu:rd %fprs, %g3
+ brnz,pn %g3, etrap_save_fpu
+ st %g0, [%g2 + REGWIN_SZ + PT_V9_FPRS]
+etrap_after_fpu:rdpr %tpc, %g3
+ stx %g1, [%g2 + REGWIN_SZ + PT_V9_TSTATE]
+ rdpr %tnpc, %g1
- wrpr %g0, 0, %canrestore
- sll %g6, 3, %g6
- wrpr %g3, 0, %otherwin
- wrpr %g6, %wstate
- sethi %uhi(KERNBASE), %g3
- sllx %g3, 32, %g3
- mov PRIMARY_CONTEXT, %g2
- ldxa [%g2] ASI_DMMU, %g6
- stxa %g0, [%g2] ASI_DMMU ! XXX fixup cache if this stays...
- mov SECONDARY_CONTEXT, %g2
- stxa %g6, [%g2] ASI_DMMU
+ stx %g3, [%g2 + REGWIN_SZ + PT_V9_TPC]
+ rd %y, %g3
+ stx %g1, [%g2 + REGWIN_SZ + PT_V9_TNPC]
+ st %g3, [%g2 + REGWIN_SZ + PT_V9_Y]
+ save %g2, -STACK_BIAS, %sp ! The ordering here is
+ rdpr %pstate, %g1 ! critical, see winfixup
+ bne,pn %xcc, 2f
+ rdpr %canrestore, %g3
- flush %g3
-2: wrpr %g0, 0x0, %tl
- mov %g1, %l1
- mov %g4, %l4
- mov %g5, %l5
- mov %g7, %l2
- wrpr %l1, PSTATE_AG, %pstate
- stx %g1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G1]
+ rdpr %wstate, %g2
+ wrpr %g0, 7, %cleanwin
+ wrpr %g0, 0, %canrestore
+ sll %g2, 3, %g2
+ wrpr %g3, 0, %otherwin
+ wrpr %g2, 0, %wstate
+ wr %g0, ASI_DMMU, %asi
+ ldxa [%g0 + PRIMARY_CONTEXT] %asi, %g2
- stx %g2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G2]
- stx %g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G3]
- stx %g4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G4]
- stx %g5, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G5]
- stx %g6, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G6]
- stx %g7, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G7]
- stx %i0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0]
- stx %i1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1]
+ stxa %g0, [%g0 + PRIMARY_CONTEXT] %asi
+ stxa %g2, [%g0 + SECONDARY_CONTEXT] %asi
+ flush %g6
+2: wrpr %g0, 0x0, %tl
+ or %g1, 0, %l1
+ add %g4, 0, %l4
+ or %g5, 0, %l5
+ add %g7, 0, %l2
- stx %i2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I2]
- stx %i3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I3]
- stx %i4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I4]
- stx %i5, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I5]
- stx %i6, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I6]
- stx %i7, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I7]
- wrpr %l1, (PSTATE_IE | PSTATE_AG), %pstate
- sethi %uhi(KERNBASE), %g4
+ or %g6, 0, %l6
+ wrpr %l1, (PSTATE_AG|PSTATE_RMO), %pstate
+ stx %g1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G1]
+ stx %g2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G2]
+ stx %g3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G3]
+ stx %g4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G4]
+ stx %g5, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G5]
+ stx %g6, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G6]
- rd %pic, %g6
- jmpl %l2 + 0x4, %g0
- sllx %g4, 32, %g4
-etraptl1:
- rdpr %tstate, %g1
- sub %sp, REGWIN_SZ + TRACEREG_SZ - STACK_BIAS, %g2
- ba,pt %xcc, 1b
- andcc %g1, TSTATE_PRIV, %g0
- nop
+ stx %g7, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G7]
+ stx %i0, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0]
+ stx %i1, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1]
+ stx %i2, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I2]
+ stx %i3, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I3]
+ stx %i4, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I4]
+ sethi %uhi(PAGE_OFFSET), %g4
+ stx %i5, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I5]
+
+ stx %i6, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I6]
+ sllx %g4, 32, %g4
+ stx %i7, [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I7]
+ wrpr %l1, (PSTATE_IE|PSTATE_AG|PSTATE_RMO), %pstate
+ jmpl %l2 + 0x4, %g0
+ mov %l6, %g6
+etrap_save_fpu: and %g3, FPRS_FEF, %g3
+ brz,pn %g3, 2f
+
+ nop
+ be,a,pt %xcc, 3f
+ add %g2, (TRACEREG_SZ + REGWIN_SZ), %g2
+ wr %g0, ASI_BLK_P, %asi
+ add %g2, ((TRACEREG_SZ+REGWIN_SZ)-FPUREG_SZ), %g2
+ andn %g2, (64 - 1), %g2
+1: st %g3, [%g2 - 0x4 /*REGWIN_SZ + PT_V9_FPRS*/]
+ rd %gsr, %g3
+
+ stx %fsr, [%g2 + 0x100]
+ stx %g3, [%g2 + 0x108]
+ membar #StoreStore | #LoadStore
+ stda %f0, [%g2 + 0x000] %asi
+ stda %f16, [%g2 + 0x040] %asi
+ stda %f32, [%g2 + 0x080] %asi
+ stda %f48, [%g2 + 0x0c0] %asi
+ membar #Sync
+
+ sub %g2, (TRACEREG_SZ + REGWIN_SZ), %g2
+2: b,pt %xcc, etrap_after_fpu
+ wr %g0, 0, %fprs
+3: /* Because Ultra lacks ASI_BLK_NUCLEUS a hack has to take place. */
+ mov SECONDARY_CONTEXT, %g3
+ stxa %g0, [%g3] ASI_DMMU
+ flush %g2
+ wr %g0, ASI_BLK_S, %asi
+ nop
+
+ b,pt %xcc, 1b
+ mov FPRS_FEF, %g3
+ nop
+etraptl1: rdpr %tstate, %g1
+ sub %sp, REGWIN_SZ + TRACEREG_SZ - STACK_BIAS, %g2
+ ba,pt %xcc, etrap_maybe_fpu
+ andcc %g1, TSTATE_PRIV, %g0
+ nop
+#undef TASK_REGOFF
+#undef FPUREG_SZ
+++ /dev/null
-/* <hack>
- This is just a huge ugly hack to get things compiled.
- Hopefully will disappear quickly, once we get everything
- to compile... */
- .text
- .align 8
- .globl breakpoint
-breakpoint: retl;nop
- .globl do_cee
-do_cee: retl;nop
- .globl do_cee_tl1
-do_cee_tl1: retl;nop
- .globl do_dae_tl1
-do_dae_tl1: retl;nop
- .globl do_div0_tl1
-do_div0_tl1: retl;nop
- .globl do_fpdis_tl1
-do_fpdis_tl1: retl;nop
- .globl do_fpieee_tl1
-do_fpieee_tl1: retl;nop
- .globl do_fpother_tl1
-do_fpother_tl1: retl;nop
- .globl do_iae_tl1
-do_iae_tl1: retl;nop
- .globl do_ill_tl1
-do_ill_tl1: retl;nop
- .globl do_irq_tl1
-do_irq_tl1: retl;nop
- .globl do_lddfmna
-do_lddfmna: retl;nop
- .globl do_lddfmna_tl1
-do_lddfmna_tl1: retl;nop
- .globl do_paw
-do_paw: retl;nop
- .globl do_paw_tl1
-do_paw_tl1: retl;nop
- .globl do_stdfmna
-do_stdfmna: retl;nop
- .globl do_stdfmna_tl1
-do_stdfmna_tl1: retl;nop
- .globl do_tof_tl1
-do_tof_tl1: retl;nop
- .globl do_vaw
-do_vaw: retl;nop
- .globl do_vaw_tl1
-do_vaw_tl1: retl;nop
- .globl floppy_hardint
-floppy_hardint: retl;nop
- .globl get_cpuid
-get_cpuid: retl;mov 0, %o0
- .globl getcc
-getcc: retl;nop
- .globl halt
-halt: retl;nop
- .globl indirect_syscall
-indirect_syscall: retl;nop
- .globl install_linux_ticker
-install_linux_ticker: retl;nop
- .globl install_obp_ticker
-install_obp_ticker: retl;nop
- .globl linux_dbvec
-linux_dbvec: retl;nop
- .globl linux_num_cpus
-linux_num_cpus: retl;nop
- .globl netbsd_syscall
-netbsd_syscall: retl;nop
- .globl setcc
-setcc: retl;nop
- .globl solaris_syscall
-solaris_syscall: retl;nop
- .globl sunos_mmap
-sunos_mmap: retl;nop
- .globl sunos_syscall
-sunos_syscall: retl;nop
- .globl svr4_getcontext
-svr4_getcontext: retl;nop
- .globl svr4_setcontext
-svr4_setcontext: retl;nop
- .globl sunos_accept
-sunos_accept: retl;nop
- .globl sunos_audit
-sunos_audit: retl;nop
- .globl sunos_brk
-sunos_brk: retl;nop
- .globl sunos_execv
-sunos_execv: retl;nop
- .globl sunos_fpathconf
-sunos_fpathconf: retl;nop
- .globl sunos_getdents
-sunos_getdents: retl;nop
- .globl sunos_getdirentries
-sunos_getdirentries: retl;nop
- .globl sunos_getdomainname
-sunos_getdomainname: retl;nop
- .globl sunos_getdtablesize
-sunos_getdtablesize: retl;nop
- .globl sunos_getgid
-sunos_getgid: retl;nop
- .globl sunos_gethostid
-sunos_gethostid: retl;nop
- .globl sunos_getpid
-sunos_getpid: retl;nop
- .globl sunos_getsockopt
-sunos_getsockopt: retl;nop
- .globl sunos_getuid
-sunos_getuid: retl;nop
- .globl sunos_indir
-sunos_indir: retl;nop
- .globl sunos_ioctl
-sunos_ioctl: retl;nop
- .globl sunos_killpg
-sunos_killpg: retl;nop
- .globl sunos_madvise
-sunos_madvise: retl;nop
- .globl sunos_mctl
-sunos_mctl: retl;nop
- .globl sunos_mincore
-sunos_mincore: retl;nop
- .globl sunos_mount
-sunos_mount: retl;nop
- .globl sunos_nop
-sunos_nop: retl;nop
- .globl sunos_nosys
-sunos_nosys: retl;nop
- .globl sunos_open
-sunos_open: retl;nop
- .globl sunos_pathconf
-sunos_pathconf: retl;nop
- .globl sunos_poll
-sunos_poll: retl;nop
- .globl sunos_read
-sunos_read: retl;nop
- .globl sunos_readv
-sunos_readv: retl;nop
- .globl sunos_recv
-sunos_recv: retl;nop
- .globl sunos_sbrk
-sunos_sbrk: retl;nop
- .globl sunos_select
-sunos_select: retl;nop
- .globl sunos_semsys
-sunos_semsys: retl;nop
- .globl sunos_send
-sunos_send: retl;nop
- .globl sunos_setpgrp
-sunos_setpgrp: retl;nop
- .globl sunos_setsockopt
-sunos_setsockopt: retl;nop
- .globl sunos_shmsys
-sunos_shmsys: retl;nop
- .globl sunos_sigaction
-sunos_sigaction: retl;nop
- .globl sunos_sigblock
-sunos_sigblock: retl;nop
- .globl sunos_sigsetmask
-sunos_sigsetmask: retl;nop
- .globl sunos_sstk
-sunos_sstk: retl;nop
- .globl sunos_sysconf
-sunos_sysconf: retl;nop
- .globl sunos_uname
-sunos_uname: retl;nop
- .globl sunos_vadvise
-sunos_vadvise: retl;nop
- .globl sunos_wait4
-sunos_wait4: retl;nop
- .globl sunos_write
-sunos_write: retl;nop
- .globl sunos_writev
-sunos_writev: retl;nop
-/* $Id: head.S,v 1.31 1997/05/30 22:35:28 davem Exp $
+/* $Id: head.S,v 1.43 1997/07/07 03:05:25 davem Exp $
* head.S: Initial boot code for the Sparc64 port of Linux.
*
* Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu)
*/
#include <linux/version.h>
+#include <linux/errno.h>
+#include <asm/asm_offsets.h>
+#include <asm/asi.h>
#include <asm/pstate.h>
#include <asm/ptrace.h>
#include <asm/spitfire.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/errno.h>
+#include <asm/signal.h>
+#include <asm/processor.h>
#include <asm/lsu.h>
#include <asm/head.h>
/* This section from from _start to sparc64_boot_end should fit into
- * 0xffff.f800.0000.4000 to 0xffff.f800.0000.8000 and will be sharing space
- * with bootup_user_stack, which is from 0xffff.f800.0000.4000 to
- * 0xffff.f800.0000.6000 and bootup_kernel_stack, which is from
- * 0xffff.f800.0000.6000 to 0xffff.f800.0000.8000.
+ * 0x0000.0000.0040.4000 to 0x0000.0000.0040.8000 and will be sharing space
+ * with bootup_user_stack, which is from 0x0000.0000.0040.4000 to
+ * 0x0000.0000.0040.6000 and bootup_kernel_stack, which is from
+ * 0x0000.0000.0040.6000 to 0x0000.0000.0040.8000.
*/
.text
_stext:
stext:
bootup_user_stack:
-! 0xfffff80000004000
+! 0x0000000000404000
b sparc64_boot
flushw /* Flush register file. */
*/
.global root_flags, ram_flags, root_dev
.global ramdisk_image, ramdisk_size
+ .globl silo_args
.ascii "HdrS"
.word LINUX_VERSION_CODE
- .half 0x0201 /* HdrS version */
+ .half 0x0202 /* HdrS version */
root_flags:
.half 1
root_dev:
.word 0
ramdisk_size:
.word 0
- .xword reboot_command
+ .xword reboot_command
+ .xword bootstr_len
/* We must be careful, 32-bit OpenBOOT will get confused if it
* tries to save away a register window to a 64-bit kernel
* Again, typically PROM has left %pil at 13 or similar, and
* (PSTATE_PRIV | PSTATE_PEF | PSTATE_IE) in %pstate.
*/
- wrpr %g0, 0xf, %pil /* Interrupts off. */
- wrpr %g0, (PSTATE_PRIV|PSTATE_PEF), %pstate
-
- /* Check if we are mapped where we expect to be in virtual
- * memory. The Solaris /boot elf format bootloader
- * will peek into our elf header and load us where
- * we want to be, otherwise we have to re-map.
- */
-current_pc:
- rd %pc, %g3
- sethi %uhi(KERNBASE), %g4
- sllx %g4, 32, %g4
-
- /* Check the run time program counter. */
-
- set current_pc, %g5
- add %g5, %g4, %g5
- cmp %g3, %g5
- be %xcc, sun4u_init
- nop
+ wrpr %g0, (PSTATE_PRIV|PSTATE_PEF|PSTATE_IE), %pstate
create_mappings:
/* %g5 holds the tlb data */
cmp %g1, %g2
be,a,pn %xcc, got_tlbentry
ldxa [%l0] ASI_ITLB_DATA_ACCESS, %g1
- cmp %l1, (63 << 3)
+ cmp %l0, (63 << 3)
blu,pt %xcc, 1b
add %l0, (1 << 3), %l0
-boot_failed:
- /* Debugging 8-) */
- set 0xdeadbeef, %g1
- t 0x11
-
got_tlbentry:
/* Nops here again, perhaps Cheetah/Blackbird are better behaved... */
nop
or %g5, %g1, %g5 /* Or it into TAG being built. */
+ clr %l0 /* TLB entry walker. */
+ sethi %hi(KERNBASE), %g3 /* 4M lower limit */
+ sethi %hi(KERNBASE<<1), %g7 /* 8M upper limit */
+ mov TLB_TAG_ACCESS, %l7
+1:
+ /* Yes, the nops seem to be necessary for now, don't ask me why. -DaveM */
+ ldxa [%l0] ASI_ITLB_TAG_READ, %g1
+ nop
+ nop
+ nop
+ andn %g1, %l2, %g1 /* Get vaddr */
+ cmp %g1, %g3
+ blu,pn %xcc, 2f
+ cmp %g1, %g7
+ bgeu,pn %xcc, 2f
+ nop
+ stxa %g0, [%l7] ASI_IMMU
+ stxa %g0, [%l0] ASI_ITLB_DATA_ACCESS
+2:
+ cmp %l0, (63 << 3)
+ blu,pt %xcc, 1b
+ add %l0, (1 << 3), %l0
+
+ nop; nop; nop
+
+ clr %l0 /* TLB entry walker. */
+1:
+ /* Yes, the nops seem to be necessary for now, don't ask me why. -DaveM */
+ ldxa [%l0] ASI_DTLB_TAG_READ, %g1
+ nop
+ nop
+ nop
+ andn %g1, %l2, %g1 /* Get vaddr */
+ cmp %g1, %g3
+ blu,pn %xcc, 2f
+ cmp %g1, %g7
+ bgeu,pn %xcc, 2f
+ nop
+ stxa %g0, [%l7] ASI_DMMU
+ stxa %g0, [%l0] ASI_DTLB_DATA_ACCESS
+2:
+ cmp %l0, (63 << 3)
+ blu,pt %xcc, 1b
+ add %l0, (1 << 3), %l0
+
+ nop; nop; nop
+
+
/* PROM never puts any TLB entries into the MMU with the lock bit
- * set. So we gladly use tlb entry 63 for KERNBASE, 62 for
- * boot time locked PROM CIF handler page, we remove the locked
- * bit for the CIF page in paging_init().
+ * set. So we gladly use tlb entry 63 for KERNBASE.
*/
- mov TLB_TAG_ACCESS, %g3
- mov (63 << 3), %g7
- stxa %g4, [%g3] ASI_IMMU /* KERNBASE into TLB TAG */
- stxa %g5, [%g7] ASI_ITLB_DATA_ACCESS /* TTE into TLB DATA */
- membar #Sync
- /* Same for DTLB */
- stxa %g4, [%g3] ASI_DMMU /* KERNBASE into TLB TAG */
+ sethi %hi(KERNBASE), %g3
+ mov (63 << 3), %g7
+ stxa %g3, [%l7] ASI_DMMU /* KERNBASE into TLB TAG */
stxa %g5, [%g7] ASI_DTLB_DATA_ACCESS /* TTE into TLB DATA */
membar #Sync
-
- /* Kill instruction prefetch queues. */
- flush %g4
+ stxa %g3, [%l7] ASI_IMMU /* KERNBASE into TLB TAG */
+ stxa %g5, [%g7] ASI_ITLB_DATA_ACCESS /* TTE into TLB DATA */
membar #Sync
-
- ba,pt %xcc, go_to_highmem
+ flush %g3
+ membar #Sync
+ ba,pt %xcc, 1f
nop
-
-go_to_highmem:
- /* Now do a non-relative jump so that PC is in high-memory */
+1:
set sun4u_init, %g2
- jmpl %g2 + %g4, %g0
+ jmpl %g2 + %g0, %g0
nop
sun4u_init:
stxa %g0, [%g7] ASI_DMMU
membar #Sync
- /* The lock bit has to be removed from this page later on,
- * but before firing up init we will use PROM a lot, so we
- * lock it there now...
- */
-
- /* Compute PROM CIF interface page TTE. */
- sethi %hi(__p1275_loc), %g7
- or %g7, (_PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_W | _PAGE_L), %g7
- sethi %uhi(_PAGE_VALID), %g5
- sethi %hi(0x8000), %g3
- sllx %g5, 32, %g5
- mov TLB_TAG_ACCESS, %g6
- or %g5, %g7, %g5
- add %g5, %g1, %g5 /* Add in physbase. */
-
- mov (62 << 3), %g7 /* TLB entry 62 */
- stxa %g3, [%g6] ASI_IMMU /* CIF page into TLB TAG */
- stxa %g5, [%g7] ASI_ITLB_DATA_ACCESS /* TTE into TLB DATA */
- membar #Sync
-
- /* Same for DTLB */
- stxa %g3, [%g6] ASI_DMMU /* CIF page into TLB TAG */
- stxa %g5, [%g7] ASI_DTLB_DATA_ACCESS /* TTE into TLB DATA */
- membar #Sync
-
- /* Kill instruction prefetch queues. */
- flush %g3
- membar #Sync
+ sethi %uhi(PAGE_OFFSET), %g4
+ sllx %g4, 32, %g4
/* We are now safely (we hope) in Nucleus context (0), rewrite
* the KERNBASE TTE's so they no longer have the global bit set.
* Don't forget to setup TAG_ACCESS first 8-)
*/
mov TLB_TAG_ACCESS, %g2
- stxa %g4, [%g2] ASI_IMMU
- stxa %g4, [%g2] ASI_DMMU
+ stxa %g3, [%g2] ASI_IMMU
+ stxa %g3, [%g2] ASI_DMMU
mov (63 << 3), %g7
ldxa [%g7] ASI_ITLB_DATA_ACCESS, %g1
membar #Sync
/* Kill instruction prefetch queues. */
- flush %g4
+ flush %g3
membar #Sync
- /* Compute the number of windows in this machine
- * store this in nwindows and nwindowsm1
- */
- rdpr %ver, %g1 /* Get VERSION register. */
- sethi %hi(nwindows), %g2
- and %g1, VERS_MAXWIN, %g5
- or %g2,%lo(nwindows),%g2
- add %g5, 1, %g6
- add %g2, (nwindows - nwindowsm1), %g3
- stx %g6, [%g2 + %g4]
- stx %g5, [%g3 + %g4]
-
sethi %hi(init_task_union), %g6
or %g6, %lo(init_task_union), %g6
- add %g6, %g4, %g6 ! g6 usage is fixed as well
mov %sp, %l6
mov %o4, %l7
+#if 0 /* We don't do it like this anymore, but for historical hack value
+ * I leave this snippet here to show how crazy we can be sometimes. 8-)
+ */
+
/* Setup "Linux Current Register", thanks Sun 8-) */
wr %g0, 0x1, %pcr
wr %g6, 0x0, %pic
+#endif
mov 1, %g5
sllx %g5, (PAGE_SHIFT + 1), %g5
add %l1, %l2, %l1
andn %l1, %l2, %l1
add %l2, 1, %l2
- add %l0, %g4, %o0
+ add %l0, %g0, %o0
1:
- clr %o1
- sethi %hi(PAGE_SIZE), %o2
- or %o2, %lo(PAGE_SIZE), %o2
- call __memset
+ mov %l2, %o1
+ call __bzero
add %l0, %l2, %l0
cmp %l0, %l1
blu,pt %xcc, 1b
- add %l0, %g4, %o0
+ add %l0, %g0, %o0
/* Now clear empty_zero_page */
- clr %o1
- sethi %hi(PAGE_SIZE), %o2
- or %o2, %lo(PAGE_SIZE), %o2
- call __memset
- mov %g4, %o0
+ mov %l2, %o1
+ call __bzero
+ mov %g3, %o0
mov %l6, %o1 ! OpenPROM stack
call prom_init
.globl setup_tba
setup_tba:
+ save %sp, -160, %sp
+
+ rdpr %tba, %g7
+ sethi %hi(prom_tba), %o1
+ or %o1, %lo(prom_tba), %o1
+ stx %g7, [%o1]
+
+ /* Setup "Linux" globals 8-) */
+ rdpr %pstate, %o1
+ mov %g6, %o2
+ wrpr %o1, (PSTATE_AG|PSTATE_IE), %pstate
sethi %hi(sparc64_ttable_tl0), %g5
- add %g5, %g4, %g5
wrpr %g5, %tba
+ mov %o2, %g6
/* Set up MMU globals */
- rdpr %pstate, %o1
- wrpr %o1, PSTATE_MG, %pstate
+ wrpr %o1, (PSTATE_MG|PSTATE_IE), %pstate
/* PGD/PMD offset mask, used by TLB miss handlers. */
sethi %hi(0x1ff8), %g2
or %g2, %lo(0x1ff8), %g2
/* Kernel PGDIR used by TLB miss handlers. */
- mov %o0, %g6
+ mov %i0, %g6
/* To catch bootup bugs, this is user PGDIR for TLB miss handlers. */
clr %g7
/* Setup Interrupt globals */
- wrpr %o1, PSTATE_IG, %pstate
- sethi %uhi(ivector_to_mask), %g4
- or %g4, %ulo(ivector_to_mask), %g4
+ wrpr %o1, (PSTATE_IG|PSTATE_IE), %pstate
sethi %hi(ivector_to_mask), %g5
- or %g5, %lo(ivector_to_mask), %g5
- or %g5, %g4, %g1 /* IVECTOR table */
+ or %g5, %lo(ivector_to_mask), %g1 /* IVECTOR table */
mov 0x40, %g2 /* INTR data 0 register */
- andn %o1, PSTATE_IE, %o1
+ /* Ok, we're done setting up all the state our trap mechanims needs,
+ * now get back into normal globals and let the PROM know what it up.
+ */
wrpr %g0, %g0, %wstate
- wrpr %o1, %g0, %pstate
+ wrpr %o1, PSTATE_IE, %pstate
/* Zap TSB BASE to zero with TSB_size==1. */
mov TSB_REG, %o4
membar #Sync
- retl
- nop
+ sethi %hi(sparc64_ttable_tl0), %g5
+ call prom_set_trap_table
+ mov %g5, %o0
+
+ rdpr %pstate, %o1
+ or %o1, PSTATE_IE, %o1
+ wrpr %o1, 0, %pstate
+
+ ret
+ restore
sparc64_boot_end:
.skip 0x2000 + _start - sparc64_boot_end
bootup_kernel_stack:
.skip 0x2000
-! 0xfffff80000008000
+! 0x0000000000408000
#include "ttable.S"
+#include "etrap.S"
+#include "rtrap.S"
+#include "winfixup.S"
+#include "entry.S"
/* This is just anal retentiveness on my part... */
.align 16384
.data
.align 8
- .globl nwindows, nwindowsm1
-nwindows: .xword 0
-nwindowsm1: .xword 0
+ .globl prom_tba
+prom_tba: .xword 0
.section ".fixup",#alloc,#execinstr
.globl __ret_efault
__ret_efault:
-/* $Id: ioport.c,v 1.7 1997/04/10 05:13:01 davem Exp $
+/* $Id: ioport.c,v 1.10 1997/06/30 09:24:02 jj Exp $
* ioport.c: Simple io mapping allocator.
*
* Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu)
/* Tell Linux resource manager about the mapping */
request_region ((vaddr | offset), len, name);
} else {
- vaddr = occupy_region(sparc_iobase_vaddr, IOBASE_END,
- (offset + len + PAGE_SIZE-1) & PAGE_MASK, PAGE_SIZE, name);
- if (vaddr == 0) {
- /* Usually we cannot see printks in this case. */
- prom_printf("alloc_io: cannot occupy %d region\n", len);
- prom_halt();
- }
+ return __va(addr);
}
base_address = vaddr;
{
unsigned long vaddr = (unsigned long) virtual & PAGE_MASK;
unsigned long plen = (((unsigned long)virtual & ~PAGE_MASK) + len + PAGE_SIZE-1) & PAGE_MASK;
+
+ if (virtual >= PAGE_OFFSET + 0x10000000000UL)
+ return;
release_region(vaddr, plen);
-/* $Id: irq.c,v 1.13 1997/05/27 07:54:28 davem Exp $
+/* $Id: irq.c,v 1.14 1997/06/24 17:30:26 davem Exp $
* irq.c: UltraSparc IRQ handling/init/registry.
*
* Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
volatile u32 limit1, _unused3;
} *prom_timers;
+static u32 prom_limit0, prom_limit1;
+
static void map_prom_timers(void)
{
unsigned int addr[3];
if(!prom_timers)
return;
+ /* Save them away for later. */
+ prom_limit0 = prom_timers->limit0;
+ prom_limit1 = prom_timers->limit1;
+
/* Just as in sun4c/sun4m PROM uses timer which ticks at IRQ 14.
* We turn both off here just to be paranoid.
*/
prom_timers->limit0 = 0;
prom_timers->limit1 = 0;
+
+ /* Wheee, eat the interrupt packet too... */
+ __asm__ __volatile__("
+ mov 0x40, %%g2
+ ldxa [%%g0] %0, %%g1
+ ldxa [%%g2] %1, %%g1
+ stxa %%g0, [%%g0] %0
+ membar #Sync
+" : /* no outputs */
+ : "i" (ASI_INTR_RECEIVE), "i" (ASI_UDB_INTR_R)
+ : "g1", "g2");
}
-#if 0 /* Unused at this time. -DaveM */
-static void enable_prom_timer(void)
+void enable_prom_timer(void)
{
if(!prom_timers)
return;
- /* Set it to fire off every 10ms. */
- prom_timers->limit1 = 0xa000270f;
+ /* Set it to whatever was there before. */
+ prom_timers->limit1 = prom_limit1;
prom_timers->count1 = 0;
+ prom_timers->limit0 = prom_limit0;
+ prom_timers->count0 = 0;
}
-#endif
__initfunc(void init_IRQ(void))
{
-/* $Id: process.c,v 1.18 1997/06/13 14:02:42 davem Exp $
+/* $Id: process.c,v 1.26 1997/07/01 21:15:07 jj Exp $
* arch/sparc64/kernel/process.c
*
* Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu)
prom_reboot(cmd);
if (*reboot_command)
prom_reboot(reboot_command);
- prom_feval ("reset");
+ prom_reboot("");
panic("Reboot failed!");
}
machine_halt();
}
-void show_regwindow(struct reg_window *rw)
+static void show_regwindow32(struct pt_regs *regs)
{
+ struct reg_window32 *rw;
+ struct reg_window32 r_w;
+ unsigned long old_fs;
+
+ __asm__ __volatile__ ("flushw");
+ rw = (struct reg_window32 *)((long)(unsigned)regs->u_regs[14]);
+ old_fs = get_fs();
+ set_fs (USER_DS);
+ if (copy_from_user (&r_w, rw, sizeof(r_w))) {
+ set_fs (old_fs);
+ return;
+ }
+ rw = &r_w;
+ set_fs (old_fs);
+ printk("l0: %016x l1: %016x l2: %016x l3: %016x\n"
+ "l4: %016x l5: %016x l6: %016x l7: %016x\n",
+ rw->locals[0], rw->locals[1], rw->locals[2], rw->locals[3],
+ rw->locals[4], rw->locals[5], rw->locals[6], rw->locals[7]);
+ printk("i0: %016x i1: %016x i2: %016x i3: %016x\n"
+ "i4: %016x i5: %016x i6: %016x i7: %016x\n",
+ rw->ins[0], rw->ins[1], rw->ins[2], rw->ins[3],
+ rw->ins[4], rw->ins[5], rw->ins[6], rw->ins[7]);
+}
+
+static void show_regwindow(struct pt_regs *regs)
+{
+ struct reg_window *rw;
+ struct reg_window r_w;
+ unsigned long old_fs;
+
+ if ((regs->tstate & TSTATE_PRIV) || !(current->tss.flags & SPARC_FLAG_32BIT)) {
+ __asm__ __volatile__ ("flushw");
+ rw = (struct reg_window *)(regs->u_regs[14] + STACK_BIAS);
+ if (!(regs->tstate & TSTATE_PRIV)) {
+ old_fs = get_fs();
+ set_fs (USER_DS);
+ if (copy_from_user (&r_w, rw, sizeof(r_w))) {
+ set_fs (old_fs);
+ return;
+ }
+ rw = &r_w;
+ set_fs (old_fs);
+ }
+ } else {
+ show_regwindow32(regs);
+ return;
+ }
printk("l0: %016lx l1: %016lx l2: %016lx l3: %016lx\n",
rw->locals[0], rw->locals[1], rw->locals[2], rw->locals[3]);
printk("l4: %016lx l5: %016lx l6: %016lx l7: %016lx\n",
rw->ins[4], rw->ins[5], rw->ins[6], rw->ins[7]);
}
-void show_regwindow32(struct reg_window32 *rw)
-{
- printk("l0: %08x l1: %08x l2: %08x l3: %08x\n"
- "l4: %08x l5: %08x l6: %08x l7: %08x\n",
- rw->locals[0], rw->locals[1], rw->locals[2], rw->locals[3],
- rw->locals[4], rw->locals[5], rw->locals[6], rw->locals[7]);
- printk("i0: %08x i1: %08x i2: %08x i3: %08x\n"
- "i4: %08x i5: %08x i6: %08x i7: %08x\n",
- rw->ins[0], rw->ins[1], rw->ins[2], rw->ins[3],
- rw->ins[4], rw->ins[5], rw->ins[6], rw->ins[7]);
-}
-
void show_stackframe(struct sparc_stackf *sf)
{
unsigned long size;
#if __MPP__
printk("CID: %d\n",mpp_cid());
#endif
- printk("TSTATE: %016lx TPC: %016lx TNPC: %016lx Y: %016lx\n", regs->tstate,
+ printk("TSTATE: %016lx TPC: %016lx TNPC: %016lx Y: %08x\n", regs->tstate,
regs->tpc, regs->tnpc, regs->y);
printk("g0: %016lx g1: %016lx g2: %016lx g3: %016lx\n",
regs->u_regs[0], regs->u_regs[1], regs->u_regs[2],
printk("o4: %016lx o5: %016lx sp: %016lx ret_pc: %016lx\n",
regs->u_regs[12], regs->u_regs[13], regs->u_regs[14],
regs->u_regs[15]);
-#if 0
- show_regwindow((struct reg_window *)(regs->u_regs[14] + STACK_BIAS));
-#endif
+ show_regwindow(regs);
}
void show_regs32(struct pt_regs32 *regs)
printk("o4: %08x o5: %08x sp: %08x ret_pc: %08x\n",
regs->u_regs[12], regs->u_regs[13], regs->u_regs[14],
regs->u_regs[15]);
- show_regwindow32((struct reg_window32 *)((unsigned long)regs->u_regs[14]));
}
void show_thread(struct thread_struct *tss)
continue;
printk("reg_window[%d]:\n", i);
printk("stack ptr: 0x%016lx\n", tss->rwbuf_stkptrs[i]);
- show_regwindow(&tss->reg_window[i]);
}
printk("w_saved: 0x%08lx\n", tss->w_saved);
- /* XXX missing: float_regs */
- printk("fsr: 0x%016lx\n", tss->fsr);
-
printk("sstk_info.stack: 0x%016lx\n",
(unsigned long)tss->sstk_info.the_stack);
printk("sstk_info.status: 0x%016lx\n",
(unsigned long)tss->sstk_info.cur_status);
- printk("flags: 0x%016lx\n", tss->flags);
- printk("current_ds: 0x%016x\n", tss->current_ds);
+ printk("flags: 0x%08x\n", tss->flags);
+ printk("current_ds: 0x%016lx\n", tss->current_ds);
/* XXX missing: core_exec */
}
-/*
- * Free current thread data structures etc..
- */
+/* Free current thread data structures etc.. */
void exit_thread(void)
{
-#ifndef __SMP__
- if(last_task_used_math == current) {
-#else
- if(current->flags & PF_USEDFPU) {
-#endif
- fprs_write(FPRS_FEF);
- if(current->tss.flags & SPARC_FLAG_32BIT)
- fpsave32((unsigned long *)¤t->tss.float_regs[0],
- ¤t->tss.fsr);
- else
- fpsave((unsigned long *)¤t->tss.float_regs[0],
- ¤t->tss.fsr);
-#ifndef __SMP__
- last_task_used_math = NULL;
-#else
- current->flags &= ~PF_USEDFPU;
-#endif
- }
}
void flush_thread(void)
current->tss.sstk_info.cur_status = 0;
current->tss.sstk_info.the_stack = 0;
- /* No new signal delivery by default */
+ /* No new signal delivery by default. */
current->tss.new_signal = 0;
-#ifndef __SMP__
- if(last_task_used_math == current) {
-#else
- if(current->flags & PF_USEDFPU) {
-#endif
- fprs_write(FPRS_FEF);
- if(current->tss.flags & SPARC_FLAG_32BIT)
- fpsave32((unsigned long *)¤t->tss.float_regs[0],
- ¤t->tss.fsr);
- else
- fpsave((unsigned long *)¤t->tss.float_regs[0],
- ¤t->tss.fsr);
-#ifndef __SMP__
- last_task_used_math = NULL;
-#else
- current->flags &= ~PF_USEDFPU;
-#endif
- }
+ current->flags &= ~PF_USEDFPU;
/* Now, this task is no longer a kernel thread. */
current->tss.current_ds = USER_DS;
}
current->tss.ctx = current->mm->context & 0x1fff;
spitfire_set_secondary_context (current->tss.ctx);
-}
-
-static __inline__ void copy_regs(struct pt_regs *dst, struct pt_regs *src)
-{
- __asm__ __volatile__("ldd\t[%1 + 0x00], %%g2\n\t"
- "ldd\t[%1 + 0x08], %%g4\n\t"
- "ldd\t[%1 + 0x10], %%o4\n\t"
- "std\t%%g2, [%0 + 0x00]\n\t"
- "std\t%%g4, [%0 + 0x08]\n\t"
- "std\t%%o4, [%0 + 0x10]\n\t"
- "ldd\t[%1 + 0x18], %%g2\n\t"
- "ldd\t[%1 + 0x20], %%g4\n\t"
- "ldd\t[%1 + 0x28], %%o4\n\t"
- "std\t%%g2, [%0 + 0x18]\n\t"
- "std\t%%g4, [%0 + 0x20]\n\t"
- "std\t%%o4, [%0 + 0x28]\n\t"
- "ldd\t[%1 + 0x30], %%g2\n\t"
- "ldd\t[%1 + 0x38], %%g4\n\t"
- "ldd\t[%1 + 0x40], %%o4\n\t"
- "std\t%%g2, [%0 + 0x30]\n\t"
- "std\t%%g4, [%0 + 0x38]\n\t"
- "ldd\t[%1 + 0x48], %%g2\n\t"
- "std\t%%o4, [%0 + 0x40]\n\t"
- "std\t%%g2, [%0 + 0x48]\n\t" : :
- "r" (dst), "r" (src) :
- "g2", "g3", "g4", "g5", "o4", "o5");
-}
-
-static __inline__ void copy_regwin(struct reg_window *dst, struct reg_window *src)
-{
- __asm__ __volatile__("ldd\t[%1 + 0x00], %%g2\n\t"
- "ldd\t[%1 + 0x08], %%g4\n\t"
- "ldd\t[%1 + 0x10], %%o4\n\t"
- "std\t%%g2, [%0 + 0x00]\n\t"
- "std\t%%g4, [%0 + 0x08]\n\t"
- "std\t%%o4, [%0 + 0x10]\n\t"
- "ldd\t[%1 + 0x18], %%g2\n\t"
- "ldd\t[%1 + 0x20], %%g4\n\t"
- "ldd\t[%1 + 0x28], %%o4\n\t"
- "std\t%%g2, [%0 + 0x18]\n\t"
- "std\t%%g4, [%0 + 0x20]\n\t"
- "std\t%%o4, [%0 + 0x28]\n\t"
- "ldd\t[%1 + 0x30], %%g2\n\t"
- "ldd\t[%1 + 0x38], %%g4\n\t"
- "std\t%%g2, [%0 + 0x30]\n\t"
- "std\t%%g4, [%0 + 0x38]\n\t" : :
- "r" (dst), "r" (src) :
- "g2", "g3", "g4", "g5", "o4", "o5");
+ __asm__ __volatile__("flush %g6");
}
static __inline__ struct sparc_stackf *
flush_user_windows();
if((window = tp->w_saved) != 0) {
int winsize = REGWIN_SZ;
+ int bias = 0;
#ifdef DEBUG_WINFIXUPS
printk("sus(%d", (int)window);
#endif
if(tp->flags & SPARC_FLAG_32BIT)
winsize = REGWIN32_SZ;
+ else
+ bias = STACK_BIAS;
window -= 1;
do {
- unsigned long sp = tp->rwbuf_stkptrs[window];
+ unsigned long sp = (tp->rwbuf_stkptrs[window] + bias);
struct reg_window *rwin = &tp->reg_window[window];
if(!copy_to_user((char *)sp, rwin, winsize)) {
struct thread_struct *tp = ¤t->tss;
unsigned long window;
int winsize = REGWIN_SZ;
+ int bias = 0;
if(tp->flags & SPARC_FLAG_32BIT)
winsize = REGWIN32_SZ;
+ else
+ bias = STACK_BIAS;
flush_user_windows();
window = tp->w_saved;
#ifdef DEBUG_WINFIXUPS
if(window != 0) {
window -= 1;
do {
- unsigned long sp = tp->rwbuf_stkptrs[window];
+ unsigned long sp = (tp->rwbuf_stkptrs[window] + bias);
struct reg_window *rwin = &tp->reg_window[window];
if(copy_to_user((char *)sp, rwin, winsize))
int copy_thread(int nr, unsigned long clone_flags, unsigned long sp,
struct task_struct *p, struct pt_regs *regs)
{
- struct pt_regs *childregs;
- struct reg_window *new_stack, *old_stack;
unsigned long stack_offset;
+ char *child_trap_frame;
+ int tframe_size;
-#ifndef __SMP__
- if(last_task_used_math == current) {
-#else
- if(current->flags & PF_USEDFPU) {
-#endif
- fprs_write(FPRS_FEF);
- fpsave((unsigned long *)&p->tss.float_regs[0], &p->tss.fsr);
-#ifdef __SMP__
- current->flags &= ~PF_USEDFPU;
+#if 0 /* Now all syscall entries flip off the fpu. */
+ if(regs->tstate & TSTATE_PRIV)
+ regs->fprs = 0;
#endif
- }
-
/* Calculate offset to stack_frame & pt_regs */
- stack_offset = ((PAGE_SIZE<<1) - TRACEREG_SZ);
-
- if(regs->tstate & TSTATE_PRIV)
- stack_offset -= REGWIN_SZ;
-
- childregs = ((struct pt_regs *) (((unsigned long)p) + stack_offset));
- *childregs = *regs;
- new_stack = (((struct reg_window *) childregs) - 1);
- old_stack = (((struct reg_window *) regs) - 1);
- *new_stack = *old_stack;
-
- p->tss.ksp = ((unsigned long) new_stack) - STACK_BIAS;
+ stack_offset = (((PAGE_SIZE << 1) -
+ ((sizeof(unsigned int)*64) + (2*sizeof(unsigned long)))) &
+ ~(64 - 1)) - (TRACEREG_SZ+REGWIN_SZ);
+ tframe_size = (TRACEREG_SZ + REGWIN_SZ) +
+ (sizeof(unsigned int) * 64) + (2 * sizeof(unsigned long));
+ child_trap_frame = ((char *)p) + stack_offset;
+ memcpy(child_trap_frame, (((struct reg_window *)regs)-1), tframe_size);
+ p->tss.ksp = ((unsigned long) child_trap_frame) - STACK_BIAS;
p->tss.kpc = ((unsigned long) ret_from_syscall) - 0x8;
- p->tss.kregs = childregs;
-
- /* Don't look... */
+ p->tss.kregs = (struct pt_regs *)(child_trap_frame+sizeof(struct reg_window));
p->tss.cwp = regs->u_regs[UREG_G0];
-
- /* tss.wstate was copied by do_fork() */
-
if(regs->tstate & TSTATE_PRIV) {
- childregs->u_regs[UREG_FP] = p->tss.ksp;
+ p->tss.kregs->u_regs[UREG_FP] = p->tss.ksp;
p->tss.flags |= SPARC_FLAG_KTHREAD;
p->tss.current_ds = KERNEL_DS;
p->tss.ctx = 0;
- childregs->u_regs[UREG_G6] = (unsigned long) p;
+ p->tss.kregs->u_regs[UREG_G6] = (unsigned long) p;
} else {
- childregs->u_regs[UREG_FP] = sp;
+ p->tss.kregs->u_regs[UREG_FP] = sp;
p->tss.flags &= ~SPARC_FLAG_KTHREAD;
p->tss.current_ds = USER_DS;
p->tss.ctx = (p->mm->context & 0x1fff);
*/
childstack = (struct sparc_stackf *)sp;
parentstack = (struct sparc_stackf *)regs->u_regs[UREG_FP];
-
-#if 0
- printk("clone: parent stack:\n");
- show_stackframe(parentstack);
-#endif
-
childstack = clone_stackframe(childstack, parentstack);
if (!childstack)
return -EFAULT;
-
-#if 0
- printk("clone: child stack:\n");
- show_stackframe(childstack);
-#endif
-
childregs->u_regs[UREG_FP] = (unsigned long)childstack;
}
#endif
}
/* Set the return value for the child. */
- childregs->u_regs[UREG_I0] = current->pid;
- childregs->u_regs[UREG_I1] = 1;
+ p->tss.kregs->u_regs[UREG_I0] = current->pid;
+ p->tss.kregs->u_regs[UREG_I1] = 1;
- /* Set the return value for the parent. */
+ /* Set the second return value for the parent. */
regs->u_regs[UREG_I1] = 0;
-#if 0
- printk("CHILD register dump\n");
- show_regs(childregs);
- show_regwindow(new_stack);
- while(1)
- barrier();
-#endif
return 0;
}
error = do_execve(filename, (char **) regs->u_regs[base + UREG_I1],
(char **) regs->u_regs[base + UREG_I2], regs);
putname(filename);
+ if(!error) {
+ fprs_write(0);
+ regs->fprs = 0;
+ }
return error;
}
/* this is a hack for non-kernel-mapped video buffers and similar */
flush_cache_page(vma, addr);
if (MAP_NR(page) < max_mapnr) {
- *(unsigned long *) (page + (addr & ~PAGE_MASK)) = data;
+ unsigned long pgaddr;
+
+ pgaddr = page + (addr & ~PAGE_MASK);
+ *(unsigned long *) (pgaddr) = data;
+
+ __asm__ __volatile__("
+ membar #StoreStore
+ flush %0
+" : : "r" (pgaddr & ~7) : "memory");
+
flush_page_to_ram(page);
}
/* we're bypassing pagetables, so we have to set the dirty bit ourselves */
/* this is a hack for non-kernel-mapped video buffers and similar */
flush_cache_page(vma, addr);
if (MAP_NR(page) < max_mapnr) {
- *(unsigned int *) (page + (addr & ~PAGE_MASK)) = data;
+ unsigned long pgaddr;
+
+ pgaddr = page + (addr & ~PAGE_MASK);
+ *(unsigned int *) (pgaddr) = data;
+
+ __asm__ __volatile__("
+ membar #StoreStore
+ flush %0
+" : : "r" (pgaddr & ~7) : "memory");
+
flush_page_to_ram(page);
}
/* we're bypassing pagetables, so we have to set the dirty bit ourselves */
pt_error_return(regs, ESRCH);
goto out;
}
+
+ if(!(child->tss.flags & SPARC_FLAG_32BIT) &&
+ ((request == PTRACE_READDATA64) ||
+ (request == PTRACE_WRITEDATA64) ||
+ (request == PTRACE_READTEXT64) ||
+ (request == PTRACE_WRITETEXT64) ||
+ (request == PTRACE_PEEKTEXT64) ||
+ (request == PTRACE_POKETEXT64) ||
+ (request == PTRACE_PEEKDATA64) ||
+ (request == PTRACE_POKEDATA64))) {
+ addr = regs->u_regs[UREG_G2];
+ addr2 = regs->u_regs[UREG_G3];
+ request -= 30; /* wheee... */
+ }
+
switch(request) {
case PTRACE_PEEKTEXT: /* read word at location addr. */
case PTRACE_PEEKDATA: {
goto out;
}
- case PTRACE_GETREGS:
- if (current->tss.flags & SPARC_FLAG_32BIT) {
- struct pt_regs32 *pregs = (struct pt_regs32 *) addr;
- struct pt_regs *cregs = child->tss.kregs;
- int rval;
-
- if (__put_user(tstate_to_psr(cregs->tstate), (&pregs->psr)) ||
- __put_user(cregs->tpc, (&pregs->pc)) ||
- __put_user(cregs->tnpc, (&pregs->npc)) ||
- __put_user(cregs->y, (&pregs->y))) {
+ case PTRACE_GETREGS: {
+ struct pt_regs32 *pregs = (struct pt_regs32 *) addr;
+ struct pt_regs *cregs = child->tss.kregs;
+ int rval;
+
+ if (__put_user(tstate_to_psr(cregs->tstate), (&pregs->psr)) ||
+ __put_user(cregs->tpc, (&pregs->pc)) ||
+ __put_user(cregs->tnpc, (&pregs->npc)) ||
+ __put_user(cregs->y, (&pregs->y))) {
+ pt_error_return(regs, EFAULT);
+ goto out;
+ }
+ for(rval = 1; rval < 16; rval++)
+ if (__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]))) {
pt_error_return(regs, EFAULT);
goto out;
}
- for(rval = 1; rval < 16; rval++)
- if (__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]))) {
- pt_error_return(regs, EFAULT);
- goto out;
- }
- pt_succ_return(regs, 0);
+ pt_succ_return(regs, 0);
#ifdef DEBUG_PTRACE
- printk ("PC=%lx nPC=%lx o7=%lx\n", cregs->tpc, cregs->tnpc, cregs->u_regs [15]);
+ printk ("PC=%lx nPC=%lx o7=%lx\n", cregs->tpc, cregs->tnpc, cregs->u_regs [15]);
#endif
+ goto out;
+ }
+
+ case PTRACE_GETREGS64: {
+ struct pt_regs *pregs = (struct pt_regs *) addr;
+ struct pt_regs *cregs = child->tss.kregs;
+ int rval;
+
+ if (__put_user(cregs->tstate, (&pregs->tstate)) ||
+ __put_user(cregs->tpc, (&pregs->tpc)) ||
+ __put_user(cregs->tnpc, (&pregs->tnpc)) ||
+ __put_user(cregs->y, (&pregs->y))) {
+ pt_error_return(regs, EFAULT);
goto out;
- } else {
- struct pt_regs *pregs = (struct pt_regs *) addr;
- struct pt_regs *cregs = child->tss.kregs;
- int rval;
-
- if (__put_user(cregs->tstate, (&pregs->tstate)) ||
- __put_user(cregs->tpc, (&pregs->tpc)) ||
- __put_user(cregs->tnpc, (&pregs->tnpc)) ||
- __put_user(cregs->y, (&pregs->y))) {
+ }
+ for(rval = 1; rval < 16; rval++)
+ if (__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]))) {
pt_error_return(regs, EFAULT);
goto out;
}
- for(rval = 1; rval < 16; rval++)
- if (__put_user(cregs->u_regs[rval], (&pregs->u_regs[rval - 1]))) {
- pt_error_return(regs, EFAULT);
- goto out;
- }
- pt_succ_return(regs, 0);
+ pt_succ_return(regs, 0);
#ifdef DEBUG_PTRACE
- printk ("PC=%lx nPC=%lx o7=%lx\n", cregs->tpc, cregs->tnpc, cregs->u_regs [15]);
+ printk ("PC=%lx nPC=%lx o7=%lx\n", cregs->tpc, cregs->tnpc, cregs->u_regs [15]);
#endif
+ goto out;
+ }
+
+ case PTRACE_SETREGS: {
+ struct pt_regs32 *pregs = (struct pt_regs32 *) addr;
+ struct pt_regs *cregs = child->tss.kregs;
+ unsigned int psr, pc, npc, y;
+ int i;
+
+ /* Must be careful, tracing process can only set certain
+ * bits in the psr.
+ */
+ if (__get_user(psr, (&pregs->psr)) ||
+ __get_user(pc, (&pregs->pc)) ||
+ __get_user(npc, (&pregs->npc)) ||
+ __get_user(y, (&pregs->y))) {
+ pt_error_return(regs, EFAULT);
goto out;
}
-
- case PTRACE_SETREGS:
- if (current->tss.flags & SPARC_FLAG_32BIT) {
- struct pt_regs32 *pregs = (struct pt_regs32 *) addr;
- struct pt_regs *cregs = child->tss.kregs;
- unsigned int psr, pc, npc, y;
- int i;
-
- /* Must be careful, tracing process can only set certain
- * bits in the psr.
- */
- if (__get_user(psr, (&pregs->psr)) ||
- __get_user(pc, (&pregs->pc)) ||
- __get_user(npc, (&pregs->npc)) ||
- __get_user(y, (&pregs->y))) {
+ cregs->tstate &= ~(TSTATE_ICC);
+ cregs->tstate |= psr_to_tstate_icc(psr);
+ if(!((pc | npc) & 3)) {
+ cregs->tpc = pc;
+ cregs->tpc = npc;
+ }
+ cregs->y = y;
+ for(i = 1; i < 16; i++)
+ if (__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]))) {
pt_error_return(regs, EFAULT);
goto out;
}
- cregs->tstate &= ~(TSTATE_ICC);
- cregs->tstate |= psr_to_tstate_icc(psr);
- if(!((pc | npc) & 3)) {
- cregs->tpc = pc;
- cregs->tpc = npc;
- }
- cregs->y = y;
- for(i = 1; i < 16; i++)
- if (__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]))) {
- pt_error_return(regs, EFAULT);
- goto out;
- }
- pt_succ_return(regs, 0);
- goto out;
- } else {
- struct pt_regs *pregs = (struct pt_regs *) addr;
- struct pt_regs *cregs = child->tss.kregs;
- unsigned long tstate, tpc, tnpc, y;
- int i;
+ pt_succ_return(regs, 0);
+ goto out;
+ }
- /* Must be careful, tracing process can only set certain
- * bits in the psr.
- */
- if (__get_user(tstate, (&pregs->tstate)) ||
- __get_user(tpc, (&pregs->tpc)) ||
- __get_user(tnpc, (&pregs->tnpc)) ||
- __get_user(y, (&pregs->y))) {
- pt_error_return(regs, EFAULT);
- goto out;
- }
- tstate &= (TSTATE_ICC | TSTATE_XCC);
- cregs->tstate &= ~(TSTATE_ICC | TSTATE_XCC);
- cregs->tstate |= tstate;
- if(!((tpc | tnpc) & 3)) {
- cregs->tpc = tpc;
- cregs->tnpc = tnpc;
- }
- cregs->y = y;
- for(i = 1; i < 16; i++)
- if (__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]))) {
- pt_error_return(regs, EFAULT);
- goto out;
- }
- pt_succ_return(regs, 0);
+ case PTRACE_SETREGS64: {
+ struct pt_regs *pregs = (struct pt_regs *) addr;
+ struct pt_regs *cregs = child->tss.kregs;
+ unsigned long tstate, tpc, tnpc, y;
+ int i;
+
+ /* Must be careful, tracing process can only set certain
+ * bits in the psr.
+ */
+ if (__get_user(tstate, (&pregs->tstate)) ||
+ __get_user(tpc, (&pregs->tpc)) ||
+ __get_user(tnpc, (&pregs->tnpc)) ||
+ __get_user(y, (&pregs->y))) {
+ pt_error_return(regs, EFAULT);
goto out;
}
-
- case PTRACE_GETFPREGS:
- if (current->tss.flags & SPARC_FLAG_32BIT) {
- struct fps {
- unsigned int regs[32];
- unsigned int fsr;
- unsigned int flags;
- unsigned int extra;
- unsigned int fpqd;
- struct fq {
- unsigned int insnaddr;
- unsigned int insn;
- } fpq[16];
- } *fps = (struct fps *) addr;
-
- if (copy_to_user(&fps->regs[0], &child->tss.float_regs[0], (32 * sizeof(unsigned int))) ||
- __put_user(child->tss.fsr, (&fps->fsr)) ||
- __put_user(0, (&fps->fpqd)) ||
- __put_user(0, (&fps->flags)) ||
- __put_user(0, (&fps->extra)) ||
- clear_user(&fps->fpq[0], 32 * sizeof(unsigned int))) {
+ tstate &= (TSTATE_ICC | TSTATE_XCC);
+ cregs->tstate &= ~(TSTATE_ICC | TSTATE_XCC);
+ cregs->tstate |= tstate;
+ if(!((tpc | tnpc) & 3)) {
+ cregs->tpc = tpc;
+ cregs->tnpc = tnpc;
+ }
+ cregs->y = y;
+ for(i = 1; i < 16; i++)
+ if (__get_user(cregs->u_regs[i], (&pregs->u_regs[i-1]))) {
pt_error_return(regs, EFAULT);
goto out;
}
- pt_succ_return(regs, 0);
+ pt_succ_return(regs, 0);
+ goto out;
+ }
+
+ case PTRACE_GETFPREGS: {
+ struct fps {
+ unsigned int regs[32];
+ unsigned int fsr;
+ unsigned int flags;
+ unsigned int extra;
+ unsigned int fpqd;
+ struct fq {
+ unsigned int insnaddr;
+ unsigned int insn;
+ } fpq[16];
+ } *fps = (struct fps *) addr;
+ unsigned long *fpregs = (unsigned long *)(child->tss.kregs+1);
+
+ if (copy_to_user(&fps->regs[0], fpregs,
+ (32 * sizeof(unsigned int))) ||
+ __put_user(((unsigned int)fpregs[32]), (&fps->fsr)) ||
+ __put_user(0, (&fps->fpqd)) ||
+ __put_user(0, (&fps->flags)) ||
+ __put_user(0, (&fps->extra)) ||
+ clear_user(&fps->fpq[0], 32 * sizeof(unsigned int))) {
+ pt_error_return(regs, EFAULT);
goto out;
- } else {
- struct fps {
- unsigned int regs[64];
- unsigned long fsr;
- } *fps = (struct fps *) addr;
+ }
+ pt_succ_return(regs, 0);
+ goto out;
+ }
- if (copy_to_user(&fps->regs[0], &child->tss.float_regs[0], (64 * sizeof(unsigned int))) ||
- __put_user(child->tss.fsr, (&fps->fsr))) {
- pt_error_return(regs, EFAULT);
- goto out;
- }
- pt_succ_return(regs, 0);
+ case PTRACE_GETFPREGS64: {
+ struct fps {
+ unsigned int regs[64];
+ unsigned long fsr;
+ } *fps = (struct fps *) addr;
+ unsigned long *fpregs = (unsigned long *)(child->tss.kregs+1);
+
+ if (copy_to_user(&fps->regs[0], fpregs,
+ (64 * sizeof(unsigned int))) ||
+ __put_user(fpregs[32], (&fps->fsr))) {
+ pt_error_return(regs, EFAULT);
goto out;
}
+ pt_succ_return(regs, 0);
+ goto out;
+ }
- case PTRACE_SETFPREGS:
- if (current->tss.flags & SPARC_FLAG_32BIT) {
- struct fps {
- unsigned int regs[32];
- unsigned int fsr;
- unsigned int flags;
- unsigned int extra;
- unsigned int fpqd;
- struct fq {
- unsigned int insnaddr;
- unsigned int insn;
- } fpq[16];
- } *fps = (struct fps *) addr;
- unsigned fsr;
-
- if (copy_from_user(&child->tss.float_regs[0], &fps->regs[0], (32 * sizeof(unsigned int))) ||
- __get_user(fsr, (&fps->fsr))) {
- pt_error_return(regs, EFAULT);
- goto out;
- }
- child->tss.fsr &= 0xffffffff00000000UL;
- child->tss.fsr |= fsr;
- pt_succ_return(regs, 0);
+ case PTRACE_SETFPREGS: {
+ struct fps {
+ unsigned int regs[32];
+ unsigned int fsr;
+ unsigned int flags;
+ unsigned int extra;
+ unsigned int fpqd;
+ struct fq {
+ unsigned int insnaddr;
+ unsigned int insn;
+ } fpq[16];
+ } *fps = (struct fps *) addr;
+ unsigned long *fpregs = (unsigned long *)(child->tss.kregs+1);
+ unsigned fsr;
+
+ if (copy_from_user(fpregs, &fps->regs[0],
+ (32 * sizeof(unsigned int))) ||
+ __get_user(fsr, (&fps->fsr))) {
+ pt_error_return(regs, EFAULT);
goto out;
- } else {
- struct fps {
- unsigned int regs[64];
- unsigned long fsr;
- } *fps = (struct fps *) addr;
+ }
+ fpregs[32] &= 0xffffffff00000000UL;
+ fpregs[32] |= fsr;
+ pt_succ_return(regs, 0);
+ goto out;
+ }
- if (copy_from_user(&child->tss.float_regs[0], &fps->regs[0], (64 * sizeof(unsigned int))) ||
- __get_user(child->tss.fsr, (&fps->fsr))) {
- pt_error_return(regs, EFAULT);
- goto out;
- }
- pt_succ_return(regs, 0);
+ case PTRACE_SETFPREGS64: {
+ struct fps {
+ unsigned int regs[64];
+ unsigned long fsr;
+ } *fps = (struct fps *) addr;
+ unsigned long *fpregs = (unsigned long *)(child->tss.kregs+1);
+
+ if (copy_from_user(fpregs, &fps->regs[0],
+ (64 * sizeof(unsigned int))) ||
+ __get_user(fpregs[32], (&fps->fsr))) {
+ pt_error_return(regs, EFAULT);
goto out;
}
+ pt_succ_return(regs, 0);
+ goto out;
+ }
case PTRACE_READTEXT:
case PTRACE_READDATA: {
current->pid, current->exit_code);
#endif
if (current->exit_code) {
- set_bit(current->exit_code + 31, ¤t->signal);
+ /* spin_lock_irq(¤t->sigmask_lock); */
+ current->signal |= (1 << (current->exit_code - 1));
+ /* spin_unlock_irq(¤t->sigmask_lock); */
}
+
current->exit_code = 0;
}
-/* $Id: rtrap.S,v 1.23 1997/06/16 07:38:41 davem Exp $
+/* $Id: rtrap.S,v 1.28 1997/06/30 10:31:39 jj Exp $
* rtrap.S: Preparing for return from trap on Sparc V9.
*
* Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
#include <asm/spitfire.h>
#include <asm/head.h>
- /* We assume here that this is entered with AG, MG and IG bits
- * in pstate clear.
- */
+ .text
+ .align 32
+ .globl rtrap_clr_l6, rtrap
+#define PTREGS_OFF (STACK_BIAS + REGWIN_SZ)
+rtrap_clr_l6: ba,pt %xcc, rtrap
+ clr %l6
+rtrap: sethi %hi(bh_active), %l2
+ sethi %hi(bh_mask), %l1
+ ldx [%l2 + %lo(bh_active)], %l4
+ ldx [%l1 + %lo(bh_mask)], %l7
- .text
- .align 32
- .globl rtrap_clr_l6, rtrap
-rtrap_clr_l6:
- ba,pt %xcc, rtrap
- clr %l6
-rtrap: sethi %hi(bh_active), %l2
- or %l2, %lo(bh_active), %l2
- sethi %hi(bh_mask), %l1
- or %l1, %lo(bh_mask), %l1
- ldx [%l2 + %g4], %l4
- ldx [%l1 + %g4], %l7
+ andcc %l4, %l7, %g0
+ be,pt %xcc, 2f
+ nop
+ call do_bottom_half
+ nop
+2: ldx [%sp + PTREGS_OFF + PT_V9_TSTATE], %l1
+ sethi %hi(0xf << 20), %l4
+ andcc %l1, TSTATE_PRIV, %l3
- andcc %l4, %l7, %g0
- be,pt %xcc, 2f
- nop
- call do_bottom_half
- nop
-2: ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TSTATE], %l1
- sethi %hi(0xf << 20), %l4
- andcc %l1, TSTATE_PRIV, %l3
+ and %l1, %l4, %l4
+ rdpr %pstate, %l7
+ andn %l1, %l4, %l1
+ be,pt %icc, to_user
+ andn %l7, PSTATE_IE, %l7
+rt_continue: ld [%sp + PTREGS_OFF + PT_V9_FPRS], %l2
+ ld [%g6 + AOFF_task_tss + AOFF_thread_ctx], %l0
+ ldx [%sp + PTREGS_OFF + PT_V9_G1], %g1
- and %l1, %l4, %l4
- rdpr %pstate, %l7
- andn %l1, %l4, %l1
- be,pt %icc, to_user
- andn %l7, PSTATE_IE, %l7
-3: ldx [%g6 + AOFF_task_tss + AOFF_thread_ctx], %l0
- ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G1], %g1
- ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G2], %g2
+ brnz,pn %l2, rt_fpu_restore
+ ldx [%sp + PTREGS_OFF + PT_V9_G2], %g2
+rt_after_fpu: ldx [%sp + PTREGS_OFF + PT_V9_G3], %g3
+ mov %g6, %l6
+ ldx [%sp + PTREGS_OFF + PT_V9_G4], %g4
+ ldx [%sp + PTREGS_OFF + PT_V9_G5], %g5
+ ldx [%sp + PTREGS_OFF + PT_V9_G6], %g6
+ ldx [%sp + PTREGS_OFF + PT_V9_G7], %g7
- ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G3], %g3
- ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G4], %g4
- ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G5], %g5
- ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G6], %g6
- ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_G7], %g7
- wrpr %l7, PSTATE_AG, %pstate
- ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I0], %i0
- ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I1], %i1
+ wrpr %l7, PSTATE_AG, %pstate
+ ldx [%sp + PTREGS_OFF + PT_V9_I0], %i0
+ ldx [%sp + PTREGS_OFF + PT_V9_I1], %i1
+ ldx [%sp + PTREGS_OFF + PT_V9_I2], %i2
+ ldx [%sp + PTREGS_OFF + PT_V9_I3], %i3
+ ldx [%sp + PTREGS_OFF + PT_V9_I4], %i4
+ ldx [%sp + PTREGS_OFF + PT_V9_I5], %i5
+ ldx [%sp + PTREGS_OFF + PT_V9_I6], %i6
- ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I2], %i2
- ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I3], %i3
- ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I4], %i4
- ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I5], %i5
- ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I6], %i6
- ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_I7], %i7
- ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_Y], %o3
- ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TPC], %l2
+ ldx [%sp + PTREGS_OFF + PT_V9_I7], %i7
+ ld [%sp + PTREGS_OFF + PT_V9_Y], %o3
+ ldx [%sp + PTREGS_OFF + PT_V9_TPC], %l2
+ ldx [%sp + PTREGS_OFF + PT_V9_TNPC], %o2
+ wr %o3, %g0, %y
+ srl %l4, 20, %l4
+ wrpr %l4, 0x0, %pil
+ wrpr %g0, 0x1, %tl
- ldx [%sp + STACK_BIAS + REGWIN_SZ + PT_V9_TNPC], %o2
- wr %o3, %g0, %y
- srl %l4, 20, %l4
- wrpr %l4, 0x0, %pil
- wrpr %g0, 0x1, %tl
- wrpr %l1, %g0, %tstate
- wrpr %l2, %g0, %tpc
- mov PRIMARY_CONTEXT, %l7
+ wrpr %l1, %g0, %tstate
+ wrpr %l2, %g0, %tpc
+ mov PRIMARY_CONTEXT, %l7
+ brnz,pn %l3, kern_rtt
+ wrpr %o2, %g0, %tnpc
+ stxa %l0, [%l7] ASI_DMMU
+ flush %l6
+ rdpr %wstate, %l1
- wrpr %o2, %g0, %tnpc
- brnz,a,pn %l3, 1f
- restore
- sethi %uhi(KERNBASE), %l5
- sllx %l5, 32, %l5
- stxa %l0, [%l7] ASI_DMMU
- flush %l5
- rdpr %wstate, %l1
+ rdpr %otherwin, %l2
+ srl %l1, 3, %l1
+ wrpr %l2, %g0, %canrestore
+ wrpr %l1, %g0, %wstate
+ wrpr %g0, %g0, %otherwin
+ restore
+ rdpr %canrestore, %g1
+ wrpr %g1, 0x0, %cleanwin
- rdpr %otherwin, %l2
- srl %l1, 3, %l1
- wrpr %l2, %g0, %canrestore
- wrpr %l1, %g0, %wstate
- wrpr %g0, %g0, %otherwin
- restore
- rdpr %canrestore, %g1
- wrpr %g1, 0x0, %cleanwin
+ retry
+kern_rtt: restore
+ retry
+to_user: sethi %hi(need_resched), %l0
+ ld [%l0 + %lo(need_resched)], %l0
+ wrpr %l7, PSTATE_IE, %pstate
+ brz,pt %l0, check_signal
+ ldx [%g6 + AOFF_task_signal], %l0
-1: retry
-to_user:
- sethi %hi(need_resched), %l0
- or %l0, %lo(need_resched), %l0
- ld [%l0 + %g4], %l0
- wrpr %l7, PSTATE_IE, %pstate
- brz,pt %l0, check_signal
- ldx [%g6 + AOFF_task_signal], %l0
- nop
+ call schedule
+ nop
+ ldx [%g6 + AOFF_task_signal], %l0
+ nop
+check_signal: ldx [%g6 + AOFF_task_blocked], %o0
+ andncc %l0, %o0, %g0
+ be,pt %xcc, check_user_wins
+ ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %o2
- call schedule
- nop
- ba,pt %xcc, check_signal
- ldx [%g6 + AOFF_task_signal], %l0
-check_signal:
- ldx [%g6 + AOFF_task_blocked], %o0
- andncc %l0, %o0, %g0
- be,a,pt %xcc, check_user_wins
- ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %o2
-
- mov %l5, %o2
- mov %l6, %o3
- call do_signal
- add %sp, STACK_BIAS + REGWIN_SZ, %o1
- ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %o2
- clr %l6
+ mov %l5, %o2
+ mov %l6, %o3
+ call do_signal
+ add %sp, STACK_BIAS + REGWIN_SZ, %o1
+ ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %o2
+ clr %l6
check_user_wins:
- brz,pt %o2, 3b
- nop
+ brz,pt %o2, rt_continue
+ nop
+
+ call fault_in_user_windows
+ add %sp, STACK_BIAS + REGWIN_SZ, %o0
+ ba,a,pt %xcc, rt_continue
+rt_fpu_restore: wr %g0, FPRS_FEF, %fprs
+ add %sp, PTREGS_OFF + TRACEREG_SZ, %g4
+ wr %g0, ASI_BLK_P, %asi
+
+ membar #StoreLoad | #LoadLoad
+ ldda [%g4 + 0x000] %asi, %f0
+ ldda [%g4 + 0x040] %asi, %f16
+ ldda [%g4 + 0x080] %asi, %f32
+ ldda [%g4 + 0x0c0] %asi, %f48
+ ldx [%g4 + 0x100], %fsr
+ ldx [%g4 + 0x108], %g3
+ membar #Sync
- call fault_in_user_windows
- add %sp, STACK_BIAS + REGWIN_SZ, %o0
- ba,a,pt %xcc, 3b
- nop
- nop
- nop
- nop
- nop
+ b,pt %xcc, rt_after_fpu
+ wr %g3, 0, %gsr
+#undef PTREGS_OFF
-/* $Id: setup.c,v 1.7 1997/05/20 07:58:56 jj Exp $
+/* $Id: setup.c,v 1.9 1997/07/05 09:52:29 davem Exp $
* linux/arch/sparc64/kernel/setup.c
*
* Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu)
*/
extern unsigned long sparc64_ttable_tl0;
-extern void breakpoint(void);
#if CONFIG_SUN_CONSOLE
extern void console_restore_palette(void);
#endif
#endif
static unsigned long memory_size = 0;
+/* XXX Implement this at some point... */
void kernel_enter_debugger(void)
{
-#if 0
- if (boot_flags & BOOTME_KGDB) {
- printk("KGDB: Entered\n");
- breakpoint();
- }
-#endif
}
int obp_system_intr(void)
{
- if (boot_flags & BOOTME_KGDB) {
- printk("KGDB: system interrupted\n");
- breakpoint();
- return 1;
- }
if (boot_flags & BOOTME_DEBUG) {
printk("OBP: system interrupted\n");
prom_halt();
break;
case 'h':
prom_printf("boot_flags_init: Halt!\n");
- halt();
+ prom_halt();
break;
default:
printk("Unknown boot switch (-%c)\n", c);
*cmdline_p = prom_getbootargs();
strcpy(saved_command_line, *cmdline_p);
- prom_printf("BOOT: args[%s] saved[%s]\n", *cmdline_p, saved_command_line);
-
printk("ARCH: SUN4U\n");
boot_flags_init(*cmdline_p);
-#if 0
- if((boot_flags&BOOTME_DEBUG) && (linux_dbvec!=0) &&
- ((*(short *)linux_dbvec) != -1)) {
- printk("Booted under KADB. Syncing trap table.\n");
- (*(linux_dbvec->teach_debugger))();
- }
- if((boot_flags & BOOTME_KGDB)) {
- set_debug_traps();
- prom_printf ("Breakpoint!\n");
- breakpoint();
- }
-#endif
idprom_init();
total = prom_probe_memory();
int get_cpuinfo(char *buffer)
{
- int cpuid=get_cpuid();
+#ifndef __SMP__
+ int cpuid=0;
+#else
+#error SMP not supported on sparc64 yet
+#endif
return sprintf(buffer, "cpu\t\t: %s\n"
"fpu\t\t: %s\n"
-/* $Id: signal.c,v 1.7 1997/06/16 06:49:59 davem Exp $
+/* $Id: signal.c,v 1.17 1997/07/05 09:52:31 davem Exp $
* arch/sparc64/kernel/signal.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
* Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
+#include <linux/config.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/signal.h>
#include <asm/svr4.h>
#include <asm/pgtable.h>
#include <asm/fpumacro.h>
+#include <asm/uctx.h>
#include <asm/smp_lock.h>
#define _S(nr) (1<<((nr)-1))
/* This turned off for production... */
/* #define DEBUG_SIGNALS 1 */
+/* {set, get}context() needed for 64-bit SparcLinux userland. */
+asmlinkage void sparc64_set_context(struct pt_regs *regs)
+{
+ struct ucontext *ucp = (struct ucontext *) regs->u_regs[UREG_I0];
+ struct thread_struct *tp = ¤t->tss;
+ mc_gregset_t *grp;
+ unsigned long pc, npc, tstate;
+ unsigned long fp, i7;
+ unsigned char fenab;
+
+ __asm__ __volatile__("flushw");
+ if(tp->w_saved ||
+ (((unsigned long)ucp) & (sizeof(unsigned long)-1)) ||
+ (!__access_ok((unsigned long)ucp, sizeof(*ucp))))
+ do_exit(SIGSEGV);
+ grp = &ucp->uc_mcontext.mc_gregs;
+ __get_user(pc, &((*grp)[MC_PC]));
+ __get_user(npc, &((*grp)[MC_NPC]));
+ if((pc | npc) & 3)
+ do_exit(SIGSEGV);
+ if(regs->u_regs[UREG_I1]) {
+ __get_user(current->blocked, &ucp->uc_sigmask);
+ current->blocked &= _BLOCKABLE;
+ }
+ regs->tpc = pc;
+ regs->tnpc = npc;
+ __get_user(regs->y, &((*grp)[MC_Y]));
+ __get_user(tstate, &((*grp)[MC_Y]));
+ regs->tstate &= ~(TSTATE_ICC | TSTATE_XCC);
+ regs->tstate |= (tstate & (TSTATE_ICC | TSTATE_XCC));
+ __get_user(regs->u_regs[UREG_G1], (&(*grp)[MC_G1]));
+ __get_user(regs->u_regs[UREG_G2], (&(*grp)[MC_G2]));
+ __get_user(regs->u_regs[UREG_G3], (&(*grp)[MC_G3]));
+ __get_user(regs->u_regs[UREG_G4], (&(*grp)[MC_G4]));
+ __get_user(regs->u_regs[UREG_G5], (&(*grp)[MC_G5]));
+ __get_user(regs->u_regs[UREG_G6], (&(*grp)[MC_G6]));
+ __get_user(regs->u_regs[UREG_G7], (&(*grp)[MC_G7]));
+ __get_user(regs->u_regs[UREG_I0], (&(*grp)[MC_O0]));
+ __get_user(regs->u_regs[UREG_I1], (&(*grp)[MC_O1]));
+ __get_user(regs->u_regs[UREG_I2], (&(*grp)[MC_O2]));
+ __get_user(regs->u_regs[UREG_I3], (&(*grp)[MC_O3]));
+ __get_user(regs->u_regs[UREG_I4], (&(*grp)[MC_O4]));
+ __get_user(regs->u_regs[UREG_I5], (&(*grp)[MC_O5]));
+ __get_user(regs->u_regs[UREG_I6], (&(*grp)[MC_O6]));
+ __get_user(regs->u_regs[UREG_I7], (&(*grp)[MC_O7]));
+
+ __get_user(fp, &(ucp->uc_mcontext.mc_fp));
+ __get_user(i7, &(ucp->uc_mcontext.mc_i7));
+ __put_user(fp, (&(((struct reg_window *)(STACK_BIAS+regs->u_regs[UREG_I6]))->ins[6])));
+ __put_user(i7, (&(((struct reg_window *)(STACK_BIAS+regs->u_regs[UREG_I6]))->ins[7])));
+
+ __get_user(fenab, &(ucp->uc_mcontext.mc_fpregs.mcfpu_enab));
+ if(fenab) {
+ unsigned long *fpregs = (unsigned long *)(regs+1);
+ copy_from_user(fpregs, &(ucp->uc_mcontext.mc_fpregs.mcfpu_fregs),
+ (sizeof(unsigned long) * 32));
+ __get_user(fpregs[32], &(ucp->uc_mcontext.mc_fpregs.mcfpu_fsr));
+ __get_user(fpregs[33], &(ucp->uc_mcontext.mc_fpregs.mcfpu_gsr));
+ regs->fprs = FPRS_FEF;
+ }
+}
+
+asmlinkage void sparc64_get_context(struct pt_regs *regs)
+{
+ struct ucontext *ucp = (struct ucontext *) regs->u_regs[UREG_I0];
+ struct thread_struct *tp = ¤t->tss;
+ mc_gregset_t *grp;
+ mcontext_t *mcp;
+ unsigned long fp, i7;
+ unsigned char fenab = (current->flags & PF_USEDFPU);
+
+ synchronize_user_stack();
+ if(tp->w_saved || clear_user(ucp, sizeof(*ucp)))
+ do_exit(SIGSEGV);
+ mcp = &ucp->uc_mcontext;
+ grp = &mcp->mc_gregs;
+
+ /* Skip over the trap instruction, first. */
+ regs->tpc = regs->tnpc;
+ regs->tnpc += 4;
+
+ __put_user(current->blocked, &ucp->uc_sigmask);
+ __put_user(regs->tstate, &((*grp)[MC_TSTATE]));
+ __put_user(regs->tpc, &((*grp)[MC_PC]));
+ __put_user(regs->tnpc, &((*grp)[MC_NPC]));
+ __put_user(regs->y, &((*grp)[MC_Y]));
+ __put_user(regs->u_regs[UREG_G1], &((*grp)[MC_G1]));
+ __put_user(regs->u_regs[UREG_G2], &((*grp)[MC_G2]));
+ __put_user(regs->u_regs[UREG_G3], &((*grp)[MC_G3]));
+ __put_user(regs->u_regs[UREG_G4], &((*grp)[MC_G4]));
+ __put_user(regs->u_regs[UREG_G5], &((*grp)[MC_G5]));
+ __put_user(regs->u_regs[UREG_G6], &((*grp)[MC_G6]));
+ __put_user(regs->u_regs[UREG_G6], &((*grp)[MC_G7]));
+ __put_user(regs->u_regs[UREG_I0], &((*grp)[MC_O0]));
+ __put_user(regs->u_regs[UREG_I1], &((*grp)[MC_O1]));
+ __put_user(regs->u_regs[UREG_I2], &((*grp)[MC_O2]));
+ __put_user(regs->u_regs[UREG_I3], &((*grp)[MC_O3]));
+ __put_user(regs->u_regs[UREG_I4], &((*grp)[MC_O4]));
+ __put_user(regs->u_regs[UREG_I5], &((*grp)[MC_O5]));
+ __put_user(regs->u_regs[UREG_I6], &((*grp)[MC_O6]));
+ __put_user(regs->u_regs[UREG_I7], &((*grp)[MC_O7]));
+
+ __get_user(fp, (&(((struct reg_window *)(STACK_BIAS+regs->u_regs[UREG_I6]))->ins[6])));
+ __get_user(i7, (&(((struct reg_window *)(STACK_BIAS+regs->u_regs[UREG_I6]))->ins[7])));
+ __put_user(fp, &(mcp->mc_fp));
+ __put_user(i7, &(mcp->mc_i7));
+
+ __put_user(fenab, &(mcp->mc_fpregs.mcfpu_enab));
+ if(fenab) {
+ unsigned long *fpregs = (unsigned long *)(regs+1);
+ copy_to_user(&(mcp->mc_fpregs.mcfpu_fregs), fpregs,
+ (sizeof(unsigned long) * 32));
+ __put_user(fpregs[32], &(mcp->mc_fpregs.mcfpu_fsr));
+ __put_user(fpregs[33], &(mcp->mc_fpregs.mcfpu_gsr));
+ __put_user(FPRS_FEF, &(mcp->mc_fpregs.mcfpu_fprs));
+ }
+}
+
/*
* The new signal frame, intended to be used for Linux applications only
* (we have enough in there to work with clone).
#ifdef CONFIG_SPARC32_COMPAT
if (current->tss.flags & SPARC_FLAG_32BIT) {
- extern asmlinkage void _sigpause32_common(unsigned int, struct pt_regs *);
+ extern asmlinkage void _sigpause32_common(unsigned int,
+ struct pt_regs *);
_sigpause32_common(set, regs);
return;
}
static inline void
restore_fpu_state(struct pt_regs *regs, __siginfo_fpu_t *fpu)
{
-#ifdef __SMP__
- if (current->flags & PF_USEDFPU)
- regs->tstate &= ~(TSTATE_PEF);
-#else
- if (current == last_task_used_math) {
- last_task_used_math = 0;
- regs->tstate &= ~(TSTATE_PEF);
- }
-#endif
- current->used_math = 1;
- current->flags &= ~PF_USEDFPU;
-
- copy_from_user(¤t->tss.float_regs[0],
- &fpu->si_float_regs[0],
+ unsigned long *fpregs = (unsigned long *)(regs+1);
+ copy_from_user(fpregs, &fpu->si_float_regs[0],
(sizeof(unsigned int) * 64));
- __get_user(current->tss.fsr, &fpu->si_fsr);
+ __get_user(fpregs[32], &fpu->si_fsr);
+ __get_user(fpregs[33], &fpu->si_gsr);
+ regs->fprs = FPRS_FEF;
}
void do_sigreturn(struct pt_regs *regs)
#ifdef CONFIG_SPARC32_COMPAT
if (current->tss.flags & SPARC_FLAG_32BIT) {
extern asmlinkage void do_sigreturn32(struct pt_regs *);
- do_sigreturn32(regs);
- return;
+ return do_sigreturn32(regs);
}
#endif
synchronize_user_stack ();
__get_user(tstate, &sf->info.si_regs.tstate);
copy_from_user(regs->u_regs, sf->info.si_regs.u_regs, sizeof(regs->u_regs));
- /* User can only change condition codes and FPU enabling in %tstate. */
- regs->tstate &= ~(TSTATE_ICC | TSTATE_PEF);
- regs->tstate |= (tstate & (TSTATE_ICC | TSTATE_PEF));
+ /* User can only change condition codes in %tstate. */
+ regs->tstate &= ~(TSTATE_ICC);
+ regs->tstate |= (tstate & TSTATE_ICC);
__get_user(fpu_save, &sf->fpu_save);
if (fpu_save)
static inline void
save_fpu_state(struct pt_regs *regs, __siginfo_fpu_t *fpu)
{
-#ifdef __SMP__
- if (current->flags & PF_USEDFPU) {
- fprs_write(FPRS_FEF);
- fpsave((unsigned long *)¤t->tss.float_regs[0],
- ¤t->tss.fsr);
- regs->tstate &= ~(TSTATE_PEF);
- current->flags &= ~(PF_USEDFPU);
- }
-#else
- if (current == last_task_used_math) {
- fprs_write(FPRS_FEF);
- fpsave((unsigned long *)¤t->tss.float_regs[0],
- ¤t->tss.fsr);
- last_task_used_math = 0;
- regs->tstate &= ~(TSTATE_PEF);
- }
-#endif
- copy_to_user(&fpu->si_float_regs[0], ¤t->tss.float_regs[0],
+ unsigned long *fpregs = (unsigned long *)(regs+1);
+ copy_to_user(&fpu->si_float_regs[0], fpregs,
(sizeof(unsigned int) * 64));
- __put_user(current->tss.fsr, &fpu->si_fsr);
- current->used_math = 0;
+ __put_user(fpregs[32], &fpu->si_fsr);
+ __put_user(fpregs[33], &fpu->si_gsr);
+ regs->fprs = 0;
}
static inline void
/* 1. Make sure everything is clean */
synchronize_user_stack();
sigframe_size = NF_ALIGNEDSZ;
- if (!current->used_math)
+ if (!(current->flags & PF_USEDFPU))
sigframe_size -= sizeof(__siginfo_fpu_t);
sf = (struct new_signal_frame *)
/* 2. Save the current process state */
copy_to_user(&sf->info.si_regs, regs, sizeof (*regs));
- if (current->used_math) {
+ if (current->flags & PF_USEDFPU) {
save_fpu_state(regs, &sf->fpu_state);
__put_user((u64)&sf->fpu_state, &sf->fpu_save);
} else {
regs->tnpc = (regs->tpc + 4);
/* Flush instruction space. */
- __asm__ __volatile__("
- membar #StoreStore
- stxa %%g0, [%0] %2
- stxa %%g0, [%1] %2
- flush %%g4
- " : /* no outputs */
- : "r" (((unsigned long)&(sf->insns[0])) & ~(PAGE_MASK)),
- "r" ((((unsigned long)&(sf->insns[0])) & ~(PAGE_MASK)) + PAGE_SIZE),
- "i" (ASI_IC_TAG));
+ {
+ unsigned long address = ((unsigned long)&(sf->insns[0]));
+ pgd_t *pgdp = pgd_offset(current->mm, address);
+ pmd_t *pmdp = pmd_offset(pgdp, address);
+ pte_t *ptep = pte_offset(pmdp, address);
+
+ if(pte_present(*ptep)) {
+ unsigned long page = pte_page(*ptep);
+
+ __asm__ __volatile__("
+ membar #StoreStore
+ flush %0 + %1"
+ : : "r" (page), "r" (address & (PAGE_SIZE - 1))
+ : "memory");
+ }
+ }
}
static inline void handle_signal(unsigned long signr, struct sigaction *sa,
-/* $Id: signal32.c,v 1.13 1997/06/01 05:46:09 davem Exp $
+/* $Id: signal32.c,v 1.23 1997/07/05 07:09:15 davem Exp $
* arch/sparc64/kernel/signal32.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
* (we have enough in there to work with clone).
* All the interesting bits are in the info field.
*/
-
struct new_signal_frame32 {
struct sparc_stackf32 ss;
__siginfo32_t info;
/* __siginfo_fpu32_t * */ u32 fpu_save;
unsigned int insns [2];
- __siginfo_fpu32_t fpu_state;
+ __siginfo_fpu_t fpu_state;
};
/* Align macros */
}
}
-static inline void
-restore_fpu_state32(struct pt_regs *regs, __siginfo_fpu32_t *fpu)
+static inline void restore_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t *fpu)
{
-#ifdef __SMP__
- if (current->flags & PF_USEDFPU)
- regs->tstate &= ~(TSTATE_PEF);
-#else
- if (current == last_task_used_math) {
- last_task_used_math = 0;
- regs->tstate &= ~(TSTATE_PEF);
- }
-#endif
- current->used_math = 1;
- current->flags &= ~PF_USEDFPU;
-
- copy_from_user(¤t->tss.float_regs[0],
- &fpu->si_float_regs[0],
- (sizeof(unsigned int) * 32));
- __get_user(current->tss.fsr, &fpu->si_fsr);
+ unsigned long *fpregs = (unsigned long *)(regs + 1);
+ copy_from_user(fpregs, &fpu->si_float_regs[0], (sizeof(unsigned int) * 64));
+ __get_user(fpregs[32], &fpu->si_fsr);
+ __get_user(fpregs[33], &fpu->si_gsr);
}
void do_new_sigreturn32(struct pt_regs *regs)
unsigned int psr;
unsigned pc, npc, fpu_save, mask;
+ regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL;
sf = (struct new_signal_frame32 *) regs->u_regs [UREG_FP];
/* 1. Make sure we are not getting garbage from the user */
__get_user(regs->u_regs[UREG_I6], &sf->info.si_regs.u_regs[UREG_I6]);
__get_user(regs->u_regs[UREG_I7], &sf->info.si_regs.u_regs[UREG_I7]);
- /* User can only change condition codes and FPU enabling in %tstate. */
- regs->tstate &= ~(TSTATE_ICC | TSTATE_PEF);
+ /* User can only change condition codes in %tstate. */
+ regs->tstate &= ~(TSTATE_ICC);
regs->tstate |= psr_to_tstate_icc(psr);
if (psr & PSR_EF)
- regs->tstate |= TSTATE_PEF;
+ regs->fprs = FPRS_FEF;
__get_user(fpu_save, &sf->fpu_save);
if (fpu_save)
if (current->tss.new_signal)
return do_new_sigreturn32(regs);
- scptr = (struct sigcontext32 *) regs->u_regs[UREG_I0];
+ scptr = (struct sigcontext32 *)
+ (regs->u_regs[UREG_I0] & 0x00000000ffffffffUL);
/* Check sanity of the user arg. */
if(verify_area(VERIFY_READ, scptr, sizeof(struct sigcontext32)) ||
(((unsigned long) scptr) & 3))
int i;
synchronize_user_stack();
+ regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL;
sframep = (struct signal_sframe32 *) regs->u_regs[UREG_FP];
sframep = (struct signal_sframe32 *) (((unsigned long) sframep)-SF_ALIGNEDSZ);
if (invalid_frame_pointer (sframep, sizeof(*sframep))){
__put_user(pc, &sc->sigc_pc);
__put_user(npc, &sc->sigc_npc);
psr = tstate_to_psr (regs->tstate);
+ if(current->flags & PF_USEDFPU)
+ psr |= PSR_EF;
__put_user(psr, &sc->sigc_psr);
__put_user(regs->u_regs[UREG_G1], &sc->sigc_g1);
__put_user(regs->u_regs[UREG_I0], &sc->sigc_o0);
}
-static inline void
-save_fpu_state32(struct pt_regs *regs, __siginfo_fpu32_t *fpu)
+static inline void save_fpu_state32(struct pt_regs *regs, __siginfo_fpu_t *fpu)
{
-#ifdef __SMP__
- if (current->flags & PF_USEDFPU) {
- fprs_write(FPRS_FEF);
- fpsave32((unsigned long *)¤t->tss.float_regs[0],
- ¤t->tss.fsr);
- regs->tstate &= ~(TSTATE_PEF);
- current->flags &= ~(PF_USEDFPU);
- }
-#else
- if (current == last_task_used_math) {
- fprs_write(FPRS_FEF);
- fpsave32((unsigned long *)¤t->tss.float_regs[0],
- ¤t->tss.fsr);
- last_task_used_math = 0;
- regs->tstate &= ~(TSTATE_PEF);
- }
-#endif
- copy_to_user(&fpu->si_float_regs[0], ¤t->tss.float_regs[0],
- (sizeof(unsigned int) * 32));
- __put_user(current->tss.fsr, &fpu->si_fsr);
- current->used_math = 0;
+ unsigned long *fpregs = (unsigned long *)(regs+1);
+ copy_to_user(&fpu->si_float_regs[0], fpregs, (sizeof(unsigned int) * 64));
+ __put_user(fpregs[32], &fpu->si_fsr);
+ __put_user(fpregs[33], &fpu->si_gsr);
+ regs->fprs = 0;
}
-static inline void
-new_setup_frame32(struct sigaction *sa, struct pt_regs *regs,
- int signo, unsigned long oldmask)
+static inline void new_setup_frame32(struct sigaction *sa, struct pt_regs *regs,
+ int signo, unsigned long oldmask)
{
struct new_signal_frame32 *sf;
int sigframe_size;
/* 1. Make sure everything is clean */
synchronize_user_stack();
sigframe_size = NF_ALIGNEDSZ;
- if (!current->used_math)
- sigframe_size -= sizeof(__siginfo_fpu32_t);
+ if (!(current->flags & PF_USEDFPU))
+ sigframe_size -= sizeof(__siginfo_fpu_t);
+ regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL;
sf = (struct new_signal_frame32 *)(regs->u_regs[UREG_FP] - sigframe_size);
if (invalid_frame_pointer (sf, sigframe_size)) {
+#ifdef DEBUG_SIGNALS
+ printk("new_setup_frame32(%s:%d): invalid_frame_pointer(%p, %d)\n",
+ current->comm, current->pid, sf, sigframe_size);
+#endif
lock_kernel ();
do_exit(SIGILL);
}
if (current->tss.w_saved != 0) {
+#ifdef DEBUG_SIGNALS
printk ("%s[%d]: Invalid user stack frame for "
"signal delivery.\n", current->comm, current->pid);
+#endif
lock_kernel ();
do_exit (SIGILL);
}
__put_user(regs->tnpc, &sf->info.si_regs.npc);
__put_user(regs->y, &sf->info.si_regs.y);
psr = tstate_to_psr (regs->tstate);
+ if(current->flags & PF_USEDFPU)
+ psr |= PSR_EF;
__put_user(psr, &sf->info.si_regs.psr);
for (i = 0; i < 16; i++)
__put_user(regs->u_regs[i], &sf->info.si_regs.u_regs[i]);
- if (current->used_math) {
+ if (psr & PSR_EF) {
save_fpu_state32(regs, &sf->fpu_state);
__put_user((u64)&sf->fpu_state, &sf->fpu_save);
} else {
regs->tnpc = (regs->tpc + 4);
/* Flush instruction space. */
- __asm__ __volatile__("
- membar #StoreStore
- stxa %%g0, [%0] %2
- stxa %%g0, [%1] %2
- flush %%g4
- " : /* no outputs */
- : "r" (((unsigned long)&(sf->insns[0])) & ~(PAGE_MASK)),
- "r" ((((unsigned long)&(sf->insns[0])) & ~(PAGE_MASK)) + PAGE_SIZE),
- "i" (ASI_IC_TAG));
+ {
+ unsigned long address = ((unsigned long)&(sf->insns[0]));
+ pgd_t *pgdp = pgd_offset(current->mm, address);
+ pmd_t *pmdp = pmd_offset(pgdp, address);
+ pte_t *ptep = pte_offset(pmdp, address);
+
+ if(pte_present(*ptep)) {
+ unsigned long page = pte_page(*ptep);
+
+ __asm__ __volatile__("
+ membar #StoreStore
+ flush %0 + %1"
+ : : "r" (page), "r" (address & (PAGE_SIZE - 1))
+ : "memory");
+ }
+ }
}
/* Setup a Solaris stack frame */
int i;
synchronize_user_stack();
+ regs->u_regs[UREG_FP] &= 0x00000000ffffffffUL;
sfp = (svr4_signal_frame_t *) regs->u_regs[UREG_FP] - REGWIN_SZ;
sfp = (svr4_signal_frame_t *) (((unsigned long) sfp)-SVR4_SF_ALIGNED);
__put_user(regs->tpc, &((*gr) [SVR4_PC]));
__put_user(regs->tnpc, &((*gr) [SVR4_NPC]));
psr = tstate_to_psr (regs->tstate);
+ if(current->flags & PF_USEDFPU)
+ psr |= PSR_EF;
__put_user(psr, &((*gr) [SVR4_PSR]));
__put_user(regs->y, &((*gr) [SVR4_Y]));
#endif
/* Arguments passed to signal handler */
if (regs->u_regs [14]){
- struct reg_window32 *rw = (struct reg_window32 *) regs->u_regs [14];
+ struct reg_window32 *rw = (struct reg_window32 *)
+ (regs->u_regs [14] & 0x00000000ffffffffUL);
__put_user(signr, &rw->ins [0]);
__put_user((u64)si, &rw->ins [1]);
/* Store registers */
__put_user(regs->tpc, &uc->mcontext.greg [SVR4_PC]);
__put_user(regs->tnpc, &uc->mcontext.greg [SVR4_NPC]);
- __put_user(tstate_to_psr(regs->tstate), &uc->mcontext.greg [SVR4_PSR]);
+ __put_user((tstate_to_psr(regs->tstate) |
+ ((current->flags & PF_USEDFPU) ? PSR_EF : 0)),
+ &uc->mcontext.greg [SVR4_PSR]);
__put_user(regs->y, &uc->mcontext.greg [SVR4_Y]);
/* Copy g [1..7] and o [0..7] registers */
__get_user(psr, &((*gr) [SVR4_PSR]));
regs->tstate &= ~(TSTATE_ICC);
regs->tstate |= psr_to_tstate_icc(psr);
+ if(psr & PSR_EF)
+ regs->fprs = FPRS_FEF;
/* Restore g[1..7] and o[0..7] registers */
for (i = 0; i < 7; i++)
- __put_user(regs->u_regs[UREG_G1+i], (&(*gr)[SVR4_G1])+i);
+ __get_user(regs->u_regs[UREG_G1+i], (&(*gr)[SVR4_G1])+i);
for (i = 0; i < 8; i++)
- __put_user(regs->u_regs[UREG_I0+i], (&(*gr)[SVR4_O0])+i);
+ __get_user(regs->u_regs[UREG_I0+i], (&(*gr)[SVR4_O0])+i);
return -EINTR;
}
int cur_status;
};
-asmlinkage int
-sys32_sigstack(struct sigstack32 *ssptr, struct sigstack32 *ossptr)
+asmlinkage int sys32_sigstack(u32 u_ssptr, u32 u_ossptr)
{
+ struct sigstack32 *ssptr = (struct sigstack32 *)((unsigned long)(u_ssptr));
+ struct sigstack32 *ossptr = (struct sigstack32 *)((unsigned long)(u_ossptr));
int ret = -EFAULT;
lock_kernel();
-/* $Id: sparc64_ksyms.c,v 1.4 1997/04/14 17:04:43 jj Exp $
- * arch/sparc/kernel/ksyms.c: Sparc specific ksyms support.
+/* $Id: sparc64_ksyms.c,v 1.8 1997/07/07 04:58:14 davem Exp $
+ * arch/sparc64/kernel/sparc64_ksyms.c: Sparc64 specific ksyms support.
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
* Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
short revents;
};
-extern int svr4_getcontext (svr4_ucontext_t *, struct pt_regs *);
-extern int svr4_setcontext (svr4_ucontext_t *, struct pt_regs *);
extern unsigned long sunos_mmap(unsigned long, unsigned long, unsigned long,
unsigned long, unsigned long, unsigned long);
void _sigpause_common (unsigned int set, struct pt_regs *);
-extern void __copy_1page(void *, const void *);
-extern void *bzero_1page(void *);
+extern void *__bzero_1page(void *);
extern void *__bzero(void *, size_t);
extern void *__memscan_zero(void *, size_t);
extern void *__memscan_generic(void *, int, size_t);
extern int __memcmp(const void *, const void *, __kernel_size_t);
extern int __strncmp(const char *, const char *, __kernel_size_t);
extern unsigned int __csum_partial_copy_sparc_generic (const char *, char *);
+extern char saved_command_line[];
extern void bcopy (const char *, char *, int);
extern int __ashrdi3(int, int);
EXPORT_SYMBOL_PRIVATE(_lock_kernel);
EXPORT_SYMBOL_PRIVATE(_unlock_kernel);
+EXPORT_SYMBOL_PRIVATE(flushw_user);
+
EXPORT_SYMBOL(mstk48t02_regs);
EXPORT_SYMBOL(request_fast_irq);
EXPORT_SYMBOL(sparc_alloc_io);
#endif
/* Solaris/SunOS binary compatibility */
-EXPORT_SYMBOL(svr4_setcontext);
-EXPORT_SYMBOL(svr4_getcontext);
EXPORT_SYMBOL(_sigpause_common);
EXPORT_SYMBOL(sunos_mmap);
EXPORT_SYMBOL(prom_getproperty);
EXPORT_SYMBOL(prom_node_has_property);
EXPORT_SYMBOL(prom_setprop);
-EXPORT_SYMBOL(prom_getbootargs);
+EXPORT_SYMBOL(saved_command_line);
EXPORT_SYMBOL(prom_getname);
EXPORT_SYMBOL(prom_feval);
EXPORT_SYMBOL(prom_getstring);
EXPORT_SYMBOL(strspn);
/* Special internal versions of library functions. */
-EXPORT_SYMBOL(__copy_1page);
EXPORT_SYMBOL(__memcpy);
EXPORT_SYMBOL(__memset);
-EXPORT_SYMBOL(bzero_1page);
+EXPORT_SYMBOL(__bzero_1page);
EXPORT_SYMBOL(__bzero);
EXPORT_SYMBOL(__memscan_zero);
EXPORT_SYMBOL(__memscan_generic);
--- /dev/null
+/* $Id: sunos_ioctl32.c,v 1.2 1997/07/05 07:09:16 davem Exp $
+ * sunos_ioctl32.c: SunOS ioctl compatability on sparc64.
+ *
+ * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/uaccess.h>
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/termios.h>
+#include <linux/ioctl.h>
+#include <linux/route.h>
+#include <linux/sockios.h>
+#include <linux/if.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+#include <asm/kbio.h>
+
+#define A(x) ((unsigned long)x)
+
+#define SUNOS_NR_OPEN 256
+
+struct rtentry32 {
+ u32 rt_pad1;
+ struct sockaddr rt_dst; /* target address */
+ struct sockaddr rt_gateway; /* gateway addr (RTF_GATEWAY) */
+ struct sockaddr rt_genmask; /* target network mask (IP) */
+ unsigned short rt_flags;
+ short rt_pad2;
+ u32 rt_pad3;
+ unsigned char rt_tos;
+ unsigned char rt_class;
+ short rt_pad4;
+ short rt_metric; /* +1 for binary compatibility! */
+ /* char * */ u32 rt_dev; /* forcing the device at add */
+ u32 rt_mtu; /* per route MTU/Window */
+ u32 rt_window; /* Window clamping */
+ unsigned short rt_irtt; /* Initial RTT */
+
+};
+
+struct ifmap32 {
+ u32 mem_start;
+ u32 mem_end;
+ unsigned short base_addr;
+ unsigned char irq;
+ unsigned char dma;
+ unsigned char port;
+};
+
+struct ifreq32 {
+#define IFHWADDRLEN 6
+#define IFNAMSIZ 16
+ union {
+ char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */
+ } ifr_ifrn;
+ union {
+ struct sockaddr ifru_addr;
+ struct sockaddr ifru_dstaddr;
+ struct sockaddr ifru_broadaddr;
+ struct sockaddr ifru_netmask;
+ struct sockaddr ifru_hwaddr;
+ short ifru_flags;
+ int ifru_ivalue;
+ int ifru_mtu;
+ struct ifmap32 ifru_map;
+ char ifru_slave[IFNAMSIZ]; /* Just fits the size */
+ __kernel_caddr_t32 ifru_data;
+ } ifr_ifru;
+};
+
+struct ifconf32 {
+ int ifc_len; /* size of buffer */
+ __kernel_caddr_t32 ifcbuf;
+};
+
+extern asmlinkage int sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg);
+
+extern asmlinkage int sys32_ioctl(unsigned int, unsigned int, u32);
+extern asmlinkage int sys_setsid(void);
+
+asmlinkage int sunos_ioctl (int fd, unsigned long cmd, u32 arg)
+{
+ struct file *filp;
+ int ret = -EBADF;
+
+ lock_kernel();
+ if(fd >= SUNOS_NR_OPEN || !(filp = current->files->fd[fd]))
+ goto out;
+ if(cmd == TIOCSETD) {
+ unsigned long old_fs = get_fs();
+ int *p, ntty = N_TTY;
+ int tmp;
+
+ p = (int *)A(arg);
+ ret = -EFAULT;
+ if(get_user(tmp, p))
+ goto out;
+ if(tmp == 2) {
+ set_fs(KERNEL_DS);
+ ret = sys_ioctl(fd, cmd, (unsigned long) &ntty);
+ set_fs(old_fs);
+ ret = (ret == -EINVAL ? -EOPNOTSUPP : ret);
+ goto out;
+ }
+ }
+ if(cmd == TIOCNOTTY) {
+ ret = sys_setsid();
+ goto out;
+ }
+ switch(cmd) {
+ case _IOW('r', 10, struct rtentry32):
+ ret = sys32_ioctl(fd, SIOCADDRT, arg);
+ goto out;
+ case _IOW('r', 11, struct rtentry32):
+ ret = sys32_ioctl(fd, SIOCDELRT, arg);
+ goto out;
+
+ case _IOW('i', 12, struct ifreq32):
+ ret = sys32_ioctl(fd, SIOCSIFADDR, arg);
+ goto out;
+ case _IOWR('i', 13, struct ifreq32):
+ ret = sys32_ioctl(fd, SIOCGIFADDR, arg);
+ goto out;
+ case _IOW('i', 14, struct ifreq32):
+ ret = sys32_ioctl(fd, SIOCSIFDSTADDR, arg);
+ goto out;
+ case _IOWR('i', 15, struct ifreq32):
+ ret = sys32_ioctl(fd, SIOCGIFDSTADDR, arg);
+ goto out;
+ case _IOW('i', 16, struct ifreq32):
+ ret = sys32_ioctl(fd, SIOCSIFFLAGS, arg);
+ goto out;
+ case _IOWR('i', 17, struct ifreq32):
+ ret = sys32_ioctl(fd, SIOCGIFFLAGS, arg);
+ goto out;
+ case _IOW('i', 18, struct ifreq32):
+ ret = sys32_ioctl(fd, SIOCSIFMEM, arg);
+ goto out;
+ case _IOWR('i', 19, struct ifreq32):
+ ret = sys32_ioctl(fd, SIOCGIFMEM, arg);
+ goto out;
+
+ case _IOWR('i', 20, struct ifconf32):
+ ret = sys32_ioctl(fd, SIOCGIFCONF, arg);
+ goto out;
+
+ case _IOW('i', 21, struct ifreq): /* SIOCSIFMTU */
+ ret = sys_ioctl(fd, SIOCSIFMTU, arg);
+ goto out;
+ case _IOWR('i', 22, struct ifreq): /* SIOCGIFMTU */
+ ret = sys_ioctl(fd, SIOCGIFMTU, arg);
+ goto out;
+
+ case _IOWR('i', 23, struct ifreq32):
+ ret = sys32_ioctl(fd, SIOCGIFBRDADDR, arg);
+ goto out;
+ case _IOW('i', 24, struct ifreq32):
+ ret = sys32_ioctl(fd, SIOCGIFBRDADDR, arg);
+ goto out;
+ case _IOWR('i', 25, struct ifreq32):
+ ret = sys32_ioctl(fd, SIOCGIFNETMASK, arg);
+ goto out;
+ case _IOW('i', 26, struct ifreq32):
+ ret = sys32_ioctl(fd, SIOCSIFNETMASK, arg);
+ goto out;
+ case _IOWR('i', 27, struct ifreq32):
+ ret = sys32_ioctl(fd, SIOCGIFMETRIC, arg);
+ goto out;
+ case _IOW('i', 28, struct ifreq32):
+ ret = sys32_ioctl(fd, SIOCSIFMETRIC, arg);
+ goto out;
+
+ case _IOW('i', 30, struct arpreq):
+ ret = sys32_ioctl(fd, SIOCSARP, arg);
+ goto out;
+ case _IOWR('i', 31, struct arpreq):
+ ret = sys32_ioctl(fd, SIOCGARP, arg);
+ goto out;
+ case _IOW('i', 32, struct arpreq):
+ ret = sys32_ioctl(fd, SIOCDARP, arg);
+ goto out;
+
+ case _IOW('i', 40, struct ifreq32): /* SIOCUPPER */
+ case _IOW('i', 41, struct ifreq32): /* SIOCLOWER */
+ case _IOW('i', 44, struct ifreq32): /* SIOCSETSYNC */
+ case _IOW('i', 45, struct ifreq32): /* SIOCGETSYNC */
+ case _IOW('i', 46, struct ifreq32): /* SIOCSSDSTATS */
+ case _IOW('i', 47, struct ifreq32): /* SIOCSSESTATS */
+ case _IOW('i', 48, struct ifreq32): /* SIOCSPROMISC */
+ ret = -EOPNOTSUPP;
+ goto out;
+
+ case _IOW('i', 49, struct ifreq32):
+ ret = sys32_ioctl(fd, SIOCADDMULTI, arg);
+ goto out;
+ case _IOW('i', 50, struct ifreq32):
+ ret = sys32_ioctl(fd, SIOCDELMULTI, arg);
+ goto out;
+
+ /* FDDI interface ioctls, unsupported. */
+
+ case _IOW('i', 51, struct ifreq32): /* SIOCFDRESET */
+ case _IOW('i', 52, struct ifreq32): /* SIOCFDSLEEP */
+ case _IOW('i', 53, struct ifreq32): /* SIOCSTRTFMWAR */
+ case _IOW('i', 54, struct ifreq32): /* SIOCLDNSTRTFW */
+ case _IOW('i', 55, struct ifreq32): /* SIOCGETFDSTAT */
+ case _IOW('i', 56, struct ifreq32): /* SIOCFDNMIINT */
+ case _IOW('i', 57, struct ifreq32): /* SIOCFDEXUSER */
+ case _IOW('i', 58, struct ifreq32): /* SIOCFDGNETMAP */
+ case _IOW('i', 59, struct ifreq32): /* SIOCFDGIOCTL */
+ printk("FDDI ioctl, returning EOPNOTSUPP\n");
+ ret = -EOPNOTSUPP;
+ goto out;
+
+ case _IOW('t', 125, int):
+ /* More stupid tty sunos ioctls, just
+ * say it worked.
+ */
+ ret = 0;
+ goto out;
+
+ /* Non posix grp */
+ case _IOW('t', 118, int): {
+ int oldval, newval, *ptr;
+
+ cmd = TIOCSPGRP;
+ ptr = (int *) A(arg);
+ ret = -EFAULT;
+ if(get_user(oldval, ptr))
+ goto out;
+ ret = sys32_ioctl(fd, cmd, arg);
+ __get_user(newval, ptr);
+ if(newval == -1) {
+ __put_user(oldval, ptr);
+ ret = -EIO;
+ }
+ if(ret == -ENOTTY)
+ ret = -EIO;
+ goto out;
+ }
+
+ case _IOR('t', 119, int): {
+ int oldval, newval, *ptr;
+
+ cmd = TIOCGPGRP;
+ ptr = (int *) A(arg);
+ ret = -EFAULT;
+ if(get_user(oldval, ptr))
+ goto out;
+ ret = sys32_ioctl(fd, cmd, arg);
+ __get_user(newval, ptr);
+ if(newval == -1) {
+ __put_user(oldval, ptr);
+ ret = -EIO;
+ }
+ if(ret == -ENOTTY)
+ ret = -EIO;
+ goto out;
+ }
+ };
+
+ ret = sys32_ioctl(fd, cmd, arg);
+ /* so stupid... */
+ ret = (ret == -EINVAL ? -EOPNOTSUPP : ret);
+out:
+ unlock_kernel();
+ return ret;
+}
--- /dev/null
+/* $Id: sys32.S,v 1.1 1997/06/29 03:38:56 davem Exp $
+ * sys32.S: I-cache tricks for 32-bit compatability layer simple
+ * conversions.
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+ .text
+
+ .align 32
+ .globl sys32_mmap, sys32_mprotect, sys32_munmap, sys32_msync
+ .globl sys32_mlock, sys32_munlock, sys32_mremap, sparc32_brk
+sys32_mmap:
+ srl %o0, 0, %o0 ! IEU0 Group
+ sethi %hi(0xffffffff), %g2 ! IEU1
+ srl %o1, 0, %o1 ! IEU0 Group
+ or %g2, %lo(0xffffffff), %g2 ! IEU1
+ srl %o2, 0, %o2 ! IEU0 Group
+ mov %o7, %g1 ! IEU1
+ and %o3, %g2, %o3 ! IEU0 Group
+ and %o4, %g2, %o4 ! IEU1
+ and %o5, %g2, %o5 ! IEU0 Group
+ call sys_mmap ! CTI Group brk forced
+ mov %g1, %o7 ! IEU0 Group (regdep)
+sys32_mprotect:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ srl %o1, 0, %o1
+ srl %o2, 0, %o2
+ call sys_mprotect
+ mov %g1, %o7
+sys32_munmap:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ srl %o1, 0, %o1
+ call sys_munmap
+ mov %g1, %o7
+sparc32_brk:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ call sys_brk
+ mov %g1, %o7
+sys32_msync:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ srl %o1, 0, %o1
+ call sys_msync
+ mov %g1, %o7
+sys32_mlock:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ srl %o1, 0, %o1
+ call sys_mlock
+ mov %g1, %o7
+sys32_munlock:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ srl %o1, 0, %o1
+ call sys_munlock
+ mov %g1, %o7
+sys32_mremap:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ srl %o1, 0, %o1
+ srl %o2, 0, %o2
+ srl %o3, 0, %o3
+ call sys_mremap
+ mov %g1, %o7
+
+ .align 32
+ .globl sys32_read, sys32_write, sys32_open, sys32_access
+ .globl sys32_chdir, sys32_lseek, sys32_llseek, sys32_poll
+ .globl sys32_readlink, sys32_unlink, sys32_rmdir, sys32_symlink
+ .globl sys32_link, sys32_rename, sys32_truncate, sys32_ftruncate
+ .globl sys32_chroot, sys32_chmod, sys32_chown, sys32_creat
+ .globl sys32_mkdir, sys32_mknod, sys32_utimes, sys32_ustat
+sys32_read:
+ srl %o1, 0, %o1
+ mov %o7, %g1
+ srl %o2, 0, %o2
+ call sys_read
+ mov %g1, %o7
+sys32_write:
+ srl %o1, 0, %o1
+ mov %o7, %g1
+ srl %o2, 0, %o2
+ call sys_write
+ mov %g1, %o7
+sys32_open:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ call sys_open
+ mov %g1, %o7
+sys32_access:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ call sys_access
+ mov %g1, %o7
+sys32_chdir:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ call sys_chdir
+ mov %g1, %o7
+sys32_lseek:
+ sra %o1, 0, %o1
+ mov %o7, %g1
+ call sys_lseek
+ mov %g1, %o7
+sys32_llseek:
+ srl %o1, 0, %o1
+ mov %o7, %g1
+ srl %o2, 0, %o2
+ srl %o3, 0, %o3
+ call sys_llseek
+ mov %g1, %o7
+sys32_poll:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ call sys_poll
+ mov %g1, %o7
+sys32_readlink:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ srl %o1, 0, %o1
+ call sys_readlink
+ mov %g1, %o7
+sys32_unlink:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ call sys_unlink
+ mov %g1, %o7
+sys32_rmdir:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ call sys_rmdir
+ mov %g1, %o7
+sys32_symlink:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ srl %o1, 0, %o1
+ call sys_symlink
+ mov %g1, %o7
+sys32_link:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ srl %o1, 0, %o1
+ call sys_link
+ mov %g1, %o7
+sys32_rename:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ srl %o1, 0, %o1
+ call sys_rename
+ mov %g1, %o7
+ nop
+sys32_truncate:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ srl %o1, 0, %o1
+ call sys_truncate
+ mov %g1, %o7
+sys32_ftruncate:
+ srl %o1, 0, %o1
+ mov %o7, %g1
+ call sys_ftruncate
+ mov %g1, %o7
+sys32_chroot:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ call sys_chroot
+ mov %g1, %o7
+sys32_chmod:
+ sll %o1, 16, %o1
+ mov %o7, %g1
+ srl %o0, 0, %o0
+ srl %o1, 16, %o1
+ call sys_chmod
+ mov %g1, %o7
+sys32_chown:
+ sll %o1, 16, %o1
+ mov %o7, %g1
+ sll %o2, 16, %o2
+ srl %o0, 0, %o0
+ srl %o1, 16, %o1
+ srl %o2, 16, %o2
+ call sys_chown
+ mov %g1, %o7
+sys32_creat:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ call sys_creat
+ mov %g1, %o7
+sys32_mkdir:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ call sys_mkdir
+ mov %g1, %o7
+sys32_mknod:
+ sll %o2, 16, %o2
+ mov %o7, %g1
+ srl %o0, 0, %o0
+ srl %o2, 16, %o2
+ call sys_mknod
+ mov %g1, %o7
+sys32_utimes:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ srl %o1, 0, %o1
+ call sys_utimes
+ mov %g1, %o7
+sys32_ustat:
+ srl %o1, 0, %o1
+ mov %o7, %g1
+ call sys_ustat
+ mov %g1, %o7
+
+ .align 32
+ .globl sys32_bind, sys32_accept, sys32_connect, sys32_getsockname
+ .globl sys32_getpeername, sys32_send, sys32_sendto, sys32_recv
+ .globl sys32_recvfrom, sys32_setsockopt, sys32_getsockopt
+sys32_bind:
+ srl %o1, 0, %o1
+ mov %o7, %g1
+ call sys_bind
+ mov %g1, %o7
+sys32_accept:
+ srl %o1, 0, %o1
+ mov %o7, %g1
+ srl %o2, 0, %o2
+ call sys_accept
+ mov %g1, %o7
+sys32_connect:
+ srl %o1, 0, %o1
+ mov %o7, %g1
+ call sys_connect
+ mov %g1, %o7
+sys32_getsockname:
+ srl %o1, 0, %o1
+ mov %o7, %g1
+ srl %o2, 0, %o2
+ call sys_getsockname
+ mov %g1, %o7
+sys32_getpeername:
+ srl %o1, 0, %o1
+ mov %o7, %g1
+ srl %o2, 0, %o2
+ call sys_getpeername
+ mov %g1, %o7
+sys32_send:
+ srl %o1, 0, %o1
+ mov %o7, %g1
+ srl %o2, 0, %o2
+ call sys_send
+ mov %g1, %o7
+sys32_sendto:
+ srl %o1, 0, %o1
+ mov %o7, %g1
+ srl %o2, 0, %o2
+ srl %o4, 0, %o4
+ call sys_sendto
+ mov %g1, %o7
+sys32_recv:
+ srl %o1, 0, %o1
+ mov %o7, %g1
+ srl %o2, 0, %o2
+ call sys_recv
+ mov %g1, %o7
+sys32_recvfrom:
+ srl %o1, 0, %o1
+ mov %o7, %g1
+ srl %o2, 0, %o2
+ srl %o4, 0, %o4
+ srl %o5, 0, %o5
+ call sys_recvfrom
+ mov %g1, %o7
+sys32_setsockopt:
+ srl %o3, 0, %o3
+ mov %o7, %g1
+ call sys_setsockopt
+ mov %g1, %o7
+sys32_getsockopt:
+ srl %o3, 0, %o3
+ mov %o7, %g1
+ srl %o4, 0, %o4
+ call sys_setsockopt
+ mov %g1, %o7
+
+ .align 32
+ .globl sys32_gettimeofday, sys32_settimeofday
+sys32_gettimeofday:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ srl %o1, 0, %o1
+ call sys_gettimeofday
+ mov %g1, %o7
+sys32_settimeofday:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ srl %o1, 0, %o1
+ call sys_settimeofday
+ mov %g1, %o7
+
+ .globl sys32_bdflush, sys32_uselib, sys32_umount, sys32_syslog
+ .globl sys32_personality, sys32_waitpid, sys32_getitimer
+ .globl sys32_setitimer, sys32_sched_setscheduler
+ .globl sys32_sched_setparam, sys32_sched_getparam, sys32_signal
+ .globl sys32_reboot, sys32_acct, sys32_newuname, sys32_olduname
+ .globl sys32_sethostname, sys32_gethostname, sys32_setdomainname
+ .globl sys32_time, sys32_swapoff, sys32_swapon, sys32_nfsservctl
+ .globl sys32_create_module, sys32_init_module, sys32_delete_module
+sys32_bdflush:
+ sra %o1, 0, %o1
+ mov %o7, %g1
+ call sys_bdflush
+ mov %g1, %o7
+sys32_uselib:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ call sys_uselib
+ mov %g1, %o7
+sys32_umount:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ call sys_umount
+ mov %g1, %o7
+sys32_syslog:
+ srl %o1, 0, %o1
+ mov %o7, %g1
+ call sys_syslog
+ mov %g1, %o7
+sys32_personality:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ call sys_personality
+ mov %g1, %o7
+sys32_waitpid:
+ srl %o1, 0, %o1
+ mov %o7, %g1
+ call sys_waitpid
+ mov %g1, %o7
+sys32_getitimer:
+ srl %o1, 0, %o1
+ mov %o7, %g1
+ call sys_getitimer
+ mov %g1, %o7
+sys32_setitimer:
+ srl %o1, 0, %o1
+ mov %o7, %g1
+ srl %o2, 0, %o2
+ call sys_setitimer
+ mov %g1, %o7
+sys32_sched_setscheduler:
+ srl %o2, 0, %o2
+ mov %o7, %g1
+ call sys_sched_setscheduler
+ mov %g1, %o7
+sys32_sched_setparam:
+ srl %o1, 0, %o1
+ mov %o7, %g1
+ call sys_sched_setparam
+ mov %g1, %o7
+sys32_sched_getparam:
+ srl %o1, 0, %o1
+ mov %o7, %g1
+ call sys_sched_getparam
+ mov %g1, %o7
+sys32_signal:
+ srl %o1, 0, %o1
+ mov %o7, %g1
+ call sys_signal
+ mov %g1, %o7
+sys32_reboot:
+ srl %o3, 0, %o3
+ mov %o7, %g1
+ call sys_reboot
+ mov %g1, %o7
+sys32_acct:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ call sys_acct
+ mov %g1, %o7
+sys32_newuname:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ call sys_newuname
+ mov %g1, %o7
+sys32_olduname:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ call sys_olduname
+ mov %g1, %o7
+sys32_sethostname:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ call sys_sethostname
+ mov %g1, %o7
+sys32_gethostname:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ call sys_gethostname
+ mov %g1, %o7
+sys32_setdomainname:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ call sys_setdomainname
+ mov %g1, %o7
+sys32_time:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ call sys_time
+ mov %g1, %o7
+sys32_swapoff:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ call sys_swapoff
+ mov %g1, %o7
+sys32_swapon:
+ srl %o0, 0, %o0
+ mov %o7, %g1
+ call sys_swapon
+ mov %g1, %o7
+sys32_nfsservctl:
+ srl %o1, 0, %o1
+ mov %o7, %g1
+ srl %o2, 0, %o2
+ call sys_nfsservctl
+ mov %g1, %o7
-/* $Id: sys_sparc.c,v 1.1 1997/04/09 08:25:18 jj Exp $
+/* $Id: sys_sparc.c,v 1.2 1997/07/05 09:52:34 davem Exp $
* linux/arch/sparc64/kernel/sys_sparc.c
*
* This file contains various random system calls that
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/sched.h>
-#include <linux/config.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/sem.h>
-/* $Id: sys_sparc32.c,v 1.32 1997/06/17 05:36:40 davem Exp $
+/* $Id: sys_sparc32.c,v 1.42 1997/07/05 09:52:36 davem Exp $
* sys_sparc32.c: Conversion between 32bit and 64bit native syscalls.
*
* Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
* environment.
*/
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/signal.h>
#include <linux/ncp_fs.h>
#include <linux/quota.h>
#include <linux/file.h>
+#include <linux/module.h>
#include <asm/types.h>
#include <asm/poll.h>
#include <asm/ipc.h>
#include <asm/uaccess.h>
+#include <asm/fpumacro.h>
/* As gcc will warn about casting u32 to some ptr, we have to cast it to
* unsigned long first, and that's what is A() for.
switch (version) {
case 0: default: {
unsigned long raddr;
+ u32 *uptr = (u32 *) A(((u32)third));
err = sys_shmat (first, (char *)A(ptr), second, &raddr);
if (err)
goto out;
err = -EFAULT;
- if(put_user (raddr, ((u32 *)A(third))))
+ if(put_user (raddr, uptr))
goto out;
err = 0;
goto out;
return err;
}
-extern asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len,
- unsigned long prot, unsigned long flags,
- unsigned long fd, unsigned long off);
-
-asmlinkage unsigned long sys32_mmap(u32 addr, u32 len, u32 prot,
- u32 flags, u32 fd, u32 off)
-{
- return sys_mmap((unsigned long)addr, (unsigned long)len,
- (unsigned long)prot, (unsigned long)flags,
- (unsigned long)fd, (unsigned long)off);
-}
-
-extern asmlinkage int sys_bdflush(int func, long data);
-
-asmlinkage int sys32_bdflush(int func, s32 data)
-{
- return sys_bdflush(func, (long)data);
-}
-
-extern asmlinkage int sys_uselib(const char * library);
-
-asmlinkage int sys32_uselib(u32 library)
-{
- return sys_uselib((const char *)A(library));
-}
-
static inline int get_flock(struct flock *kfl, struct flock32 *ufl)
{
if(get_user(kfl->l_type, &ufl->l_type) ||
}
}
-extern asmlinkage int sys_mknod(const char * filename, int mode, dev_t dev);
-
-asmlinkage int sys32_mknod(u32 filename, int mode, __kernel_dev_t32 dev)
-{
- return sys_mknod((const char *)A(filename), mode, dev);
-}
-
-extern asmlinkage int sys_mkdir(const char * pathname, int mode);
-
-asmlinkage int sys32_mkdir(u32 pathname, int mode)
-{
- return sys_mkdir((const char *)A(pathname), mode);
-}
-
-extern asmlinkage int sys_rmdir(const char * pathname);
-
-asmlinkage int sys32_rmdir(u32 pathname)
-{
- return sys_rmdir((const char *)A(pathname));
-}
-
-extern asmlinkage int sys_unlink(const char * pathname);
-
-asmlinkage int sys32_unlink(u32 pathname)
-{
- return sys_unlink((const char *)A(pathname));
-}
-
-extern asmlinkage int sys_symlink(const char * oldname, const char * newname);
-
-asmlinkage int sys32_symlink(u32 oldname, u32 newname)
-{
- return sys_symlink((const char *)A(oldname), (const char *)A(newname));
-}
-
-extern asmlinkage int sys_link(const char * oldname, const char * newname);
-
-asmlinkage int sys32_link(u32 oldname, u32 newname)
-{
- return sys_link((const char *)A(oldname), (const char *)A(newname));
-}
-
-extern asmlinkage int sys_rename(const char * oldname, const char * newname);
-
-asmlinkage int sys32_rename(u32 oldname, u32 newname)
-{
- return sys_rename((const char *)A(oldname), (const char *)A(newname));
-}
-
struct dqblk32 {
__u32 dqb_bhardlimit;
__u32 dqb_bsoftlimit;
return ret;
}
-extern asmlinkage int sys_truncate(const char * path, unsigned long length);
-
-asmlinkage int sys32_truncate(u32 path, u32 length)
-{
- return sys_truncate((const char *)A(path), (unsigned long)length);
-}
-
-extern asmlinkage int sys_ftruncate(unsigned int fd, unsigned long length);
-
-asmlinkage int sys32_ftruncate(unsigned int fd, u32 length)
-{
- return sys_ftruncate(fd, (unsigned long)length);
-}
-
extern asmlinkage int sys_utime(char * filename, struct utimbuf * times);
asmlinkage int sys32_utime(u32 filename, u32 times)
return ret;
}
-extern asmlinkage int sys_utimes(char * filename, struct timeval * utimes);
-
-asmlinkage int sys32_utimes(u32 filename, u32 utimes)
-{
- /* struct timeval is the same :)) */
- return sys_utimes((char *)A(filename), (struct timeval *)A(utimes));
-}
-
-extern asmlinkage int sys_access(const char * filename, int mode);
-
-asmlinkage int sys32_access(u32 filename, int mode)
-{
- return sys_access((const char *)A(filename), mode);
-}
-
-extern asmlinkage int sys_chdir(const char * filename);
-
-asmlinkage int sys32_chdir(u32 filename)
-{
- return sys_chdir((const char *)A(filename));
-}
-
-extern asmlinkage int sys_chroot(const char * filename);
-
-asmlinkage int sys32_chroot(u32 filename)
-{
- return sys_chroot((const char *)A(filename));
-}
-
-extern asmlinkage int sys_chmod(const char * filename, mode_t mode);
-
-asmlinkage int sys32_chmod(u32 filename, __kernel_mode_t32 mode)
-{
- return sys_chmod((const char *)A(filename), mode);
-}
-
-extern asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group);
-
-asmlinkage int sys32_chown(u32 filename, __kernel_uid_t32 user, __kernel_gid_t32 group)
-{
- return sys_chown((const char *)A(filename), user, group);
-}
-
-extern asmlinkage int sys_open(const char * filename,int flags,int mode);
-
-asmlinkage int sys32_open(u32 filename, int flags, int mode)
-{
- return sys_open((const char *)A(filename), flags, mode);
-}
-
-extern asmlinkage int sys_creat(const char * pathname, int mode);
-
-asmlinkage int sys32_creat(u32 pathname, int mode)
-{
- return sys_creat((const char *)A(pathname), mode);
-}
-
-extern asmlinkage long sys_lseek(unsigned int fd, off_t offset, unsigned int origin);
-
-asmlinkage long sys32_lseek(unsigned int fd, s32 offset, unsigned int origin)
-{
- return sys_lseek(fd, (off_t)offset, origin);
-}
-
-extern asmlinkage int sys_llseek(unsigned int fd, unsigned long offset_high,
- unsigned long offset_low,
- loff_t *result, unsigned int origin);
-
-asmlinkage int sys32_llseek(unsigned int fd, u32 offset_high,
- u32 offset_low, u32 result, unsigned int origin)
-{
- /* loff_t is the same :)) */
- return sys_llseek(fd, (unsigned long)offset_high, (unsigned long)offset_low,
- (loff_t *)A(result), origin);
-}
-
-extern asmlinkage long sys_read(unsigned int fd, char * buf, unsigned long count);
-
-asmlinkage long sys32_read(unsigned int fd, u32 buf, u32 count)
-{
- return sys_read(fd, (char *)A(buf), (unsigned long)count);
-}
-
-extern asmlinkage long sys_write(unsigned int fd, const char * buf, unsigned long count);
-
-asmlinkage long sys32_write(unsigned int fd, u32 buf, u32 count)
-{
- return sys_write(fd, (const char *)A(buf), (unsigned long)count);
-}
-
struct iovec32 { u32 iov_base; __kernel_size_t32 iov_len; };
typedef long (*IO_fn_t)(struct inode *, struct file *, char *, unsigned long);
return ret;
}
-extern asmlinkage int sys_poll(struct pollfd * ufds, unsigned int nfds, int timeout);
-
-asmlinkage int sys32_poll(u32 ufds, unsigned int nfds, int timeout)
-{
- return sys_poll((struct pollfd *)A(ufds), nfds, timeout);
-}
-
static inline int putstat(struct stat32 *ubuf, struct stat *kbuf)
{
if (put_user (kbuf->st_dev, &ubuf->st_dev) ||
return ret;
}
-extern asmlinkage int sys_readlink(const char * path, char * buf, int bufsiz);
-
-asmlinkage int sys32_readlink(u32 path, u32 buf, int bufsiz)
-{
- return sys_readlink((const char *)A(path), (char *)A(buf), bufsiz);
-}
-
extern asmlinkage int sys_sysfs(int option, ...);
asmlinkage int sys32_sysfs(int option, ...)
return ret;
}
-extern asmlinkage int sys_ustat(dev_t dev, struct ustat * ubuf);
-
-asmlinkage int sys32_ustat(dev_t dev, u32 ubuf)
-{
- /* ustat is the same :)) */
- return sys_ustat(dev, (struct ustat *)A(ubuf));
-}
-
-extern asmlinkage int sys_umount(char * name);
-
-asmlinkage int sys32_umount(u32 name)
-{
- return sys_umount((char *)A(name));
-}
-
struct ncp_mount_data32 {
int version;
unsigned int ncp_fd;
}
}
-extern asmlinkage int sys_syslog(int type, char * bug, int count);
-
-asmlinkage int sys32_syslog(int type, u32 bug, int count)
-{
- return sys_syslog(type, (char *)A(bug), count);
-}
-
-extern asmlinkage int sys_personality(unsigned long personality);
-
-asmlinkage int sys32_personality(u32 personality)
-{
- return sys_personality((unsigned long)personality);
-}
-
struct rusage32 {
struct timeval ru_utime;
struct timeval ru_stime;
}
}
-extern asmlinkage int sys_waitpid(pid_t pid,unsigned int * stat_addr, int options);
-
-asmlinkage int sys32_waitpid(__kernel_pid_t32 pid, u32 stat_addr, int options)
-{
- return sys_waitpid(pid, (unsigned int *)A(stat_addr), options);
-}
-
struct sysinfo32 {
s32 uptime;
u32 loads[3];
return ret;
}
-extern asmlinkage int sys_getitimer(int which, struct itimerval *value);
-
-asmlinkage int sys32_getitimer(int which, u32 value)
-{
- /* itimerval is the same :)) */
- return sys_getitimer(which, (struct itimerval *)A(value));
-}
-
-extern asmlinkage int sys_setitimer(int which, struct itimerval *value,
- struct itimerval *ovalue);
-
-asmlinkage int sys32_setitimer(int which, u32 value, u32 ovalue)
-{
- return sys_setitimer(which, (struct itimerval *)A(value),
- (struct itimerval *)A(ovalue));
-}
-
-extern asmlinkage int sys_sched_setscheduler(pid_t pid, int policy,
- struct sched_param *param);
-
-asmlinkage int sys32_sched_setscheduler(__kernel_pid_t32 pid, int policy, u32 param)
-{
- /* sched_param is the same :)) */
- return sys_sched_setscheduler(pid, policy, (struct sched_param *)A(param));
-}
-
-extern asmlinkage int sys_sched_setparam(pid_t pid, struct sched_param *param);
-
-asmlinkage int sys32_sched_setparam(__kernel_pid_t32 pid, u32 param)
-{
- return sys_sched_setparam(pid, (struct sched_param *)A(param));
-}
-
-extern asmlinkage int sys_sched_getparam(pid_t pid, struct sched_param *param);
-
-asmlinkage int sys32_sched_getparam(__kernel_pid_t32 pid, u32 param)
-{
- return sys_sched_getparam(pid, (struct sched_param *)A(param));
-}
-
struct timespec32 {
s32 tv_sec;
s32 tv_nsec;
return ret;
}
-extern asmlinkage unsigned long sys_signal(int signum, __sighandler_t handler);
-
-asmlinkage unsigned long sys32_signal(int signum, u32 handler)
-{
- return sys_signal(signum, (__sighandler_t)A(handler));
-}
-
-extern asmlinkage int sys_reboot(int magic1, int magic2, int cmd, void * arg);
-
-asmlinkage int sys32_reboot(int magic1, int magic2, int cmd, u32 arg)
-{
- return sys_reboot(magic1, magic2, cmd, (void *)A(arg));
-}
-
-extern asmlinkage int sys_acct(const char *name);
-
-asmlinkage int sys32_acct(u32 name)
-{
- return sys_acct((const char *)A(name));
-}
-
extern asmlinkage int sys_setreuid(uid_t ruid, uid_t euid);
asmlinkage int sys32_setreuid(__kernel_uid_t32 ruid, __kernel_uid_t32 euid)
return ret;
}
-extern asmlinkage int sys_newuname(struct new_utsname * name);
-
-asmlinkage int sys32_newuname(u32 name)
-{
- /* utsname is the same :)) */
- return sys_newuname((struct new_utsname *)A(name));
-}
-
-extern asmlinkage int sys_olduname(struct oldold_utsname * name);
-
-asmlinkage int sys32_olduname(u32 name)
-{
- return sys_olduname((struct oldold_utsname *)A(name));
-}
-
-extern asmlinkage int sys_sethostname(char *name, int len);
-
-asmlinkage int sys32_sethostname(u32 name, int len)
-{
- return sys_sethostname((char *)A(name), len);
-}
-
-extern asmlinkage int sys_gethostname(char *name, int len);
-
-asmlinkage int sys32_gethostname(u32 name, int len)
-{
- return sys_gethostname((char *)A(name), len);
-}
-
-extern asmlinkage int sys_setdomainname(char *name, int len);
-
-asmlinkage int sys32_setdomainname(u32 name, int len)
-{
- return sys_setdomainname((char *)A(name), len);
-}
-
#define RLIM_INFINITY32 0x7fffffff
#define RESOURCE32(x) ((x > RLIM_INFINITY32) ? RLIM_INFINITY32 : x)
return ret;
}
-extern asmlinkage int sys_time(int * tloc);
-
-asmlinkage int sys32_time(u32 tloc)
-{
- return sys_time((int *)A(tloc));
-}
-
-extern asmlinkage int sys_gettimeofday(struct timeval *tv, struct timezone *tz);
-
-asmlinkage int sys32_gettimeofday(u32 tv, u32 tz)
-{
- /* both timeval and timezone are ok :)) */
- return sys_gettimeofday((struct timeval *)A(tv), (struct timezone *)A(tz));
-}
-
-extern asmlinkage int sys_settimeofday(struct timeval *tv, struct timezone *tz);
-
-asmlinkage int sys32_settimeofday(u32 tv, u32 tz)
-{
- return sys_settimeofday((struct timeval *)A(tv), (struct timezone *)A(tz));
-}
-
struct timex32 {
unsigned int modes;
s32 offset;
return ret;
}
-extern asmlinkage int sys_msync(unsigned long start, size_t len, int flags);
-
-asmlinkage int sys32_msync(u32 start, __kernel_size_t32 len, int flags)
-{
- return sys_msync((unsigned long)start, (size_t)len, flags);
-}
-
-extern asmlinkage int sys_mlock(unsigned long start, size_t len);
-
-asmlinkage int sys32_mlock(u32 start, __kernel_size_t32 len)
-{
- return sys_mlock((unsigned long)start, (size_t)len);
-}
-
-extern asmlinkage int sys_munlock(unsigned long start, size_t len);
-
-asmlinkage int sys32_munlock(u32 start, __kernel_size_t32 len)
-{
- return sys_munlock((unsigned long)start, (size_t)len);
-}
-
-extern asmlinkage unsigned long sys_brk(unsigned long brk);
-
-asmlinkage unsigned long sparc32_brk(u32 brk)
-{
- return sys_brk((unsigned long)brk);
-}
-
-extern asmlinkage int sys_munmap(unsigned long addr, size_t len);
-
-asmlinkage int sys32_munmap(u32 addr, __kernel_size_t32 len)
-{
- return sys_munmap((unsigned long)addr, (size_t)len);
-}
-
-extern asmlinkage int sys_mprotect(unsigned long start, size_t len, unsigned long prot);
-
-asmlinkage int sys32_mprotect(u32 start, __kernel_size_t32 len, u32 prot)
-{
- return sys_mprotect((unsigned long)start, (size_t)len, (unsigned long)prot);
-}
-
-extern asmlinkage unsigned long sys_mremap(unsigned long addr, unsigned long old_len,
- unsigned long new_len, unsigned long flags);
-
-asmlinkage unsigned long sys32_mremap(u32 addr, u32 old_len, u32 new_len, u32 flags)
-{
- return sys_mremap((unsigned long)addr, (unsigned long)old_len,
- (unsigned long)new_len, (unsigned long)flags);
-}
-
-extern asmlinkage int sys_swapoff(const char * specialfile);
-
-asmlinkage int sys32_swapoff(u32 specialfile)
-{
- return sys_swapoff((const char *)A(specialfile));
-}
-
-extern asmlinkage int sys_swapon(const char * specialfile, int swap_flags);
-
-asmlinkage int sys32_swapon(u32 specialfile, int swap_flags)
-{
- return sys_swapon((const char *)A(specialfile), swap_flags);
-}
-
-extern asmlinkage int sys_bind(int fd, struct sockaddr *umyaddr, int addrlen);
-
-asmlinkage inline int sys32_bind(int fd, u32 umyaddr, int addrlen)
-{
- /* sockaddr is the same :)) */
- return sys_bind(fd, (struct sockaddr *)A(umyaddr), addrlen);
-}
-
-extern asmlinkage int sys_accept(int fd, struct sockaddr *upeer_sockaddr,
- int *upeer_addrlen);
-
-asmlinkage inline int sys32_accept(int fd, u32 upeer_sockaddr, u32 upeer_addrlen)
-{
- return sys_accept(fd, (struct sockaddr *)A(upeer_sockaddr),
- (int *)A(upeer_addrlen));
-}
-
-extern asmlinkage int sys_connect(int fd, struct sockaddr *uservaddr, int addrlen);
-
-asmlinkage inline int sys32_connect(int fd, u32 uservaddr, int addrlen)
-{
- return sys_connect(fd, (struct sockaddr *)A(uservaddr), addrlen);
-}
-
-extern asmlinkage int sys_getsockname(int fd, struct sockaddr *usockaddr,
- int *usockaddr_len);
-
-asmlinkage int sys32_getsockname(int fd, u32 usockaddr, u32 usockaddr_len)
-{
- return sys_getsockname(fd, (struct sockaddr *)A(usockaddr),
- (int *)A(usockaddr_len));
-}
-
-extern asmlinkage int sys_getpeername(int fd, struct sockaddr *usockaddr,
- int *usockaddr_len);
-
-asmlinkage int sys32_getpeername(int fd, u32 usockaddr, u32 usockaddr_len)
-{
- return sys_getpeername(fd, (struct sockaddr *)A(usockaddr),
- (int *)A(usockaddr_len));
-}
-
-extern asmlinkage int sys_send(int fd, void * buff, size_t len, unsigned flags);
-
-asmlinkage inline int sys32_send(int fd, u32 buff,
- __kernel_size_t32 len, unsigned flags)
-{
- return sys_send(fd, (void *)A(buff), (size_t)len, flags);
-}
-
-extern asmlinkage int sys_sendto(int fd, void * buff, size_t len, unsigned flags,
- struct sockaddr *addr, int addr_len);
-
-asmlinkage inline int sys32_sendto(int fd, u32 buff, __kernel_size_t32 len,
- unsigned flags, u32 addr, int addr_len)
-{
- return sys_sendto(fd, (void *)A(buff), (size_t)len, flags,
- (struct sockaddr *)A(addr), addr_len);
-}
-
-extern asmlinkage int sys_recv(int fd, void * ubuf, size_t size, unsigned flags);
-
-asmlinkage inline int sys32_recv(int fd, u32 ubuf,
- __kernel_size_t32 size, unsigned flags)
-{
- return sys_recv(fd, (void *)A(ubuf), (size_t)size, flags);
-}
-
-extern asmlinkage int sys_recvfrom(int fd, void * ubuf, size_t size, unsigned flags,
- struct sockaddr *addr, int *addr_len);
-
-asmlinkage inline int sys32_recvfrom(int fd, u32 ubuf, __kernel_size_t32 size,
- unsigned flags, u32 addr, u32 addr_len)
-{
- return sys_recvfrom(fd, (void *)A(ubuf), (size_t)size, flags,
- (struct sockaddr *)A(addr), (int *)A(addr_len));
-}
-
-extern asmlinkage int sys_setsockopt(int fd, int level, int optname,
- char *optval, int optlen);
-
-asmlinkage inline int sys32_setsockopt(int fd, int level, int optname,
- u32 optval, int optlen)
-{
- /* XXX handle ip_fw32->ip_fw conversion for IP firewalling and accounting.
- Do it using some macro in ip_sockglue.c
- Other optval arguments are mostly just ints or 32<->64bit transparent */
- return sys_setsockopt(fd, level, optname, (char *)A(optval), optlen);
-}
-
-extern asmlinkage int sys_getsockopt(int fd, int level, int optname,
- char *optval, int *optlen);
-
-asmlinkage inline int sys32_getsockopt(int fd, int level, int optname,
- u32 optval, u32 optlen)
-{
- return sys_getsockopt(fd, level, optname, (char *)A(optval), (int *)A(optlen));
-}
-
/* XXX This really belongs in some header file... -DaveM */
#define MAX_SOCK_ADDR 128 /* 108 for Unix domain -
16 for IP, 16 for IPX,
AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)};
#undef AL
+extern asmlinkage int sys32_bind(int fd, u32 umyaddr, int addrlen);
+extern asmlinkage int sys32_connect(int fd, u32 uservaddr, int addrlen);
+extern asmlinkage int sys32_accept(int fd, u32 upeer_sockaddr, u32 upeer_addrlen);
+extern asmlinkage int sys32_getsockname(int fd, u32 usockaddr, u32 usockaddr_len);
+extern asmlinkage int sys32_getpeername(int fd, u32 usockaddr, u32 usockaddr_len);
+extern asmlinkage int sys32_send(int fd, u32 buff, __kernel_size_t32 len,
+ unsigned flags);
+extern asmlinkage int sys32_sendto(int fd, u32 buff, __kernel_size_t32 len,
+ unsigned flags, u32 addr, int addr_len);
+extern asmlinkage int sys32_recv(int fd, u32 ubuf, __kernel_size_t32 size,
+ unsigned flags);
+extern asmlinkage int sys32_recvfrom(int fd, u32 ubuf, __kernel_size_t32 size,
+ unsigned flags, u32 addr, u32 addr_len);
+extern asmlinkage int sys32_setsockopt(int fd, int level, int optname,
+ u32 optval, int optlen);
+extern asmlinkage int sys32_getsockopt(int fd, int level, int optname,
+ u32 optval, u32 optlen);
+
extern asmlinkage int sys_socket(int family, int type, int protocol);
extern asmlinkage int sys_socketpair(int family, int type, int protocol,
int usockvec[2]);
old_sa.sa_mask = (sigset_t32)(p->sa_mask);
old_sa.sa_flags = (unsigned)(p->sa_flags);
old_sa.sa_restorer = (unsigned)(u64)(p->sa_restorer);
- if (copy_to_user(A(oldaction), p, sizeof(struct sigaction32)))
+ if (copy_to_user(A(oldaction), &old_sa, sizeof(struct sigaction32)))
goto out;
}
return err;
}
-extern asmlinkage int sys_nfsservctl(int cmd, void *argp, void *resp);
-
-asmlinkage int sys32_nfsservctl(int cmd, u32 argp, u32 resp)
-{
- /* XXX handle argp and resp args */
- return sys_nfsservctl(cmd, (void *)A(argp), (void *)A(resp));
-}
-
/*
* count32() counts the number of arguments/envelopes
*/
(u32 *)A((u32)regs->u_regs[base + UREG_I1]),
(u32 *)A((u32)regs->u_regs[base + UREG_I2]), regs);
putname(filename);
+ if(!error) {
+ fprs_write(0);
+ regs->fprs = 0;
+ }
return error;
}
-/* Modules will be supported with 64bit modutils only */
-asmlinkage int sys32_no_modules(void)
+#ifdef CONFIG_MODULES
+
+extern asmlinkage unsigned long sys_create_module(const char *name_user, size_t size);
+
+asmlinkage unsigned long sys32_create_module(u32 name_user, __kernel_size_t32 size)
+{
+ return sys_create_module((const char *)A(name_user), (size_t)size);
+}
+
+extern asmlinkage int sys_init_module(const char *name_user, struct module *mod_user);
+
+/* Hey, when you're trying to init module, take time and prepare us a nice 64bit
+ * module structure, even if from 32bit modutils... Why to pollute kernel... :))
+ */
+asmlinkage int sys32_init_module(u32 nameuser, u32 mod_user)
+{
+ return sys_init_module((const char *)A(nameuser), (struct module *)A(mod_user));
+}
+
+extern asmlinkage int sys_delete_module(const char *name_user);
+
+asmlinkage int sys32_delete_module(u32 name_user)
+{
+ return sys_delete_module((const char *)A(name_user));
+}
+
+struct module_info32 {
+ u32 addr;
+ u32 size;
+ u32 flags;
+ s32 usecount;
+};
+
+extern asmlinkage int sys_query_module(const char *name_user, int which, char *buf, size_t bufsize, size_t *ret);
+
+asmlinkage int sys32_query_module(u32 name_user, int which, u32 buf, __kernel_size_t32 bufsize, u32 retv)
+{
+ char *buff;
+ unsigned long old_fs = get_fs();
+ size_t val;
+ int ret, i, j;
+ unsigned long *p;
+ char *usernam = NULL;
+ int bufsiz = bufsize;
+ struct module_info mi;
+
+ switch (which) {
+ case 0: return sys_query_module ((const char *)A(name_user), which, (char *)A(buf), (size_t)bufsize, (size_t *)A(retv));
+ case QM_SYMBOLS:
+ bufsiz <<= 1;
+ case QM_MODULES:
+ case QM_REFS:
+ case QM_DEPS:
+ if (name_user && (ret = getname32 (name_user, &usernam)))
+ return ret;
+ buff = kmalloc (bufsiz, GFP_KERNEL);
+ if (!buff) {
+ if (name_user) putname32 (usernam);
+ return -ENOMEM;
+ }
+qmsym_toshort:
+ set_fs (KERNEL_DS);
+ ret = sys_query_module (usernam, which, buff, bufsiz, &val);
+ set_fs (old_fs);
+ if (which != QM_SYMBOLS) {
+ if (ret == -ENOSPC || !ret) {
+ if (put_user (val, (__kernel_size_t32 *)A(retv)))
+ ret = -EFAULT;
+ }
+ if (!ret) {
+ if (copy_to_user ((char *)A(buf), buff, bufsize))
+ ret = -EFAULT;
+ }
+ } else {
+ if (ret == -ENOSPC) {
+ if (put_user (2 * val, (__kernel_size_t32 *)A(retv)))
+ ret = -EFAULT;
+ }
+ p = (unsigned long *)buff;
+ if (!ret) {
+ if (put_user (val, (__kernel_size_t32 *)A(retv)))
+ ret = -EFAULT;
+ }
+ if (!ret) {
+ j = val * 8;
+ for (i = 0; i < val; i++, p += 2) {
+ if (bufsize < (2 * sizeof (u32))) {
+ bufsiz = 0;
+ goto qmsym_toshort;
+ }
+ if (put_user (p[0], (u32 *)A(buf)) ||
+ __put_user (p[1] - j, (((u32 *)A(buf))+1))) {
+ ret = -EFAULT;
+ break;
+ }
+ bufsize -= (2 * sizeof (u32));
+ buf += (2 * sizeof (u32));
+ }
+ }
+ if (!ret && val) {
+ char *strings = buff + ((unsigned long *)buff)[1];
+ j = *(p - 1) - ((unsigned long *)buff)[1];
+ j = j + strlen (buff + j) + 1;
+ if (bufsize < j) {
+ bufsiz = 0;
+ goto qmsym_toshort;
+ }
+ if (copy_to_user ((char *)A(buf), strings, j))
+ ret = -EFAULT;
+ }
+ }
+ kfree (buff);
+ if (name_user) putname32 (usernam);
+ return ret;
+ case QM_INFO:
+ if (name_user && (ret = getname32 (name_user, &usernam)))
+ return ret;
+ set_fs (KERNEL_DS);
+ ret = sys_query_module (usernam, which, (char *)&mi, sizeof (mi), &val);
+ set_fs (old_fs);
+ if (!ret) {
+ if (put_user (sizeof (struct module_info32), (__kernel_size_t32 *)A(retv)))
+ ret = -EFAULT;
+ else if (bufsize < sizeof (struct module_info32))
+ ret = -ENOSPC;
+ }
+ if (!ret) {
+ if (put_user (mi.addr, &(((struct module_info32 *)A(buf))->addr)) ||
+ __put_user (mi.size, &(((struct module_info32 *)A(buf))->size)) ||
+ __put_user (mi.flags, &(((struct module_info32 *)A(buf))->flags)) ||
+ __put_user (mi.usecount, &(((struct module_info32 *)A(buf))->usecount)))
+ ret = -EFAULT;
+ }
+ if (name_user) putname32 (usernam);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+struct kernel_sym32 {
+ u32 value;
+ char name[60];
+};
+
+extern asmlinkage int sys_get_kernel_syms(struct kernel_sym *table);
+
+asmlinkage int sys32_get_kernel_syms(u32 table)
+{
+ int len, i;
+ struct kernel_sym *tbl;
+ unsigned long old_fs;
+
+ len = sys_get_kernel_syms(NULL);
+ if (!table) return len;
+ tbl = kmalloc (len * sizeof (struct kernel_sym), GFP_KERNEL);
+ if (!tbl) return -ENOMEM;
+ old_fs = get_fs();
+ set_fs (KERNEL_DS);
+ sys_get_kernel_syms(tbl);
+ set_fs (old_fs);
+ for (i = 0; i < len; i++, table += sizeof (struct kernel_sym32)) {
+ if (put_user (tbl[i].value, &(((struct kernel_sym32 *)A(table))->value)) ||
+ copy_to_user (((struct kernel_sym32 *)A(table))->name, tbl[i].name, 60))
+ break;
+ }
+ kfree (tbl);
+ return i;
+}
+
+#else /* CONFIG_MODULES */
+
+asmlinkage unsigned long
+sys_create_module(const char *name_user, size_t size)
+{
+ return -ENOSYS;
+}
+
+asmlinkage int
+sys_init_module(const char *name_user, struct module *mod_user)
+{
+ return -ENOSYS;
+}
+
+asmlinkage int
+sys_delete_module(const char *name_user)
{
return -ENOSYS;
}
+
+asmlinkage int
+sys_query_module(const char *name_user, int which, char *buf, size_t bufsize,
+ size_t *ret)
+{
+ /* Let the program know about the new interface. Not that
+ it'll do them much good. */
+ if (which == 0)
+ return 0;
+
+ return -ENOSYS;
+}
+
+asmlinkage int
+sys_get_kernel_syms(struct kernel_sym *table)
+{
+ return -ENOSYS;
+}
+
+#endif /* CONFIG_MODULES */
--- /dev/null
+/* $Id: sys_sunos32.c,v 1.2 1997/07/05 07:09:16 davem Exp $
+ * sys_sunos32.c: SunOS binary compatability layer on sparc64.
+ *
+ * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ *
+ * Based upon preliminary work which is:
+ *
+ * Copyright (C) 1995 Adrian M. Rodriguez (adrian@remus.rutgers.edu)
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/swap.h>
+#include <linux/fs.h>
+#include <linux/resource.h>
+#include <linux/ipc.h>
+#include <linux/shm.h>
+#include <linux/msg.h>
+#include <linux/sem.h>
+#include <linux/signal.h>
+#include <linux/uio.h>
+#include <linux/utsname.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <linux/stat.h>
+#include <linux/malloc.h>
+#include <linux/pagemap.h>
+#include <linux/errno.h>
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
+
+#include <asm/uaccess.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/pconf.h>
+#include <asm/idprom.h> /* for gethostid() */
+#include <asm/unistd.h>
+#include <asm/system.h>
+
+/* For the nfs mount emulation */
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/nfs.h>
+#include <linux/nfs_mount.h>
+
+/* for sunos_select */
+#include <linux/time.h>
+#include <linux/personality.h>
+
+#define A(x) ((unsigned long)x)
+
+#define SUNOS_NR_OPEN 256
+
+extern unsigned long get_unmapped_area(unsigned long addr, unsigned long len);
+
+asmlinkage u32 sunos_mmap(u32 addr, u32 len, u32 prot, u32 flags, u32 fd, u32 off)
+{
+ struct file *file = NULL;
+ unsigned long retval, ret_type;
+
+ lock_kernel();
+ current->personality |= PER_BSD;
+ if(flags & MAP_NORESERVE) {
+ printk("%s: unimplemented SunOS MAP_NORESERVE mmap() flag\n",
+ current->comm);
+ flags &= ~MAP_NORESERVE;
+ }
+ retval = -EBADF;
+ if(!(flags & MAP_ANONYMOUS))
+ if(fd >= SUNOS_NR_OPEN || !(file = current->files->fd[fd]))
+ goto out;
+ retval = -ENOMEM;
+ if(!(flags & MAP_FIXED) && !addr) {
+ unsigned long attempt = get_unmapped_area(addr, len);
+ if(!attempt || (attempt >= 0xf0000000UL))
+ goto out;
+ addr = (u32) attempt;
+ }
+ if(MAJOR(file->f_inode->i_rdev) == MEM_MAJOR &&
+ MINOR(file->f_inode->i_rdev) == 5) {
+ flags |= MAP_ANONYMOUS;
+ file = 0;
+ }
+ if(!(flags & MAP_FIXED))
+ addr = 0;
+ ret_type = flags & _MAP_NEW;
+ flags &= ~_MAP_NEW;
+
+ retval = do_mmap(file,
+ (unsigned long) addr, (unsigned long) len,
+ (unsigned long) prot, (unsigned long) flags,
+ (unsigned long) off);
+ if(!ret_type)
+ retval = ((retval < 0xf0000000) ? 0 : retval);
+out:
+ unlock_kernel();
+ return (u32) retval;
+}
+
+asmlinkage int sunos_mctl(u32 addr, u32 len, int function, u32 arg)
+{
+ return 0;
+}
+
+asmlinkage int sunos_brk(u32 baddr)
+{
+ int freepages, retval = -ENOMEM;
+ unsigned long rlim;
+ unsigned long newbrk, oldbrk, brk = (unsigned long) baddr;
+
+ lock_kernel();
+ if (brk < current->mm->end_code)
+ goto out;
+ newbrk = PAGE_ALIGN(brk);
+ oldbrk = PAGE_ALIGN(current->mm->brk);
+ retval = 0;
+ if (oldbrk == newbrk) {
+ current->mm->brk = brk;
+ goto out;
+ }
+ /* Always allow shrinking brk. */
+ if (brk <= current->mm->brk) {
+ current->mm->brk = brk;
+ do_munmap(newbrk, oldbrk-newbrk);
+ goto out;
+ }
+ /* Check against rlimit and stack.. */
+ retval = -ENOMEM;
+ rlim = current->rlim[RLIMIT_DATA].rlim_cur;
+ if (rlim >= RLIM_INFINITY)
+ rlim = ~0;
+ if (brk - current->mm->end_code > rlim)
+ goto out;
+ /* Check against existing mmap mappings. */
+ if (find_vma_intersection(current->mm, oldbrk, newbrk+PAGE_SIZE))
+ goto out;
+ /* stupid algorithm to decide if we have enough memory: while
+ * simple, it hopefully works in most obvious cases.. Easy to
+ * fool it, but this should catch most mistakes.
+ */
+ freepages = buffermem >> PAGE_SHIFT;
+ freepages += page_cache_size;
+ freepages >>= 1;
+ freepages += nr_free_pages;
+ freepages += nr_swap_pages;
+ freepages -= num_physpages >> 4;
+ freepages -= (newbrk-oldbrk) >> PAGE_SHIFT;
+ if (freepages < 0)
+ goto out;
+ /* Ok, we have probably got enough memory - let it rip. */
+ current->mm->brk = brk;
+ do_mmap(NULL, oldbrk, newbrk-oldbrk,
+ PROT_READ|PROT_WRITE|PROT_EXEC,
+ MAP_FIXED|MAP_PRIVATE, 0);
+ retval = 0;
+out:
+ unlock_kernel();
+ return retval;
+}
+
+asmlinkage u32 sunos_sbrk(int increment)
+{
+ int error, oldbrk;
+
+ /* This should do it hopefully... */
+ lock_kernel();
+ oldbrk = (int)current->mm->brk;
+ error = sunos_brk(((int) current->mm->brk) + increment);
+ if(!error)
+ error = oldbrk;
+ unlock_kernel();
+ return error;
+}
+
+asmlinkage u32 sunos_sstk(int increment)
+{
+ lock_kernel();
+ printk("%s: Call to sunos_sstk(increment<%d>) is unsupported\n",
+ current->comm, increment);
+ unlock_kernel();
+ return (u32)-1;
+}
+
+/* Give hints to the kernel as to what paging strategy to use...
+ * Completely bogus, don't remind me.
+ */
+#define VA_NORMAL 0 /* Normal vm usage expected */
+#define VA_ABNORMAL 1 /* Abnormal/random vm usage probable */
+#define VA_SEQUENTIAL 2 /* Accesses will be of a sequential nature */
+#define VA_INVALIDATE 3 /* Page table entries should be flushed ??? */
+static char *vstrings[] = {
+ "VA_NORMAL",
+ "VA_ABNORMAL",
+ "VA_SEQUENTIAL",
+ "VA_INVALIDATE",
+};
+
+asmlinkage void sunos_vadvise(u32 strategy)
+{
+ /* I wanna see who uses this... */
+ lock_kernel();
+ printk("%s: Advises us to use %s paging strategy\n",
+ current->comm,
+ strategy <= 3 ? vstrings[strategy] : "BOGUS");
+ unlock_kernel();
+}
+
+/* Same as vadvise, and just as bogus, but for a range of virtual
+ * process address space.
+ */
+#define MADV_NORMAL 0 /* Nothing special... */
+#define MADV_RANDOM 1 /* I am emacs... */
+#define MADV_SEQUENTIAL 2 /* I am researcher code... */
+#define MADV_WILLNEED 3 /* Pages in this range will be needed */
+#define MADV_DONTNEED 4 /* Pages in this range won't be needed */
+
+static char *mstrings[] = {
+ "MADV_NORMAL",
+ "MADV_RANDOM",
+ "MADV_SEQUENTIAL",
+ "MADV_WILLNEED",
+ "MADV_DONTNEED",
+};
+
+asmlinkage void sunos_madvise(u32 address, u32 len, u32 strategy)
+{
+ /* I wanna see who uses this... */
+ lock_kernel();
+ printk("%s: Advises us to use %s paging strategy for addr<%08x> len<%08x>\n",
+ current->comm, strategy <= 4 ? mstrings[strategy] : "BOGUS",
+ address, len);
+ unlock_kernel();
+}
+
+/* Places into character array, the status of all the pages in the passed
+ * range from 'addr' to 'addr + len'. -1 on failure, 0 on success...
+ * The encoding in each character is:
+ * low-bit is zero == Page is not in physical ram right now
+ * low-bit is one == Page is currently residing in core
+ * All other bits are undefined within the character so there...
+ * Also, if you try to get stats on an area outside of the user vm area
+ * *or* the passed base address is not aligned on a page boundary you
+ * get an error.
+ */
+asmlinkage int sunos_mincore(u32 addr, u32 len, u32 u_array)
+{
+ pgd_t *pgdp;
+ pmd_t *pmdp;
+ pte_t *ptep;
+ unsigned long limit;
+ int num_pages, pnum, retval = -EINVAL;
+ char *array = (char *)A(u_array);
+
+ lock_kernel();
+ if(addr & ~(4096))
+ goto out;
+ num_pages = (len / 4096);
+ retval = -EFAULT;
+ if(verify_area(VERIFY_WRITE, array, num_pages))
+ goto out;
+ retval = -ENOMEM;
+ if((addr >= 0xf0000000) || ((addr + len) > 0xf0000000))
+ goto out; /* I'm sure you're curious about kernel mappings.. */
+ /* Wheee, go through pte's */
+ pnum = 0;
+ for(limit = addr + len; addr < limit; addr += 4096, pnum++) {
+ pgdp = pgd_offset(current->mm, addr);
+ if(pgd_none(*pgdp))
+ goto out; /* As per SunOS manpage */
+ pmdp = pmd_offset(pgdp, addr);
+ if(pmd_none(*pmdp))
+ goto out; /* As per SunOS manpage */
+ ptep = pte_offset(pmdp, addr);
+ if(pte_none(*ptep))
+ goto out; /* As per SunOS manpage */
+ /* Page in core or Swapped page? */
+ __put_user((pte_present(*ptep) ? 1 : 0), &array[pnum]);
+ }
+ retval = 0; /* Success... I think... */
+out:
+ unlock_kernel();
+ return retval;
+}
+
+/* This just wants the soft limit (ie. rlim_cur element) of the RLIMIT_NOFILE
+ * resource limit and is for backwards compatibility with older sunos
+ * revs.
+ */
+asmlinkage int sunos_getdtablesize(void)
+{
+ return SUNOS_NR_OPEN;
+}
+
+#define _S(nr) (1<<((nr)-1))
+
+#define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
+
+asmlinkage u32 sunos_sigblock(u32 blk_mask)
+{
+ unsigned long flags;
+ u32 old;
+
+ lock_kernel();
+ save_and_cli(flags);
+ old = (u32) current->blocked;
+ current->blocked |= (blk_mask & _BLOCKABLE);
+ restore_flags(flags);
+ unlock_kernel();
+ return old;
+}
+
+asmlinkage u32 sunos_sigsetmask(u32 newmask)
+{
+ unsigned long flags;
+ u32 retval;
+
+ lock_kernel();
+ save_and_cli(flags);
+ retval = (u32) current->blocked;
+ current->blocked = (newmask & _BLOCKABLE);
+ restore_flags(flags);
+ unlock_kernel();
+ return retval;
+}
+
+/* SunOS getdents is very similar to the newer Linux (iBCS2 compliant) */
+/* getdents system call, the format of the structure just has a different */
+/* layout (d_off+d_ino instead of d_ino+d_off) */
+struct sunos_dirent {
+ s32 d_off;
+ u32 d_ino;
+ u16 d_reclen;
+ u16 d_namlen;
+ char d_name[1];
+};
+
+struct sunos_dirent_callback {
+ struct sunos_dirent *curr;
+ struct sunos_dirent *previous;
+ int count;
+ int error;
+};
+
+#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de)))
+#define ROUND_UP(x) (((x)+sizeof(s32)-1) & ~(sizeof(s32)-1))
+
+static int sunos_filldir(void * __buf, const char * name, int namlen,
+ off_t offset, ino_t ino)
+{
+ struct sunos_dirent * dirent;
+ struct sunos_dirent_callback * buf = (struct sunos_dirent_callback *) __buf;
+ int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1);
+
+ buf->error = -EINVAL; /* only used if we fail.. */
+ if (reclen > buf->count)
+ return -EINVAL;
+ dirent = buf->previous;
+ if (dirent)
+ put_user(offset, &dirent->d_off);
+ dirent = buf->curr;
+ buf->previous = dirent;
+ put_user(ino, &dirent->d_ino);
+ put_user(namlen, &dirent->d_namlen);
+ put_user(reclen, &dirent->d_reclen);
+ copy_to_user(dirent->d_name, name, namlen);
+ put_user(0, dirent->d_name + namlen);
+ ((char *) dirent) += reclen;
+ buf->curr = dirent;
+ buf->count -= reclen;
+ return 0;
+}
+
+asmlinkage int sunos_getdents(unsigned int fd, u32 u_dirent, int cnt)
+{
+ struct file * file;
+ struct sunos_dirent * lastdirent;
+ struct sunos_dirent_callback buf;
+ int error = -EBADF;
+ void *dirent = (void *)A(u_dirent);
+
+ lock_kernel();
+ if (fd >= SUNOS_NR_OPEN || !(file = current->files->fd[fd]))
+ goto out;
+ error = -ENOTDIR;
+ if (!file->f_op || !file->f_op->readdir)
+ goto out;
+ error = -EINVAL;
+ if(cnt < (sizeof(struct sunos_dirent) + 255))
+ goto out;
+
+ buf.curr = (struct sunos_dirent *) dirent;
+ buf.previous = NULL;
+ buf.count = cnt;
+ buf.error = 0;
+ error = file->f_op->readdir(file->f_inode, file, &buf, sunos_filldir);
+ if (error < 0)
+ goto out;
+ lastdirent = buf.previous;
+ if (!lastdirent) {
+ error = buf.error;
+ } else {
+ put_user(file->f_pos, &lastdirent->d_off);
+ error = cnt - buf.count;
+ }
+out:
+ unlock_kernel();
+ return error;
+}
+
+/* Old sunos getdirentries, severely broken compatibility stuff here. */
+struct sunos_direntry {
+ u32 d_ino;
+ u16 d_reclen;
+ u16 d_namlen;
+ char d_name[1];
+};
+
+struct sunos_direntry_callback {
+ struct sunos_direntry *curr;
+ struct sunos_direntry *previous;
+ int count;
+ int error;
+};
+
+static int sunos_filldirentry(void * __buf, const char * name, int namlen,
+ off_t offset, ino_t ino)
+{
+ struct sunos_direntry * dirent;
+ struct sunos_direntry_callback * buf = (struct sunos_direntry_callback *) __buf;
+ int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1);
+
+ buf->error = -EINVAL; /* only used if we fail.. */
+ if (reclen > buf->count)
+ return -EINVAL;
+ dirent = buf->previous;
+ dirent = buf->curr;
+ buf->previous = dirent;
+ put_user(ino, &dirent->d_ino);
+ put_user(namlen, &dirent->d_namlen);
+ put_user(reclen, &dirent->d_reclen);
+ copy_to_user(dirent->d_name, name, namlen);
+ put_user(0, dirent->d_name + namlen);
+ ((char *) dirent) += reclen;
+ buf->curr = dirent;
+ buf->count -= reclen;
+ return 0;
+}
+
+asmlinkage int sunos_getdirentries(unsigned int fd, u32 u_dirent,
+ int cnt, u32 u_basep)
+{
+ struct file * file;
+ struct sunos_direntry * lastdirent;
+ struct sunos_direntry_callback buf;
+ int error = -EBADF;
+ void *dirent = (void *) A(u_dirent);
+ unsigned int *basep = (unsigned int *)A(u_basep);
+
+ lock_kernel();
+ if (fd >= SUNOS_NR_OPEN || !(file = current->files->fd[fd]))
+ goto out;
+ error = -ENOTDIR;
+ if (!file->f_op || !file->f_op->readdir)
+ goto out;
+ error = -EINVAL;
+ if(cnt < (sizeof(struct sunos_direntry) + 255))
+ goto out;
+
+ buf.curr = (struct sunos_direntry *) dirent;
+ buf.previous = NULL;
+ buf.count = cnt;
+ buf.error = 0;
+ error = file->f_op->readdir(file->f_inode, file, &buf, sunos_filldirentry);
+ if (error < 0)
+ goto out;
+ lastdirent = buf.previous;
+ if (!lastdirent) {
+ error = buf.error;
+ } else {
+ put_user(file->f_pos, basep);
+ error = cnt - buf.count;
+ }
+out:
+ unlock_kernel();
+ return error;
+}
+
+asmlinkage int sunos_getdomainname(u32 u_name, int len)
+{
+ int nlen = strlen(system_utsname.domainname);
+ int ret = -EFAULT;
+ char *name = (char *)A(u_name);
+
+ lock_kernel();
+ if (nlen < len)
+ len = nlen;
+
+ if(len > __NEW_UTS_LEN)
+ goto out;
+ if(copy_to_user(name, system_utsname.domainname, len))
+ goto out;
+ ret = 0;
+out:
+ unlock_kernel();
+ return ret;
+}
+
+struct sunos_utsname {
+ char sname[9];
+ char nname[9];
+ char nnext[56];
+ char rel[9];
+ char ver[9];
+ char mach[9];
+};
+
+asmlinkage int sunos_uname(u32 u_name)
+{
+ struct sunos_utsname *name = (struct sunos_utsname *)A(u_name);
+ int ret = -EFAULT;
+
+ lock_kernel();
+ if(!name)
+ goto out;
+ if(copy_to_user(&name->sname[0],
+ &system_utsname.sysname[0],
+ sizeof(name->sname) - 1))
+ goto out;
+ copy_to_user(&name->nname[0],
+ &system_utsname.nodename[0],
+ sizeof(name->nname) - 1);
+ put_user('\0', &name->nname[8]);
+ copy_to_user(&name->rel[0], &system_utsname.release[0], sizeof(name->rel) - 1);
+ copy_to_user(&name->ver[0], &system_utsname.version[0], sizeof(name->ver) - 1);
+ copy_to_user(&name->mach[0], &system_utsname.machine[0], sizeof(name->mach) - 1);
+ ret = 0;
+out:
+ unlock_kernel();
+ return ret;
+}
+
+asmlinkage int sunos_nosys(void)
+{
+ struct pt_regs *regs;
+
+ lock_kernel();
+ regs = current->tss.kregs;
+ current->tss.sig_address = regs->tpc;
+ current->tss.sig_desc = regs->u_regs[UREG_G1];
+ send_sig(SIGSYS, current, 1);
+ printk("Process makes ni_syscall number %d, register dump:\n",
+ (int) regs->u_regs[UREG_G1]);
+ show_regs(regs);
+ unlock_kernel();
+ return -ENOSYS;
+}
+
+/* This is not a real and complete implementation yet, just to keep
+ * the easy SunOS binaries happy.
+ */
+asmlinkage int sunos_fpathconf(int fd, int name)
+{
+ int ret;
+
+ lock_kernel();
+ switch(name) {
+ case _PCONF_LINK:
+ ret = LINK_MAX;
+ break;
+ case _PCONF_CANON:
+ ret = MAX_CANON;
+ break;
+ case _PCONF_INPUT:
+ ret = MAX_INPUT;
+ break;
+ case _PCONF_NAME:
+ ret = NAME_MAX;
+ break;
+ case _PCONF_PATH:
+ ret = PATH_MAX;
+ break;
+ case _PCONF_PIPE:
+ ret = PIPE_BUF;
+ break;
+ case _PCONF_CHRESTRICT: /* XXX Investigate XXX */
+ ret = 1;
+ break;
+ case _PCONF_NOTRUNC: /* XXX Investigate XXX */
+ case _PCONF_VDISABLE:
+ ret = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ unlock_kernel();
+ return ret;
+}
+
+asmlinkage int sunos_pathconf(u32 u_path, int name)
+{
+ int ret;
+
+ lock_kernel();
+ ret = sunos_fpathconf(0, name); /* XXX cheese XXX */
+ unlock_kernel();
+ return ret;
+}
+
+/* SunOS mount system call emulation */
+extern asmlinkage int
+sys32_select(int n, u32 inp, u32 outp, u32 exp, u32 tvp);
+
+asmlinkage int sunos_select(int width, u32 inp, u32 outp, u32 exp, u32 tvp)
+{
+ int ret;
+
+ /* SunOS binaries expect that select won't change the tvp contents */
+ lock_kernel();
+ current->personality |= STICKY_TIMEOUTS;
+ ret = sys32_select (width, inp, outp, exp, tvp);
+ unlock_kernel();
+ return ret;
+}
+
+asmlinkage void sunos_nop(void)
+{
+ return;
+}
+
+/* XXXXXXXXXX SunOS mount/umount. XXXXXXXXXXX */
+#define SMNT_RDONLY 1
+#define SMNT_NOSUID 2
+#define SMNT_NEWTYPE 4
+#define SMNT_GRPID 8
+#define SMNT_REMOUNT 16
+#define SMNT_NOSUB 32
+#define SMNT_MULTI 64
+#define SMNT_SYS5 128
+
+struct sunos_fh_t {
+ char fh_data [NFS_FHSIZE];
+};
+
+struct sunos_nfs_mount_args {
+ struct sockaddr_in *addr; /* file server address */
+ struct nfs_fh *fh; /* File handle to be mounted */
+ int flags; /* flags */
+ int wsize; /* write size in bytes */
+ int rsize; /* read size in bytes */
+ int timeo; /* initial timeout in .1 secs */
+ int retrans; /* times to retry send */
+ char *hostname; /* server's hostname */
+ int acregmin; /* attr cache file min secs */
+ int acregmax; /* attr cache file max secs */
+ int acdirmin; /* attr cache dir min secs */
+ int acdirmax; /* attr cache dir max secs */
+ char *netname; /* server's netname */
+};
+
+extern int do_mount(kdev_t, const char *, const char *, char *, int, void *);
+extern dev_t get_unnamed_dev(void);
+extern void put_unnamed_dev(dev_t);
+extern asmlinkage int sys_mount(char *, char *, char *, unsigned long, void *);
+extern asmlinkage int sys_connect(int fd, struct sockaddr *uservaddr, int addrlen);
+extern asmlinkage int sys_socket(int family, int type, int protocol);
+extern asmlinkage int sys_bind(int fd, struct sockaddr *umyaddr, int addrlen);
+
+
+/* Bind the socket on a local reserved port and connect it to the
+ * remote server. This on Linux/i386 is done by the mount program,
+ * not by the kernel.
+ */
+/* XXXXXXXXXXXXXXXXXXXX */
+static int
+sunos_nfs_get_server_fd (int fd, struct sockaddr_in *addr)
+{
+ struct sockaddr_in local;
+ struct sockaddr_in server;
+ int try_port;
+ int ret;
+ struct socket *socket;
+ struct inode *inode;
+ struct file *file;
+
+ file = current->files->fd [fd];
+ inode = file->f_inode;
+ if (!inode || !inode->i_sock)
+ return 0;
+
+ socket = &inode->u.socket_i;
+ local.sin_family = AF_INET;
+ local.sin_addr.s_addr = INADDR_ANY;
+
+ /* IPPORT_RESERVED = 1024, can't find the definition in the kernel */
+ try_port = 1024;
+ do {
+ local.sin_port = htons (--try_port);
+ ret = socket->ops->bind(socket, (struct sockaddr*)&local,
+ sizeof(local));
+ } while (ret && try_port > (1024 / 2));
+
+ if (ret)
+ return 0;
+
+ server.sin_family = AF_INET;
+ server.sin_addr = addr->sin_addr;
+ server.sin_port = NFS_PORT;
+
+ /* Call sys_connect */
+ ret = socket->ops->connect (socket, (struct sockaddr *) &server,
+ sizeof (server), file->f_flags);
+ if (ret < 0)
+ return 0;
+ return 1;
+}
+
+/* XXXXXXXXXXXXXXXXXXXX */
+static int get_default (int value, int def_value)
+{
+ if (value)
+ return value;
+ else
+ return def_value;
+}
+
+/* XXXXXXXXXXXXXXXXXXXX */
+asmlinkage int sunos_nfs_mount(char *dir_name, int linux_flags, void *data)
+{
+ int ret = -ENODEV;
+ int server_fd;
+ char *the_name;
+ struct nfs_mount_data linux_nfs_mount;
+ struct sunos_nfs_mount_args *sunos_mount = data;
+ dev_t dev;
+
+ /* Ok, here comes the fun part: Linux's nfs mount needs a
+ * socket connection to the server, but SunOS mount does not
+ * require this, so we use the information on the destination
+ * address to create a socket and bind it to a reserved
+ * port on this system
+ */
+ server_fd = sys_socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (server_fd < 0)
+ return -ENXIO;
+
+ if (!sunos_nfs_get_server_fd (server_fd, sunos_mount->addr)){
+ sys_close (server_fd);
+ return -ENXIO;
+ }
+
+ /* Now, bind it to a locally reserved port */
+ linux_nfs_mount.version = NFS_MOUNT_VERSION;
+ linux_nfs_mount.flags = sunos_mount->flags;
+ linux_nfs_mount.addr = *sunos_mount->addr;
+ linux_nfs_mount.root = *sunos_mount->fh;
+ linux_nfs_mount.fd = server_fd;
+
+ linux_nfs_mount.rsize = get_default (sunos_mount->rsize, 8192);
+ linux_nfs_mount.wsize = get_default (sunos_mount->wsize, 8192);
+ linux_nfs_mount.timeo = get_default (sunos_mount->timeo, 10);
+ linux_nfs_mount.retrans = sunos_mount->retrans;
+
+ linux_nfs_mount.acregmin = sunos_mount->acregmin;
+ linux_nfs_mount.acregmax = sunos_mount->acregmax;
+ linux_nfs_mount.acdirmin = sunos_mount->acdirmin;
+ linux_nfs_mount.acdirmax = sunos_mount->acdirmax;
+
+ if (getname (sunos_mount->hostname, &the_name))
+ return -EFAULT;
+
+ strncpy (linux_nfs_mount.hostname, the_name, 254);
+ linux_nfs_mount.hostname [255] = 0;
+ putname (the_name);
+
+ dev = get_unnamed_dev ();
+
+ ret = do_mount (dev, "", dir_name, "nfs", linux_flags, &linux_nfs_mount);
+ if (ret)
+ put_unnamed_dev(dev);
+
+ return ret;
+}
+
+/* XXXXXXXXXXXXXXXXXXXX */
+asmlinkage int
+sunos_mount(char *type, char *dir, int flags, void *data)
+{
+ int linux_flags = MS_MGC_MSK; /* new semantics */
+ int ret = -EINVAL;
+ char *dev_fname = 0;
+
+ lock_kernel();
+ /* We don't handle the integer fs type */
+ if ((flags & SMNT_NEWTYPE) == 0)
+ goto out;
+
+ /* Do not allow for those flags we don't support */
+ if (flags & (SMNT_GRPID|SMNT_NOSUB|SMNT_MULTI|SMNT_SYS5))
+ goto out;
+
+ if(flags & SMNT_REMOUNT)
+ linux_flags |= MS_REMOUNT;
+ if(flags & SMNT_RDONLY)
+ linux_flags |= MS_RDONLY;
+ if(flags & SMNT_NOSUID)
+ linux_flags |= MS_NOSUID;
+ if(strcmp(type, "ext2") == 0) {
+ dev_fname = (char *) data;
+ } else if(strcmp(type, "iso9660") == 0) {
+ dev_fname = (char *) data;
+ } else if(strcmp(type, "minix") == 0) {
+ dev_fname = (char *) data;
+ } else if(strcmp(type, "nfs") == 0) {
+ ret = sunos_nfs_mount (dir, flags, data);
+ goto out;
+ } else if(strcmp(type, "ufs") == 0) {
+ printk("Warning: UFS filesystem mounts unsupported.\n");
+ ret = -ENODEV;
+ goto out;
+ } else if(strcmp(type, "proc")) {
+ ret = -ENODEV;
+ goto out;
+ }
+ ret = sys_mount(dev_fname, dir, type, linux_flags, NULL);
+out:
+ unlock_kernel();
+ return ret;
+}
+
+extern asmlinkage int sys_setsid(void);
+extern asmlinkage int sys_setpgid(pid_t, pid_t);
+
+asmlinkage int sunos_setpgrp(pid_t pid, pid_t pgid)
+{
+ int ret;
+
+ /* So stupid... */
+ lock_kernel();
+ if((!pid || pid == current->pid) &&
+ !pgid) {
+ sys_setsid();
+ ret = 0;
+ } else {
+ ret = sys_setpgid(pid, pgid);
+ }
+ unlock_kernel();
+ return ret;
+}
+
+/* So stupid... */
+extern asmlinkage int sys32_wait4(__kernel_pid_t32 pid,
+ u32 stat_addr, int options, u32 ru);
+
+asmlinkage int sunos_wait4(__kernel_pid_t32 pid, u32 stat_addr, int options, u32 ru)
+{
+ int ret;
+
+ lock_kernel();
+ ret = sys32_wait4((pid ? pid : ((__kernel_pid_t32)-1)),
+ stat_addr, options, ru);
+ unlock_kernel();
+ return ret;
+}
+
+extern int kill_pg(int, int, int);
+asmlinkage int sunos_killpg(int pgrp, int sig)
+{
+ int ret;
+
+ lock_kernel();
+ ret = kill_pg(pgrp, sig, 0);
+ unlock_kernel();
+ return ret;
+}
+
+asmlinkage int sunos_audit(void)
+{
+ lock_kernel();
+ printk ("sys_audit\n");
+ unlock_kernel();
+ return -1;
+}
+
+extern asmlinkage u32 sunos_gethostid(void)
+{
+ u32 ret;
+
+ lock_kernel();
+ ret = (((u32)idprom->id_machtype << 24) | ((u32)idprom->id_sernum));
+ unlock_kernel();
+ return ret;
+}
+
+/* sysconf options, for SunOS compatibility */
+#define _SC_ARG_MAX 1
+#define _SC_CHILD_MAX 2
+#define _SC_CLK_TCK 3
+#define _SC_NGROUPS_MAX 4
+#define _SC_OPEN_MAX 5
+#define _SC_JOB_CONTROL 6
+#define _SC_SAVED_IDS 7
+#define _SC_VERSION 8
+
+extern asmlinkage s32 sunos_sysconf (int name)
+{
+ s32 ret;
+
+ lock_kernel();
+ switch (name){
+ case _SC_ARG_MAX:
+ ret = ARG_MAX;
+ break;
+ case _SC_CHILD_MAX:
+ ret = CHILD_MAX;
+ break;
+ case _SC_CLK_TCK:
+ ret = HZ;
+ break;
+ case _SC_NGROUPS_MAX:
+ ret = NGROUPS_MAX;
+ break;
+ case _SC_OPEN_MAX:
+ ret = OPEN_MAX;
+ break;
+ case _SC_JOB_CONTROL:
+ ret = 1; /* yes, we do support job control */
+ break;
+ case _SC_SAVED_IDS:
+ ret = 1; /* yes, we do support saved uids */
+ break;
+ case _SC_VERSION:
+ /* mhm, POSIX_VERSION is in /usr/include/unistd.h
+ * should it go on /usr/include/linux?
+ */
+ ret = 199009;
+ break;
+ default:
+ ret = -1;
+ break;
+ };
+ unlock_kernel();
+ return ret;
+}
+
+extern asmlinkage int sys_semctl (int semid, int semnum, int cmd, union semun arg);
+extern asmlinkage int sys_semget (key_t key, int nsems, int semflg);
+extern asmlinkage int sys_semop (int semid, struct sembuf *tsops, unsigned nsops);
+
+asmlinkage int sunos_semsys(int op, u32 arg1, u32 arg2, u32 arg3, u32 ptr)
+{
+ union semun arg4;
+ int ret;
+
+ lock_kernel();
+ switch (op) {
+ case 0:
+ /* Most arguments match on a 1:1 basis but cmd doesn't */
+ switch(arg3) {
+ case 4:
+ arg3=GETPID; break;
+ case 5:
+ arg3=GETVAL; break;
+ case 6:
+ arg3=GETALL; break;
+ case 3:
+ arg3=GETNCNT; break;
+ case 7:
+ arg3=GETZCNT; break;
+ case 8:
+ arg3=SETVAL; break;
+ case 9:
+ arg3=SETALL; break;
+ }
+ /* sys_semctl(): */
+ arg4.__pad=(void *)A(ptr); /* value to modify semaphore to */
+ ret = sys_semctl((int)arg1, (int)arg2, (int)arg3, arg4);
+ break;
+ case 1:
+ /* sys_semget(): */
+ ret = sys_semget((key_t)arg1, (int)arg2, (int)arg3);
+ break;
+ case 2:
+ /* sys_semop(): */
+ ret = sys_semop((int)arg1, (struct sembuf *)A(arg2), (unsigned)arg3);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ };
+ unlock_kernel();
+ return ret;
+}
+
+struct msgbuf32 {
+ s32 mtype;
+ char mtext[1];
+};
+
+struct ipc_perm32
+{
+ key_t key;
+ __kernel_uid_t32 uid;
+ __kernel_gid_t32 gid;
+ __kernel_uid_t32 cuid;
+ __kernel_gid_t32 cgid;
+ __kernel_mode_t32 mode;
+ unsigned short seq;
+};
+
+struct msqid_ds32
+{
+ struct ipc_perm32 msg_perm;
+ u32 msg_first;
+ u32 msg_last;
+ __kernel_time_t32 msg_stime;
+ __kernel_time_t32 msg_rtime;
+ __kernel_time_t32 msg_ctime;
+ u32 wwait;
+ u32 rwait;
+ unsigned short msg_cbytes;
+ unsigned short msg_qnum;
+ unsigned short msg_qbytes;
+ __kernel_ipc_pid_t32 msg_lspid;
+ __kernel_ipc_pid_t32 msg_lrpid;
+};
+
+static inline int sunos_msqid_get(struct msqid_ds32 *user,
+ struct msqid_ds *kern)
+{
+ if(get_user(kern->msg_perm.key, &user->msg_perm.key) ||
+ __get_user(kern->msg_perm.uid, &user->msg_perm.uid) ||
+ __get_user(kern->msg_perm.gid, &user->msg_perm.gid) ||
+ __get_user(kern->msg_perm.cuid, &user->msg_perm.cuid) ||
+ __get_user(kern->msg_perm.cgid, &user->msg_perm.cgid) ||
+ __get_user(kern->msg_stime, &user->msg_stime) ||
+ __get_user(kern->msg_rtime, &user->msg_rtime) ||
+ __get_user(kern->msg_ctime, &user->msg_ctime) ||
+ __get_user(kern->msg_ctime, &user->msg_cbytes) ||
+ __get_user(kern->msg_ctime, &user->msg_qnum) ||
+ __get_user(kern->msg_ctime, &user->msg_qbytes) ||
+ __get_user(kern->msg_ctime, &user->msg_lspid) ||
+ __get_user(kern->msg_ctime, &user->msg_lrpid))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int sunos_msqid_put(struct msqid_ds32 *user,
+ struct msqid_ds *kern)
+{
+ if(put_user(kern->msg_perm.key, &user->msg_perm.key) ||
+ __put_user(kern->msg_perm.uid, &user->msg_perm.uid) ||
+ __put_user(kern->msg_perm.gid, &user->msg_perm.gid) ||
+ __put_user(kern->msg_perm.cuid, &user->msg_perm.cuid) ||
+ __put_user(kern->msg_perm.cgid, &user->msg_perm.cgid) ||
+ __put_user(kern->msg_stime, &user->msg_stime) ||
+ __put_user(kern->msg_rtime, &user->msg_rtime) ||
+ __put_user(kern->msg_ctime, &user->msg_ctime) ||
+ __put_user(kern->msg_ctime, &user->msg_cbytes) ||
+ __put_user(kern->msg_ctime, &user->msg_qnum) ||
+ __put_user(kern->msg_ctime, &user->msg_qbytes) ||
+ __put_user(kern->msg_ctime, &user->msg_lspid) ||
+ __put_user(kern->msg_ctime, &user->msg_lrpid))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int sunos_msgbuf_get(struct msgbuf32 *user, struct msgbuf *kern, int len)
+{
+ if(get_user(kern->mtype, &user->mtype) ||
+ __copy_from_user(kern->mtext, &user->mtext, len))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int sunos_msgbuf_put(struct msgbuf32 *user, struct msgbuf *kern, int len)
+{
+ if(put_user(kern->mtype, &user->mtype) ||
+ __copy_to_user(user->mtext, kern->mtext, len))
+ return -EFAULT;
+ return 0;
+}
+
+extern asmlinkage int sys_msgget (key_t key, int msgflg);
+extern asmlinkage int sys_msgrcv (int msqid, struct msgbuf *msgp,
+ size_t msgsz, long msgtyp, int msgflg);
+extern asmlinkage int sys_msgsnd (int msqid, struct msgbuf *msgp,
+ size_t msgsz, int msgflg);
+extern asmlinkage int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf);
+
+asmlinkage int sunos_msgsys(int op, u32 arg1, u32 arg2, u32 arg3, u32 arg4)
+{
+ struct sparc_stackf32 *sp;
+ struct msqid_ds kds;
+ struct msgbuf *kmbuf;
+ unsigned long old_fs = get_fs();
+ u32 arg5;
+ int rval;
+
+ lock_kernel();
+ switch(op) {
+ case 0:
+ rval = sys_msgget((key_t)arg1, (int)arg2);
+ break;
+ case 1:
+ if(!sunos_msqid_get((struct msqid_ds32 *)A(arg3), &kds)) {
+ set_fs(KERNEL_DS);
+ rval = sys_msgctl((int)arg1, (int)arg2,
+ (struct msqid_ds *)A(arg3));
+ set_fs(old_fs);
+ if(!rval)
+ rval = sunos_msqid_put((struct msqid_ds32 *)A(arg3),
+ &kds);
+ } else
+ rval = -EFAULT;
+ break;
+ case 2:
+ rval = -EFAULT;
+ kmbuf = (struct msgbuf *)kmalloc(sizeof(struct msgbuf) + arg3,
+ GFP_KERNEL);
+ if(!kmbuf)
+ break;
+ sp = (struct sparc_stackf32 *)
+ (current->tss.kregs->u_regs[UREG_FP] & 0xffffffffUL);
+ if(get_user(arg5, &sp->xxargs[0])) {
+ rval = -EFAULT;
+ break;
+ }
+ set_fs(KERNEL_DS);
+ rval = sys_msgrcv((int)arg1, kmbuf, (size_t)arg3,
+ (long)arg4, (int)arg5);
+ set_fs(old_fs);
+ if(!rval)
+ rval = sunos_msgbuf_put((struct msgbuf32 *)A(arg2),
+ kmbuf, arg3);
+ kfree(kmbuf);
+ break;
+ case 3:
+ rval = -EFAULT;
+ kmbuf = (struct msgbuf *)kmalloc(sizeof(struct msgbuf) + arg3,
+ GFP_KERNEL);
+ if(!kmbuf || sunos_msgbuf_get((struct msgbuf32 *)A(arg2),
+ kmbuf, arg3))
+ break;
+ set_fs(KERNEL_DS);
+ rval = sys_msgsnd((int)arg1, kmbuf, (size_t)arg3, (int)arg4);
+ set_fs(old_fs);
+ kfree(kmbuf);
+ break;
+ default:
+ rval = -EINVAL;
+ break;
+ }
+ unlock_kernel();
+ return rval;
+}
+
+struct shmid_ds32 {
+ struct ipc_perm32 shm_perm;
+ int shm_segsz;
+ __kernel_time_t32 shm_atime;
+ __kernel_time_t32 shm_dtime;
+ __kernel_time_t32 shm_ctime;
+ __kernel_ipc_pid_t32 shm_cpid;
+ __kernel_ipc_pid_t32 shm_lpid;
+ unsigned short shm_nattch;
+ unsigned short shm_npages;
+ u32 shm_pages;
+ u32 attaches;
+};
+
+static inline int sunos_shmid_get(struct shmid_ds32 *user,
+ struct shmid_ds *kern)
+{
+ if(get_user(kern->shm_perm.key, &user->shm_perm.key) ||
+ __get_user(kern->shm_perm.uid, &user->shm_perm.uid) ||
+ __get_user(kern->shm_perm.gid, &user->shm_perm.gid) ||
+ __get_user(kern->shm_perm.cuid, &user->shm_perm.cuid) ||
+ __get_user(kern->shm_perm.cgid, &user->shm_perm.cgid) ||
+ __get_user(kern->shm_segsz, &user->shm_segsz) ||
+ __get_user(kern->shm_atime, &user->shm_atime) ||
+ __get_user(kern->shm_dtime, &user->shm_dtime) ||
+ __get_user(kern->shm_ctime, &user->shm_ctime) ||
+ __get_user(kern->shm_cpid, &user->shm_cpid) ||
+ __get_user(kern->shm_lpid, &user->shm_lpid) ||
+ __get_user(kern->shm_nattch, &user->shm_nattch) ||
+ __get_user(kern->shm_npages, &user->shm_npages))
+ return -EFAULT;
+ return 0;
+}
+
+static inline int sunos_shmid_put(struct shmid_ds32 *user,
+ struct shmid_ds *kern)
+{
+ if(put_user(kern->shm_perm.key, &user->shm_perm.key) ||
+ __put_user(kern->shm_perm.uid, &user->shm_perm.uid) ||
+ __put_user(kern->shm_perm.gid, &user->shm_perm.gid) ||
+ __put_user(kern->shm_perm.cuid, &user->shm_perm.cuid) ||
+ __put_user(kern->shm_perm.cgid, &user->shm_perm.cgid) ||
+ __put_user(kern->shm_segsz, &user->shm_segsz) ||
+ __put_user(kern->shm_atime, &user->shm_atime) ||
+ __put_user(kern->shm_dtime, &user->shm_dtime) ||
+ __put_user(kern->shm_ctime, &user->shm_ctime) ||
+ __put_user(kern->shm_cpid, &user->shm_cpid) ||
+ __put_user(kern->shm_lpid, &user->shm_lpid) ||
+ __put_user(kern->shm_nattch, &user->shm_nattch) ||
+ __put_user(kern->shm_npages, &user->shm_npages))
+ return -EFAULT;
+ return 0;
+}
+
+extern asmlinkage int sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr);
+extern asmlinkage int sys_shmctl (int shmid, int cmd, struct shmid_ds *buf);
+extern asmlinkage int sys_shmdt (char *shmaddr);
+extern asmlinkage int sys_shmget (key_t key, int size, int shmflg);
+
+asmlinkage int sunos_shmsys(int op, u32 arg1, u32 arg2, u32 arg3)
+{
+ struct shmid_ds ksds;
+ unsigned long raddr, old_fs = get_fs();
+ int rval;
+
+ lock_kernel();
+ switch(op) {
+ case 0:
+ /* sys_shmat(): attach a shared memory area */
+ rval = sys_shmat((int)arg1,(char *)A(arg2),(int)arg3,&raddr);
+ if(!rval)
+ rval = (int) raddr;
+ break;
+ case 1:
+ /* sys_shmctl(): modify shared memory area attr. */
+ if(!sunos_shmid_get((struct shmid_ds32 *)A(arg3), &ksds)) {
+ set_fs(KERNEL_DS);
+ rval = sys_shmctl((int)arg1,(int)arg2, &ksds);
+ set_fs(old_fs);
+ if(!rval)
+ rval = sunos_shmid_put((struct shmid_ds32 *)A(arg3),
+ &ksds);
+ } else
+ rval = -EFAULT;
+ break;
+ case 2:
+ /* sys_shmdt(): detach a shared memory area */
+ rval = sys_shmdt((char *)A(arg1));
+ break;
+ case 3:
+ /* sys_shmget(): get a shared memory area */
+ rval = sys_shmget((key_t)arg1,(int)arg2,(int)arg3);
+ break;
+ default:
+ rval = -EINVAL;
+ break;
+ };
+ unlock_kernel();
+ return rval;
+}
+
+asmlinkage int sunos_open(u32 filename, int flags, int mode)
+{
+ int ret;
+
+ lock_kernel();
+ current->personality |= PER_BSD;
+ ret = sys_open ((char *)A(filename), flags, mode);
+ unlock_kernel();
+ return ret;
+}
+
+#define SUNOS_EWOULDBLOCK 35
+
+/* see the sunos man page read(2v) for an explanation
+ of this garbage. We use O_NDELAY to mark
+ file descriptors that have been set non-blocking
+ using 4.2BSD style calls. (tridge) */
+
+static inline int check_nonblock(int ret, int fd)
+{
+ if (ret == -EAGAIN && (current->files->fd[fd]->f_flags & O_NDELAY))
+ return -SUNOS_EWOULDBLOCK;
+ return ret;
+}
+
+extern asmlinkage int sys32_read(unsigned int fd, u32 buf, int count);
+extern asmlinkage int sys32_write(unsigned int fd, u32 buf,int count);
+extern asmlinkage int sys32_recv(int fd, u32 ubuf, int size, unsigned flags);
+extern asmlinkage int sys32_send(int fd, u32 buff, int len, unsigned flags);
+extern asmlinkage int sys32_accept(int fd, u32 sa, u32 addrlen);
+extern asmlinkage int sys32_readv(u32 fd, u32 vector, s32 count);
+extern asmlinkage int sys32_writev(u32 fd, u32 vector, s32 count);
+
+asmlinkage int sunos_read(unsigned int fd, u32 buf, int count)
+{
+ int ret;
+
+ lock_kernel();
+ ret = check_nonblock(sys32_read(fd, buf, count), fd);
+ unlock_kernel();
+ return ret;
+}
+
+asmlinkage int sunos_readv(u32 fd, u32 vector, s32 count)
+{
+ int ret;
+
+ lock_kernel();
+ ret = check_nonblock(sys32_readv(fd, vector, count), fd);
+ lock_kernel();
+ return ret;
+}
+
+asmlinkage int sunos_write(unsigned int fd, u32 buf, int count)
+{
+ int ret;
+
+ lock_kernel();
+ ret = check_nonblock(sys32_write(fd, buf, count), fd);
+ unlock_kernel();
+ return ret;
+}
+
+asmlinkage int sunos_writev(u32 fd, u32 vector, s32 count)
+{
+ int ret;
+
+ lock_kernel();
+ ret = check_nonblock(sys32_writev(fd, vector, count), fd);
+ unlock_kernel();
+ return ret;
+}
+
+asmlinkage int sunos_recv(int fd, u32 ubuf, int size, unsigned flags)
+{
+ int ret;
+
+ lock_kernel();
+ ret = check_nonblock(sys32_recv(fd, ubuf, size, flags), fd);
+ unlock_kernel();
+ return ret;
+}
+
+asmlinkage int sunos_send(int fd, u32 buff, int len, unsigned flags)
+{
+ int ret;
+
+ lock_kernel();
+ ret = check_nonblock(sys32_send(fd, buff, len, flags), fd);
+ unlock_kernel();
+ return ret;
+}
+
+asmlinkage int sunos_accept(int fd, u32 sa, u32 addrlen)
+{
+ int ret;
+
+ lock_kernel();
+ ret = check_nonblock(sys32_accept(fd, sa, addrlen), fd);
+ unlock_kernel();
+ return ret;
+}
+
+#define SUNOS_SV_INTERRUPT 2
+
+extern void check_pending(int signum);
+
+asmlinkage int sunos_sigaction(int signum, u32 action, u32 oldaction)
+{
+ struct sigaction32 new_sa, old_sa;
+ struct sigaction *p;
+ const int sigaction_size = sizeof (struct sigaction32) - sizeof (u32);
+
+ current->personality |= PER_BSD;
+ if(signum < 1 || signum > 32)
+ return -EINVAL;
+
+ p = signum - 1 + current->sig->action;
+
+ if(action) {
+ if (signum==SIGKILL || signum==SIGSTOP)
+ return -EINVAL;
+ memset(&new_sa, 0, sizeof(struct sigaction32));
+ if(copy_from_user(&new_sa, (struct sigaction32 *)A(action),
+ sigaction_size))
+ return -EFAULT;
+ if (((__sighandler_t)A(new_sa.sa_handler) != SIG_DFL) &&
+ (__sighandler_t)A(new_sa.sa_handler) != SIG_IGN) {
+ if(verify_area(VERIFY_READ,
+ (__sighandler_t)A(new_sa.sa_handler), 1))
+ return -EFAULT;
+ }
+ new_sa.sa_flags ^= SUNOS_SV_INTERRUPT;
+ }
+
+ if (oldaction) {
+ /* In the clone() case we could copy half consistant
+ * state to the user, however this could sleep and
+ * deadlock us if we held the signal lock on SMP. So for
+ * now I take the easy way out and do no locking.
+ * But then again we don't support SunOS lwp's anyways ;-)
+ */
+ old_sa.sa_handler = (unsigned)(u64)(p->sa_handler);
+ old_sa.sa_mask = (sigset_t32)(p->sa_mask);
+ old_sa.sa_flags = (unsigned)(p->sa_flags);
+
+ if (old_sa.sa_flags & SA_RESTART)
+ old_sa.sa_flags &= ~SA_RESTART;
+ else
+ old_sa.sa_flags |= SUNOS_SV_INTERRUPT;
+ if (copy_to_user((struct sigaction32 *)A(oldaction),
+ &old_sa, sigaction_size))
+ return -EFAULT;
+ }
+
+ if (action) {
+ spin_lock_irq(¤t->sig->siglock);
+ p->sa_handler = (__sighandler_t)A(new_sa.sa_handler);
+ p->sa_mask = (sigset_t)(new_sa.sa_mask);
+ p->sa_flags = new_sa.sa_flags;
+ p->sa_restorer = (void (*)(void))0;
+ check_pending(signum);
+ spin_unlock_irq(¤t->sig->siglock);
+ }
+ return 0;
+}
+
+
+extern asmlinkage int sys32_setsockopt(int fd, int level, int optname,
+ u32 optval, int optlen);
+extern asmlinkage int sys32_getsockopt(int fd, int level, int optname,
+ u32 optval, u32 optlen);
+
+asmlinkage int sunos_setsockopt(int fd, int level, int optname, u32 optval,
+ int optlen)
+{
+ int tr_opt = optname;
+ int ret;
+
+ lock_kernel();
+ if (level == SOL_IP) {
+ /* Multicast socketopts (ttl, membership) */
+ if (tr_opt >=2 && tr_opt <= 6)
+ tr_opt += 30;
+ }
+ ret = sys32_setsockopt(fd, level, tr_opt, optval, optlen);
+ unlock_kernel();
+ return ret;
+}
+
+asmlinkage int sunos_getsockopt(int fd, int level, int optname,
+ u32 optval, u32 optlen)
+{
+ int tr_opt = optname;
+ int ret;
+
+ lock_kernel();
+ if (level == SOL_IP) {
+ /* Multicast socketopts (ttl, membership) */
+ if (tr_opt >=2 && tr_opt <= 6)
+ tr_opt += 30;
+ }
+ ret = sys32_getsockopt(fd, level, tr_opt, optval, optlen);
+ unlock_kernel();
+ return ret;
+}
-/* $Id: systbls.S,v 1.16 1997/06/16 05:37:41 davem Exp $
+/* $Id: systbls.S,v 1.21 1997/07/05 07:09:17 davem Exp $
* systbls.S: System call entry point tables for OS compatibility.
* The native Linux system call table lives here also.
*
.xword sys32_quotactl, sys_nis_syscall, sys32_mount, sys32_ustat, sys_nis_syscall
/*170*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_getdents
.xword sys_setsid, sys_fchdir, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
-/*180*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_sigpending, sys32_no_modules
+/*180*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_sigpending, sys32_query_module
.xword sys_setpgid, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys32_newuname
-/*190*/ .xword sys32_no_modules, sys32_personality, sys_prof, sys_break, sys_lock
+/*190*/ .xword sys32_init_module, sys32_personality, sys_prof, sys_break, sys_lock
.xword sys_mpx, sys_ulimit, sys_getppid, sparc32_sigaction, sys_sgetmask
/*200*/ .xword sys_ssetmask, sys_sigsuspend, sys32_newlstat, sys32_uselib, old32_readdir
.xword sys_nis_syscall, sys32_socketcall, sys32_syslog, sys32_olduname, sys_nis_syscall
/*210*/ .xword sys_idle, sys_nis_syscall, sys32_waitpid, sys32_swapoff, sys32_sysinfo
.xword sys32_ipc, sys_sigreturn, sys_clone, sys_nis_syscall, sys32_adjtimex
-/*220*/ .xword sys32_sigprocmask, sys32_no_modules, sys32_no_modules, sys32_no_modules, sys_getpgid
+/*220*/ .xword sys32_sigprocmask, sys32_create_module, sys32_delete_module, sys32_get_kernel_syms, sys_getpgid
.xword sys32_bdflush, sys32_sysfs, sys_nis_syscall, sys_setfsuid, sys_setfsgid
/*230*/ .xword sys32_llseek, sys32_time, sys_nis_syscall, sys_stime, sys_nis_syscall
.xword sys_nis_syscall, sys32_llseek, sys32_mlock, sys32_munlock, sys_mlockall
.xword sys_quotactl, sys_nis_syscall, sys_mount, sys_ustat, sys_nis_syscall
/*170*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_getdents
.xword sys_setsid, sys_fchdir, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall
-/*180*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_sigpending, sys_nis_syscall
+/*180*/ .xword sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_sigpending, sys_query_module
.xword sys_setpgid, sys_nis_syscall, sys_nis_syscall, sys_nis_syscall, sys_newuname
/*190*/ .xword sys_init_module, sys_personality, sys_prof, sys_break, sys_lock
.xword sys_mpx, sys_ulimit, sys_getppid, sparc_sigaction, sys_sgetmask
/* Now the 32-bit SunOS syscall table. */
- .align 4
+ .align 8
.globl sunos_sys_table
sunos_sys_table:
/*0*/ .xword sunos_indir, sys_exit, sys_fork
.xword sunos_read, sunos_write, sunos_open
- .xword sys_close, sunos_wait4, sys_creat
- .xword sys_link, sys_unlink, sunos_execv
- .xword sys_chdir, sunos_nosys, sys_mknod
- .xword sys_chmod, sys_chown, sunos_brk
- .xword sunos_nosys, sys_lseek, sunos_getpid
+ .xword sys_close, sunos_wait4, sys32_creat
+ .xword sys32_link, sys32_unlink, sunos_execv
+ .xword sys32_chdir, sunos_nosys, sys32_mknod
+ .xword sys32_chmod, sys32_chown, sunos_brk
+ .xword sunos_nosys, sys32_lseek, sunos_getpid
.xword sunos_nosys, sunos_nosys, sunos_nosys
.xword sunos_getuid, sunos_nosys, sys_ptrace
.xword sunos_nosys, sunos_nosys, sunos_nosys
.xword sunos_nosys, sunos_nosys, sunos_nosys
- .xword sys_access, sunos_nosys, sunos_nosys
- .xword sys_sync, sys_kill, sys_newstat
- .xword sunos_nosys, sys_newlstat, sys_dup
+ .xword sys32_access, sunos_nosys, sunos_nosys
+ .xword sys_sync, sys_kill, sys32_newstat
+ .xword sunos_nosys, sys32_newlstat, sys_dup
.xword sys_pipe, sunos_nosys, sys_profil
.xword sunos_nosys, sunos_nosys, sunos_getgid
.xword sunos_nosys, sunos_nosys
-/*50*/ .xword sunos_nosys, sys_acct, sunos_nosys
- .xword sunos_mctl, sunos_ioctl, sys_reboot
- .xword sunos_nosys, sys_symlink, sys_readlink
- .xword sys32_execve, sys_umask, sys_chroot
- .xword sys_newfstat, sunos_nosys, sys_getpagesize
- .xword sys_msync, sys_vfork, sunos_nosys
+/*50*/ .xword sunos_nosys, sys32_acct, sunos_nosys
+ .xword sunos_mctl, sunos_ioctl, sys32_reboot
+ .xword sunos_nosys, sys32_symlink, sys32_readlink
+ .xword sys32_execve, sys_umask, sys32_chroot
+ .xword sys32_newfstat, sunos_nosys, sys_getpagesize
+ .xword sys32_msync, sys_vfork, sunos_nosys
.xword sunos_nosys, sunos_sbrk, sunos_sstk
- .xword sunos_mmap, sunos_vadvise, sys_munmap
- .xword sys_mprotect, sunos_madvise, sys_vhangup
- .xword sunos_nosys, sunos_mincore, sys_getgroups
- .xword sys_setgroups, sys_getpgrp, sunos_setpgrp
- .xword sys_setitimer, sunos_nosys, sys_swapon
- .xword sys_getitimer, sys_gethostname, sys_sethostname
+ .xword sunos_mmap, sunos_vadvise, sys32_munmap
+ .xword sys32_mprotect, sunos_madvise, sys_vhangup
+ .xword sunos_nosys, sunos_mincore, sys32_getgroups
+ .xword sys32_setgroups, sys_getpgrp, sunos_setpgrp
+ .xword sys32_setitimer, sunos_nosys, sys32_swapon
+ .xword sys32_getitimer, sys32_gethostname, sys32_sethostname
.xword sunos_getdtablesize, sys_dup2, sunos_nop
- .xword sys_fcntl, sunos_select, sunos_nop
+ .xword sys32_fcntl, sunos_select, sunos_nop
.xword sys_fsync, sys_setpriority, sys_socket
- .xword sys_connect, sunos_accept
+ .xword sys32_connect, sunos_accept
/*100*/ .xword sys_getpriority, sunos_send, sunos_recv
- .xword sunos_nosys, sys_bind, sunos_setsockopt
+ .xword sunos_nosys, sys32_bind, sunos_setsockopt
.xword sys_listen, sunos_nosys, sunos_sigaction
.xword sunos_sigblock, sunos_sigsetmask, sys_sigpause
- .xword sys_sigstack, sys_recvmsg, sys_sendmsg
- .xword sunos_nosys, sys_gettimeofday, sys_getrusage
+ .xword sys32_sigstack, sys32_recvmsg, sys32_sendmsg
+ .xword sunos_nosys, sys_gettimeofday, sys32_getrusage
.xword sunos_getsockopt, sunos_nosys, sunos_readv
.xword sunos_writev, sys_settimeofday, sys_fchown
- .xword sys_fchmod, sys_recvfrom, sys_setreuid
- .xword sys_setregid, sys_rename, sys_truncate
- .xword sys_ftruncate, sys_flock, sunos_nosys
- .xword sys_sendto, sys_shutdown, sys_socketpair
- .xword sys_mkdir, sys_rmdir, sys_utimes
- .xword sys_sigreturn, sunos_nosys, sys_getpeername
- .xword sunos_gethostid, sunos_nosys, sys_getrlimit
- .xword sys_setrlimit, sunos_killpg, sunos_nosys
+ .xword sys_fchmod, sys32_recvfrom, sys32_setreuid
+ .xword sys_setregid, sys32_rename, sys32_truncate
+ .xword sys32_ftruncate, sys_flock, sunos_nosys
+ .xword sys32_sendto, sys_shutdown, sys_socketpair
+ .xword sys32_mkdir, sys32_rmdir, sys32_utimes
+ .xword sys_sigreturn, sunos_nosys, sys32_getpeername
+ .xword sunos_gethostid, sunos_nosys, sys32_getrlimit
+ .xword sys32_setrlimit, sunos_killpg, sunos_nosys
.xword sunos_nosys, sunos_nosys
-/*150*/ .xword sys_getsockname, sunos_nosys, sunos_nosys
- .xword sunos_poll, sunos_nosys, sunos_nosys
- .xword sunos_getdirentries, sys_statfs, sys_fstatfs
- .xword sys_umount, sunos_nosys, sunos_nosys
- .xword sunos_getdomainname, sys_setdomainname
- .xword sunos_nosys, sys_quotactl, sunos_nosys
- .xword sunos_mount, sys_ustat, sunos_semsys
+/*150*/ .xword sys32_getsockname, sunos_nosys, sunos_nosys
+ .xword sys32_poll, sunos_nosys, sunos_nosys
+ .xword sunos_getdirentries, sys32_statfs, sys32_fstatfs
+ .xword sys32_umount, sunos_nosys, sunos_nosys
+ .xword sunos_getdomainname, sys32_setdomainname
+ .xword sunos_nosys, sys32_quotactl, sunos_nosys
+ .xword sunos_mount, sys32_ustat, sunos_semsys
.xword sunos_nosys, sunos_shmsys, sunos_audit
.xword sunos_nosys, sunos_getdents, sys_setsid
.xword sys_fchdir, sunos_nosys, sunos_nosys
.xword sunos_nosys, sunos_nosys, sunos_nosys
- .xword sunos_nosys, sys_sigpending, sunos_nosys
+ .xword sunos_nosys, sys32_sigpending, sunos_nosys
.xword sys_setpgid, sunos_pathconf, sunos_fpathconf
.xword sunos_sysconf, sunos_uname, sunos_nosys
.xword sunos_nosys, sunos_nosys, sunos_nosys
-/* $Id: time.c,v 1.2 1997/04/10 03:02:35 davem Exp $
+/* $Id: time.c,v 1.3 1997/06/17 13:25:29 jj Exp $
* time.c: UltraSparc timer and TOD clock support.
*
* Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
return (data1 == data2); /* Was the write blocked? */
}
-/* XXX HACK HACK HACK, delete me soon */
-static struct linux_prom_ranges XXX_sbus_ranges[PROMREG_MAX];
-static int XXX_sbus_nranges;
/* Probe for the real time clock chip. */
__initfunc(static void clock_probe(void))
char model[128];
int node, sbusnd, err;
+ /* XXX HACK HACK HACK, delete me soon */
+ struct linux_prom_ranges XXX_sbus_ranges[PROMREG_MAX];
+ int XXX_sbus_nranges;
+
node = prom_getchild(prom_root_node);
sbusnd = prom_searchsiblings(node, "sbus");
node = prom_getchild(sbusnd);
-/* $Id: traps.c,v 1.19 1997/06/05 06:22:49 davem Exp $
- * arch/sparc/kernel/traps.c
+/* $Id: traps.c,v 1.29 1997/07/05 09:52:38 davem Exp $
+ * arch/sparc64/kernel/traps.c
*
* Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu)
* Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
/*
- * I hate traps on the sparc, grrr...
+ * I like traps on v9, :))))
*/
+#include <linux/config.h>
#include <linux/sched.h> /* for jiffies */
#include <linux/kernel.h>
#include <linux/signal.h>
int i;
#endif
+ if(strcmp(current->comm, "bash.sunos"))
+ return;
printk("SYS[%s:%d]: PC(%016lx) <%3d> ",
current->comm, current->pid, regs->tpc, (int)g1);
#ifdef VERBOSE_SYSCALL_TRACING
unsigned long syscall_trace_exit(unsigned long retval, struct pt_regs *regs)
{
- printk("ret[%016lx]\n", retval);
+ if(!strcmp(current->comm, "bash.sunos"))
+ printk("ret[%016lx]\n", retval);
return retval;
}
#endif /* SYSCALL_TRACING */
-#if 0
-void user_rtrap_report(struct pt_regs *regs)
-{
- static int hits = 0;
-
- /* Bwahhhhrggg... */
- if(regs->tpc == 0x1f294UL && ++hits == 2) {
- register unsigned long ctx asm("o4");
- register unsigned long paddr asm("o5");
- unsigned long cwp, wstate;
-
- printk("RT[%016lx:%016lx] ", regs->tpc, regs->u_regs[UREG_I6]);
- __asm__ __volatile__("rdpr %%cwp, %0" : "=r" (cwp));
- __asm__ __volatile__("rdpr %%wstate, %0" : "=r" (wstate));
- printk("CWP[%d] WSTATE[%016lx]\n"
- "TSS( ksp[%016lx] kpc[%016lx] wstate[%016lx] w_saved[%d] flgs[%x]"
- " cur_ds[%d] )\n", cwp, wstate,
- current->tss.ksp, current->tss.kpc, current->tss.wstate,
- (int) current->tss.w_saved, current->tss.flags,
- current->tss.current_ds);
- __asm__ __volatile__("
- rdpr %%pstate, %%o3
- wrpr %%o3, %2, %%pstate
- mov %%g7, %%o5
- mov 0x10, %%o4
- ldxa [%%o4] %3, %%o4
- wrpr %%o3, 0x0, %%pstate
- " : "=r" (ctx), "=r" (paddr)
- : "i" (PSTATE_MG|PSTATE_IE), "i" (ASI_DMMU));
-
- printk("MMU[ppgd(%016lx)sctx(%d)] ", paddr, ctx);
- printk("mm->context(%016lx) mm->pgd(%p)\n",
- current->mm->context, current->mm->pgd);
- printk("TASK: signal[%016lx] blocked[%016lx]\n",
- current->signal, current->blocked);
- show_regs(regs);
- while(1)
- barrier();
- }
-}
-#endif
-
void bad_trap (struct pt_regs *regs, long lvl)
{
lock_kernel ();
{
char buffer[24];
- lock_kernel ();
+ lock_kernel();
sprintf (buffer, "Bad trap %lx at tl>0", lvl);
die_if_kernel (buffer, regs);
+ unlock_kernel();
}
void data_access_exception (struct pt_regs *regs)
{
- lock_kernel ();
- printk ("Unhandled data access exception ");
- printk("sfsr %016lx sfar %016lx\n", spitfire_get_dsfsr(), spitfire_get_sfar());
- die_if_kernel("Data access exception", regs);
+ send_sig(SIGSEGV, current, 1);
}
void do_dae(struct pt_regs *regs)
{
- printk("DAE: at %016lx\n", regs->tpc);
- while(1)
- barrier();
+ send_sig(SIGSEGV, current, 1);
}
void instruction_access_exception (struct pt_regs *regs)
{
- lock_kernel ();
- printk ("Unhandled instruction access exception ");
- printk("sfsr %016lx\n", spitfire_get_isfsr());
- die_if_kernel("Instruction access exception", regs);
+ send_sig(SIGSEGV, current, 1);
}
void do_iae(struct pt_regs *regs)
{
- printk("IAE at %016lx\n", regs->tpc);
- while(1)
- barrier();
-}
-
-static unsigned long init_fsr = 0x0UL;
-static unsigned int init_fregs[64] __attribute__ ((aligned (64))) =
- { ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U,
- ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U,
- ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U,
- ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U,
- ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U,
- ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U,
- ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U,
- ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U, ~0U };
-
-void do_fpdis(struct pt_regs *regs)
-{
- lock_kernel();
-
- regs->tstate |= TSTATE_PEF;
- fprs_write(FPRS_FEF);
-
- /* This is allowed now because the V9 ABI varargs passes floating
- * point args in floating point registers, so vsprintf() and sprintf()
- * cause problems. Luckily we never actually pass floating point values
- * to those routines in the kernel and the code generated just does
- * stores of them to the stack. Therefore, for the moment this fix
- * is sufficient. -DaveM
- */
- if(regs->tstate & TSTATE_PRIV)
- goto out;
-
-#ifndef __SMP__
- if(last_task_used_math == current)
- goto out;
- if(last_task_used_math) {
- struct task_struct *fptask = last_task_used_math;
-
- if(fptask->tss.flags & SPARC_FLAG_32BIT)
- fpsave32((unsigned long *)&fptask->tss.float_regs[0],
- &fptask->tss.fsr);
- else
- fpsave((unsigned long *)&fptask->tss.float_regs[0],
- &fptask->tss.fsr);
- }
- last_task_used_math = current;
- if(current->used_math) {
- if(current->tss.flags & SPARC_FLAG_32BIT)
- fpload32(¤t->tss.float_regs[0],
- ¤t->tss.fsr);
- else
- fpload(¤t->tss.float_regs[0],
- ¤t->tss.fsr);
- } else {
- /* Set inital sane state. */
- fpload(&init_fregs[0], &init_fsr);
- current->used_math = 1;
- }
-#else
- if(!current->used_math) {
- fpload(&init_fregs[0], &init_fsr);
- current->used_math = 1;
- } else {
- if(current->tss.flags & SPARC_FLAG_32BIT)
- fpload32(¤t->tss.float_regs[0],
- ¤t->tss.fsr);
- else
- fpload(¤t->tss.float_regs[0],
- ¤t->tss.fsr);
- }
- current->flags |= PF_USEDFPU;
-#endif
-#ifndef __SMP__
-out:
-#endif
- unlock_kernel();
+ send_sig(SIGSEGV, current, 1);
}
-static unsigned long fake_regs[32] __attribute__ ((aligned (8)));
-static unsigned long fake_fsr;
-
void do_fpe_common(struct pt_regs *regs)
{
- static int calls = 0;
-#ifndef __SMP__
- struct task_struct *fpt = last_task_used_math;
-#else
- struct task_struct *fpt = current;
-#endif
-
- lock_kernel();
- fprs_write(FPRS_FEF);
-
-#ifndef __SMP__
- if(!fpt) {
-#else
- if(!(fpt->flags & PF_USEDFPU)) {
-#endif
- fpsave(&fake_regs[0], &fake_fsr);
- regs->tstate &= ~(TSTATE_PEF);
- goto out;
- }
- if(fpt->tss.flags & SPARC_FLAG_32BIT)
- fpsave32((unsigned long *)&fpt->tss.float_regs[0], &fpt->tss.fsr);
- else
- fpsave((unsigned long *)&fpt->tss.float_regs[0], &fpt->tss.fsr);
- fpt->tss.sig_address = regs->tpc;
- fpt->tss.sig_desc = SUBSIG_FPERROR;
-#ifdef __SMP__
- fpt->flags &= ~PF_USEDFPU;
-#endif
if(regs->tstate & TSTATE_PRIV) {
- printk("WARNING: FPU exception from kernel mode. at pc=%016lx\n",
- regs->tpc);
regs->tpc = regs->tnpc;
regs->tnpc += 4;
- calls++;
- if(calls > 2)
- die_if_kernel("Too many Penguin-FPU traps from kernel mode",
- regs);
- goto out;
+ } else {
+ lock_kernel();
+ current->tss.sig_address = regs->tpc;
+ current->tss.sig_desc = SUBSIG_FPERROR;
+ send_sig(SIGFPE, current, 1);
+ unlock_kernel();
}
- send_sig(SIGFPE, fpt, 1);
-#ifndef __SMP__
- last_task_used_math = NULL;
-#endif
- regs->tstate &= ~TSTATE_PEF;
- if(calls > 0)
- calls = 0;
-out:
- unlock_kernel();
}
void do_fpieee(struct pt_regs *regs)
void do_tof(struct pt_regs *regs)
{
- printk("TOF: at %016lx\n", regs->tpc);
- while(1)
- barrier();
+ if(regs->tstate & TSTATE_PRIV)
+ die_if_kernel("Penguin overflow trap from kernel mode", regs);
+ current->tss.sig_address = regs->tpc;
+ current->tss.sig_desc = SUBSIG_TAG; /* as good as any */
+ send_sig(SIGEMT, current, 1);
}
void do_div0(struct pt_regs *regs)
{
- printk("DIV0: at %016lx\n", regs->tpc);
- while(1)
- barrier();
+ send_sig(SIGILL, current, 1);
}
void instruction_dump (unsigned int *pc)
/* Amuse the user. */
printk(
" \\|/ ____ \\|/\n"
-" \"@'/ .` \\`@\"\n"
+" \"@'/ .. \\`@\"\n"
" /_| \\__/ |_\\\n"
" \\__U_/\n");
struct reg_window *rw = (struct reg_window *)
(regs->u_regs[UREG_FP] + STACK_BIAS);
- if(rw) {
+ /* Stop the back trace when we hit userland or we
+ * find some badly aligned kernel stack.
+ */
+ while(rw &&
+ (((unsigned long) rw) >= PAGE_OFFSET) &&
+ !(((unsigned long) rw) & 0x7)) {
printk("Caller[%016lx]\n", rw->ins[7]);
rw = (struct reg_window *)
(rw->ins[6] + STACK_BIAS);
- if(rw) {
- printk("Caller[%016lx]\n", rw->ins[7]);
- rw = (struct reg_window *)
- (rw->ins[6] + STACK_BIAS);
- if(rw)
- printk("Caller[%016lx]\n", rw->ins[7]);
- }
}
}
printk("Instruction DUMP:");
lock_kernel();
if(tstate & TSTATE_PRIV)
die_if_kernel("Kernel illegal instruction", regs);
-#if 1
- {
- unsigned int insn;
-
- printk("Ill instr. at pc=%016lx ", pc);
- get_user(insn, ((unsigned int *)pc));
- printk("insn=[%08x]\n", insn);
- show_regs(regs);
- }
-#endif
current->tss.sig_address = pc;
current->tss.sig_desc = SUBSIG_ILLINST;
send_sig(SIGILL, current, 1);
void mem_address_unaligned(struct pt_regs *regs)
{
- printk("AIEEE: do_mna at %016lx\n", regs->tpc);
- show_regs(regs);
if(regs->tstate & TSTATE_PRIV) {
- printk("MNA from kernel, spinning\n");
- sti();
- while(1)
- barrier();
+ extern void kernel_unaligned_trap(struct pt_regs *regs,
+ unsigned int insn);
+
+ return kernel_unaligned_trap(regs, *((unsigned int *)regs->tpc));
} else {
current->tss.sig_address = regs->tpc;
current->tss.sig_desc = SUBSIG_PRIVINST;
void do_privop(struct pt_regs *regs)
{
- printk("PRIVOP: at %016lx\n", regs->tpc);
- while(1)
- barrier();
+ current->tss.sig_address = regs->tpc;
+ current->tss.sig_desc = SUBSIG_PRIVINST;
+ send_sig(SIGILL, current, 1);
}
void do_privact(struct pt_regs *regs)
{
- printk("PRIVACT: at %016lx\n", regs->tpc);
- while(1)
- barrier();
+ current->tss.sig_address = regs->tpc;
+ current->tss.sig_desc = SUBSIG_PRIVINST;
+ send_sig(SIGILL, current, 1);
+ unlock_kernel();
}
void do_priv_instruction(struct pt_regs *regs, unsigned long pc, unsigned long npc,
}
current->tss.sig_address = pc;
current->tss.sig_desc = SUBSIG_PRIVINST;
-#if 0
- show_regs (regs);
- instruction_dump ((unsigned long *) regs->tpc);
- printk ("do_MNA!\n");
-#endif
send_sig(SIGBUS, current, 1);
unlock_kernel();
}
unlock_kernel();
}
+/* Trap level 1 stuff or other traps we should never see... */
+void do_cee(struct pt_regs *regs)
+{
+ die_if_kernel("TL0: Cache Error Exception", regs);
+}
+
+void do_cee_tl1(struct pt_regs *regs)
+{
+ die_if_kernel("TL1: Cache Error Exception", regs);
+}
+
+void do_dae_tl1(struct pt_regs *regs)
+{
+ die_if_kernel("TL1: Data Access Exception", regs);
+}
+
+void do_iae_tl1(struct pt_regs *regs)
+{
+ die_if_kernel("TL1: Instruction Access Exception", regs);
+}
+
+void do_div0_tl1(struct pt_regs *regs)
+{
+ die_if_kernel("TL1: DIV0 Exception", regs);
+}
+
+void do_fpdis_tl1(struct pt_regs *regs)
+{
+ die_if_kernel("TL1: FPU Disabled", regs);
+}
+
+void do_fpieee_tl1(struct pt_regs *regs)
+{
+ die_if_kernel("TL1: FPU IEEE Exception", regs);
+}
+
+void do_fpother_tl1(struct pt_regs *regs)
+{
+ die_if_kernel("TL1: FPU Other Exception", regs);
+}
+
+void do_ill_tl1(struct pt_regs *regs)
+{
+ die_if_kernel("TL1: Illegal Instruction Exception", regs);
+}
+
+void do_irq_tl1(struct pt_regs *regs)
+{
+ die_if_kernel("TL1: IRQ Exception", regs);
+}
+
+void do_lddfmna(struct pt_regs *regs)
+{
+ die_if_kernel("TL0: LDDF Exception", regs);
+}
+
+void do_lddfmna_tl1(struct pt_regs *regs)
+{
+ die_if_kernel("TL1: LDDF Exception", regs);
+}
+
+void do_stdfmna(struct pt_regs *regs)
+{
+ die_if_kernel("TL0: STDF Exception", regs);
+}
+
+void do_stdfmna_tl1(struct pt_regs *regs)
+{
+ die_if_kernel("TL1: STDF Exception", regs);
+}
+
+void do_paw(struct pt_regs *regs)
+{
+ die_if_kernel("TL0: Phys Watchpoint Exception", regs);
+}
+
+void do_paw_tl1(struct pt_regs *regs)
+{
+ die_if_kernel("TL1: Phys Watchpoint Exception", regs);
+}
+
+void do_vaw(struct pt_regs *regs)
+{
+ die_if_kernel("TL0: Virt Watchpoint Exception", regs);
+}
+
+void do_vaw_tl1(struct pt_regs *regs)
+{
+ die_if_kernel("TL1: Virt Watchpoint Exception", regs);
+}
+
+void do_tof_tl1(struct pt_regs *regs)
+{
+ die_if_kernel("TL1: Tag Overflow Exception", regs);
+}
+
+#ifdef CONFIG_EC_FLUSH_TRAP
+void cache_flush_trap(struct pt_regs *regs)
+{
+#ifndef __SMP__
+ unsigned node = linux_cpus[get_cpuid()].prom_node;
+#else
+#error SMP not supported on sparc64 yet
+#endif
+ int size = prom_getintdefault(node, "ecache-size", 512*1024);
+ int i, j;
+ unsigned long addr, page_nr;
+
+ regs->tpc = regs->tnpc;
+ regs->tnpc = regs->tnpc + 4;
+ if (!suser()) return;
+ size >>= PAGE_SHIFT;
+ addr = PAGE_OFFSET - PAGE_SIZE;
+ for (i = 0; i < size; i++) {
+ do {
+ addr += PAGE_SIZE;
+ page_nr = MAP_NR(addr);
+ if (page_nr >= max_mapnr) {
+ return;
+ }
+ } while (!PageReserved (mem_map + page_nr));
+ /* E-Cache line size is 64B. Let us pollute it :)) */
+ for (j = 0; j < PAGE_SIZE; j += 64)
+ __asm__ __volatile__ ("ldx [%0 + %1], %%g1" : : "r" (j), "r" (addr) : "g1");
+ }
+}
+#endif
+
void trap_init(void)
{
}
-/* $Id: ttable.S,v 1.13 1997/06/02 06:33:34 davem Exp $
+/* $Id: ttable.S,v 1.18 1997/07/05 09:52:41 davem Exp $
* ttable.S: Sparc V9 Trap Table(s) with SpitFire extensions.
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
*/
+#include <linux/config.h>
+
.globl sparc64_ttable_tl0, sparc64_ttable_tl1
sparc64_ttable_tl0:
tl0_resv012: BTRAP(0x12) BTRAP(0x13) BTRAP(0x14) BTRAP(0x15) BTRAP(0x16) BTRAP(0x17)
tl0_resv018: BTRAP(0x18) BTRAP(0x19) BTRAP(0x1a) BTRAP(0x1b) BTRAP(0x1c) BTRAP(0x1d)
tl0_resv01e: BTRAP(0x1e) BTRAP(0x1f)
-tl0_fpdis: TRAP(do_fpdis)
+tl0_fpdis: TRAP_NOSAVE(do_fpdis)
tl0_fpieee: TRAP(do_fpieee)
tl0_fpother: TRAP(do_fpother)
tl0_tof: TRAP(do_tof)
tl0_resv15f: BTRAP(0x15f) BTRAP(0x160) BTRAP(0x161) BTRAP(0x162) BTRAP(0x163)
tl0_resv164: BTRAP(0x164) BTRAP(0x165) BTRAP(0x166) BTRAP(0x167) BTRAP(0x168)
tl0_resv169: BTRAP(0x169) BTRAP(0x16a) BTRAP(0x16b) BTRAP(0x16c) BTRAP(0x16d)
-tl0_resv16e: BTRAP(0x16e) BTRAP(0x16f) BTRAP(0x170) BTRAP(0x171) BTRAP(0x172)
+tl0_gsctx: TRAP(sparc64_get_context) TRAP(sparc64_set_context)
+tl0_resv170: BTRAP(0x170) BTRAP(0x171)
+#ifdef CONFIG_EC_FLUSH_TRAP
+ TRAP(cache_flush_trap)
+#else
+ BTRAP(0x172)
+#endif
tl0_resv173: BTRAP(0x173) BTRAP(0x174) BTRAP(0x175) BTRAP(0x176) BTRAP(0x177)
tl0_resv178: BTRAP(0x178) BTRAP(0x179) BTRAP(0x17a) BTRAP(0x17b) BTRAP(0x17c)
tl0_resv17d: BTRAP(0x17d) BTRAP(0x17e) BTRAP(0x17f)
tl1_resv016: BTRAPTL1(0x16) BTRAPTL1(0x17) BTRAPTL1(0x18) BTRAPTL1(0x19)
tl1_resv01a: BTRAPTL1(0x1a) BTRAPTL1(0x1b) BTRAPTL1(0x1c) BTRAPTL1(0x1d)
tl1_resv01e: BTRAPTL1(0x1e) BTRAPTL1(0x1f)
-tl1_fpdis: TRAPTL1(do_fpdis_tl1)
+tl1_fpdis: TRAP_NOSAVE(do_fpdis)
tl1_fpieee: TRAPTL1(do_fpieee_tl1)
tl1_fpother: TRAPTL1(do_fpother_tl1)
tl1_tof: TRAPTL1(do_tof_tl1)
-/* $Id: unaligned.c,v 1.1 1997/06/06 10:56:19 jj Exp $
+/* $Id: unaligned.c,v 1.3 1997/06/25 10:12:15 jj Exp $
* unaligned.c: Unaligned load/store trap handling with special
* cases for the kernel to do them more quickly.
*
#include <linux/smp.h>
#include <linux/smp_lock.h>
-#define DEBUG_MNA
+/* #define DEBUG_MNA */
enum direction {
load, /* ld, ldd, ldh, ldsh */
"stx %%g7, [%0 + 8]\n" \
"0:\n\n\t" \
".section __ex_table\n\t" \
- ".word 4b, " #errh "\n\t" \
- ".word 5b, " #errh "\n\t" \
- ".word 6b, " #errh "\n\t" \
- ".word 7b, " #errh "\n\t" \
- ".word 8b, " #errh "\n\t" \
- ".word 9b, " #errh "\n\t" \
- ".word 10b, " #errh "\n\t" \
- ".word 11b, " #errh "\n\t" \
- ".word 12b, " #errh "\n\t" \
- ".word 13b, " #errh "\n\t" \
- ".word 14b, " #errh "\n\t" \
- ".word 15b, " #errh "\n\t" \
- ".word 16b, " #errh "\n\n\t" \
+ ".xword 4b, " #errh "\n\t" \
+ ".xword 5b, " #errh "\n\t" \
+ ".xword 6b, " #errh "\n\t" \
+ ".xword 7b, " #errh "\n\t" \
+ ".xword 8b, " #errh "\n\t" \
+ ".xword 9b, " #errh "\n\t" \
+ ".xword 10b, " #errh "\n\t" \
+ ".xword 11b, " #errh "\n\t" \
+ ".xword 12b, " #errh "\n\t" \
+ ".xword 13b, " #errh "\n\t" \
+ ".xword 14b, " #errh "\n\t" \
+ ".xword 15b, " #errh "\n\t" \
+ ".xword 16b, " #errh "\n\n\t" \
".previous\n\t" \
: : "r" (dest_reg), "r" (size), "r" (saddr), "r" (is_signed), "r" (asi) \
: "l1", "l2", "g7", "g1", "cc"); \
"17:\t" "stba %%l1, [%0 + 1] %%asi\n" \
"0:\n\n\t" \
".section __ex_table\n\t" \
- ".word 4b, " #errh "\n\t" \
- ".word 5b, " #errh "\n\t" \
- ".word 6b, " #errh "\n\t" \
- ".word 7b, " #errh "\n\t" \
- ".word 8b, " #errh "\n\t" \
- ".word 9b, " #errh "\n\t" \
- ".word 10b, " #errh "\n\t" \
- ".word 11b, " #errh "\n\t" \
- ".word 12b, " #errh "\n\t" \
- ".word 13b, " #errh "\n\t" \
- ".word 14b, " #errh "\n\t" \
- ".word 15b, " #errh "\n\t" \
- ".word 16b, " #errh "\n\t" \
- ".word 17b, " #errh "\n\n\t" \
+ ".xword 4b, " #errh "\n\t" \
+ ".xword 5b, " #errh "\n\t" \
+ ".xword 6b, " #errh "\n\t" \
+ ".xword 7b, " #errh "\n\t" \
+ ".xword 8b, " #errh "\n\t" \
+ ".xword 9b, " #errh "\n\t" \
+ ".xword 10b, " #errh "\n\t" \
+ ".xword 11b, " #errh "\n\t" \
+ ".xword 12b, " #errh "\n\t" \
+ ".xword 13b, " #errh "\n\t" \
+ ".xword 14b, " #errh "\n\t" \
+ ".xword 15b, " #errh "\n\t" \
+ ".xword 16b, " #errh "\n\t" \
+ ".xword 17b, " #errh "\n\n\t" \
".previous\n\t" \
: : "r" (dst_addr), "r" (size), "r" (src_val), "r" (asi) \
: "l1", "l2", "g7", "g1", "cc"); \
-/* $Id: winfixup.S,v 1.8 1997/06/02 06:33:35 davem Exp $
+/* $Id: winfixup.S,v 1.15 1997/07/04 01:41:07 davem Exp $
*
* winfixup.S: Handle cases where user stack pointer is found to be bogus.
*
fill_fixup:
rdpr %tstate, %g1
andcc %g1, TSTATE_PRIV, %g0
+ clr %g4
be,pt %xcc, window_scheisse_from_user_common
and %g1, TSTATE_CWP, %g1
rdpr %wstate, %g2 ! Grab user mode wstate.
wrpr %g1, %cwp ! Get into the right window.
sll %g2, 3, %g2 ! NORMAL-->OTHER
- wrpr %g0, 0x0, %canrestore ! Standard etrap stuff.
+ wrpr %g0, 0x0, %canrestore ! Standard etrap stuff.
+ wr %g0, 0x0, %fprs ! zap FPU just in case...
wrpr %g2, 0x0, %wstate ! This must be consistant.
wrpr %g0, 0x0, %otherwin ! We know this.
- sethi %uhi(KERNBASE), %g2 ! Set this up
- sllx %g2, 32, %g2 ! for the iflush
mov PRIMARY_CONTEXT, %g1 ! Change contexts...
stxa %g0, [%g1] ASI_DMMU ! Back into the nucleus.
- flush %g2 ! Flush instruction buffers
+ flush %g6 ! Flush instruction buffers
rdpr %pstate, %l1 ! Prepare to change globals.
- mov %g4, %o5 ! Setup args for
- mov %g5, %o4 ! final call to do_sparc64_fault.
+ mov %g6, %o7 ! Get current.
+ mov %g5, %l5 ! Fault address
+ clr %l4 ! It was a load, not a store
wrpr %g0, 0x0, %tl ! Out of trap levels.
wrpr %l1, (PSTATE_IE | PSTATE_AG), %pstate
- sethi %uhi(KERNBASE), %g4 ! Restore med-any global reg.
- rd %pic, %g6 ! Get current as well.
+ sethi %uhi(PAGE_OFFSET), %g4 ! Prepare page_offset global reg
+ mov %o7, %g6
b,pt %xcc, window_scheisse_merge ! And merge.
- sllx %g4, 32, %g4 ! Finish med-any reg setup.
+
+ sllx %g4, 32, %g4 ! and finish it...
/* Be very careful about usage of the alternate globals here.
* You cannot touch %g4/%g5 as that has the fault information
* do not touch %g7 or %g2 so we handle the two cases fine.
*/
spill_fixup:
- rd %pic, %g1
- ldx [%g1 + AOFF_task_tss + AOFF_thread_flags], %g6
- andcc %g6, SPARC_FLAG_32BIT, %g0
- ldx [%g1 + AOFF_task_tss + AOFF_thread_w_saved], %g6
- sll %g6, 3, %g3
- add %g1, %g3, %g3
+ ld [%g6 + AOFF_task_tss + AOFF_thread_flags], %g1
+ andcc %g1, SPARC_FLAG_32BIT, %g0
+ ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %g1
+ sll %g1, 3, %g3
+ add %g6, %g3, %g3
stx %sp, [%g3 + AOFF_task_tss + AOFF_thread_rwbuf_stkptrs]
- sll %g6, 7, %g3
+ sll %g1, 7, %g3
bne,pt %xcc, 1f
- add %g1, %g3, %g3
+ add %g6, %g3, %g3
stx %l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00]
stx %l1, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x08]
stx %l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10]
stx %i5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x68]
stx %i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x70]
- stx %i7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x78]
b,pt %xcc, 2f
- add %g6, 1, %g6
-1: std %l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00]
- std %l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x08]
- std %l4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10]
- std %l6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x18]
-
- std %i0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x20]
- std %i2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x28]
- std %i4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x30]
- std %i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x38]
- add %g6, 1, %g6
-2: stx %g6, [%g1 + AOFF_task_tss + AOFF_thread_w_saved]
+ stx %i7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x78]
+1: stw %l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00]
+ stw %l1, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x04]
+ stw %l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x08]
+ stw %l3, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x0c]
+ stw %l4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10]
+
+ stw %l5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x14]
+ stw %l6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x18]
+ stw %l7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x1c]
+ stw %i0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x20]
+ stw %i1, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x24]
+ stw %i2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x28]
+ stw %i3, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x2c]
+ stw %i4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x30]
+
+ stw %i5, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x34]
+ stw %i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x38]
+ stw %i7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x3c]
+2: add %g1, 1, %g1
+ stx %g1, [%g6 + AOFF_task_tss + AOFF_thread_w_saved]
rdpr %tstate, %g1
- nop
-
andcc %g1, TSTATE_PRIV, %g0
saved
+
and %g1, TSTATE_CWP, %g1
be,a,pn %xcc, window_scheisse_from_user_common
or %g4, 0x4, %g4 ! we know it was a write
retry
window_scheisse_from_user_common:
- nop
wrpr %g1, %cwp
-
ba,pt %xcc, etrap
rd %pc, %g7
- mov %l5, %o4
- mov %l4, %o5
window_scheisse_merge:
- srlx %o4, PAGE_SHIFT, %o3
- clr %o1
- sllx %o3, PAGE_SHIFT, %o3
- and %o5, 0x4, %o2
+ srlx %l5, PAGE_SHIFT, %o1
+ and %l4, 0x4, %o2
+ sllx %o1, PAGE_SHIFT, %o1
call do_sparc64_fault
add %sp, STACK_BIAS + REGWIN_SZ, %o0
ba,pt %xcc, rtrap
winfix_trampoline:
andn %g3, 0x7f, %g3
add %g3, 0x7c, %g3
+
wrpr %g3, %tnpc
done
wrpr %g0, 0x0, %canrestore ! Standard etrap stuff.
wrpr %g2, 0x0, %wstate ! This must be consistant.
wrpr %g0, 0x0, %otherwin ! We know this.
- sethi %uhi(KERNBASE), %g2 ! Set this up
- sllx %g2, 32, %g2 ! for the iflush
mov PRIMARY_CONTEXT, %g1 ! Change contexts...
stxa %g0, [%g1] ASI_DMMU ! Back into the nucleus.
- flush %g2 ! Flush instruction buffers
+ flush %g6 ! Flush instruction buffers
rdpr %pstate, %l1 ! Prepare to change globals.
mov %g4, %o5 ! Setup args for
mov %g5, %o4 ! final call to do_sparc64_fault.
+ mov %g6, %o7 ! Stash away current.
wrpr %g0, 0x0, %tl ! Out of trap levels.
wrpr %l1, (PSTATE_IE | PSTATE_AG), %pstate
- sethi %uhi(KERNBASE), %g4 ! Restore med-any global reg.
- rd %pic, %g6 ! Get current as well.
+ sethi %uhi(PAGE_OFFSET), %g4 ! Set page_offset global reg.
+ mov %o7, %g6 ! Get current back.
b,pt %xcc, window_mna_merge ! And merge.
- sllx %g4, 32, %g4 ! Finish med-any reg setup.
+ sllx %g4, 32, %g4 ! Finish it.
+
spill_fixup_mna:
- rd %pic, %g1
- ldx [%g1 + AOFF_task_tss + AOFF_thread_flags], %g6
- andcc %g6, SPARC_FLAG_32BIT, %g0
- ldx [%g1 + AOFF_task_tss + AOFF_thread_w_saved], %g6
- sll %g6, 3, %g3
- add %g1, %g3, %g3
+ ld [%g6 + AOFF_task_tss + AOFF_thread_flags], %g1
+ andcc %g1, SPARC_FLAG_32BIT, %g0
+ ldx [%g6 + AOFF_task_tss + AOFF_thread_w_saved], %g1
+ sll %g1, 3, %g3
+ add %g6, %g3, %g3
stx %sp, [%g3 + AOFF_task_tss + AOFF_thread_rwbuf_stkptrs]
- sll %g6, 7, %g3
+ sll %g1, 7, %g3
bne,pt %xcc, 1f
- add %g1, %g3, %g3
+ add %g6, %g3, %g3
stx %l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00]
stx %l1, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x08]
stx %l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10]
stx %i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x70]
stx %i7, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x78]
b,pt %xcc, 2f
- add %g6, 1, %g6
+ add %g1, 1, %g1
1: std %l0, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x00]
std %l2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x08]
std %l4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x10]
std %i2, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x28]
std %i4, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x30]
std %i6, [%g3 + AOFF_task_tss + AOFF_thread_reg_window + 0x38]
- add %g6, 1, %g6
-2: stx %g6, [%g1 + AOFF_task_tss + AOFF_thread_w_saved]
+ add %g1, 1, %g1
+2: stx %g1, [%g6 + AOFF_task_tss + AOFF_thread_w_saved]
rdpr %tstate, %g1
nop
-# $Id: Makefile,v 1.7 1997/04/07 18:57:05 jj Exp $
+# $Id: Makefile,v 1.12 1997/06/25 10:12:18 jj Exp $
# Makefile for Sparc library files..
#
CFLAGS := $(CFLAGS) -ansi
-OBJS = memset.o blockops.o locks.o memcpy.o strlen.o strncmp.o \
+OBJS = blockops.o locks.o strlen.o strncmp.o \
memscan.o strncpy_from_user.o strlen_user.o memcmp.o checksum.o \
- copy_to_user.o copy_from_user.o
+ VIScopy.o VISbzero.o VISmemset.o
lib.a: $(OBJS)
$(AR) rcs lib.a $(OBJS)
sync
-blockops.o: blockops.S
- $(CC) -ansi -c -o blockops.o blockops.S
+VIScopy.o: VIScopy.S VIS.h
+VISbzero.o: VISbzero.S VIS.h
-memset.o: memset.S
- $(CC) -D__ASSEMBLY__ -ansi -c -o memset.o memset.S
+.S.s:
+ $(CPP) -D__ASSEMBLY__ -ansi $< -o $*.s
-copy_to_user.o: copy_to_user.S
- $(CC) -D__ASSEMBLY__ -ansi -c -o copy_to_user.o copy_to_user.S
-
-copy_from_user.o: copy_from_user.S
- $(CC) -D__ASSEMBLY__ -ansi -c -o copy_from_user.o copy_from_user.S
-
-memcpy.o: memcpy.S
- $(CC) -D__ASSEMBLY__ -ansi -c -o memcpy.o memcpy.S
-
-strlen.o: strlen.S
- $(CC) -D__ASSEMBLY__ -ansi -c -o strlen.o strlen.S
-
-strncmp.o: strncmp.S
- $(CC) -D__ASSEMBLY__ -ansi -c -o strncmp.o strncmp.S
-
-memcmp.o: memcmp.S
- $(CC) -D__ASSEMBLY__ -ansi -c -o memcmp.o memcmp.S
-
-locks.o: locks.S
- $(CC) -D__ASSEMBLY__ -ansi -c -o locks.o locks.S
-
-checksum.o: checksum.S
- $(CC) -D__ASSEMBLY__ -ansi -c -o checksum.o checksum.S
-
-memscan.o: memscan.S
- $(CC) -D__ASSEMBLY__ -ansi -c -o memscan.o memscan.S
-
-strncpy_from_user.o: strncpy_from_user.S
- $(CC) -D__ASSEMBLY__ -ansi -c -o strncpy_from_user.o strncpy_from_user.S
-
-strlen_user.o: strlen_user.S
- $(CC) -D__ASSEMBLY__ -ansi -c -o strlen_user.o strlen_user.S
+.S.o:
+ $(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o
dep:
--- /dev/null
+/* $Id: VIS.h,v 1.3 1997/06/27 14:53:18 jj Exp $
+ * VIS.h: High speed copy/clear operations utilizing the UltraSparc
+ * Visual Instruction Set.
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996, 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+ /* VIS code can be used for numerous copy/set operation variants.
+ * It can be made to work in the kernel, one single instance,
+ * for all of memcpy, copy_to_user, and copy_from_user by setting
+ * the ASI src/dest globals correctly. Furthermore it can
+ * be used for kernel-->kernel page copies as well, a hook label
+ * is put in here just for this purpose.
+ *
+ * For userland, compiling this without __KERNEL__ defined makes
+ * it work just fine as a generic libc bcopy and memcpy.
+ * If for userland it is compiled with a 32bit gcc (but you need
+ * -Wa,-Av9a), the code will just rely on lower 32bits of
+ * IEU registers, if you compile it with 64bit gcc (ie. define
+ * __sparc_v9__), the code will use full 64bit.
+ */
+
+#ifndef __VIS_H
+#define __VIS_H
+
+#ifdef __KERNEL__
+#include <asm/head.h>
+#include <asm/asi.h>
+#else
+#define ASI_P 0x80 /* Primary, implicit */
+#define ASI_S 0x81 /* Secondary, implicit */
+#define ASI_BLK_COMMIT_P 0xe0 /* Primary, blk store commit */
+#define ASI_BLK_COMMIT_S 0xe1 /* Secondary, blk store commit */
+#define ASI_BLK_P 0xf0 /* Primary, blk ld/st */
+#define ASI_BLK_S 0xf1 /* Secondary, blk ld/st */
+#define FPRS_FEF 0x04
+#endif
+
+ /* I'm telling you, they really did this chip right.
+ * Perhaps the SunSoft folks should visit some of the
+ * people in Sun Microelectronics and start some brain
+ * cell exchange program...
+ */
+#define ASI_BLK_XOR (ASI_P ^ ASI_BLK_P)
+
+#define asi_src %o3
+#define asi_dest %o4
+
+#ifdef __KERNEL__
+#define ASI_SETSRC_BLK wr asi_src, 0, %asi;
+#define ASI_SETSRC_NOBLK wr asi_src, ASI_BLK_XOR, %asi;
+#define ASI_SETDST_BLK wr asi_dest, 0, %asi;
+#define ASI_SETDST_NOBLK wr asi_dest, ASI_BLK_XOR, %asi;
+#define ASIBLK %asi
+#define ASINORMAL %asi
+#define LDUB lduba
+#define LDUH lduha
+#define LDUW lduwa
+#define LDX ldxa
+#define LDD ldda
+#define LDDF ldda
+#define LDBLK ldda
+#define STB stba
+#define STH stha
+#define STW stwa
+#define STD stda
+#define STX stxa
+#define STDF stda
+#define STBLK stda
+#else
+#define ASI_SETSRC_BLK
+#define ASI_SETSRC_NOBLK
+#define ASI_SETDST_BLK
+#define ASI_SETDST_NOBLK
+#define ASI_SETDST_SPECIAL
+#define ASIBLK %asi
+#define ASINORMAL
+#define LDUB ldub
+#define LDUH lduh
+#define LDUW lduw
+#define LDD ldd
+#define LDX ldx
+#define LDDF ldd
+#define LDBLK ldda
+#define STB stb
+#define STH sth
+#define STW stw
+#define STD std
+#define STX stx
+#define STDF std
+#define STBLK stda
+#endif
+
+#ifdef __KERNEL__
+
+#define REGS_64BIT
+
+#else
+
+#ifndef REGS_64BIT
+#ifdef __sparc_v9__
+#define REGS_64BIT
+#endif
+#endif
+
+#endif
+
+#ifndef REGS_64BIT
+#define xcc icc
+#endif
+
+#endif
--- /dev/null
+/* $Id: VISbzero.S,v 1.4 1997/06/28 17:21:21 jj Exp $
+ * VISbzero.S: High speed clear operations utilizing the UltraSparc
+ * Visual Instruction Set.
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996, 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include "VIS.h"
+
+#ifdef __KERNEL__
+#define EXN(x,y,a,b,z) \
+98: x,y; \
+ .section .fixup; \
+ .align 4; \
+99: ba VISbzerofixup_ret##z; \
+ a, b, %o0; \
+ .section __ex_table; \
+ .align 8; \
+ .xword 98b, 99b; \
+ .text; \
+ .align 4;
+#define EXC(x,y,a,b,c...) \
+98: x,y; \
+ .section .fixup; \
+ .align 4; \
+99: c; \
+ ba VISbzerofixup_ret0; \
+ a, b, %o0; \
+ .section __ex_table; \
+ .align 8; \
+ .xword 98b, 99b; \
+ .text; \
+ .align 4;
+#define EXO1(x,y) \
+98: x,y; \
+ .section __ex_table; \
+ .align 8; \
+ .xword 98b, VISbzerofixup_reto1; \
+ .text; \
+ .align 4;
+#define EX(x,y,a,b) EXN(x,y,a,b,0)
+#define EX1(x,y,a,b) EXN(x,y,a,b,1)
+#define EX2(x,y,a,b) EXN(x,y,a,b,2)
+#define EXT(start,end,handler) \
+ .section __ex_table; \
+ .align 8; \
+ .xword start, 0, end, handler; \
+ .text; \
+ .align 4
+#else
+#define EX(x,y,a,b) x,y
+#define EX1(x,y,a,b) x,y
+#define EX2(x,y,a,b) x,y
+#define EXC(x,y,a,b,c...) x,y
+#define EXO1(x,y) x,y
+#define EXT(a,b,c)
+#endif
+
+#define ZERO_BLOCKS(base, offset, source) \
+ STX source, [base - offset - 0x38] ASINORMAL; \
+ STX source, [base - offset - 0x30] ASINORMAL; \
+ STX source, [base - offset - 0x28] ASINORMAL; \
+ STX source, [base - offset - 0x20] ASINORMAL; \
+ STX source, [base - offset - 0x18] ASINORMAL; \
+ STX source, [base - offset - 0x10] ASINORMAL; \
+ STX source, [base - offset - 0x08] ASINORMAL; \
+ STX source, [base - offset - 0x00] ASINORMAL;
+
+#ifdef __KERNEL__
+#define RETL clr %o0
+#else
+#define RETL mov %g3, %o0
+#endif
+
+ /* Well, bzero is a lot easier to get right than bcopy... */
+#ifdef __KERNEL__
+ .section __ex_table,#alloc
+ .section .fixup,#alloc,#execinstr
+#endif
+ .text
+ .align 32
+#ifdef __KERNEL__
+ .globl __bzero, __bzero_noasi
+__bzero:
+ wr %g0, ASI_P, %asi ! LSU Group
+__bzero_noasi:
+#else
+ .globl bzero
+bzero_private:
+bzero:
+#ifndef REGS_64BIT
+ srl %o1, 0, %o1
+#endif
+ mov %o0, %g3
+#endif
+ cmp %o1, 7
+ bleu,pn %xcc, 17f
+ andcc %o0, 3, %o2
+ be,a,pt %xcc, 4f
+ andcc %o0, 4, %g0
+ cmp %o2, 3
+ be,pn %xcc, 2f
+ EXO1(STB %g0, [%o0 + 0x00] ASINORMAL)
+ cmp %o2, 2
+ be,pt %xcc, 2f
+ EX(STB %g0, [%o0 + 0x01] ASINORMAL, sub %o1, 1)
+ EX(STB %g0, [%o0 + 0x02] ASINORMAL, sub %o1, 2)
+2: sub %o2, 4, %o2
+ sub %o0, %o2, %o0
+ add %o1, %o2, %o1
+ andcc %o0, 4, %g0
+4: be,pt %xcc, 2f
+ cmp %o1, 128
+ EXO1(STW %g0, [%o0] ASINORMAL)
+ sub %o1, 4, %o1
+ add %o0, 4, %o0
+2: blu,pn %xcc, 9f
+ andcc %o0, 0x38, %o2
+ be,pn %icc, 6f
+ mov 64, %o5
+ andcc %o0, 8, %g0
+ be,pn %icc, 1f
+ sub %o5, %o2, %o5
+ EX(STX %g0, [%o0] ASINORMAL, sub %o1, 0)
+ add %o0, 8, %o0
+1: andcc %o5, 16, %g0
+ be,pn %icc, 1f
+ sub %o1, %o5, %o1
+ EX1(STX %g0, [%o0] ASINORMAL, add %g0, 0)
+ EX1(STX %g0, [%o0 + 8] ASINORMAL, sub %g0, 8)
+ add %o0, 16, %o0
+1: andcc %o5, 32, %g0
+ be,pn %icc, 7f
+ andncc %o1, 0x3f, %o3
+ EX(STX %g0, [%o0] ASINORMAL, add %o1, 32)
+ EX(STX %g0, [%o0 + 8] ASINORMAL, add %o1, 24)
+ EX(STX %g0, [%o0 + 16] ASINORMAL, add %o1, 16)
+ EX(STX %g0, [%o0 + 24] ASINORMAL, add %o1, 8)
+ add %o0, 32, %o0
+6: andncc %o1, 0x3f, %o3
+7: be,pn %xcc, 9f
+#ifdef __KERNEL__
+ rd %asi, %g7
+ wr %g0, FPRS_FEF, %fprs
+ wr %g7, ASI_BLK_XOR, %asi
+#else
+ wr %g0, ASI_BLK_P, %asi
+#endif
+ membar #StoreStore | #LoadStore
+ fzero %f0
+ andcc %o3, 0xc0, %o2
+ and %o1, 0x3f, %o1
+ fzero %f2
+ andn %o3, 0xff, %o3
+ faddd %f0, %f2, %f4
+ fmuld %f0, %f2, %f6
+ cmp %o2, 64
+ faddd %f0, %f2, %f8
+ fmuld %f0, %f2, %f10
+ faddd %f0, %f2, %f12
+ brz,pn %o2, 10f
+ fmuld %f0, %f2, %f14
+ be,pn %icc, 2f
+ EXC(STBLK %f0, [%o0 + 0x00] ASIBLK, add %o3, %o2, add %o2, %o1, %o2)
+ cmp %o2, 128
+ be,pn %icc, 2f
+ EXC(STBLK %f0, [%o0 + 0x40] ASIBLK, add %o3, %o2, add %o2, %o1, %o2; sub %o2, 64, %o2)
+ EXC(STBLK %f0, [%o0 + 0x80] ASIBLK, add %o3, %o2, add %o2, %o1, %o2; sub %o2, 128, %o2)
+2: brz,pn %o3, 12f
+ add %o0, %o2, %o0
+10: EX(STBLK %f0, [%o0 + 0x00] ASIBLK, add %o3, %o1)
+ EXC(STBLK %f0, [%o0 + 0x40] ASIBLK, add %o3, %o1, sub %o1, 64, %o1)
+ EXC(STBLK %f0, [%o0 + 0x80] ASIBLK, add %o3, %o1, sub %o1, 128, %o1)
+ EXC(STBLK %f0, [%o0 + 0xc0] ASIBLK, add %o3, %o1, sub %o1, 192, %o1)
+11: subcc %o3, 256, %o3
+ bne,pt %xcc, 10b
+ add %o0, 256, %o0
+12:
+#ifdef __KERNEL__
+ wr %g0, 0, %fprs
+ wr %g7, 0x0, %asi
+#endif
+ membar #Sync
+9: andcc %o1, 0xf8, %o2
+ be,pn %xcc, 13f
+ andcc %o1, 7, %o1
+14: rd %pc, %o4
+ srl %o2, 1, %o3
+ sub %o4, %o3, %o4
+ jmpl %o4 + (13f - 14b), %g0
+ add %o0, %o2, %o0
+12: ZERO_BLOCKS(%o0, 0xc8, %g0)
+ ZERO_BLOCKS(%o0, 0x88, %g0)
+ ZERO_BLOCKS(%o0, 0x48, %g0)
+ ZERO_BLOCKS(%o0, 0x08, %g0)
+ EXT(12b,13f,VISbzerofixup_zb)
+13: be,pn %xcc, 8f
+ andcc %o1, 4, %g0
+ be,pn %xcc, 1f
+ andcc %o1, 2, %g0
+ EX(STW %g0, [%o0] ASINORMAL, and %o1, 7)
+ add %o0, 4, %o0
+1: be,pn %xcc, 1f
+ andcc %o1, 1, %g0
+ EX(STH %g0, [%o0] ASINORMAL, and %o1, 3)
+ add %o0, 2, %o0
+1: bne,a,pn %xcc, 8f
+ EX(STB %g0, [%o0] ASINORMAL, add %g0, 1)
+8: retl
+ RETL
+17: be,pn %xcc, 13b
+ orcc %o1, 0, %g0
+ be,pn %xcc, 0f
+8: add %o0, 1, %o0
+ subcc %o1, 1, %o1
+ bne,pt %xcc, 8b
+ EX(STB %g0, [%o0 - 1] ASINORMAL, add %o1, 1)
+0: retl
+ RETL
+
+#ifdef __KERNEL__
+ .section .fixup
+ .align 4
+VISbzerofixup_reto1:
+ mov %o1, %o0
+VISbzerofixup_ret0:
+ retl
+ wr %g0, 0, %fprs
+VISbzerofixup_ret1:
+ and %o5, 0x30, %o5
+ add %o5, %o1, %o5
+ ba,pt %xcc, VISbzerofixup_ret0
+ add %o0, %o5, %o0
+VISbzerofixup_ret2:
+ and %o5, 0x20, %o5
+ add %o5, %o1, %o5
+ ba,pt %xcc, VISbzerofixup_ret0
+ add %o0, %o5, %o0
+VISbzerofixup_zb:
+ andcc %o1, 7, %o1
+ sll %g2, 3, %g2
+ add %o1, 256, %o1
+ ba,pt %xcc, VISbzerofixup_ret0
+ sub %o1, %g2, %o0
+#endif
--- /dev/null
+/* $Id: VIScopy.S,v 1.8 1997/06/28 17:21:22 jj Exp $
+ * VIScopy.S: High speed copy operations utilizing the UltraSparc
+ * Visual Instruction Set.
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996, 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include "VIS.h"
+
+ /* VIS code can be used for numerous copy/set operation variants.
+ * It can be made to work in the kernel, one single instance,
+ * for all of memcpy, copy_to_user, and copy_from_user by setting
+ * the ASI src/dest globals correctly. Furthermore it can
+ * be used for kernel-->kernel page copies as well, a hook label
+ * is put in here just for this purpose.
+ *
+ * For userland, compiling this without __KERNEL__ defined makes
+ * it work just fine as a generic libc bcopy and memcpy.
+ * If for userland it is compiled with a 32bit gcc (but you need
+ * -Wa,-Av9a for as), the code will just rely on lower 32bits of
+ * IEU registers, if you compile it with 64bit gcc (ie. define
+ * __sparc_v9__), the code will use full 64bit.
+ */
+
+#ifdef __KERNEL__
+#define FPU_CLEAN_RETL \
+ wr %g0, 0, %fprs; \
+ retl; \
+ clr %o0;
+#define FPU_RETL \
+ wr %g0, 0, %fprs; \
+ retl; \
+ clr %o0;
+#define NORMAL_RETL \
+ retl; \
+ clr %o0;
+#define EX(x,y,a,b) \
+98: x,y; \
+ .section .fixup; \
+ .align 4; \
+99: ba VIScopyfixup_ret; \
+ a, b, %o0; \
+ .section __ex_table; \
+ .align 8; \
+ .xword 98b, 99b; \
+ .text; \
+ .align 4;
+#define EX2(x,y,c,d,e,a,b) \
+98: x,y; \
+ .section .fixup; \
+ .align 4; \
+99: c, d, e; \
+ ba VIScopyfixup_ret; \
+ a, b, %o0; \
+ .section __ex_table; \
+ .align 8; \
+ .xword 98b, 99b; \
+ .text; \
+ .align 4;
+#define EXO2(x,y) \
+98: x,y; \
+ .section __ex_table; \
+ .align 8; \
+ .xword 98b, VIScopyfixup_reto2; \
+ .text; \
+ .align 4;
+#define EXVISN(x,y,n) \
+98: x,y; \
+ .section __ex_table; \
+ .align 8; \
+ .xword 98b, VIScopyfixup_vis##n; \
+ .text; \
+ .align 4;
+#define EXT(start,end,handler) \
+ .section __ex_table; \
+ .align 8; \
+ .xword start, 0, end, handler; \
+ .text; \
+ .align 4;
+#else
+#define FPU_CLEAN_RETL \
+ retl; \
+ mov %g6, %o0;
+#define FPU_RETL \
+ retl; \
+ mov %g6, %o0;
+#define NORMAL_RETL \
+ retl; \
+ mov %g6, %o0;
+#define EX(x,y,a,b) x,y
+#define EX2(x,y,c,d,e,a,b) x,y
+#define EXO2(x,y) x,y
+#define EXVISN(x,y,n) x,y
+#define EXT(a,b,c)
+#endif
+#define EXVIS(x,y) EXVISN(x,y,0)
+#define EXVIS1(x,y) EXVISN(x,y,1)
+#define EXVIS2(x,y) EXVISN(x,y,2)
+#define EXVIS3(x,y) EXVISN(x,y,3)
+#define EXVIS4(x,y) EXVISN(x,y,4)
+#define EXVIS5(x,y) EXVISN(x,y,5)
+
+#define FREG_FROB(f1, f2, f3, f4, f5, f6, f7, f8, f9) \
+ faligndata %f1, %f2, %f48; \
+ faligndata %f2, %f3, %f50; \
+ faligndata %f3, %f4, %f52; \
+ faligndata %f4, %f5, %f54; \
+ faligndata %f5, %f6, %f56; \
+ faligndata %f6, %f7, %f58; \
+ faligndata %f7, %f8, %f60; \
+ faligndata %f8, %f9, %f62;
+
+#define MAIN_LOOP_CHUNK(src, dest, fdest, fsrc, len, jmptgt) \
+ EXVIS(LDBLK [%src] ASIBLK, %fdest); \
+ add %src, 0x40, %src; \
+ ASI_SETDST_BLK \
+ add %dest, 0x40, %dest; \
+ subcc %len, 0x40, %len; \
+ be,pn %xcc, jmptgt; \
+ EXVIS2(STBLK %fsrc, [%dest - 0x40] ASIBLK); \
+ ASI_SETSRC_BLK
+
+#define LOOP_CHUNK1(src, dest, len, branch_dest) \
+ MAIN_LOOP_CHUNK(src, dest, f0, f48, len, branch_dest)
+#define LOOP_CHUNK2(src, dest, len, branch_dest) \
+ MAIN_LOOP_CHUNK(src, dest, f16, f48, len, branch_dest)
+#define LOOP_CHUNK3(src, dest, len, branch_dest) \
+ MAIN_LOOP_CHUNK(src, dest, f32, f48, len, branch_dest)
+
+#define STORE_SYNC(dest, fsrc) \
+ EXVIS(STBLK %fsrc, [%dest] ASIBLK); \
+ add %dest, 0x40, %dest;
+
+#define STORE_JUMP(dest, fsrc, target) \
+ EXVIS3(STBLK %fsrc, [%dest] ASIBLK); \
+ add %dest, 0x40, %dest; \
+ ba,pt %xcc, target;
+
+#ifndef __KERNEL__
+#define VISLOOP_PAD nop; nop; nop; nop; \
+ nop; nop; nop; nop; \
+ nop; nop; nop; nop; \
+ nop; nop; nop;
+#else
+#define VISLOOP_PAD nop; nop; nop; nop; \
+ nop; nop; nop; nop; \
+ nop;
+#endif
+
+#define FINISH_VISCHUNK(dest, f0, f1, left) \
+ ASI_SETDST_NOBLK \
+ subcc %left, 8, %left; \
+ bl,pn %xcc, vis_out; \
+ faligndata %f0, %f1, %f48; \
+ EXVIS4(STDF %f48, [%dest] ASINORMAL); \
+ add %dest, 8, %dest;
+
+#define UNEVEN_VISCHUNK(dest, f0, f1, left) \
+ subcc %left, 8, %left; \
+ bl,pn %xcc, vis_out; \
+ fsrc1 %f0, %f1; \
+ ba,a,pt %xcc, vis_slk;
+
+ /* Macros for non-VIS memcpy code. */
+#ifdef REGS_64BIT
+
+#define MOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3) \
+ ASI_SETSRC_NOBLK \
+ LDX [%src + offset + 0x00] ASINORMAL, %t0; \
+ LDX [%src + offset + 0x08] ASINORMAL, %t1; \
+ LDX [%src + offset + 0x10] ASINORMAL, %t2; \
+ LDX [%src + offset + 0x18] ASINORMAL, %t3; \
+ ASI_SETDST_NOBLK \
+ STW %t0, [%dst + offset + 0x04] ASINORMAL; \
+ srlx %t0, 32, %t0; \
+ STW %t0, [%dst + offset + 0x00] ASINORMAL; \
+ STW %t1, [%dst + offset + 0x0c] ASINORMAL; \
+ srlx %t1, 32, %t1; \
+ STW %t1, [%dst + offset + 0x08] ASINORMAL; \
+ STW %t2, [%dst + offset + 0x14] ASINORMAL; \
+ srlx %t2, 32, %t2; \
+ STW %t2, [%dst + offset + 0x10] ASINORMAL; \
+ STW %t3, [%dst + offset + 0x1c] ASINORMAL; \
+ srlx %t3, 32, %t3; \
+ STW %t3, [%dst + offset + 0x18] ASINORMAL;
+
+#define MOVE_BIGALIGNCHUNK(src, dst, offset, t0, t1, t2, t3) \
+ ASI_SETSRC_NOBLK \
+ LDX [%src + offset + 0x00] ASINORMAL, %t0; \
+ LDX [%src + offset + 0x08] ASINORMAL, %t1; \
+ LDX [%src + offset + 0x10] ASINORMAL, %t2; \
+ LDX [%src + offset + 0x18] ASINORMAL, %t3; \
+ ASI_SETDST_NOBLK \
+ STX %t0, [%dst + offset + 0x00] ASINORMAL; \
+ STX %t1, [%dst + offset + 0x08] ASINORMAL; \
+ STX %t2, [%dst + offset + 0x10] ASINORMAL; \
+ STX %t3, [%dst + offset + 0x18] ASINORMAL; \
+ ASI_SETSRC_NOBLK \
+ LDX [%src + offset + 0x20] ASINORMAL, %t0; \
+ LDX [%src + offset + 0x28] ASINORMAL, %t1; \
+ LDX [%src + offset + 0x30] ASINORMAL, %t2; \
+ LDX [%src + offset + 0x38] ASINORMAL, %t3; \
+ ASI_SETDST_NOBLK \
+ STX %t0, [%dst + offset + 0x20] ASINORMAL; \
+ STX %t1, [%dst + offset + 0x28] ASINORMAL; \
+ STX %t2, [%dst + offset + 0x30] ASINORMAL; \
+ STX %t3, [%dst + offset + 0x38] ASINORMAL;
+
+#define MOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) \
+ ASI_SETSRC_NOBLK \
+ LDX [%src - offset - 0x10] ASINORMAL, %t0; \
+ LDX [%src - offset - 0x08] ASINORMAL, %t1; \
+ ASI_SETDST_NOBLK \
+ STW %t0, [%dst - offset - 0x0c] ASINORMAL; \
+ srlx %t0, 32, %t2; \
+ STW %t2, [%dst - offset - 0x10] ASINORMAL; \
+ STW %t1, [%dst - offset - 0x04] ASINORMAL; \
+ srlx %t1, 32, %t3; \
+ STW %t3, [%dst - offset - 0x08] ASINORMAL;
+
+#define MOVE_LASTALIGNCHUNK(src, dst, offset, t0, t1) \
+ ASI_SETSRC_NOBLK \
+ LDX [%src - offset - 0x10] ASINORMAL, %t0; \
+ LDX [%src - offset - 0x08] ASINORMAL, %t1; \
+ ASI_SETDST_NOBLK \
+ STX %t0, [%dst - offset - 0x10] ASINORMAL; \
+ STX %t1, [%dst - offset - 0x08] ASINORMAL;
+
+#else /* !REGS_64BIT */
+
+#define MOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3) \
+ lduw [%src + offset + 0x00], %t0; \
+ lduw [%src + offset + 0x04], %t1; \
+ lduw [%src + offset + 0x08], %t2; \
+ lduw [%src + offset + 0x0c], %t3; \
+ stw %t0, [%dst + offset + 0x00]; \
+ stw %t1, [%dst + offset + 0x04]; \
+ stw %t2, [%dst + offset + 0x08]; \
+ stw %t3, [%dst + offset + 0x0c]; \
+ lduw [%src + offset + 0x10], %t0; \
+ lduw [%src + offset + 0x14], %t1; \
+ lduw [%src + offset + 0x18], %t2; \
+ lduw [%src + offset + 0x1c], %t3; \
+ stw %t0, [%dst + offset + 0x10]; \
+ stw %t1, [%dst + offset + 0x14]; \
+ stw %t2, [%dst + offset + 0x18]; \
+ stw %t3, [%dst + offset + 0x1c];
+
+#define MOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) \
+ lduw [%src - offset - 0x10], %t0; \
+ lduw [%src - offset - 0x0c], %t1; \
+ lduw [%src - offset - 0x08], %t2; \
+ lduw [%src - offset - 0x04], %t3; \
+ stw %t0, [%dst - offset - 0x10]; \
+ stw %t1, [%dst - offset - 0x0c]; \
+ stw %t2, [%dst - offset - 0x08]; \
+ stw %t3, [%dst - offset - 0x04];
+
+#endif /* !REGS_64BIT */
+
+#ifdef __KERNEL__
+ .section __ex_table,#alloc
+ .section .fixup,#alloc,#execinstr
+#endif
+
+ .text
+ .align 32
+ .globl memcpy
+ .type memcpy,@function
+
+ .globl bcopy
+ .type bcopy,@function
+
+#ifdef __KERNEL__
+ .globl __memcpy
+ .type __memcpy,@function
+
+ .globl __memcpy_384plus
+ .type __memcpy_384plus,@function
+
+ .globl __memcpy_16plus
+ .type __memcpy_16plus,@function
+
+ .globl __memcpy_short
+ .type __memcpy_short,@function
+
+ .globl __memcpy_entry
+ .type __memcpy_entry,@function
+
+ .globl copy_page
+ .type copy_page,@function
+
+memcpy_private:
+__memcpy:
+memcpy: mov ASI_BLK_P, asi_src ! IEU0 Group
+ brnz,pt %o2, __memcpy_entry ! CTI
+ mov ASI_BLK_P, asi_dest ! IEU1
+ retl
+ clr %o0
+
+copy_page: wr %g0, FPRS_FEF, %fprs ! FPU Group
+ sethi %hi(8192), %o2 ! IEU0 Group
+ mov ASI_BLK_P, asi_src ! IEU1
+ b,pt %xcc, dest_is_64byte_aligned ! CTI
+ mov ASI_BLK_COMMIT_P, asi_dest ! IEU0 Group
+
+ .align 32
+ .globl __copy_from_user
+ .type __copy_from_user,@function
+__copy_from_user:mov ASI_BLK_S, asi_src ! IEU0 Group
+ brnz,pt %o2, __memcpy_entry ! CTI
+ mov ASI_BLK_P, asi_dest ! IEU1
+
+ .globl __copy_to_user
+ .type __copy_to_user,@function
+__copy_to_user: mov ASI_BLK_P, asi_src ! IEU0 Group
+ brnz,pt %o2, __memcpy_entry ! CTI
+ mov ASI_BLK_S, asi_dest ! IEU1
+ retl ! CTI Group
+ clr %o0 ! IEU0 Group
+#endif
+
+bcopy: or %o0, 0, %g3 ! IEU0 Group
+ addcc %o1, 0, %o0 ! IEU1
+ brgez,pt %o2, memcpy_private ! CTI
+ or %g3, 0, %o1 ! IEU0 Group
+ retl ! CTI Group brk forced
+ clr %o0 ! IEU0
+
+
+ .align 32
+#ifdef __KERNEL__
+__memcpy_384plus:
+ andcc %o0, 7, %g2 ! IEU1 Group
+#endif
+VIS_enter:
+ be,pt %xcc, dest_is_8byte_aligned ! CTI
+ andcc %o0, 0x38, %g5 ! IEU1 Group
+do_dest_8byte_align:
+ mov 8, %g1 ! IEU0
+ sub %g1, %g2, %g2 ! IEU0 Group
+ andcc %o0, 1, %g0 ! IEU1
+ be,pt %icc, 2f ! CTI
+ sub %o2, %g2, %o2 ! IEU0 Group
+1: ASI_SETSRC_NOBLK ! LSU Group
+ EX(LDUB [%o1] ASINORMAL, %o5,
+ add %o2, %g2) ! Load Group
+ add %o1, 1, %o1 ! IEU0
+ add %o0, 1, %o0 ! IEU1
+ ASI_SETDST_NOBLK ! LSU Group
+ subcc %g2, 1, %g2 ! IEU1 Group
+ be,pn %xcc, 3f ! CTI
+ EX2(STB %o5, [%o0 - 1] ASINORMAL,
+ add %g2, 1, %g2,
+ add %o2, %g2) ! Store
+2: ASI_SETSRC_NOBLK ! LSU Group
+ EX(LDUB [%o1] ASINORMAL, %o5,
+ add %o2, %g2) ! Load Group
+ add %o0, 2, %o0 ! IEU0
+ EX(LDUB [%o1 + 1] ASINORMAL, %g3,
+ add %o2, %g2) ! Load Group
+ ASI_SETDST_NOBLK ! LSU Group
+ subcc %g2, 2, %g2 ! IEU1 Group
+ EX2(STB %o5, [%o0 - 2] ASINORMAL,
+ add %g2, 2, %g2,
+ add %o2, %g2) ! Store
+ add %o1, 2, %o1 ! IEU0
+ bne,pt %xcc, 2b ! CTI Group
+ EX2(STB %g3, [%o0 - 1] ASINORMAL,
+ add %g2, 1, %g2,
+ add %o2, %g2) ! Store
+3: andcc %o0, 0x38, %g5 ! IEU1 Group
+dest_is_8byte_aligned:
+ be,pt %icc, dest_is_64byte_aligned ! CTI
+#ifdef __KERNEL__
+ wr %g0, FPRS_FEF, %fprs ! FPU Group
+do_dest_64byte_align:
+ mov 64, %g1 ! IEU0 Group
+#else
+ mov 64, %g1 ! IEU0 Group
+do_dest_64byte_align:
+#endif
+ fmovd %f0, %f2 ! FPU
+ sub %g1, %g5, %g5 ! IEU0 Group
+ ASI_SETSRC_NOBLK ! LSU Group
+ alignaddr %o1, %g0, %g1 ! GRU Group
+ EXO2(LDDF [%g1] ASINORMAL, %f4) ! Load Group
+ sub %o2, %g5, %o2 ! IEU0
+1: EX(LDDF [%g1 + 0x8] ASINORMAL, %f6,
+ add %o2, %g5) ! Load Group
+ add %g1, 0x8, %g1 ! IEU0 Group
+ subcc %g5, 8, %g5 ! IEU1
+ ASI_SETDST_NOBLK ! LSU Group
+ faligndata %f4, %f6, %f0 ! GRU Group
+ EX2(STDF %f0, [%o0] ASINORMAL,
+ add %g5, 8, %g5,
+ add %o2, %g5) ! Store
+ add %o1, 8, %o1 ! IEU0 Group
+ be,pn %xcc, dest_is_64byte_aligned ! CTI
+ add %o0, 8, %o0 ! IEU1
+ ASI_SETSRC_NOBLK ! LSU Group
+ EX(LDDF [%g1 + 0x8] ASINORMAL, %f4,
+ add %o2, %g5) ! Load Group
+ add %g1, 8, %g1 ! IEU0
+ subcc %g5, 8, %g5 ! IEU1
+ ASI_SETDST_NOBLK ! LSU Group
+ faligndata %f6, %f4, %f0 ! GRU Group
+ EX2(STDF %f0, [%o0] ASINORMAL,
+ add %g5, 8, %g5,
+ add %o2, %g5) ! Store
+ add %o1, 8, %o1 ! IEU0
+ ASI_SETSRC_NOBLK ! LSU Group
+ bne,pt %xcc, 1b ! CTI Group
+ add %o0, 8, %o0 ! IEU0
+dest_is_64byte_aligned:
+ membar #LoadStore | #StoreStore | #StoreLoad ! LSU Group
+#ifndef __KERNEL__
+ wr %g0, ASI_BLK_P, %asi ! LSU Group
+#endif
+ subcc %o2, 0x40, %g7 ! IEU1 Group
+ mov %o1, %g1 ! IEU0
+ andncc %g7, (0x40 - 1), %g7 ! IEU1 Group
+ srl %g1, 3, %g2 ! IEU0
+ sub %o2, %g7, %g3 ! IEU0 Group
+ andn %o1, (0x40 - 1), %o1 ! IEU1
+ and %g2, 7, %g2 ! IEU0 Group
+ andncc %g3, 0x7, %g3 ! IEU1
+ fmovd %f0, %f2 ! FPU
+ sub %g3, 0x10, %g3 ! IEU0 Group
+ sub %o2, %g7, %o2 ! IEU1
+ alignaddr %g1, %g0, %g0 ! GRU Group
+ add %g1, %g7, %g1 ! IEU0 Group
+ subcc %o2, %g3, %o2 ! IEU1
+ ASI_SETSRC_BLK ! LSU Group
+ EXVIS1(LDBLK [%o1 + 0x00] ASIBLK, %f0) ! LSU Group
+ add %g1, %g3, %g1 ! IEU0
+ EXVIS1(LDBLK [%o1 + 0x40] ASIBLK, %f16) ! LSU Group
+ sub %g7, 0x80, %g7 ! IEU0
+ EXVIS(LDBLK [%o1 + 0x80] ASIBLK, %f32) ! LSU Group
+ ! Clk1 Group 8-(
+ ! Clk2 Group 8-(
+ ! Clk3 Group 8-(
+ ! Clk4 Group 8-(
+vispc: rd %pc, %g5 ! PDU Group 8-(
+ addcc %g5, %lo(vis00 - vispc), %g5 ! IEU1 Group
+ sll %g2, 9, %g2 ! IEU0
+ jmpl %g5 + %g2, %g0 ! CTI Group brk forced
+ addcc %o1, 0xc0, %o1 ! IEU1 Group
+ .align 512 /* OK, here comes the fun part... */
+vis00:FREG_FROB(f0, f2, f4, f6, f8, f10,f12,f14,f16) LOOP_CHUNK1(o1, o0, g7, vis01)
+ FREG_FROB(f16,f18,f20,f22,f24,f26,f28,f30,f32) LOOP_CHUNK2(o1, o0, g7, vis02)
+ FREG_FROB(f32,f34,f36,f38,f40,f42,f44,f46,f0) LOOP_CHUNK3(o1, o0, g7, vis03)
+ b,pt %xcc, vis00+4; faligndata %f0, %f2, %f48
+vis01:FREG_FROB(f16,f18,f20,f22,f24,f26,f28,f30,f32) STORE_SYNC(o0, f48) membar #Sync
+ FREG_FROB(f32,f34,f36,f38,f40,f42,f44,f46,f0) STORE_JUMP(o0, f48, finish_f0) membar #Sync
+vis02:FREG_FROB(f32,f34,f36,f38,f40,f42,f44,f46,f0) STORE_SYNC(o0, f48) membar #Sync
+ FREG_FROB(f0, f2, f4, f6, f8, f10,f12,f14,f16) STORE_JUMP(o0, f48, check_finish_f16) add %o2, %g3, %g7
+vis03:FREG_FROB(f0, f2, f4, f6, f8, f10,f12,f14,f16) STORE_SYNC(o0, f48) membar #Sync
+ FREG_FROB(f16,f18,f20,f22,f24,f26,f28,f30,f32) STORE_JUMP(o0, f48, finish_f32) membar #Sync
+ VISLOOP_PAD
+vis10:FREG_FROB(f2, f4, f6, f8, f10,f12,f14,f16,f18) LOOP_CHUNK1(o1, o0, g7, vis11)
+ FREG_FROB(f18,f20,f22,f24,f26,f28,f30,f32,f34) LOOP_CHUNK2(o1, o0, g7, vis12)
+ FREG_FROB(f34,f36,f38,f40,f42,f44,f46,f0, f2) LOOP_CHUNK3(o1, o0, g7, vis13)
+ b,pt %xcc, vis10+4; faligndata %f2, %f4, %f48
+vis11:FREG_FROB(f18,f20,f22,f24,f26,f28,f30,f32,f34) STORE_SYNC(o0, f48) membar #Sync
+ FREG_FROB(f34,f36,f38,f40,f42,f44,f46,f0, f2) STORE_JUMP(o0, f48, finish_f2) membar #Sync
+vis12:FREG_FROB(f34,f36,f38,f40,f42,f44,f46,f0, f2) STORE_SYNC(o0, f48) membar #Sync
+ FREG_FROB(f2, f4, f6, f8, f10,f12,f14,f16,f18) STORE_JUMP(o0, f48, finish_f18) membar #Sync
+vis13:FREG_FROB(f2, f4, f6, f8, f10,f12,f14,f16,f18) STORE_SYNC(o0, f48) membar #Sync
+ FREG_FROB(f18,f20,f22,f24,f26,f28,f30,f32,f34) STORE_JUMP(o0, f48, finish_f34) membar #Sync
+ VISLOOP_PAD
+vis20:FREG_FROB(f4, f6, f8, f10,f12,f14,f16,f18,f20) LOOP_CHUNK1(o1, o0, g7, vis21)
+ FREG_FROB(f20,f22,f24,f26,f28,f30,f32,f34,f36) LOOP_CHUNK2(o1, o0, g7, vis22)
+ FREG_FROB(f36,f38,f40,f42,f44,f46,f0, f2, f4) LOOP_CHUNK3(o1, o0, g7, vis23)
+ b,pt %xcc, vis20+4; faligndata %f4, %f6, %f48
+vis21:FREG_FROB(f20,f22,f24,f26,f28,f30,f32,f34,f36) STORE_SYNC(o0, f48) membar #Sync
+ FREG_FROB(f36,f38,f40,f42,f44,f46,f0, f2, f4) STORE_JUMP(o0, f48, finish_f4) membar #Sync
+vis22:FREG_FROB(f36,f38,f40,f42,f44,f46,f0, f2, f4) STORE_SYNC(o0, f48) membar #Sync
+ FREG_FROB(f4, f6, f8, f10,f12,f14,f16,f18,f20) STORE_JUMP(o0, f48, finish_f20) membar #Sync
+vis23:FREG_FROB(f4, f6, f8, f10,f12,f14,f16,f18,f20) STORE_SYNC(o0, f48) membar #Sync
+ FREG_FROB(f20,f22,f24,f26,f28,f30,f32,f34,f36) STORE_JUMP(o0, f48, finish_f36) membar #Sync
+ VISLOOP_PAD
+vis30:FREG_FROB(f6, f8, f10,f12,f14,f16,f18,f20,f22) LOOP_CHUNK1(o1, o0, g7, vis31)
+ FREG_FROB(f22,f24,f26,f28,f30,f32,f34,f36,f38) LOOP_CHUNK2(o1, o0, g7, vis32)
+ FREG_FROB(f38,f40,f42,f44,f46,f0, f2, f4, f6) LOOP_CHUNK3(o1, o0, g7, vis33)
+ b,pt %xcc, vis30+4; faligndata %f6, %f8, %f48
+vis31:FREG_FROB(f22,f24,f26,f28,f30,f32,f34,f36,f38) STORE_SYNC(o0, f48) membar #Sync
+ FREG_FROB(f38,f40,f42,f44,f46,f0, f2, f4, f6) STORE_JUMP(o0, f48, finish_f6) membar #Sync
+vis32:FREG_FROB(f38,f40,f42,f44,f46,f0, f2, f4, f6) STORE_SYNC(o0, f48) membar #Sync
+ FREG_FROB(f6, f8, f10,f12,f14,f16,f18,f20,f22) STORE_JUMP(o0, f48, finish_f22) membar #Sync
+vis33:FREG_FROB(f6, f8, f10,f12,f14,f16,f18,f20,f22) STORE_SYNC(o0, f48) membar #Sync
+ FREG_FROB(f22,f24,f26,f28,f30,f32,f34,f36,f38) STORE_JUMP(o0, f48, finish_f38) membar #Sync
+ VISLOOP_PAD
+vis40:FREG_FROB(f8, f10,f12,f14,f16,f18,f20,f22,f24) LOOP_CHUNK1(o1, o0, g7, vis41)
+ FREG_FROB(f24,f26,f28,f30,f32,f34,f36,f38,f40) LOOP_CHUNK2(o1, o0, g7, vis42)
+ FREG_FROB(f40,f42,f44,f46,f0, f2, f4, f6, f8) LOOP_CHUNK3(o1, o0, g7, vis43)
+ b,pt %xcc, vis40+4; faligndata %f8, %f10, %f48
+vis41:FREG_FROB(f24,f26,f28,f30,f32,f34,f36,f38,f40) STORE_SYNC(o0, f48) membar #Sync
+ FREG_FROB(f40,f42,f44,f46,f0, f2, f4, f6, f8) STORE_JUMP(o0, f48, finish_f8) membar #Sync
+vis42:FREG_FROB(f40,f42,f44,f46,f0, f2, f4, f6, f8) STORE_SYNC(o0, f48) membar #Sync
+ FREG_FROB(f8, f10,f12,f14,f16,f18,f20,f22,f24) STORE_JUMP(o0, f48, finish_f24) membar #Sync
+vis43:FREG_FROB(f8, f10,f12,f14,f16,f18,f20,f22,f24) STORE_SYNC(o0, f48) membar #Sync
+ FREG_FROB(f24,f26,f28,f30,f32,f34,f36,f38,f40) STORE_JUMP(o0, f48, finish_f40) membar #Sync
+ VISLOOP_PAD
+vis50:FREG_FROB(f10,f12,f14,f16,f18,f20,f22,f24,f26) LOOP_CHUNK1(o1, o0, g7, vis51)
+ FREG_FROB(f26,f28,f30,f32,f34,f36,f38,f40,f42) LOOP_CHUNK2(o1, o0, g7, vis52)
+ FREG_FROB(f42,f44,f46,f0, f2, f4, f6, f8, f10) LOOP_CHUNK3(o1, o0, g7, vis53)
+ b,pt %xcc, vis50+4; faligndata %f10, %f12, %f48
+vis51:FREG_FROB(f26,f28,f30,f32,f34,f36,f38,f40,f42) STORE_SYNC(o0, f48) membar #Sync
+ FREG_FROB(f42,f44,f46,f0, f2, f4, f6, f8, f10) STORE_JUMP(o0, f48, finish_f10) membar #Sync
+vis52:FREG_FROB(f42,f44,f46,f0, f2, f4, f6, f8, f10) STORE_SYNC(o0, f48) membar #Sync
+ FREG_FROB(f10,f12,f14,f16,f18,f20,f22,f24,f26) STORE_JUMP(o0, f48, finish_f26) membar #Sync
+vis53:FREG_FROB(f10,f12,f14,f16,f18,f20,f22,f24,f26) STORE_SYNC(o0, f48) membar #Sync
+ FREG_FROB(f26,f28,f30,f32,f34,f36,f38,f40,f42) STORE_JUMP(o0, f48, finish_f42) membar #Sync
+ VISLOOP_PAD
+vis60:FREG_FROB(f12,f14,f16,f18,f20,f22,f24,f26,f28) LOOP_CHUNK1(o1, o0, g7, vis61)
+ FREG_FROB(f28,f30,f32,f34,f36,f38,f40,f42,f44) LOOP_CHUNK2(o1, o0, g7, vis62)
+ FREG_FROB(f44,f46,f0, f2, f4, f6, f8, f10,f12) LOOP_CHUNK3(o1, o0, g7, vis63)
+ b,pt %xcc, vis60+4; faligndata %f12, %f14, %f48
+vis61:FREG_FROB(f28,f30,f32,f34,f36,f38,f40,f42,f44) STORE_SYNC(o0, f48) membar #Sync
+ FREG_FROB(f44,f46,f0, f2, f4, f6, f8, f10,f12) STORE_JUMP(o0, f48, finish_f12) membar #Sync
+vis62:FREG_FROB(f44,f46,f0, f2, f4, f6, f8, f10,f12) STORE_SYNC(o0, f48) membar #Sync
+ FREG_FROB(f12,f14,f16,f18,f20,f22,f24,f26,f28) STORE_JUMP(o0, f48, finish_f28) membar #Sync
+vis63:FREG_FROB(f12,f14,f16,f18,f20,f22,f24,f26,f28) STORE_SYNC(o0, f48) membar #Sync
+ FREG_FROB(f28,f30,f32,f34,f36,f38,f40,f42,f44) STORE_JUMP(o0, f48, finish_f44) membar #Sync
+ VISLOOP_PAD
+vis70:FREG_FROB(f14,f16,f18,f20,f22,f24,f26,f28,f30) LOOP_CHUNK1(o1, o0, g7, vis71)
+ FREG_FROB(f30,f32,f34,f36,f38,f40,f42,f44,f46) LOOP_CHUNK2(o1, o0, g7, vis72)
+ FREG_FROB(f46,f0, f2, f4, f6, f8, f10,f12,f14) LOOP_CHUNK3(o1, o0, g7, vis73)
+ b,pt %xcc, vis70+4; faligndata %f14, %f16, %f48
+vis71:FREG_FROB(f30,f32,f34,f36,f38,f40,f42,f44,f46) STORE_SYNC(o0, f48) membar #Sync
+ FREG_FROB(f46,f0, f2, f4, f6, f8, f10,f12,f14) STORE_JUMP(o0, f48, finish_f14) membar #Sync
+vis72:FREG_FROB(f46,f0, f2, f4, f6, f8, f10,f12,f14) STORE_SYNC(o0, f48) membar #Sync
+ FREG_FROB(f14,f16,f18,f20,f22,f24,f26,f28,f30) STORE_JUMP(o0, f48, finish_f30) membar #Sync
+vis73:FREG_FROB(f14,f16,f18,f20,f22,f24,f26,f28,f30) STORE_SYNC(o0, f48) membar #Sync
+ FREG_FROB(f30,f32,f34,f36,f38,f40,f42,f44,f46) STORE_JUMP(o0, f48, finish_f46) membar #Sync
+ VISLOOP_PAD
+finish_f0: FINISH_VISCHUNK(o0, f0, f2, g3)
+finish_f2: FINISH_VISCHUNK(o0, f2, f4, g3)
+finish_f4: FINISH_VISCHUNK(o0, f4, f6, g3)
+finish_f6: FINISH_VISCHUNK(o0, f6, f8, g3)
+finish_f8: FINISH_VISCHUNK(o0, f8, f10, g3)
+finish_f10: FINISH_VISCHUNK(o0, f10, f12, g3)
+finish_f12: FINISH_VISCHUNK(o0, f12, f14, g3)
+finish_f14: UNEVEN_VISCHUNK(o0, f14, f0, g3)
+/* This is a special hack to speed up 8K page copy */
+check_finish_f16:
+ andcc %g1, 7, %g0
+ bne,pn %icc, finish_f16
+ cmp %g7, 0x40
+ bne,pn %icc, finish_f16
+ FREG_FROB(f16,f18,f20,f22,f24,f26,f28,f30,f32)
+ membar #Sync
+ EXVIS1(STBLK %f48, [%o0] ASIBLK)
+ b,pt %xcc, vis_ret
+finish_f16: membar #Sync
+ FINISH_VISCHUNK(o0, f16, f18, g3)
+finish_f18: FINISH_VISCHUNK(o0, f18, f20, g3)
+finish_f20: FINISH_VISCHUNK(o0, f20, f22, g3)
+finish_f22: FINISH_VISCHUNK(o0, f22, f24, g3)
+finish_f24: FINISH_VISCHUNK(o0, f24, f26, g3)
+finish_f26: FINISH_VISCHUNK(o0, f26, f28, g3)
+finish_f28: FINISH_VISCHUNK(o0, f28, f30, g3)
+finish_f30: UNEVEN_VISCHUNK(o0, f30, f0, g3)
+finish_f32: FINISH_VISCHUNK(o0, f32, f34, g3)
+finish_f34: FINISH_VISCHUNK(o0, f34, f36, g3)
+finish_f36: FINISH_VISCHUNK(o0, f36, f38, g3)
+finish_f38: FINISH_VISCHUNK(o0, f38, f40, g3)
+finish_f40: FINISH_VISCHUNK(o0, f40, f42, g3)
+finish_f42: FINISH_VISCHUNK(o0, f42, f44, g3)
+finish_f44: FINISH_VISCHUNK(o0, f44, f46, g3)
+finish_f46: UNEVEN_VISCHUNK(o0, f46, f0, g3)
+vis_slk:ASI_SETSRC_NOBLK ! LSU Group
+ EXVIS4(LDDF [%o1] ASINORMAL, %f2) ! Load Group
+ add %o1, 8, %o1 ! IEU0
+ subcc %g3, 8, %g3 ! IEU1
+ ASI_SETDST_NOBLK ! LSU Group
+ faligndata %f0, %f2, %f8 ! GRU Group
+ EXVIS5(STDF %f8, [%o0] ASINORMAL) ! Store
+ bl,pn %xcc, vis_out ! CTI
+ add %o0, 8, %o0 ! IEU0 Group
+ ASI_SETSRC_NOBLK ! LSU Group
+ EXVIS4(LDDF [%o1] ASINORMAL, %f0) ! Load Group
+ add %o1, 8, %o1 ! IEU0
+ subcc %g3, 8, %g3 ! IEU1
+ ASI_SETDST_NOBLK ! LSU Group
+ faligndata %f2, %f0, %f8 ! GRU Group
+ EXVIS5(STDF %f8, [%o0] ASINORMAL) ! Store
+ bge,pt %xcc, vis_slk ! CTI
+ add %o0, 8, %o0 ! IEU0 Group
+vis_out:brz,pt %o2, vis_ret ! CTI Group
+ mov %g1, %o1 ! IEU0
+vis_slp:ASI_SETSRC_NOBLK ! LSU Group
+ EXO2(LDUB [%o1] ASINORMAL, %g5) ! LOAD
+ add %o1, 1, %o1 ! IEU0
+ add %o0, 1, %o0 ! IEU1
+ ASI_SETDST_NOBLK ! LSU Group
+ subcc %o2, 1, %o2 ! IEU1
+ bne,pt %xcc, vis_slp ! CTI
+ EX(STB %g5, [%o0 - 1] ASINORMAL,
+ add %o2, 1) ! Store Group
+vis_ret:membar #StoreLoad | #StoreStore ! LSU Group
+ FPU_CLEAN_RETL
+
+
+__memcpy_short:
+ andcc %o2, 1, %g0 ! IEU1 Group
+ be,pt %icc, 2f ! CTI
+1: ASI_SETSRC_NOBLK ! LSU Group
+ EXO2(LDUB [%o1] ASINORMAL, %g5) ! LOAD Group
+ add %o1, 1, %o1 ! IEU0
+ add %o0, 1, %o0 ! IEU1
+ ASI_SETDST_NOBLK ! LSU Group
+ subcc %o2, 1, %o2 ! IEU1 Group
+ be,pn %xcc, short_ret ! CTI
+ EX(STB %g5, [%o0 - 1] ASINORMAL,
+ add %o2, 1) ! Store
+2: ASI_SETSRC_NOBLK ! LSU Group
+ EXO2(LDUB [%o1] ASINORMAL, %g5) ! LOAD Group
+ add %o0, 2, %o0 ! IEU0
+ EXO2(LDUB [%o1 + 1] ASINORMAL, %o5) ! LOAD Group
+ add %o1, 2, %o1 ! IEU0
+ ASI_SETDST_NOBLK ! LSU Group
+ subcc %o2, 2, %o2 ! IEU1 Group
+ EX(STB %g5, [%o0 - 2] ASINORMAL,
+ add %o2, 2) ! Store
+ bne,pt %xcc, 2b ! CTI
+ EX(STB %o5, [%o0 - 1] ASINORMAL,
+ add %o2, 1) ! Store
+short_ret:
+ NORMAL_RETL
+
+#ifndef __KERNEL__
+memcpy_private:
+memcpy:
+#ifndef REGS_64BIT
+ srl %o2, 0, %o2 ! IEU1 Group
+#endif
+ brz,pn %o2, short_ret ! CTI Group
+ mov %o0, %g6 ! IEU0
+#endif
+__memcpy_entry:
+ cmp %o2, 15 ! IEU1 Group
+ bleu,pn %xcc, __memcpy_short ! CTI
+ cmp %o2, (64 * 6) ! IEU1 Group
+ bgeu,pn %xcc, VIS_enter ! CTI
+#ifdef __KERNEL__
+__memcpy_16plus:
+#endif
+ andcc %o0, 7, %g2 ! IEU1 Group
+ sub %o0, %o1, %g5 ! IEU0
+ andcc %g5, 3, %o5 ! IEU1 Group
+ bne,pn %xcc, memcpy_noVIS_misaligned ! CTI
+ andcc %o1, 3, %g0 ! IEU1 Group
+#ifdef REGS_64BIT
+ be,a,pt %xcc, 3f ! CTI
+ andcc %o1, 4, %g0 ! IEU1 Group
+ andcc %o1, 1, %g0 ! IEU1 Group
+#else /* !REGS_64BIT */
+ be,pt %xcc, 5f ! CTI
+ andcc %o1, 1, %g0 ! IEU1 Group
+#endif /* !REGS_64BIT */
+ be,pn %xcc, 4f ! CTI
+ andcc %o1, 2, %g0 ! IEU1 Group
+ ASI_SETSRC_NOBLK ! LSU Group
+ EXO2(LDUB [%o1] ASINORMAL, %g2) ! Load Group
+ add %o1, 1, %o1 ! IEU0
+ add %o0, 1, %o0 ! IEU1
+ sub %o2, 1, %o2 ! IEU0 Group
+ ASI_SETDST_NOBLK ! LSU Group
+ bne,pn %xcc, 5f ! CTI Group
+ EX(STB %g2, [%o0 - 1] ASINORMAL,
+ add %o2, 1) ! Store
+4: ASI_SETSRC_NOBLK ! LSU Group
+ EXO2(LDUH [%o1] ASINORMAL, %g2) ! Load Group
+ add %o1, 2, %o1 ! IEU0
+ add %o0, 2, %o0 ! IEU1
+ ASI_SETDST_NOBLK ! LSU Group
+ sub %o2, 2, %o2 ! IEU0
+ EX(STH %g2, [%o0 - 2] ASINORMAL,
+ add %o2, 2) ! Store Group + bubble
+#ifdef REGS_64BIT
+5: andcc %o1, 4, %g0 ! IEU1
+3: be,a,pn %xcc, 2f ! CTI
+ andcc %o2, -128, %g7 ! IEU1 Group
+ ASI_SETSRC_NOBLK ! LSU Group
+ EXO2(LDUW [%o1] ASINORMAL, %g5) ! Load Group
+ add %o1, 4, %o1 ! IEU0
+ add %o0, 4, %o0 ! IEU1
+ ASI_SETDST_NOBLK ! LSU Group
+ sub %o2, 4, %o2 ! IEU0 Group
+ EX(STW %g5, [%o0 - 4] ASINORMAL,
+ add %o2, 4) ! Store
+ andcc %o2, -128, %g7 ! IEU1 Group
+2: be,pn %xcc, 3f ! CTI
+ andcc %o0, 4, %g0 ! IEU1 Group
+ be,pn %xcc, 82f + 4 ! CTI Group
+#else /* !REGS_64BIT */
+5: andcc %o2, -128, %g7 ! IEU1
+ be,a,pn %xcc, 41f ! CTI
+ andcc %o2, 0x70, %g7 ! IEU1 Group
+#endif /* !REGS_64BIT */
+5: MOVE_BIGCHUNK(o1, o0, 0x00, g1, g3, g5, o5)
+ MOVE_BIGCHUNK(o1, o0, 0x20, g1, g3, g5, o5)
+ MOVE_BIGCHUNK(o1, o0, 0x40, g1, g3, g5, o5)
+ MOVE_BIGCHUNK(o1, o0, 0x60, g1, g3, g5, o5)
+ EXT(5b,35f,VIScopyfixup1)
+35: subcc %g7, 128, %g7 ! IEU1 Group
+ add %o1, 128, %o1 ! IEU0
+ bne,pt %xcc, 5b ! CTI
+ add %o0, 128, %o0 ! IEU0 Group
+3: andcc %o2, 0x70, %g7 ! IEU1 Group
+41: be,pn %xcc, 80f ! CTI
+ andcc %o2, 8, %g0 ! IEU1 Group
+ ! Clk1 8-(
+ ! Clk2 8-(
+ ! Clk3 8-(
+ ! Clk4 8-(
+79: rd %pc, %o5 ! PDU Group
+#ifdef __KERNEL__
+ sll %g7, 1, %g5 ! IEU0 Group
+ add %o1, %g7, %o1 ! IEU1
+ srl %g7, 1, %g2 ! IEU0 Group
+ sub %o5, %g5, %o5 ! IEU1
+ sub %o5, %g2, %o5 ! IEU0 Group
+ jmpl %o5 + %lo(80f - 79b), %g0 ! CTI Group brk forced
+ add %o0, %g7, %o0 ! IEU0 Group
+#else
+ sll %g7, 1, %g5 ! IEU0 Group
+ add %o1, %g7, %o1 ! IEU1
+ sub %o5, %g5, %o5 ! IEU0 Group
+ jmpl %o5 + %lo(80f - 79b), %g0 ! CTI Group brk forced
+ add %o0, %g7, %o0 ! IEU0 Group
+#endif
+36: MOVE_LASTCHUNK(o1, o0, 0x60, g2, g3, g5, o5)
+ MOVE_LASTCHUNK(o1, o0, 0x50, g2, g3, g5, o5)
+ MOVE_LASTCHUNK(o1, o0, 0x40, g2, g3, g5, o5)
+ MOVE_LASTCHUNK(o1, o0, 0x30, g2, g3, g5, o5)
+ MOVE_LASTCHUNK(o1, o0, 0x20, g2, g3, g5, o5)
+ MOVE_LASTCHUNK(o1, o0, 0x10, g2, g3, g5, o5)
+ MOVE_LASTCHUNK(o1, o0, 0x00, g2, g3, g5, o5)
+ EXT(36b,80f,VIScopyfixup2)
+80: be,pt %xcc, 81f ! CTI
+ andcc %o2, 4, %g0 ! IEU1
+#ifdef REGS_64BIT
+ ASI_SETSRC_NOBLK ! LSU Group
+ EX(LDX [%o1] ASINORMAL, %g2,
+ and %o2, 0xf) ! Load Group
+ add %o0, 8, %o0 ! IEU0
+ ASI_SETDST_NOBLK ! LSU Group
+ EX(STW %g2, [%o0 - 0x4] ASINORMAL,
+ and %o2, 0xf) ! Store Group
+ add %o1, 8, %o1 ! IEU1
+ srlx %g2, 32, %g2 ! IEU0 Group
+ EX2(STW %g2, [%o0 - 0x8] ASINORMAL,
+ and %o2, 0xf, %o2,
+ sub %o2, 4) ! Store
+#else /* !REGS_64BIT */
+ lduw [%o1], %g2 ! Load Group
+ add %o0, 8, %o0 ! IEU0
+ lduw [%o1 + 0x4], %g3 ! Load Group
+ add %o1, 8, %o1 ! IEU0
+ stw %g2, [%o0 - 0x8] ! Store Group
+ stw %g3, [%o0 - 0x4] ! Store Group
+#endif /* !REGS_64BIT */
+81: be,pt %xcc, 1f ! CTI
+ andcc %o2, 2, %g0 ! IEU1 Group
+ ASI_SETSRC_NOBLK ! LSU Group
+ EX(LDUW [%o1] ASINORMAL, %g2,
+ and %o2, 0x7) ! Load Group
+ add %o1, 4, %o1 ! IEU0
+ ASI_SETDST_NOBLK ! LSU Group
+ EX(STW %g2, [%o0] ASINORMAL,
+ and %o2, 0x7) ! Store Group
+ add %o0, 4, %o0 ! IEU0
+1: be,pt %xcc, 1f ! CTI
+ andcc %o2, 1, %g0 ! IEU1 Group
+ ASI_SETSRC_NOBLK ! LSU Group
+ EX(LDUH [%o1] ASINORMAL, %g2,
+ and %o2, 0x3) ! Load Group
+ add %o1, 2, %o1 ! IEU0
+ ASI_SETDST_NOBLK ! LSU Group
+ EX(STH %g2, [%o0] ASINORMAL,
+ and %o2, 0x3) ! Store Group
+ add %o0, 2, %o0 ! IEU0
+1: be,pt %xcc, normal_retl ! CTI
+ nop ! IEU1
+ ASI_SETSRC_NOBLK ! LSU Group
+ EX(LDUB [%o1] ASINORMAL, %g2,
+ add %g0, 1) ! Load Group
+ ASI_SETDST_NOBLK ! LSU Group
+ EX(STB %g2, [%o0] ASINORMAL,
+ add %g0, 1) ! Store Group + bubble
+normal_retl:
+ NORMAL_RETL
+
+#ifdef REGS_64BIT
+82: MOVE_BIGALIGNCHUNK(o1, o0, 0x00, g1, g3, g5, o5)
+ MOVE_BIGALIGNCHUNK(o1, o0, 0x40, g1, g3, g5, o5)
+ EXT(82b,37f,VIScopyfixup3)
+37: subcc %g7, 128, %g7 ! IEU1 Group
+ add %o1, 128, %o1 ! IEU0
+ bne,pt %xcc, 82b ! CTI
+ add %o0, 128, %o0 ! IEU0 Group
+ andcc %o2, 0x70, %g7 ! IEU1
+ be,pn %xcc, 84f ! CTI
+ andcc %o2, 8, %g0 ! IEU1 Group
+ ! Clk1 8-(
+ ! Clk2 8-(
+ ! Clk3 8-(
+ ! Clk4 8-(
+83: rd %pc, %o5 ! PDU Group
+#ifdef __KERNEL__
+ srl %g7, 1, %g5 ! IEU0 Group
+ add %g7, %g5, %g5 ! IEU0 Group
+ add %o1, %g7, %o1 ! IEU1
+ sub %o5, %g5, %o5 ! IEU0 Group
+ jmpl %o5 + %lo(84f - 83b), %g0 ! CTI Group brk forced
+ add %o0, %g7, %o0 ! IEU0 Group
+#else
+ add %o1, %g7, %o1 ! IEU0 Group
+ sub %o5, %g7, %o5 ! IEU1
+ jmpl %o5 + %lo(84f - 83b), %g0 ! CTI Group brk forced
+ add %o0, %g7, %o0 ! IEU0 Group
+#endif
+38: MOVE_LASTALIGNCHUNK(o1, o0, 0x60, g2, g3)
+ MOVE_LASTALIGNCHUNK(o1, o0, 0x50, g2, g3)
+ MOVE_LASTALIGNCHUNK(o1, o0, 0x40, g2, g3)
+ MOVE_LASTALIGNCHUNK(o1, o0, 0x30, g2, g3)
+ MOVE_LASTALIGNCHUNK(o1, o0, 0x20, g2, g3)
+ MOVE_LASTALIGNCHUNK(o1, o0, 0x10, g2, g3)
+ MOVE_LASTALIGNCHUNK(o1, o0, 0x00, g2, g3)
+ EXT(38b,84f,VIScopyfixup4)
+84: be,pt %xcc, 85f ! CTI Group
+ andcc %o2, 4, %g0 ! IEU1
+ ASI_SETSRC_NOBLK ! LSU Group
+ EX(LDX [%o1] ASINORMAL, %g2,
+ and %o2, 0xf) ! Load Group
+ add %o1, 8, %o1 ! IEU0
+ ASI_SETDST_NOBLK ! LSU Group
+ add %o0, 8, %o0 ! IEU0 Group
+ EX(STX %g2, [%o0 - 0x8] ASINORMAL,
+ and %o2, 0xf) ! Store
+85: be,pt %xcc, 1f ! CTI
+ andcc %o2, 2, %g0 ! IEU1 Group
+ ASI_SETSRC_NOBLK ! LSU Group
+ EX(LDUW [%o1] ASINORMAL, %g2,
+ and %o2, 0x7) ! Load Group
+ add %o1, 4, %o1 ! IEU0
+ ASI_SETDST_NOBLK ! LSU Group
+ add %o0, 4, %o0 ! IEU0 Group
+ EX(STW %g2, [%o0 - 0x4] ASINORMAL,
+ and %o2, 0x7) ! Store
+1: be,pt %xcc, 1f ! CTI
+ andcc %o2, 1, %g0 ! IEU1 Group
+ ASI_SETSRC_NOBLK ! LSU Group
+ EX(LDUH [%o1] ASINORMAL, %g2,
+ and %o2, 0x3) ! Load Group
+ add %o1, 2, %o1 ! IEU0
+ ASI_SETDST_NOBLK ! LSU Group
+ add %o0, 2, %o0 ! IEU0 Group
+ EX(STH %g2, [%o0 - 0x2] ASINORMAL,
+ and %o2, 0x3) ! Store
+1: be,pt %xcc, 1f ! CTI
+ nop ! IEU0 Group
+ ASI_SETSRC_NOBLK ! LSU Group
+ EX(LDUB [%o1] ASINORMAL, %g2,
+ add %g0, 1) ! Load Group
+ ASI_SETDST_NOBLK ! LSU Group
+ EX(STB %g2, [%o0] ASINORMAL,
+ add %g0, 1) ! Store Group + bubble
+1: NORMAL_RETL
+#endif /* REGS_64BIT */
+
+memcpy_noVIS_misaligned:
+ brz,pt %g2, 2f ! CTI Group
+ mov 8, %g1 ! IEU0
+ sub %g1, %g2, %g2 ! IEU0 Group
+ sub %o2, %g2, %o2 ! IEU0 Group
+1: ASI_SETSRC_NOBLK ! LSU Group
+ EX(LDUB [%o1] ASINORMAL, %g5,
+ add %o2, %g2) ! Load Group
+ add %o1, 1, %o1 ! IEU0
+ add %o0, 1, %o0 ! IEU1
+ ASI_SETDST_NOBLK ! LSU Group
+ subcc %g2, 1, %g2 ! IEU1 Group
+ bne,pt %xcc, 1b ! CTI
+ EX2(STB %g5, [%o0 - 1] ASINORMAL,
+ add %o2, %g2, %o2,
+ add %o2, 1) ! Store
+2:
+#ifdef __KERNEL__
+ wr %g0, FPRS_FEF, %fprs ! FPU Group
+#endif
+ andn %o2, 7, %g5 ! IEU0 Group
+ and %o2, 7, %o2 ! IEU1
+ fmovd %f0, %f2 ! FPU
+ ASI_SETSRC_NOBLK ! LSU Group
+ alignaddr %o1, %g0, %g1 ! GRU Group
+ EXO2(LDDF [%g1] ASINORMAL, %f4) ! Load Group
+1: EX(LDDF [%g1 + 0x8] ASINORMAL, %f6,
+ add %o2, %g5) ! Load Group
+ add %g1, 0x8, %g1 ! IEU0 Group
+ subcc %g5, 8, %g5 ! IEU1
+ ASI_SETDST_NOBLK ! LSU Group
+ faligndata %f4, %f6, %f0 ! GRU Group
+ EX2(STDF %f0, [%o0] ASINORMAL,
+ add %o2, %g5, %o2,
+ add %o2, 8) ! Store
+ add %o1, 8, %o1 ! IEU0 Group
+ be,pn %xcc, end_cruft ! CTI
+ add %o0, 8, %o0 ! IEU1
+ ASI_SETSRC_NOBLK ! LSU Group
+ EX(LDDF [%g1 + 0x8] ASINORMAL, %f4,
+ add %o2, %g5) ! Load Group
+ add %g1, 8, %g1 ! IEU0
+ subcc %g5, 8, %g5 ! IEU1
+ ASI_SETDST_NOBLK ! LSU Group
+ faligndata %f6, %f4, %f0 ! GRU Group
+ EX2(STDF %f0, [%o0] ASINORMAL,
+ add %o2, %g5, %o2,
+ add %o2, 8) ! Store
+ add %o1, 8, %o1 ! IEU0
+ ASI_SETSRC_NOBLK ! LSU Group
+ bne,pn %xcc, 1b ! CTI Group
+ add %o0, 8, %o0 ! IEU0
+end_cruft:
+ brz,pn %o2, fpu_retl ! CTI Group
+#ifndef __KERNEL__
+ nop ! IEU0
+#else
+ ASI_SETSRC_NOBLK ! LSU Group
+#endif
+ EXO2(LDUB [%o1] ASINORMAL, %g5) ! LOAD
+ add %o1, 1, %o1 ! IEU0
+ add %o0, 1, %o0 ! IEU1
+ ASI_SETDST_NOBLK ! LSU Group
+ subcc %o2, 1, %o2 ! IEU1
+ bne,pt %xcc, vis_slp ! CTI
+ EX(STB %g5, [%o0 - 1] ASINORMAL,
+ add %o2, 1) ! Store Group
+fpu_retl:
+ FPU_RETL
+
+#ifdef __KERNEL__
+ .section .fixup
+ .align 4
+VIScopyfixup_reto2:
+ mov %o2, %o0
+VIScopyfixup_ret:
+ retl
+ wr %g0, 0, %fprs
+VIScopyfixup1: subcc %g2, 18, %g2
+ bgeu,a,pt %icc, VIScopyfixup1
+ sub %g7, 32, %g7
+ rd %pc, %g5
+ add %g2, 18, %g2
+ add %g2, 20, %g2
+ ldub [%g5 + %g2], %g2
+ ba,a,pt %xcc, 2f
+.byte 0, 0, 0, 0, 0, 0, 0, 4, 4, 8, 12, 12, 16, 20, 20, 24, 28, 28
+ .align 4
+VIScopyfixup2: mov (7 * 16), %g7
+1: subcc %g2, 10, %g2
+ bgeu,a,pt %icc, 1b
+ sub %g7, 16, %g7
+ rd %pc, %g5
+ add %g2, 10, %g2
+ add %g2, 20, %g2
+ ldub [%g5 + %g2], %g2
+ ba,a,pt %xcc, 4f
+.byte 0, 0, 0, 0, 0, 4, 4, 8, 12, 12
+ .align 4
+VIScopyfixup3: subcc %g2, 10, %g2
+ bgeu,a,pt %icc, VIScopyfixup3
+ sub %g7, 32, %g7
+ rd %pc, %g5
+ add %g2, 10, %g2
+ add %g2, 20, %g2
+ ldub [%g5 + %g2], %g2
+ ba,a,pt %xcc, 2f
+.byte 0, 0, 0, 0, 0, 0, 0, 8, 16, 24
+ .align 4
+2: and %g1, 0x7f, %g1
+ sub %g7, %g2, %g7
+ ba,pt %xcc, VIScopyfixup_ret
+ add %g7, %g1, %o0
+VIScopyfixup4: mov (7 * 16), %g7
+3: subcc %g2, 6, %g2
+ bgeu,a,pt %icc, 3b
+ sub %g7, 16, %g7
+ rd %pc, %g5
+ add %g2, 6, %g2
+ add %g2, 20, %g2
+ ldub [%g5 + %g2], %g2
+ ba,a,pt %xcc, 4f
+.byte 0, 0, 0, 0, 0, 8
+ .align 4
+4: and %g1, 7, %g1
+ ba,pt %xcc, VIScopyfixup_ret
+ add %g7, %g1, %o0
+VIScopyfixup_vis3:
+ sub %o2, 0x80, %o2
+VIScopyfixup_vis2:
+ add %o2, 0x40, %o2
+VIScopyfixup_vis0:
+ add %o2, 0x80, %o2
+VIScopyfixup_vis1:
+ add %g7, %g3, %g7
+ ba,pt %xcc, VIScopyfixup_ret
+ add %o2, %g7, %o0
+VIScopyfixup_vis5:
+ add %g3, 8, %g3
+VIScopyfixup_vis4:
+ add %g3, 8, %g3
+ ba,pt %xcc, VIScopyfixup_ret
+ add %o2, %g3, %o0
+#endif
+
+#ifdef __KERNEL__
+ .text
+ .align 32
+
+ .globl __memmove
+ .type __memmove,@function
+
+ .globl memmove
+ .type memmove,@function
+
+memmove:
+__memmove: cmp %o0, %o1
+ blu,pt %xcc, memcpy_private
+ sub %o0, %o1, %g5
+ add %o1, %o2, %g3
+ cmp %g3, %o0
+ bleu,pt %xcc, memcpy_private
+ add %o1, %o2, %g5
+ add %o0, %o2, %o5
+
+ sub %g5, 1, %o1
+ sub %o5, 1, %o0
+1: ldub [%o1], %g5
+ subcc %o2, 1, %o2
+ sub %o1, 1, %o1
+ stb %g5, [%o0]
+ bne,pt %icc, 1b
+ sub %o0, 1, %o0
+
+ retl
+ clr %o0
+#endif
--- /dev/null
+/* $Id: VISmemset.S,v 1.4 1997/07/02 19:00:39 jj Exp $
+ * VISmemset.S: High speed memset operations utilizing the UltraSparc
+ * Visual Instruction Set.
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996, 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include "VIS.h"
+
+#ifdef REGS_64BIT
+#define SET_BLOCKS(base, offset, source) \
+ stx source, [base - offset - 0x18]; \
+ stx source, [base - offset - 0x10]; \
+ stx source, [base - offset - 0x08]; \
+ stx source, [base - offset - 0x00];
+#else
+#define SET_BLOCKS(base, offset, source) \
+ stw source, [base - offset - 0x18]; \
+ stw source, [base - offset - 0x14]; \
+ stw source, [base - offset - 0x10]; \
+ stw source, [base - offset - 0x0c]; \
+ stw source, [base - offset - 0x08]; \
+ stw source, [base - offset - 0x04]; \
+ stw source, [base - offset - 0x00]; \
+ stw source, [base - offset + 0x04];
+#endif
+
+#ifndef __KERNEL__
+/* So that the brz,a,pt in memset doesn't have to get through PLT, here we go... */
+#include "VISbzero.S"
+#endif
+
+#ifdef __KERNEL__
+#define RETL clr %o0
+#else
+#define RETL mov %g3, %o0
+#endif
+
+ /* Well, memset is a lot easier to get right than bcopy... */
+ .text
+ .align 32
+#ifdef __KERNEL__
+ .globl __memset
+__memset:
+#endif
+ .globl memset
+memset:
+#ifndef __KERNEL__
+ brz,a,pt %o1, bzero_private
+ mov %o2, %o1
+#ifndef REGS_64BIT
+ srl %o2, 0, %o2
+#endif
+ mov %o0, %g3
+#endif
+ cmp %o2, 7
+ bleu,pn %xcc, 17f
+ andcc %o0, 3, %g5
+ be,pt %xcc, 4f
+ and %o1, 0xff, %o1
+ cmp %g5, 3
+ be,pn %xcc, 2f
+ stb %o1, [%o0 + 0x00]
+ cmp %g5, 2
+ be,pt %xcc, 2f
+ stb %o1, [%o0 + 0x01]
+ stb %o1, [%o0 + 0x02]
+2: sub %g5, 4, %g5
+ sub %o0, %g5, %o0
+ add %o2, %g5, %o2
+4: sllx %o1, 8, %g1
+ andcc %o0, 4, %g0
+ or %o1, %g1, %o1
+ sllx %o1, 16, %g1
+ or %o1, %g1, %o1
+ be,pt %xcc, 2f
+#ifdef REGS_64BIT
+ sllx %o1, 32, %g1
+#else
+ cmp %o2, 128
+#endif
+ stw %o1, [%o0]
+ sub %o2, 4, %o2
+ add %o0, 4, %o0
+2:
+#ifdef REGS_64BIT
+ cmp %o2, 128
+ or %o1, %g1, %o1
+#endif
+ blu,pn %xcc, 9f
+ andcc %o0, 0x38, %g5
+ be,pn %icc, 6f
+ mov 64, %o5
+ andcc %o0, 8, %g0
+ be,pn %icc, 1f
+ sub %o5, %g5, %o5
+#ifdef REGS_64BIT
+ stx %o1, [%o0]
+#else
+ stw %o1, [%o0]
+ stw %o1, [%o0 + 4]
+#endif
+ add %o0, 8, %o0
+1: andcc %o5, 16, %g0
+ be,pn %icc, 1f
+ sub %o2, %o5, %o2
+#ifdef REGS_64BIT
+ stx %o1, [%o0]
+ stx %o1, [%o0 + 8]
+#else
+ stw %o1, [%o0]
+ stw %o1, [%o0 + 4]
+ stw %o1, [%o0 + 8]
+ stw %o1, [%o0 + 12]
+#endif
+ add %o0, 16, %o0
+1: andcc %o5, 32, %g0
+ be,pn %icc, 7f
+ andncc %o2, 0x3f, %o3
+#ifdef REGS_64BIT
+ stx %o1, [%o0]
+ stx %o1, [%o0 + 8]
+ stx %o1, [%o0 + 16]
+ stx %o1, [%o0 + 24]
+#else
+ stw %o1, [%o0]
+ stw %o1, [%o0 + 4]
+ stw %o1, [%o0 + 8]
+ stw %o1, [%o0 + 12]
+ stw %o1, [%o0 + 16]
+ stw %o1, [%o0 + 20]
+ stw %o1, [%o0 + 24]
+ stw %o1, [%o0 + 28]
+#endif
+ add %o0, 32, %o0
+7: be,pn %xcc, 9f
+#ifdef __KERNEL__
+ wr %g0, FPRS_FEF, %fprs
+#endif
+ ldd [%o0 - 8], %f0
+18: wr %g0, ASI_BLK_P, %asi
+ membar #StoreStore | #LoadStore
+ andcc %o3, 0xc0, %g5
+ and %o2, 0x3f, %o2
+ fmovd %f0, %f2
+ fmovd %f0, %f4
+ andn %o3, 0xff, %o3
+ fmovd %f0, %f6
+ cmp %g5, 64
+ fmovd %f0, %f8
+ fmovd %f0, %f10
+ fmovd %f0, %f12
+ brz,pn %g5, 10f
+ fmovd %f0, %f14
+ be,pn %icc, 2f
+ stda %f0, [%o0 + 0x00] %asi
+ cmp %g5, 128
+ be,pn %icc, 2f
+ stda %f0, [%o0 + 0x40] %asi
+ stda %f0, [%o0 + 0x80] %asi
+2: brz,pn %o3, 12f
+ add %o0, %g5, %o0
+10: stda %f0, [%o0 + 0x00] %asi
+ stda %f0, [%o0 + 0x40] %asi
+ stda %f0, [%o0 + 0x80] %asi
+ stda %f0, [%o0 + 0xc0] %asi
+11: subcc %o3, 256, %o3
+ bne,pt %xcc, 10b
+ add %o0, 256, %o0
+12:
+#ifdef __KERNEL__
+ wr %g0, 0, %fprs
+#endif
+ membar #Sync
+9: andcc %o2, 0x78, %g5
+ be,pn %xcc, 13f
+ andcc %o2, 7, %o2
+14: rd %pc, %o4
+#ifdef REGS_64BIT
+ srl %g5, 1, %o3
+ sub %o4, %o3, %o4
+#else
+ sub %o4, %g5, %o4
+#endif
+ jmpl %o4 + (13f - 14b), %g0
+ add %o0, %g5, %o0
+12: SET_BLOCKS(%o0, 0x68, %o1)
+ SET_BLOCKS(%o0, 0x48, %o1)
+ SET_BLOCKS(%o0, 0x28, %o1)
+ SET_BLOCKS(%o0, 0x08, %o1)
+13: be,pn %xcc, 8f
+ andcc %o2, 4, %g0
+ be,pn %xcc, 1f
+ andcc %o2, 2, %g0
+ stw %o1, [%o0]
+ add %o0, 4, %o0
+1: be,pn %xcc, 1f
+ andcc %o2, 1, %g0
+ sth %o1, [%o0]
+ add %o0, 2, %o0
+1: bne,a,pn %xcc, 8f
+ stb %o1, [%o0]
+8: retl
+ RETL
+17: brz,pn %o2, 0f
+8: add %o0, 1, %o0
+ subcc %o2, 1, %o2
+ bne,pt %xcc, 8b
+ stb %o1, [%o0 - 1]
+0: retl
+ RETL
+6:
+#ifdef REGS_64BIT
+ stx %o1, [%o0]
+#else
+ stw %o1, [%o0]
+ stw %o1, [%o0 + 4]
+#endif
+ andncc %o2, 0x3f, %o3
+ be,pn %xcc, 9b
+#ifdef __KERNEL__
+ wr %g0, FPRS_FEF, %fprs
+#else
+ nop
+#endif
+ ba,pt %xcc, 18b
+ ldd [%o0], %f0
-/* $Id: blockops.S,v 1.7 1997/06/14 21:27:55 davem Exp $
+/* $Id: blockops.S,v 1.10 1997/06/24 17:29:10 jj Exp $
* arch/sparc64/lib/blockops.S: UltraSparc block zero optimized routines.
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
* Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
-#include <asm/asi.h>
+#include "VIS.h"
-#undef VIS_BLOCKOPS
+ .text
+ .align 32
- /* Zero out 256 bytes of memory at (buf + offset). */
-#define BLAST_BLOCK(buf, offset) \
- stda %f48, [buf + offset + 0x00] %asi; \
- stda %f48, [buf + offset + 0x40] %asi; \
- stda %f48, [buf + offset + 0x80] %asi; \
- stda %f48, [buf + offset + 0xc0] %asi;
+ .globl __bfill64
+__bfill64: /* %o0 = buf, %o1= ptr to pattern */
+ wr %g0, FPRS_FEF, %fprs ! FPU Group
+ ldd [%o1], %f48 ! Load Group
+ wr %g0, ASI_BLK_P, %asi ! LSU Group
+ membar #StoreStore | #LoadStore ! LSU Group
+ mov 32, %g2 ! IEU0 Group
- /* Copy 256 bytes of memory at (src + offset) to
- * (dst + offset).
+ /* Cannot perform real arithmatic on the pattern, that can
+ * lead to fp_exception_other ;-)
*/
-#define MIRROR_BLOCK(dst, src, offset, sync) \
- ldda [src + offset + 0x000] %asi, %f0; \
- ldda [src + offset + 0x040] %asi, %f16; \
- ldda [src + offset + 0x080] %asi, %f32; \
- ldda [src + offset + 0x0c0] %asi, %f48; \
- membar sync; \
- stda %f0, [dst + offset + 0x000] %asi; \
- stda %f16, [dst + offset + 0x040] %asi; \
- stda %f32, [dst + offset + 0x080] %asi; \
- stda %f48, [dst + offset + 0x0c0] %asi;
+ fmovd %f48, %f50 ! FPA Group
+ fmovd %f48, %f52 ! FPA Group
+ fmovd %f48, %f54 ! FPA Group
+ fmovd %f48, %f56 ! FPA Group
+ fmovd %f48, %f58 ! FPA Group
+ fmovd %f48, %f60 ! FPA Group
+ fmovd %f48, %f62 ! FPA Group
- .text
- .align 4
-
-#ifdef VIS_BLOCKOPS
- .globl __bzero_1page
-__bzero_1page:
- /* %o0 = buf */
- mov %o0, %o1
- wr %g0, ASI_BLK_P, %asi
- mov 0x08, %g2
- membar #Sync|#StoreLoad
- fzero %f48
- fzero %f50
- fzero %f52
- fzero %f54
- fzero %f56
- fzero %f58
- fzero %f60
- fzero %f62
-1:
- BLAST_BLOCK(%o0, 0x000)
- BLAST_BLOCK(%o0, 0x100)
- BLAST_BLOCK(%o0, 0x200)
- BLAST_BLOCK(%o0, 0x300)
- subcc %g2, 1, %g2
- bne,pt %icc, 1b
- add %o0, 0x400, %o0
+1: stda %f48, [%o0 + 0x00] %asi ! Store Group
+ stda %f48, [%o0 + 0x40] %asi ! Store Group
+ stda %f48, [%o0 + 0x80] %asi ! Store Group
+ stda %f48, [%o0 + 0xc0] %asi ! Store Group
+ subcc %g2, 1, %g2 ! IEU1 Group
+ bne,pt %icc, 1b ! CTI
+ add %o0, 0x100, %o0 ! IEU0
+ membar #Sync ! LSU Group
- membar #Sync|#LoadStore|#StoreStore
-
- retl
- mov %o1, %o0
-#else
-#define BZERO_256BYTES(base) \
- stx %g0, [base + 0x00]; stx %g0, [base + 0x08]; \
- stx %g0, [base + 0x10]; stx %g0, [base + 0x18]; \
- stx %g0, [base + 0x20]; stx %g0, [base + 0x28]; \
- stx %g0, [base + 0x30]; stx %g0, [base + 0x38]; \
- stx %g0, [base + 0x40]; stx %g0, [base + 0x48]; \
- stx %g0, [base + 0x50]; stx %g0, [base + 0x58]; \
- stx %g0, [base + 0x60]; stx %g0, [base + 0x68]; \
- stx %g0, [base + 0x70]; stx %g0, [base + 0x78]; \
- stx %g0, [base + 0x80]; stx %g0, [base + 0x88]; \
- stx %g0, [base + 0x90]; stx %g0, [base + 0x98]; \
- stx %g0, [base + 0xa0]; stx %g0, [base + 0xa8]; \
- stx %g0, [base + 0xb0]; stx %g0, [base + 0xb8]; \
- stx %g0, [base + 0xc0]; stx %g0, [base + 0xc8]; \
- stx %g0, [base + 0xd0]; stx %g0, [base + 0xd8]; \
- stx %g0, [base + 0xe0]; stx %g0, [base + 0xe8]; \
- stx %g0, [base + 0xf0]; stx %g0, [base + 0xf8];
+ jmpl %o7 + 0x8, %g0 ! CTI Group brk forced
+ wr %g0, 0, %fprs ! FPU Group
.align 32
.globl __bzero_1page
__bzero_1page:
- mov 32, %g1
-1:
- BZERO_256BYTES(%g2)
- subcc %g1, 1, %g1
- bne,pt %icc, 1b
- add %g2, 0x100, %g2
- jmpl %o7 + 0x8, %g0
- mov %g3, %o7
-#endif
-
- .globl __bfill64
-__bfill64:
-#if 1
- /* %o0 = buf, %o1 = 64-bit pattern */
-#define FILL_BLOCK(buf, offset) \
- stx %o1, [buf + offset + 0x38]; \
- stx %o1, [buf + offset + 0x30]; \
- stx %o1, [buf + offset + 0x28]; \
- stx %o1, [buf + offset + 0x20]; \
- stx %o1, [buf + offset + 0x18]; \
- stx %o1, [buf + offset + 0x10]; \
- stx %o1, [buf + offset + 0x08]; \
- stx %o1, [buf + offset + 0x00];
-
- mov 0x20, %g2
-1:
- FILL_BLOCK(%o0, 0x00)
- FILL_BLOCK(%o0, 0x40)
- FILL_BLOCK(%o0, 0x80)
- FILL_BLOCK(%o0, 0xc0)
- subcc %g2, 1, %g2
- bne,pt %icc, 1b
- add %o0, 0x100, %o0
- retl
- nop
-#undef FILL_BLOCK
-
-#else
- /* %o0 = buf */
- stx %o1, [%sp + 0x7ff + 128]
- wr %g0, ASI_BLK_P, %asi
- mov 0x08, %g2
- ldd [%sp + 0x7ff + 128], %f48
- membar #Sync|#StoreLoad
- fmovd %f48, %f50
- fmovd %f48, %f52
- fmovd %f48, %f54
- fmovd %f48, %f56
- fmovd %f48, %f58
- fmovd %f48, %f60
- fmovd %f48, %f62
-1:
- BLAST_BLOCK(%o0, 0x000)
- BLAST_BLOCK(%o0, 0x100)
- BLAST_BLOCK(%o0, 0x200)
- BLAST_BLOCK(%o0, 0x300)
- subcc %g2, 1, %g2
- bne,pt %icc, 1b
- add %o0, 0x400, %o0
-
- retl
- membar #Sync|#LoadStore|#StoreStore
-#endif
+ wr %g0, FPRS_FEF, %fprs ! FPU Group
+ fzero %f0 ! FPA Group
+ mov 32, %g1 ! IEU0
+ fzero %f2 ! FPA Group
+ faddd %f0, %f2, %f4 ! FPA Group
+ fmuld %f0, %f2, %f6 ! FPM
+ faddd %f0, %f2, %f8 ! FPA Group
+ fmuld %f0, %f2, %f10 ! FPM
-#if 0
- .globl __copy_1page
-__copy_1page:
- /* %o0 = dst, %o1 = src */
- or %g0, 0x08, %g1
- wr %g0, ASI_BLK_P, %asi
- membar #Sync|#StoreLoad
-1:
- MIRROR_BLOCK(%o0, %o1, 0x000, #Sync)
- MIRROR_BLOCK(%o0, %o1, 0x100, #Sync)
- MIRROR_BLOCK(%o0, %o1, 0x200, #Sync)
- MIRROR_BLOCK(%o0, %o1, 0x300, #Sync)
- subcc %g1, 1, %g1
- add %o0, 0x400, %o0
- bne,pt %icc, 1b
- add %o1, 0x400, %o1
+ faddd %f0, %f2, %f12 ! FPA Group
+ fmuld %f0, %f2, %f14 ! FPM
+ wr %g0, ASI_BLK_P, %asi ! LSU Group
+ membar #StoreStore | #LoadStore ! LSU Group
+1: stda %f0, [%o0 + 0x00] %asi ! Store Group
+ stda %f0, [%o0 + 0x40] %asi ! Store Group
+ stda %f0, [%o0 + 0x80] %asi ! Store Group
+ stda %f0, [%o0 + 0xc0] %asi ! Store Group
- retl
- membar #Sync|#LoadStore|#StoreStore
-#endif
+ subcc %g1, 1, %g1 ! IEU1
+ bne,pt %icc, 1b ! CTI
+ add %o0, 0x100, %o0 ! IEU0 Group
+ membar #Sync ! LSU Group
+ jmpl %o7 + 0x8, %g0 ! CTI Group brk forced
+ wr %g0, 0, %fprs ! FPU Group
#include <asm/head.h>
#include <asm/ptrace.h>
#include <asm/asi.h>
+#include <asm/page.h>
-#define CSUM_BIGCHUNK(buf, offset, sum, t0, t1, t2, t3, t4, t5) \
- ldd [buf + offset + 0x00], t0; \
- ldd [buf + offset + 0x08], t2; \
- addccc t0, sum, sum; \
- addccc t1, sum, sum; \
- ldd [buf + offset + 0x10], t4; \
- addccc t2, sum, sum; \
- addccc t3, sum, sum; \
- ldd [buf + offset + 0x18], t0; \
- addccc t4, sum, sum; \
- addccc t5, sum, sum; \
- addccc t0, sum, sum; \
- addccc t1, sum, sum;
+ /* The problem with the "add with carry" instructions on Ultra
+ * are two fold. Firstly, they cannot pair with jack shit,
+ * and also they only add in the 32-bit carry condition bit
+ * into the accumulated sum. The following is much better.
+ *
+ * This should run at max bandwidth for ecache hits, a better
+ * technique is to use VIS and fpu operations somehow, but
+ * this requires more reasoning on my part...
+ *
+ * Assuming ecache hits and branches predicted well, this
+ * can be expected to run at a rate of 16 cycles per 64-bytes
+ * of data summed. (the old code summed 32 bytes in 20
+ * cycles, with numerous bubbles and unnecessary stalls)
+ */
+#define CSUM_ECACHE_LOAD(buf, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
+ ldx [buf + offset + 0x00], t0; \
+ ldx [buf + offset + 0x08], t1; \
+ ldx [buf + offset + 0x10], t2; \
+ ldx [buf + offset + 0x18], t3; \
+ ldx [buf + offset + 0x20], t4; \
+ ldx [buf + offset + 0x28], t5; \
+ ldx [buf + offset + 0x30], t6; \
+ ldx [buf + offset + 0x38], t7; \
+ nop; nop; /* THIS IS CRITICAL!!!!!!!!! */
+
+#define CSUM_ECACHE_BLOCK_LDNEXT(buf, offset, sum, t0, t1, t2, t3, t4, t5, t6, t7) \
+ addcc sum, t0, sum; \
+ bcc,pt %xcc, 11f; \
+ ldx [buf + offset + 0x00], t0; \
+ add sum, 1, sum; \
+11: addcc sum, t1, sum; \
+ bcc,pt %xcc, 12f; \
+ ldx [buf + offset + 0x08], t1; \
+ add sum, 1, sum; \
+12: addcc sum, t2, sum; \
+ bcc,pt %xcc, 13f; \
+ ldx [buf + offset + 0x10], t2; \
+ add sum, 1, sum; \
+13: addcc sum, t3, sum; \
+ bcc,pt %xcc, 14f; \
+ ldx [buf + offset + 0x18], t3; \
+ add sum, 1, sum; \
+14: addcc sum, t4, sum; \
+ bcc,pt %xcc, 15f; \
+ ldx [buf + offset + 0x20], t4; \
+ add sum, 1, sum; \
+15: addcc sum, t5, sum; \
+ bcc,pt %xcc, 16f; \
+ ldx [buf + offset + 0x28], t5; \
+ add sum, 1, sum; \
+16: addcc sum, t6, sum; \
+ bcc,pt %xcc, 17f; \
+ ldx [buf + offset + 0x30], t6; \
+ add sum, 1, sum; \
+17: addcc sum, t7, sum; \
+ bcc,pt %xcc, 18f; \
+ ldx [buf + offset + 0x38], t7; \
+ add sum, 1, sum; \
+18: nop; nop; /* DO NOT TOUCH! */
+
+#define CSUM_ECACHE_BLOCK(sum, t0, t1, t2, t3, t4, t5, t6, t7) \
+ addcc sum, t0, sum; \
+ bcs,a,pn %xcc, 21f; \
+ add sum, 1, sum; \
+21: addcc sum, t1, sum; \
+ bcs,a,pn %xcc, 22f; \
+ add sum, 1, sum; \
+22: addcc sum, t2, sum; \
+ bcs,a,pn %xcc, 23f; \
+ add sum, 1, sum; \
+23: addcc sum, t3, sum; \
+ bcs,a,pn %xcc, 24f; \
+ add sum, 1, sum; \
+24: addcc sum, t4, sum; \
+ bcs,a,pn %xcc, 25f; \
+ add sum, 1, sum; \
+25: addcc sum, t5, sum; \
+ bcs,a,pn %xcc, 26f; \
+ add sum, 1, sum; \
+26: addcc sum, t6, sum; \
+ bcs,a,pn %xcc, 27f; \
+ add sum, 1, sum; \
+27: addcc sum, t7, sum; \
+ bcs,a,pn %xcc, 28f; \
+ add sum, 1, sum; \
+28:
-#define CSUM_LASTCHUNK(buf, offset, sum, t0, t1, t2, t3) \
- ldd [buf - offset - 0x08], t0; \
- ldd [buf - offset - 0x00], t2; \
- addccc t0, sum, sum; \
- addccc t1, sum, sum; \
- addccc t2, sum, sum; \
- addccc t3, sum, sum;
+#define CSUM_LASTCHUNK(buf, offset, sum, t0, t1) \
+ ldx [buf - offset - 0x08], t0; \
+ ldx [buf - offset - 0x00], t1; \
+ addcc t0, sum, sum; \
+ bcs,a,pn %xcc, 31f; \
+ add sum, 1, sum; \
+31: addcc t1, sum, sum; \
+ bcs,a,pn %xcc, 32f; \
+ add sum, 1, sum; \
+32:
- /* Do end cruft out of band to get better cache patterns. */
+ .text
+ /* Keep this garbage from swiping the icache. */
csum_partial_end_cruft:
- andcc %o1, 8, %g0 ! check how much
- be,pn %icc, 1f ! caller asks %o1 & 0x8
- and %o1, 4, %g5 ! nope, check for word remaining
- ldd [%o0], %g2 ! load two
- addcc %g2, %o2, %o2 ! add first word to sum
- addccc %g3, %o2, %o2 ! add second word as well
- add %o0, 8, %o0 ! advance buf ptr
- addc %g0, %o2, %o2 ! add in final carry
-1: brz,pn %g5, 1f ! nope, skip this code
- andcc %o1, 3, %o1 ! check for trailing bytes
- ld [%o0], %g2 ! load it
- addcc %g2, %o2, %o2 ! add to sum
- add %o0, 4, %o0 ! advance buf ptr
- addc %g0, %o2, %o2 ! add in final carry
-1: brz,pn %o1, 1f ! no trailing bytes, return
- addcc %o1, -1, %g0 ! only one byte remains?
- bne,pn %icc, 2f ! at least two bytes more
- subcc %o1, 2, %o1 ! only two bytes more?
- ba,pt %xcc, 4f ! only one byte remains
- clr %o4 ! clear fake hword value
-2: lduh [%o0], %o4 ! get hword
- be,pn %icc, 6f ! jmp if only hword remains
- add %o0, 2, %o0 ! advance buf ptr either way
- sll %o4, 16, %o4 ! create upper hword
-4: ldub [%o0], %o5 ! get final byte
- sll %o5, 8, %o5 ! put into place
- or %o5, %o4, %o4 ! coalese with hword (if any)
-6: addcc %o4, %o2, %o2 ! add to sum
-1: sllx %g4, 32, %g4 ! give gfp back
- addc %g0, %o2, %o0 ! add final carry into retval
- retl ! get outta here
- srl %o0, 0, %o0
+ andcc %o1, 8, %g0 ! IEU1 Group
+ be,pn %icc, 1f ! CTI
+ and %o1, 4, %g5 ! IEU0
+ ldx [%o0 + 0x00], %g2 ! Load Group
+ add %o0, 0x8, %o0 ! IEU0
+ addcc %g2, %o2, %o2 ! IEU1 Group + 2 bubbles
+ bcs,a,pn %xcc, 1f ! CTI
+ add %o2, 1, %o2 ! IEU0 4 clocks (mispredict)
+1: andcc %o1, 2, %g0 ! IEU1 Group
+ brz,pn %g5, 1f ! CTI Group (needs IEU1)
+ clr %g2 ! IEU0
+ ld [%o0], %g2 ! Load
+ add %o0, 4, %o0 ! IEU0 Group
+ sllx %g2, 32, %g2 ! IEU0 Group + 2 bubbles
+1: and %o1, 1, %o1 ! IEU1
+ be,pn %icc, 1f ! CTI
+ clr %o4 ! IEU0 Group
+ lduh [%o0], %o4 ! Load
+ add %o0, 2, %o0 ! IEU1
+ sll %o4, 16, %o4 ! IEU0 Group + 2 bubbles
+1: brz,pn %o1, 1f ! CTI
+ clr %o5 ! IEU1
+ ldub [%o0], %o5 ! Load Group
+ sll %o5, 8, %o5 ! IEU0 Group + 2 bubbles
+1: or %g2, %o4, %o4 ! IEU1
+ or %o5, %o4, %o4 ! IEU0 Group
+ addcc %o4, %o2, %o2 ! IEU1 Group (regdep)
+ bcc,pt %xcc, cfold ! CTI
+ sethi %uhi(PAGE_OFFSET), %g4 ! IEU0
+1: b,pt %xcc, cfold ! CTI Group
+ add %o2, 1, %o2 ! IEU0
- /* Also do alignment out of band to get better cache patterns. */
-csum_partial_fix_alignment:
+csum_partial_fixit:
+ bl,pn %icc, cpte ! CTI Group
+ and %o1, 0xf, %o3 ! IEU0
+ andcc %o0, 0x2, %g0 ! IEU1
+ be,pn %icc, 1f ! CTI Group
+ and %o0, 0x4, %g7 ! IEU0
+ lduh [%o0 + 0x00], %g2 ! Load
+ sub %o1, 2, %o1 ! IEU0 Group
+ addcc %o0, 2, %o0 ! IEU1
+ and %o0, 0x4, %g7 ! IEU0 Group
+ sll %g2, 16, %g2 ! IEU0 Group (no load stall)
+ addcc %g2, %o2, %o2 ! IEU1 Group (regdep)
+ bcc,pt %icc, 0f ! CTI
+ andn %o1, 0xff, %o3 ! IEU0
+ srl %o2, 16, %g2 ! IEU0 Group
+ b,pt %xcc, 9f ! CTI
+ add %g2, 1, %g2 ! IEU1
+0: srl %o2, 16, %g2 ! IEU0 Group 8-(
+9: sll %o2, 16, %o2 ! IEU0 Group 8-(
+ sll %g2, 16, %g3 ! IEU0 Group 8-(
+ srl %o2, 16, %o2 ! IEU0 Group 8-(
+ or %g3, %o2, %o2 ! IEU1
+1: brnz,pt %g7, 2f ! CTI Group
+ sub %o1, 4, %o1 ! IEU0
+ b,pt %xcc, csum_partial_aligned ! CTI Group
+ add %o1, 4, %o1 ! IEU0
+2: ld [%o0 + 0x00], %g2 ! Load Group
+ add %o0, 4, %o0 ! IEU0
+ andn %o1, 0xff, %o3 ! IEU1
+ addcc %g2, %o2, %o2 ! IEU1 Group + 2 bubbles
+ bcc,pt %xcc, csum_partial_aligned ! CTI
+ nop ! IEU0
+ b,pt %xcc, csum_partial_aligned ! CTI Group
+ add %o2, 1, %o2 ! IEU0
- /* The common case is to get called with a nicely aligned
- * buffer of size 0x20. Follow the code path for that case.
- */
- .globl csum_partial
+ .align 32
+ .globl csum_partial
csum_partial: /* %o0=buf, %o1=len, %o2=sum */
- srl %o1, 0, %o1 ! doof scheiss
- andcc %o0, 0x7, %g0 ! alignment problems?
- srl %o2, 0, %o2
- be,pt %icc, csum_partial_fix_aligned ! yep, handle it
- andn %o1, 0x7f, %o3 ! num loop iterations
- cmp %o1, 6
- bl,pn %icc, cpte - 0x4
- andcc %o0, 0x2, %g0
- be,pn %icc, 1f
- and %o0, 0x4, %g7
- lduh [%o0 + 0x00], %g2
- sub %o1, 2, %o1
- add %o0, 2, %o0
- sll %g2, 16, %g2
- addcc %g2, %o2, %o2
- srl %o2, 16, %g3
- addc %g0, %g3, %g2
- sll %o2, 16, %o2
- and %o0, 0x4, %g7
- sll %g2, 16, %g3
- srl %o2, 16, %o2
- or %g3, %o2, %o2
-1: brz,pn %g7, csum_partial_fix_aligned
- andn %o1, 0x7f, %o3
- ld [%o0 + 0x00], %g2
- sub %o1, 4, %o1
- addcc %g2, %o2, %o2
- add %o0, 4, %o0
- andn %o1, 0x7f, %o3
- addc %g0, %o2, %o2
-csum_partial_fix_aligned:
- brz,pt %o3, 3f ! none to do
- andcc %o1, 0x70, %g1 ! clears carry flag too
-5: CSUM_BIGCHUNK(%o0, 0x00, %o2, %o4, %o5, %g2, %g3, %g4, %g5)
- CSUM_BIGCHUNK(%o0, 0x20, %o2, %o4, %o5, %g2, %g3, %g4, %g5)
- CSUM_BIGCHUNK(%o0, 0x40, %o2, %o4, %o5, %g2, %g3, %g4, %g5)
- CSUM_BIGCHUNK(%o0, 0x60, %o2, %o4, %o5, %g2, %g3, %g4, %g5)
- addc %g0, %o2, %o2 ! sink in final carry
- subcc %o3, 128, %o3 ! detract from loop iters
- bne,pt %icc, 5b ! more to do
- add %o0, 128, %o0 ! advance buf ptr
-3: brz,pn %g1, cpte ! nope
- andcc %o1, 0xf, %o3 ! anything left at all?
-10: rd %pc, %g7 ! get pc
- srl %g1, 1, %o4 ! compute offset
- sub %g7, %g1, %g7 ! adjust jmp ptr
- sub %g7, %o4, %g7 ! final jmp ptr adjust
- jmp %g7 + (11f-10b) ! enter the table
- add %o0, %g1, %o0 ! advance buf ptr
-cptbl: CSUM_LASTCHUNK(%o0, 0x68, %o2, %g2, %g3, %g4, %g5)
- CSUM_LASTCHUNK(%o0, 0x58, %o2, %g2, %g3, %g4, %g5)
- CSUM_LASTCHUNK(%o0, 0x48, %o2, %g2, %g3, %g4, %g5)
- CSUM_LASTCHUNK(%o0, 0x38, %o2, %g2, %g3, %g4, %g5)
- CSUM_LASTCHUNK(%o0, 0x28, %o2, %g2, %g3, %g4, %g5)
- CSUM_LASTCHUNK(%o0, 0x18, %o2, %g2, %g3, %g4, %g5)
- CSUM_LASTCHUNK(%o0, 0x08, %o2, %g2, %g3, %g4, %g5)
-11: addc %g0, %o2, %o2 ! fetch final carry
- andcc %o1, 0xf, %o3 ! anything left at all?
-cpte: brnz,pn %o3, csum_partial_end_cruft ! yep, handle it
- sethi %uhi(KERNBASE), %g4
- mov %o2, %o0 ! return computed csum
- retl ! get outta here
- sllx %g4, 32, %g4 ! give gfp back
+ andcc %o0, 0x7, %g0 ! IEU1 Group
+ srl %o1, 0, %o1 ! IEU0
+ srl %o2, 0, %o2 ! IEU0 Group
+ be,pt %icc, csum_partial_aligned ! CTI
+ andn %o1, 0xff, %o3 ! IEU1
+ b,pt %xcc, csum_partial_fixit ! CTI Group
+ cmp %o1, 6 ! IEU0
+ nop
+csum_partial_aligned:
+ brz,pt %o3, 3f ! CTI Group
+ and %o1, 0xf0, %g1 ! IEU0
+5: CSUM_ECACHE_LOAD( %o0, 0x000, %o4, %o5, %g2, %g3, %g4, %g5, %g1, %g7)
+ CSUM_ECACHE_BLOCK_LDNEXT(%o0, 0x040, %o2, %o4, %o5, %g2, %g3, %g4, %g5, %g1, %g7)
+ CSUM_ECACHE_BLOCK_LDNEXT(%o0, 0x080, %o2, %o4, %o5, %g2, %g3, %g4, %g5, %g1, %g7)
+ CSUM_ECACHE_BLOCK_LDNEXT(%o0, 0x0c0, %o2, %o4, %o5, %g2, %g3, %g4, %g5, %g1, %g7)
+ CSUM_ECACHE_BLOCK( %o2, %o4, %o5, %g2, %g3, %g4, %g5, %g1, %g7)
+ subcc %o3, 256, %o3 ! IEU1 Group
+ bne,pt %icc, 5b ! CTI
+ add %o0, 256, %o0 ! IEU0
+ and %o1, 0xf0, %g1 ! IEU0 Group
+3: brz,pn %g1, cpte ! CTI
+ and %o1, 0xf, %o3 ! IEU1 Group
+10: rd %pc, %g7 ! LSU Group + 4 clocks
+ sll %g1, 1, %o4 ! IEU0 Group
+ sub %g7, %o4, %g7 ! IEU1
+ jmp %g7 + %lo(cpte - 10b) ! CTI Group brk forced
+ add %o0, %g1, %o0 ! IEU0
+cptbl: CSUM_LASTCHUNK(%o0, 0xe8, %o2, %g2, %g3)
+ CSUM_LASTCHUNK(%o0, 0xd8, %o2, %g2, %g3)
+ CSUM_LASTCHUNK(%o0, 0xc8, %o2, %g2, %g3)
+ CSUM_LASTCHUNK(%o0, 0xb8, %o2, %g2, %g3)
+ CSUM_LASTCHUNK(%o0, 0xa8, %o2, %g2, %g3)
+ CSUM_LASTCHUNK(%o0, 0x98, %o2, %g2, %g3)
+ CSUM_LASTCHUNK(%o0, 0x88, %o2, %g2, %g3)
+ CSUM_LASTCHUNK(%o0, 0x78, %o2, %g2, %g3)
+ CSUM_LASTCHUNK(%o0, 0x68, %o2, %g2, %g3)
+ CSUM_LASTCHUNK(%o0, 0x58, %o2, %g2, %g3)
+ CSUM_LASTCHUNK(%o0, 0x48, %o2, %g2, %g3)
+ CSUM_LASTCHUNK(%o0, 0x38, %o2, %g2, %g3)
+ CSUM_LASTCHUNK(%o0, 0x28, %o2, %g2, %g3)
+ CSUM_LASTCHUNK(%o0, 0x18, %o2, %g2, %g3)
+ CSUM_LASTCHUNK(%o0, 0x08, %o2, %g2, %g3)
+cpte: brnz,pn %o3, csum_partial_end_cruft ! CTI Group
+ sethi %uhi(PAGE_OFFSET), %g4 ! IEU0
+cfold: sllx %o2, 32, %o0 ! IEU0 Group
+ addcc %o2, %o0, %o0 ! IEU1 Group (regdep)
+ srlx %o0, 32, %o0 ! IEU0 Group (regdep)
+ bcs,a,pn %xcc, 1f ! CTI
+ add %o0, 1, %o0 ! IEU1 4 clocks (mispredict)
+1: retl ! CTI Group brk forced
+ sllx %g4, 32, %g4 ! IEU0 Group
.globl __csum_partial_copy_start, __csum_partial_copy_end
__csum_partial_copy_start:
-#define EX(x,y,a,b,z) \
-98: x,y; \
- .section .fixup,z##alloc,z##execinstr; \
- .align 4; \
-99: ba,pt %xcc, 30f; \
- a, b, %o3; \
- .section __ex_table,z##alloc; \
- .align 8; \
- .xword 98b, 99b; \
- .text; \
- .align 4
+ /* I think I have an erection... Once _AGAIN_ the SunSoft
+ * engineers are caught asleep at the keyboard, tsk tsk...
+ */
+#define CSUMCOPY_ECACHE_LOAD(src, off, t0, t1, t2, t3, t4, t5, t6, t7) \
+ ldxa [src + off + 0x00] %asi, t0; \
+ ldxa [src + off + 0x08] %asi, t1; \
+ ldxa [src + off + 0x10] %asi, t2; \
+ ldxa [src + off + 0x18] %asi, t3; \
+ ldxa [src + off + 0x20] %asi, t4; \
+ ldxa [src + off + 0x28] %asi, t5; \
+ ldxa [src + off + 0x30] %asi, t6; \
+ ldxa [src + off + 0x38] %asi, t7; \
+ nop; nop; /* DO NOT TOUCH THIS!!!!! */
-#define EX2(x,y,z) \
-98: x,y; \
- .section __ex_table,z##alloc; \
- .align 8; \
- .xword 98b, 30f; \
- .text; \
- .align 4
+#define CSUMCOPY_EC_STALIGNED_LDNXT(src, dest, off, sum, t0, t1, t2, t3, t4, t5, t6, t7)\
+ stx t0, [dest + off - 0x40]; \
+ addcc sum, t0, sum; \
+ bcc,pt %xcc, 11f; \
+ ldxa [src + off + 0x00] %asi, t0; \
+ add sum, 1, sum; \
+11: stx t1, [dest + off - 0x38]; \
+ addcc sum, t1, sum; \
+ bcc,pt %xcc, 12f; \
+ ldxa [src + off + 0x08] %asi, t1; \
+ add sum, 1, sum; \
+12: stx t2, [dest + off - 0x30]; \
+ addcc sum, t2, sum; \
+ bcc,pt %xcc, 13f; \
+ ldxa [src + off + 0x10] %asi, t2; \
+ add sum, 1, sum; \
+13: stx t3, [dest + off - 0x28]; \
+ addcc sum, t3, sum; \
+ bcc,pt %xcc, 14f; \
+ ldxa [src + off + 0x18] %asi, t3; \
+ add sum, 1, sum; \
+14: stx t4, [dest + off - 0x20]; \
+ addcc sum, t4, sum; \
+ bcc,pt %xcc, 15f; \
+ ldxa [src + off + 0x20] %asi, t4; \
+ add sum, 1, sum; \
+15: stx t5, [dest + off - 0x18]; \
+ addcc sum, t5, sum; \
+ bcc,pt %xcc, 16f; \
+ ldxa [src + off + 0x28] %asi, t5; \
+ add sum, 1, sum; \
+16: stx t6, [dest + off - 0x10]; \
+ addcc sum, t6, sum; \
+ bcc,pt %xcc, 17f; \
+ ldxa [src + off + 0x30] %asi, t6; \
+ add sum, 1, sum; \
+17: stx t7, [dest + off - 0x08]; \
+ addcc sum, t7, sum; \
+ bcc,pt %xcc, 18f; \
+ ldxa [src + off + 0x38] %asi, t7; \
+ add sum, 1, sum; \
+18:
-#define EX3(x,y,z) \
-98: x,y; \
- .section __ex_table,z##alloc; \
- .align 8; \
- .xword 98b, 96f; \
- .text; \
- .align 4
+#define CSUMCOPY_EC_STUNALIGN_LDNXT(src, dest, off, sum, t0, t1, t2, t3, t4, t5, t6, t7)\
+ stw t0, [dest + off - 0x3c]; \
+ addcc sum, t0, sum; \
+ srlx t0, 32, t0; \
+ stw t0, [dest + off - 0x40]; \
+ bcc,pt %xcc, 21f; \
+ ldxa [src + off + 0x00] %asi, t0; \
+ add sum, 1, sum; \
+21: stw t1, [dest + off - 0x34]; \
+ addcc sum, t1, sum; \
+ srlx t1, 32, t1; \
+ stw t1, [dest + off - 0x38]; \
+ bcc,pt %xcc, 22f; \
+ ldxa [src + off + 0x08] %asi, t1; \
+ add sum, 1, sum; \
+22: stw t2, [dest + off - 0x2c]; \
+ addcc sum, t2, sum; \
+ srlx t2, 32, t2; \
+ stw t2, [dest + off - 0x30]; \
+ bcc,pt %xcc, 23f; \
+ ldxa [src + off + 0x10] %asi, t2; \
+ add sum, 1, sum; \
+23: stw t3, [dest + off - 0x24]; \
+ addcc sum, t3, sum; \
+ srlx t3, 32, t3; \
+ stw t3, [dest + off - 0x28]; \
+ bcc,pt %xcc, 24f; \
+ ldxa [src + off + 0x18] %asi, t3; \
+ add sum, 1, sum; \
+24: stw t4, [dest + off - 0x1c]; \
+ addcc sum, t4, sum; \
+ srlx t4, 32, t4; \
+ stw t4, [dest + off - 0x20]; \
+ bcc,pt %xcc, 25f; \
+ ldxa [src + off + 0x20] %asi, t4; \
+ add sum, 1, sum; \
+25: stw t5, [dest + off - 0x14]; \
+ addcc sum, t5, sum; \
+ srlx t5, 32, t5; \
+ stw t5, [dest + off - 0x18]; \
+ bcc,pt %xcc, 26f; \
+ ldxa [src + off + 0x28] %asi, t5; \
+ add sum, 1, sum; \
+26: stw t6, [dest + off - 0x0c]; \
+ addcc sum, t6, sum; \
+ srlx t6, 32, t6; \
+ stw t6, [dest + off - 0x10]; \
+ bcc,pt %xcc, 27f; \
+ ldxa [src + off + 0x30] %asi, t6; \
+ add sum, 1, sum; \
+27: stw t7, [dest + off - 0x04]; \
+ addcc sum, t7, sum; \
+ srlx t7, 32, t7; \
+ stw t7, [dest + off - 0x08]; \
+ bcc,pt %xcc, 28f; \
+ ldxa [src + off + 0x38] %asi, t7; \
+ add sum, 1, sum; \
+28:
-#define EXT(start,end,handler,z) \
- .section __ex_table,z##alloc; \
- .align 8; \
- .xword start, 0, end, handler; \
- .text; \
- .align 4
+#define CSUMCOPY_EC_STALIGNED(dest, off, sum, t0, t1, t2, t3, t4, t5, t6, t7) \
+ addcc sum, t0, sum; \
+ bcc,pt %xcc, 31f; \
+ stx t0, [dest + off + 0x00]; \
+ add sum, 1, sum; \
+31: addcc sum, t1, sum; \
+ bcc,pt %xcc, 32f; \
+ stx t1, [dest + off + 0x08]; \
+ add sum, 1, sum; \
+32: addcc sum, t2, sum; \
+ bcc,pt %xcc, 33f; \
+ stx t2, [dest + off + 0x10]; \
+ add sum, 1, sum; \
+33: addcc sum, t3, sum; \
+ bcc,pt %xcc, 34f; \
+ stx t3, [dest + off + 0x18]; \
+ add sum, 1, sum; \
+34: addcc sum, t4, sum; \
+ bcc,pt %xcc, 35f; \
+ stx t4, [dest + off + 0x20]; \
+ add sum, 1, sum; \
+35: addcc sum, t5, sum; \
+ bcc,pt %xcc, 36f; \
+ stx t5, [dest + off + 0x28]; \
+ add sum, 1, sum; \
+36: addcc sum, t6, sum; \
+ bcc,pt %xcc, 37f; \
+ stx t6, [dest + off + 0x30]; \
+ add sum, 1, sum; \
+37: addcc sum, t7, sum; \
+ bcc,pt %xcc, 38f; \
+ stx t7, [dest + off + 0x38]; \
+ add sum, 1, sum; \
+38:
- /* This aligned version executes typically in 8.5 superscalar cycles, this
- * is the best I can do. I say 8.5 because the final add will pair with
- * the next ldd in the main unrolled loop. Thus the pipe is always full.
- * If you change these macros (including order of instructions),
- * please check the fixup code below as well.
- */
-#define CSUMCOPY_BIGCHUNK_ALIGNED(src, dst, sum, off, t0, t1, t2, t3, t4, t5, t6, t7) \
- ldda [src + off + 0x00] %asi, t0; \
- ldda [src + off + 0x08] %asi, t2; \
- addccc t0, sum, sum; \
- ldda [src + off + 0x10] %asi, t4; \
- addccc t1, sum, sum; \
- ldda [src + off + 0x18] %asi, t6; \
- addccc t2, sum, sum; \
- std t0, [dst + off + 0x00]; \
- addccc t3, sum, sum; \
- std t2, [dst + off + 0x08]; \
- addccc t4, sum, sum; \
- std t4, [dst + off + 0x10]; \
- addccc t5, sum, sum; \
- std t6, [dst + off + 0x18]; \
- addccc t6, sum, sum; \
- addccc t7, sum, sum;
+#define CSUMCOPY_EC_STUNALIGN(dest, off, sum, t0, t1, t2, t3, t4, t5, t6, t7) \
+ stw t0, [dest + off + 0x04]; \
+ addcc sum, t0, sum; \
+ srlx t0, 32, t0; \
+ bcc,pt %xcc, 41f; \
+ stw t0, [dest + off + 0x00]; \
+ add sum, 1, sum; \
+41: stw t1, [dest + off + 0x0c]; \
+ addcc sum, t1, sum; \
+ srlx t1, 32, t1; \
+ bcc,pt %xcc, 42f; \
+ stw t1, [dest + off + 0x08]; \
+ add sum, 1, sum; \
+42: stw t2, [dest + off + 0x14]; \
+ addcc sum, t2, sum; \
+ srlx t2, 32, t2; \
+ bcc,pt %xcc, 43f; \
+ stw t2, [dest + off + 0x10]; \
+ add sum, 1, sum; \
+43: stw t3, [dest + off + 0x1c]; \
+ addcc sum, t3, sum; \
+ srlx t3, 32, t3; \
+ bcc,pt %xcc, 44f; \
+ stw t3, [dest + off + 0x18]; \
+ add sum, 1, sum; \
+44: stw t4, [dest + off + 0x24]; \
+ addcc sum, t4, sum; \
+ srlx t4, 32, t4; \
+ bcc,pt %xcc, 45f; \
+ stw t4, [dest + off + 0x20]; \
+ add sum, 1, sum; \
+45: stw t5, [dest + off + 0x2c]; \
+ addcc sum, t5, sum; \
+ srlx t5, 32, t5; \
+ bcc,pt %xcc, 46f; \
+ stw t5, [dest + off + 0x28]; \
+ add sum, 1, sum; \
+46: stw t6, [dest + off + 0x34]; \
+ addcc sum, t6, sum; \
+ srlx t6, 32, t6; \
+ bcc,pt %xcc, 47f; \
+ stw t6, [dest + off + 0x30]; \
+ add sum, 1, sum; \
+47: stw t7, [dest + off + 0x3c]; \
+ addcc sum, t7, sum; \
+ srlx t7, 32, t7; \
+ bcc,pt %xcc, 48f; \
+ stw t7, [dest + off + 0x38]; \
+ add sum, 1, sum; \
+48:
- /* 12 superscalar cycles seems to be the limit for this case,
- * because of this we thus do all the ldd's together to get
- * Viking MXCC into streaming mode. Ho hum...
- */
-#define CSUMCOPY_BIGCHUNK(src, dst, sum, off, t0, t1, t2, t3, t4, t5, t6, t7) \
- ldda [src + off + 0x00] %asi, t0; \
- ldda [src + off + 0x08] %asi, t2; \
- ldda [src + off + 0x10] %asi, t4; \
- ldda [src + off + 0x18] %asi, t6; \
- st t0, [dst + off + 0x00]; \
- addccc t0, sum, sum; \
- st t1, [dst + off + 0x04]; \
- addccc t1, sum, sum; \
- st t2, [dst + off + 0x08]; \
- addccc t2, sum, sum; \
- st t3, [dst + off + 0x0c]; \
- addccc t3, sum, sum; \
- st t4, [dst + off + 0x10]; \
- addccc t4, sum, sum; \
- st t5, [dst + off + 0x14]; \
- addccc t5, sum, sum; \
- st t6, [dst + off + 0x18]; \
- addccc t6, sum, sum; \
- st t7, [dst + off + 0x1c]; \
- addccc t7, sum, sum;
+#define CSUMCOPY_LASTCHUNK(src, dst, sum, off, t0, t1) \
+ ldxa [src - off - 0x08] %asi, t0; \
+ ldxa [src - off - 0x00] %asi, t1; \
+ nop; nop; \
+ addcc t0, sum, sum; \
+ stw t0, [dst - off - 0x04]; \
+ srlx t0, 32, t0; \
+ bcc,pt %xcc, 51f; \
+ stw t0, [dst - off - 0x08]; \
+ add sum, 1, sum; \
+51: addcc t1, sum, sum; \
+ stw t1, [dst - off + 0x04]; \
+ srlx t1, 32, t1; \
+ bcc,pt %xcc, 52f; \
+ stw t1, [dst - off - 0x00]; \
+ add sum, 1, sum; \
+52:
- /* Yuck, 6 superscalar cycles... */
-#define CSUMCOPY_LASTCHUNK(src, dst, sum, off, t0, t1, t2, t3) \
- ldda [src - off - 0x08] %asi, t0; \
- ldda [src - off - 0x00] %asi, t2; \
- addccc t0, sum, sum; \
- st t0, [dst - off - 0x08]; \
- addccc t1, sum, sum; \
- st t1, [dst - off - 0x04]; \
- addccc t2, sum, sum; \
- st t2, [dst - off - 0x00]; \
- addccc t3, sum, sum; \
- st t3, [dst - off + 0x04];
-
- /* Handle the end cruft code out of band for better cache patterns. */
cc_end_cruft:
- andcc %o3, 8, %g0 ! begin checks for that code
- be,pn %icc, 1f
- and %o3, 4, %g5
- EX(ldda [%o0 + 0x00] %asi, %g2, and %o3, 0xf,#)
- add %o1, 8, %o1
- addcc %g2, %g7, %g7
- add %o0, 8, %o0
- addccc %g3, %g7, %g7
- EX2(st %g2, [%o1 - 0x08],#)
- addc %g0, %g7, %g7
- EX2(st %g3, [%o1 - 0x04],#)
-1: brz,pt %g5, 1f
- andcc %o3, 3, %o3
- EX(lda [%o0 + 0x00] %asi, %g2, add %o3, 4,#)
- add %o1, 4, %o1
- addcc %g2, %g7, %g7
- EX2(st %g2, [%o1 - 0x04],#)
- addc %g0, %g7, %g7
- add %o0, 4, %o0
-1: brz,pn %o3, 1f
- addcc %o3, -1, %g0
- bne,pn %icc, 2f
- subcc %o3, 2, %o3
- ba,pt %xcc, 4f
- clr %o4
-2: EX(lduha [%o0 + 0x00] %asi, %o4, add %o3, 2,#)
- add %o0, 2, %o0
- EX2(sth %o4, [%o1 + 0x00],#)
- be,pn %icc, 6f
- add %o1, 2, %o1
- sll %o4, 16, %o4
-4: EX(lduba [%o0 + 0x00] %asi, %o5, add %g0, 1,#)
- EX2(stb %o5, [%o1 + 0x00],#)
- sll %o5, 8, %o5
- or %o5, %o4, %o4
-6: addcc %o4, %g7, %g7
-1: sllx %g4, 32, %g4
- addc %g0, %g7, %o0
- retl
- srl %o0, 0, %o0
+ andcc %o3, 8, %g0 ! IEU1 Group
+ be,pn %icc, 1f ! CTI
+ and %o3, 4, %g5 ! IEU0
+ ldxa [%o0 + 0x00] %asi, %g2 ! Load Group
+ add %o1, 8, %o1 ! IEU0
+ add %o0, 8, %o0 ! IEU1
+ addcc %g2, %g7, %g7 ! IEU1 Group + 2 bubbles
+ stw %g2, [%o1 - 0x04] ! Store
+ srlx %g2, 32, %g2 ! IEU0
+ bcc,pt %xcc, 1f ! CTI Group
+ stw %g2, [%o1 - 0x08] ! Store
+ add %g7, 1, %g7 ! IEU0
+1: brz,pt %g5, 1f ! CTI Group
+ clr %g2 ! IEU0
+ lduwa [%o0 + 0x00] %asi, %g2 ! Load
+ add %o1, 4, %o1 ! IEU0 Group
+ add %o0, 4, %o0 ! IEU1
+ stw %g2, [%o1 - 0x04] ! Store Group + 2 bubbles
+ sllx %g2, 32, %g2 ! IEU0
+1: andcc %o3, 2, %g0 ! IEU1
+ be,pn %icc, 1f ! CTI Group
+ clr %o4 ! IEU1
+ lduha [%o0 + 0x00] %asi, %o4 ! Load
+ add %o0, 2, %o0 ! IEU0 Group
+ add %o1, 2, %o1 ! IEU1
+ sth %o4, [%o1 - 0x2] ! Store Group + 2 bubbles
+ sll %o4, 16, %o4 ! IEU0
+1: andcc %o3, 1, %g0 ! IEU1
+ be,pn %icc, 1f ! CTI Group
+ clr %o5 ! IEU0
+ lduba [%o0 + 0x00] %asi, %o5 ! Load
+ stb %o5, [%o1 + 0x00] ! Store Group + 2 bubbles
+ sll %o5, 8, %o5 ! IEU0
+1: or %g2, %o4, %o4 ! IEU1
+ or %o5, %o4, %o4 ! IEU0 Group
+ addcc %o4, %g7, %g7 ! IEU1
+ bcc,pt %xcc, ccfold ! CTI
+ sethi %uhi(PAGE_OFFSET), %g4 ! IEU0 Group
+ b,pt %xcc, ccfold ! CTI
+ add %g7, 1, %g7 ! IEU1
- /* Sun, you just can't beat me, you just can't. Stop trying,
- * give up. I'm serious, I am going to kick the living shit
- * out of you, game over, lights out.
- */
- .align 8
- .globl __csum_partial_copy_sparc_generic
-__csum_partial_copy_sparc_generic:
- /* %o0=src, %o1=dest, %g1=len, %g7=sum */
- srl %g7, 0, %g7 ! you neve know...
- xor %o0, %o1, %o4 ! get changing bits
- srl %g1, 0, %g1 ! doof scheiss
- andcc %o4, 3, %g0 ! check for mismatched alignment
- bne,pn %icc, ccslow ! better this than unaligned/fixups
- andcc %o0, 7, %g0 ! need to align things?
- be,pt %icc, cc_dword_aligned ! yes, we check for short lengths there
- andn %g1, 0x7f, %g2 ! can we use unrolled loop?
- cmp %g1, 6
- bl,a,pn %icc, ccte
- andcc %g1, 0xf, %o3
- andcc %o0, 0x1, %g0
- bne,pn %icc, ccslow
- andcc %o0, 0x2, %g0
- be,pn %icc, 1f
- andcc %o0, 0x4, %g0
- EX(lduha [%o0 + 0x00] %asi, %g4, add %g1, 0,#)
- sub %g1, 2, %g1
- EX2(sth %g4, [%o1 + 0x00],#)
- add %o0, 2, %o0
- sll %g4, 16, %g4
- addcc %g4, %g7, %g7
- add %o1, 2, %o1
- srl %g7, 16, %g3
- addc %g0, %g3, %g4
- sll %g7, 16, %g7
- sll %g4, 16, %g3
- srl %g7, 16, %g7
- andcc %o0, 0x4, %g0
- or %g3, %g7, %g7
-1: be,pt %icc, 3f
- andn %g1, 0x7f, %g2
- EX(lda [%o0 + 0x00] %asi, %g4, add %g1, 0,#)
- sub %g1, 4, %g1
- EX2(st %g4, [%o1 + 0x00],#)
- add %o0, 4, %o0
- addcc %g4, %g7, %g7
- add %o1, 4, %o1
- andn %g1, 0x7f, %g2
- addc %g0, %g7, %g7
+cc_fixit:
+ bl,a,pn %icc, ccte ! CTI
+ andcc %g1, 0xf, %o3 ! IEU1 Group
+ andcc %o0, 1, %g0 ! IEU1 Group
+ bne,pn %icc, ccslow ! CTI
+ andcc %o0, 2, %g0 ! IEU1 Group
+ be,pn %icc, 1f ! CTI
+ andcc %o0, 0x4, %g0 ! IEU1 Group
+ lduha [%o0 + 0x00] %asi, %g4 ! Load
+ sub %g1, 2, %g1 ! IEU0
+ add %o0, 2, %o0 ! IEU0 Group
+ add %o1, 2, %o1 ! IEU1
+ sll %g4, 16, %g3 ! IEU0 Group + 1 bubble
+ addcc %g3, %g7, %g7 ! IEU1
+ bcc,pt %xcc, 0f ! CTI
+ srl %g7, 16, %g3 ! IEU0 Group
+ add %g3, 1, %g3 ! IEU0 4 clocks (mispredict)
+0: andcc %o0, 0x4, %g0 ! IEU1 Group
+ sth %g4, [%o1 - 0x2] ! Store
+ sll %g7, 16, %g7 ! IEU0
+ sll %g3, 16, %g3 ! IEU0 Group
+ srl %g7, 16, %g7 ! IEU0 Group
+ or %g3, %g7, %g7 ! IEU0 Group (regdep)
+1: be,pt %icc, cc_dword_aligned ! CTI
+ andn %g1, 0xff, %g2 ! IEU1
+ lduwa [%o0 + 0x00] %asi, %g4 ! Load Group
+ sub %g1, 4, %g1 ! IEU0
+ add %o0, 4, %o0 ! IEU1
+ add %o1, 4, %o1 ! IEU0 Group
+ addcc %g4, %g7, %g7 ! IEU1 Group + 1 bubble
+ stw %g4, [%o1 - 0x4] ! Store
+ bcc,pt %xcc, cc_dword_aligned ! CTI
+ andn %g1, 0xff, %g2 ! IEU0 Group
+ b,pt %xcc, cc_dword_aligned ! CTI 4 clocks (mispredict)
+ add %g7, 1, %g7 ! IEU0
+
+ .align 32
+ .globl __csum_partial_copy_sparc_generic, csum_partial_copy
+csum_partial_copy:
+__csum_partial_copy_sparc_generic: /* %o0=src, %o1=dest, %g1=len, %g7=sum */
+ xorcc %o0, %o1, %o4 ! IEU1 Group
+ srl %g7, 0, %g7 ! IEU0
+ andcc %o4, 3, %g0 ! IEU1 Group
+ srl %g1, 0, %g1 ! IEU0
+ bne,pn %icc, ccslow ! CTI
+ andcc %o0, 7, %g0 ! IEU1 Group
+ be,pt %icc, cc_dword_aligned ! CTI
+ andn %g1, 0xff, %g2 ! IEU0
+ b,pt %xcc, cc_fixit ! CTI Group
+ cmp %g1, 6 ! IEU1
cc_dword_aligned:
-3: brz,pn %g2, 3f ! nope, less than one loop remains
- andcc %o1, 4, %g0 ! dest aligned on 4 or 8 byte boundry?
- be,pn %icc, ccdbl + 4 ! 8 byte aligned, kick ass
-5: CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x00,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
- CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x20,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
- CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x40,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
- CSUMCOPY_BIGCHUNK(%o0,%o1,%g7,0x60,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
-10: EXT(5b, 10b, 20f,#) ! note for exception handling
- sub %g1, 128, %g1 ! detract from length
- addc %g0, %g7, %g7 ! add in last carry bit
- andncc %g1, 0x7f, %g0 ! more to csum?
- add %o0, 128, %o0 ! advance src ptr
- bne,pt %icc, 5b ! we did not go negative, continue looping
- add %o1, 128, %o1 ! advance dest ptr
-3: andcc %g1, 0x70, %o2 ! can use table?
-ccmerge:be,pn %icc, ccte ! nope, go and check for end cruft
- andcc %g1, 0xf, %o3 ! get low bits of length (clears carry btw)
- srl %o2, 1, %o4 ! begin negative offset computation
-13: rd %pc, %o5 ! set up table ptr end
- add %o0, %o2, %o0 ! advance src ptr
- sub %o5, %o4, %o5 ! continue table calculation
- sll %o2, 1, %g2 ! constant multiplies are fun...
- sub %o5, %g2, %o5 ! some more adjustments
- jmpl %o5 + (12f-13b), %g0 ! jump into it, duff style, wheee...
- add %o1, %o2, %o1 ! advance dest ptr (carry is clear btw)
-cctbl: CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x68,%g2,%g3,%g4,%g5)
- CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x58,%g2,%g3,%g4,%g5)
- CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x48,%g2,%g3,%g4,%g5)
- CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x38,%g2,%g3,%g4,%g5)
- CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x28,%g2,%g3,%g4,%g5)
- CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x18,%g2,%g3,%g4,%g5)
- CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x08,%g2,%g3,%g4,%g5)
-12: EXT(cctbl, 12b, 22f,#) ! note for exception table handling
- addc %g0, %g7, %g7
- andcc %g1, 0xf, %o3 ! check for low bits set
-ccte: bne,pn %icc, cc_end_cruft ! something left, handle it out of band
- sethi %uhi(KERNBASE), %g4 ! restore gfp
- mov %g7, %o0 ! give em the computed checksum
- sllx %g4, 32, %g4 ! finish gfp restoration
- retl ! return
- srl %o0, 0, %o0
-ccdbl: CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x00,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
- CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x20,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
- CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x40,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
- CSUMCOPY_BIGCHUNK_ALIGNED(%o0,%o1,%g7,0x60,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
-11: EXT(ccdbl, 11b, 21f,#) ! note for exception table handling
- sub %g1, 128, %g1 ! detract from length
- addc %g0, %g7, %g7 ! add in last carry bit
- andncc %g1, 0x7f, %g0 ! more to csum?
- add %o0, 128, %o0 ! advance src ptr
- bne,pt %icc, ccdbl ! we did not go negative, continue looping
- add %o1, 128, %o1 ! advance dest ptr
- ba,pt %xcc, ccmerge ! finish it off, above
- andcc %g1, 0x70, %o2 ! can use table? (clears carry btw)
+ brz,pn %g2, 3f ! CTI Group
+ andcc %o1, 4, %g0 ! IEU1 Group (brz uses IEU1)
+ be,pn %icc, ccdbl + 4 ! CTI
+5: CSUMCOPY_ECACHE_LOAD( %o0, 0x00, %o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+ CSUMCOPY_EC_STUNALIGN_LDNXT(%o0,%o1,0x40,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+ CSUMCOPY_EC_STUNALIGN_LDNXT(%o0,%o1,0x80,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+ CSUMCOPY_EC_STUNALIGN_LDNXT(%o0,%o1,0xc0,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+ CSUMCOPY_EC_STUNALIGN( %o1,0xc0,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+10:
+ sub %g1, 256, %g1 ! IEU0 Group
+ add %o0, 256, %o0 ! IEU1
+ andncc %g1, 0xff, %g0 ! IEU1 Group
+ bne,pt %icc, 5b ! CTI
+ add %o1, 256, %o1 ! IEU0
+3: andcc %g1, 0xf0, %o2 ! IEU1 Group
+ccmerge:be,pn %icc, ccte ! CTI
+ andcc %g1, 0xf, %o3 ! IEU1 Group
+ sll %o2, 2, %o4 ! IEU0
+13: rd %pc, %o5 ! LSU Group + 4 clocks
+ add %o0, %o2, %o0 ! IEU0 Group
+ sub %o5, %o4, %o5 ! IEU1 Group
+ jmpl %o5 + (12f - 13b), %g0 ! CTI Group brk forced
+ add %o1, %o2, %o1 ! IEU0 Group
+cctbl: CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0xe8,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0xd8,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0xc8,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0xb8,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0xa8,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x98,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x88,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x78,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x68,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x58,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x48,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x38,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x28,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x18,%g2,%g3)
+ CSUMCOPY_LASTCHUNK(%o0,%o1,%g7,0x08,%g2,%g3)
+12:
+ andcc %g1, 0xf, %o3 ! IEU1 Group
+ccte: bne,pn %icc, cc_end_cruft ! CTI
+ sethi %uhi(PAGE_OFFSET), %g4 ! IEU0
+ccfold: sllx %g7, 32, %o0 ! IEU0 Group
+ addcc %g7, %o0, %o0 ! IEU1 Group (regdep)
+ srlx %o0, 32, %o0 ! IEU0 Group (regdep)
+ bcs,a,pn %xcc, 1f ! CTI
+ add %o0, 1, %o0 ! IEU1 4 clocks (mispredict)
+1: retl ! CTI Group brk forced
+ sllx %g4, 32,%g4 ! IEU0 Group
+ccdbl: CSUMCOPY_ECACHE_LOAD( %o0, 0x00, %o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+ CSUMCOPY_EC_STALIGNED_LDNXT(%o0,%o1,0x40,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+ CSUMCOPY_EC_STALIGNED_LDNXT(%o0,%o1,0x80,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+ CSUMCOPY_EC_STALIGNED_LDNXT(%o0,%o1,0xc0,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+ CSUMCOPY_EC_STALIGNED( %o1,0xc0,%g7,%o4,%o5,%g2,%g3,%g4,%g5,%o2,%o3)
+11:
+ sub %g1, 256, %g1 ! IEU0 Group
+ add %o0, 256, %o0 ! IEU1
+ andncc %g1, 0xff, %g0 ! IEU1 Group
+ bne,pt %icc, ccdbl ! CTI
+ add %o1, 256, %o1 ! IEU0
+ b,pt %xcc, ccmerge ! CTI Group
+ andcc %g1, 0xf0, %o2 ! IEU1
ccslow: mov 0, %g5
brlez,pn %g1, 4f
be,a,pt %icc, 1f
srl %g1, 1, %o3
sub %g1, 1, %g1
- EX(lduba [%o0] %asi, %g5, add %g1, 1,#)
+ lduba [%o0] %asi, %g5
add %o0, 1, %o0
- EX2(stb %g5, [%o1],#)
+ stb %g5, [%o1]
srl %g1, 1, %o3
add %o1, 1, %o1
1: brz,a,pn %o3, 3f
andcc %o0, 2, %g0
be,a,pt %icc, 1f
srl %o3, 1, %o3
- EX(lduha [%o0] %asi, %o4, add %g1, 0,#)
+ lduha [%o0] %asi, %o4
sub %g1, 2, %g1
srl %o4, 8, %g2
sub %o3, 1, %o3
- EX2(stb %g2, [%o1],#)
+ stb %g2, [%o1]
add %o4, %g5, %g5
- EX2(stb %o4, [%o1 + 1],#)
+ stb %o4, [%o1 + 1]
add %o0, 2, %o0
srl %o3, 1, %o3
add %o1, 2, %o1
1: brz,a,pn %o3, 2f
andcc %g1, 2, %g0
- EX3(lda [%o0] %asi, %o4,#)
+ lda [%o0] %asi, %o4
5: srl %o4, 24, %g2
srl %o4, 16, %g3
- EX2(stb %g2, [%o1],#)
+ stb %g2, [%o1]
srl %o4, 8, %g2
- EX2(stb %g3, [%o1 + 1],#)
+ stb %g3, [%o1 + 1]
add %o0, 4, %o0
- EX2(stb %g2, [%o1 + 2],#)
+ stb %g2, [%o1 + 2]
addcc %o4, %g5, %g5
- EX2(stb %o4, [%o1 + 3],#)
+ stb %o4, [%o1 + 3]
addc %g5, %g0, %g5 ! I am now to lazy to optimize this (question is if it
add %o1, 4, %o1 ! is worthy). Maybe some day - with the sll/srl
subcc %o3, 1, %o3 ! tricks
bne,a,pt %icc, 5b
- EX3(lda [%o0] %asi, %o4,#)
+ lda [%o0] %asi, %o4
sll %g5, 16, %g2
srl %g5, 16, %g5
srl %g2, 16, %g2
add %g2, %g5, %g5
2: be,a,pt %icc, 3f
andcc %g1, 1, %g0
- EX(lduha [%o0] %asi, %o4, and %g1, 3,#)
+ lduha [%o0] %asi, %o4
andcc %g1, 1, %g0
srl %o4, 8, %g2
add %o0, 2, %o0
- EX2(stb %g2, [%o1],#)
+ stb %g2, [%o1]
add %g5, %o4, %g5
- EX2(stb %o4, [%o1 + 1],#)
+ stb %o4, [%o1 + 1]
add %o1, 2, %o1
3: be,a,pt %icc, 1f
sll %g5, 16, %o4
- EX(lduba [%o0] %asi, %g2, add %g0, 1,#)
+ lduba [%o0] %asi, %g2
sll %g2, 8, %o4
- EX2(stb %g2, [%o1],#)
+ stb %g2, [%o1]
add %g5, %o4, %g5
sll %g5, 16, %o4
1: addcc %o4, %g5, %g5
retl
srl %o0, 0, %o0
__csum_partial_copy_end:
-
- .section .fixup,#alloc,#execinstr
- .align 4
-/* We do these strange calculations for the csum_*_from_user case only, ie.
- * we only bother with faults on loads... */
-
-/* o2 = ((g2%20)&3)*8
- * o3 = g1 - (g2/20)*32 - o2 */
-20:
- cmp %g2, 20
- blu,a,pn %icc, 1f
- and %g2, 3, %o2
- sub %g1, 32, %g1
- ba,pt %xcc, 20b
- sub %g2, 20, %g2
-1:
- sll %o2, 3, %o2
- ba,pt %xcc, 31f
- sub %g1, %o2, %o3
-
-/* o2 = (!(g2 & 15) ? 0 : (((g2 & 15) + 1) & ~1)*8)
- * o3 = g1 - (g2/16)*32 - o2 */
-21:
- andcc %g2, 15, %o3
- srl %g2, 4, %g2
- be,a,pn %icc, 1f
- clr %o2
- add %o3, 1, %o3
- and %o3, 14, %o3
- sll %o3, 3, %o2
-1:
- sll %g2, 5, %g2
- sub %g1, %g2, %o3
- ba,pt %xcc, 31f
- sub %o3, %o2, %o3
-
-/* o0 += (g2/10)*16 - 0x70
- * 01 += (g2/10)*16 - 0x70
- * o2 = (g2 % 10) ? 8 : 0
- * o3 += 0x70 - (g2/10)*16 - o2 */
-22:
- cmp %g2, 10
- blu,a,pt %xcc, 1f
- sub %o0, 0x70, %o0
- add %o0, 16, %o0
- add %o1, 16, %o1
- sub %o3, 16, %o3
- ba,pt %xcc, 22b
- sub %g2, 10, %g2
-1:
- sub %o1, 0x70, %o1
- add %o3, 0x70, %o3
- clr %o2
- movrnz %g2, 8, %o2
- ba,pt %xcc, 31f
- sub %o3, %o2, %o3
-96:
- and %g1, 3, %g1
- sll %o3, 2, %o3
- add %g1, %o3, %o3
-30:
-/* %o1 is dst
- * %o3 is # bytes to zero out
- * %o4 is faulting address
- * %o5 is %pc where fault occured */
- clr %o2
-31:
-/* %o0 is src
- * %o1 is dst
- * %o2 is # of bytes to copy from src to dst
- * %o3 is # bytes to zero out
- * %o4 is faulting address
- * %o5 is %pc where fault occured */
- save %sp, -136, %sp
- mov %i5, %o0
- mov %i7, %o1
- mov %i4, %o2
- call lookup_fault
- mov %g7, %i4
- cmp %o0, 2
- bne,pn %icc, 1f
- add %g0, -EFAULT, %i5
- brz,pn %i2, 2f
- mov %i0, %o1
- mov %i1, %o0
- call __copy_from_user
- mov %i2, %o2
- brnz,a,pn %o0, 2f
- add %i3, %i2, %i3
- add %i1, %i2, %i1
-2:
- mov %i1, %o0
- wr %g0, ASI_S, %asi
- call __bzero_noasi
- mov %i3, %o1
-1:
- ldx [%sp + STACK_BIAS + 264], %o2 ! struct_ptr of parent
- st %i5, [%o2]
- ret
- restore
+++ /dev/null
-/* copy_user.S: Sparc optimized copy_from_user code.
- *
- * Copyright(C) 1995 Linus Torvalds
- * Copyright(C) 1996 David S. Miller
- * Copyright(C) 1996 Eddie C. Dost
- * Copyright(C) 1996,1997 Jakub Jelinek
- *
- * derived from:
- * e-mail between David and Eddie.
- *
- * Returns 0 if successful, otherwise count of bytes not copied yet
- *
- * FIXME: This code should be optimized for sparc64... -jj
- */
-
-#include <asm/ptrace.h>
-#include <asm/asi.h>
-#include <asm/head.h>
-
-#define PRE_RETL sethi %uhi(KERNBASE), %g4; sllx %g4, 32, %g4;
-
-#define EX(x,y,a,b,z) \
-98: x,y; \
- .section .fixup,z##alloc,z##execinstr; \
- .align 4; \
-99: PRE_RETL \
- retl; \
- a, b, %o0; \
- .section __ex_table,z##alloc; \
- .align 8; \
- .xword 98b, 99b; \
- .text; \
- .align 4
-
-#define EX2(x,y,c,d,e,a,b,z) \
-98: x,y; \
- .section .fixup,z##alloc,z##execinstr; \
- .align 4; \
-99: c, d, e; \
- PRE_RETL \
- retl; \
- a, b, %o0; \
- .section __ex_table,z##alloc; \
- .align 8; \
- .xword 98b, 99b; \
- .text; \
- .align 4
-
-#define EXO2(x,y,z) \
-98: x,##y; \
- .section __ex_table,z##alloc; \
- .align 8; \
- .xword 98b, 97f; \
- .text; \
- .align 4
-
-#define EXT(start,end,handler,z) \
- .section __ex_table,z##alloc; \
- .align 8; \
- .xword start, 0, end, handler; \
- .text; \
- .align 4
-
-/* Please do not change following macros unless you change logic used
- * in .fixup at the end of this file as well
- */
-
-/* Both these macros have to start with exactly the same insn */
-#define MOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
- ldda [%src + offset + 0x00] %asi, %t0; \
- ldda [%src + offset + 0x08] %asi, %t2; \
- ldda [%src + offset + 0x10] %asi, %t4; \
- ldda [%src + offset + 0x18] %asi, %t6; \
- st %t0, [%dst + offset + 0x00]; \
- st %t1, [%dst + offset + 0x04]; \
- st %t2, [%dst + offset + 0x08]; \
- st %t3, [%dst + offset + 0x0c]; \
- st %t4, [%dst + offset + 0x10]; \
- st %t5, [%dst + offset + 0x14]; \
- st %t6, [%dst + offset + 0x18]; \
- st %t7, [%dst + offset + 0x1c];
-
-#define MOVE_BIGALIGNCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
- ldda [%src + offset + 0x00] %asi, %t0; \
- ldda [%src + offset + 0x08] %asi, %t2; \
- ldda [%src + offset + 0x10] %asi, %t4; \
- ldda [%src + offset + 0x18] %asi, %t6; \
- std %t0, [%dst + offset + 0x00]; \
- std %t2, [%dst + offset + 0x08]; \
- std %t4, [%dst + offset + 0x10]; \
- std %t6, [%dst + offset + 0x18];
-
-#define MOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) \
- ldda [%src - offset - 0x10] %asi, %t0; \
- ldda [%src - offset - 0x08] %asi, %t2; \
- st %t0, [%dst - offset - 0x10]; \
- st %t1, [%dst - offset - 0x0c]; \
- st %t2, [%dst - offset - 0x08]; \
- st %t3, [%dst - offset - 0x04];
-
-#define MOVE_HALFCHUNK(src, dst, offset, t0, t1, t2, t3) \
- lduha [%src + offset + 0x00] %asi, %t0; \
- lduha [%src + offset + 0x02] %asi, %t1; \
- lduha [%src + offset + 0x04] %asi, %t2; \
- lduha [%src + offset + 0x06] %asi, %t3; \
- sth %t0, [%dst + offset + 0x00]; \
- sth %t1, [%dst + offset + 0x02]; \
- sth %t2, [%dst + offset + 0x04]; \
- sth %t3, [%dst + offset + 0x06];
-
-#define MOVE_SHORTCHUNK(src, dst, offset, t0, t1) \
- lduba [%src - offset - 0x02] %asi, %t0; \
- lduba [%src - offset - 0x01] %asi, %t1; \
- stb %t0, [%dst - offset - 0x02]; \
- stb %t1, [%dst - offset - 0x01];
-
- .text
- .align 4
-
- .globl __copy_from_user
-dword_align:
- andcc %o1, 1, %g0
- be 4f
- andcc %o1, 2, %g0
-
- EXO2(lduba [%o1] %asi, %g2,#)
- add %o1, 1, %o1
- stb %g2, [%o0]
- sub %o2, 1, %o2
- bne 3f
- add %o0, 1, %o0
-
- EXO2(lduha [%o1] %asi, %g2,#)
- add %o1, 2, %o1
- sth %g2, [%o0]
- sub %o2, 2, %o2
- ba,pt %xcc, 3f
- add %o0, 2, %o0
-4:
- EXO2(lduha [%o1] %asi, %g2,#)
- add %o1, 2, %o1
- sth %g2, [%o0]
- sub %o2, 2, %o2
- ba,pt %xcc, 3f
- add %o0, 2, %o0
-
-__copy_from_user: /* %o0=dst %o1=src %o2=len */
- wr %g0, ASI_S, %asi
- xor %o0, %o1, %o4
-1:
- andcc %o4, 3, %o5
-2:
- bne,pn %icc, cannot_optimize
- cmp %o2, 15
-
- bleu,pn %xcc, short_aligned_end
- andcc %o1, 3, %g0
-
- bne,pn %icc, dword_align
-3:
- andcc %o1, 4, %g0
-
- be,pt %icc, 2f
- mov %o2, %g1
-
- EXO2(lda [%o1] %asi, %o4,#)
- sub %g1, 4, %g1
- st %o4, [%o0]
- add %o1, 4, %o1
- add %o0, 4, %o0
-2:
- andcc %g1, 0xffffffffffffff80, %g7
- be,pn %xcc, 3f
- andcc %o0, 4, %g0
-
- be,pn %icc, ldd_std + 4
-5:
- MOVE_BIGCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5)
- MOVE_BIGCHUNK(o1, o0, 0x20, o2, o3, o4, o5, g2, g3, g4, g5)
- MOVE_BIGCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5)
- MOVE_BIGCHUNK(o1, o0, 0x60, o2, o3, o4, o5, g2, g3, g4, g5)
-80:
- EXT(5b, 80b, 50f,#)
- subcc %g7, 128, %g7
- add %o1, 128, %o1
- bne,pt %xcc, 5b
- add %o0, 128, %o0
-3:
- andcc %g1, 0x70, %g7
- be,pn %icc, copy_user_table_end
- andcc %g1, 8, %g0
-100:
- rd %pc, %o5
- srl %g7, 1, %o4
- add %g7, %o4, %o4
- add %o1, %g7, %o1
- sub %o5, %o4, %o5
- jmpl %o5 + (copy_user_table_end - 100b), %g0
- add %o0, %g7, %o0
-
-copy_user_table:
- MOVE_LASTCHUNK(o1, o0, 0x60, g2, g3, g4, g5)
- MOVE_LASTCHUNK(o1, o0, 0x50, g2, g3, g4, g5)
- MOVE_LASTCHUNK(o1, o0, 0x40, g2, g3, g4, g5)
- MOVE_LASTCHUNK(o1, o0, 0x30, g2, g3, g4, g5)
- MOVE_LASTCHUNK(o1, o0, 0x20, g2, g3, g4, g5)
- MOVE_LASTCHUNK(o1, o0, 0x10, g2, g3, g4, g5)
- MOVE_LASTCHUNK(o1, o0, 0x00, g2, g3, g4, g5)
-copy_user_table_end:
- EXT(copy_user_table, copy_user_table_end, 51f,#)
- be,pt %icc, copy_user_last7
- andcc %g1, 4, %g0
-
- EX(ldda [%o1] %asi, %g2, and %g1, 0xf,#)
- add %o0, 8, %o0
- add %o1, 8, %o1
- st %g2, [%o0 - 0x08]
- st %g3, [%o0 - 0x04]
-copy_user_last7:
- be,pn %icc, 1f
- andcc %g1, 2, %g0
-
- EX(lda [%o1] %asi, %g2, and %g1, 7,#)
- add %o1, 4, %o1
- st %g2, [%o0]
- add %o0, 4, %o0
-1:
- be,pn %icc, 1f
- andcc %g1, 1, %g0
-
- EX(lduha [%o1] %asi, %g2, and %g1, 3,#)
- add %o1, 2, %o1
- sth %g2, [%o0]
- add %o0, 2, %o0
-1:
- be,pn %icc, 1f
- nop
-
- EX(lduba [%o1] %asi, %g2, add %g0, 1,#)
- stb %g2, [%o0]
-1:
- PRE_RETL
- retl
- clr %o0
-
-ldd_std:
- MOVE_BIGALIGNCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5)
- MOVE_BIGALIGNCHUNK(o1, o0, 0x20, o2, o3, o4, o5, g2, g3, g4, g5)
- MOVE_BIGALIGNCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5)
- MOVE_BIGALIGNCHUNK(o1, o0, 0x60, o2, o3, o4, o5, g2, g3, g4, g5)
-81:
- EXT(ldd_std, 81b, 52f,#)
- subcc %g7, 128, %g7
- add %o1, 128, %o1
- bne,pt %xcc, ldd_std
- add %o0, 128, %o0
-
- andcc %g1, 0x70, %g7
- be,pn %icc, copy_user_table_end
- andcc %g1, 8, %g0
-101:
- rd %pc, %o5
- srl %g7, 1, %o4
- add %g7, %o4, %o4
- add %o1, %g7, %o1
- sub %o5, %o4, %o5
- jmpl %o5 + (copy_user_table_end - 101b), %g0
- add %o0, %g7, %o0
-
-cannot_optimize:
- bleu short_end
- cmp %o5, 2
-
- bne byte_chunk
- and %o2, 0xfffffffffffffff0, %o3
-
- andcc %o1, 1, %g0
- be 10f
- nop
-
- EXO2(lduba [%o1] %asi, %g2,#)
- add %o1, 1, %o1
- stb %g2, [%o0]
- sub %o2, 1, %o2
- andcc %o2, 0xfffffffffffffff0, %o3
- be short_end
- add %o0, 1, %o0
-10:
- MOVE_HALFCHUNK(o1, o0, 0x00, g2, g3, g4, g5)
- MOVE_HALFCHUNK(o1, o0, 0x08, g2, g3, g4, g5)
-82:
- EXT(10b, 82b, 53f,#)
- subcc %o3, 0x10, %o3
- add %o1, 0x10, %o1
- bne 10b
- add %o0, 0x10, %o0
- ba,pt %xcc, 2f
- and %o2, 0xe, %o3
-
-byte_chunk:
- MOVE_SHORTCHUNK(o1, o0, -0x02, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, -0x04, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, -0x06, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, -0x08, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, -0x0a, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, -0x0c, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, -0x0e, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, -0x10, g2, g3)
-83:
- EXT(byte_chunk, 83b, 54f,#)
- subcc %o3, 0x10, %o3
- add %o1, 0x10, %o1
- bne,pt %xcc, byte_chunk
- add %o0, 0x10, %o0
-
-short_end:
- and %o2, 0xe, %o3
-2:
- rd %pc, %o5
- sll %o3, 3, %o4
- add %o0, %o3, %o0
- sub %o5, %o4, %o5
- add %o1, %o3, %o1
- jmpl %o5 + (short_table_end - 2b), %g0
- andcc %o2, 1, %g0
-84:
- MOVE_SHORTCHUNK(o1, o0, 0x0c, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, 0x0a, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, 0x08, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, 0x06, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, 0x04, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, 0x02, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, 0x00, g2, g3)
-short_table_end:
- EXT(84b, short_table_end, 55f,#)
- be 1f
- nop
- EX(lduba [%o1] %asi, %g2, add %g0, 1,#)
- stb %g2, [%o0]
-1:
- PRE_RETL
- retl
- clr %o0
-
-short_aligned_end:
- bne short_end
- andcc %o2, 8, %g0
-
- be 1f
- andcc %o2, 4, %g0
-
- EXO2(lda [%o1 + 0x00] %asi, %g2,#)
- EX(lda [%o1 + 0x04] %asi, %g3, sub %o2, 4,#)
- add %o1, 8, %o1
- st %g2, [%o0 + 0x00]
- st %g3, [%o0 + 0x04]
- add %o0, 8, %o0
-1:
- ba,pt %xcc, copy_user_last7
- mov %o2, %g1
-
- .section .fixup,#alloc,#execinstr
- .align 4
-97:
- PRE_RETL
- retl
- mov %o2, %o0
-/* exception routine sets %g2 to (broken_insn - first_insn)>>2 */
-50:
-/* This magic counts how many bytes are left when crash in MOVE_BIGCHUNK
- * happens. This is derived from the amount ldd reads, st stores, etc.
- * x = g2 % 12;
- * o0 = g1 + g7 - ((g2 / 12) * 32 + (x < 4) ? x * 8 : (x - 4) * 4)
- */
- cmp %g2, 12
- bcs 1f
- cmp %g2, 24
- bcs 2f
- cmp %g2, 36
- bcs 3f
- nop
- sub %g2, 12, %g2
- sub %g7, 32, %g7
-3:
- sub %g2, 12, %g2
- sub %g7, 32, %g7
-2:
- sub %g2, 12, %g2
- sub %g7, 32, %g7
-1:
- cmp %g2, 4
- bcs,a 1f
- sll %g2, 3, %g2
- sub %g2, 4, %g2
- sll %g2, 2, %g2
-1:
- and %g1, 0x7f, %o0
- add %o0, %g7, %o0
- PRE_RETL
- retl
- sub %o0, %g2, %o0
-51:
-/* i = 41 - g2; j = i % 6;
- * o0 = (g1 & 15) + (i / 6) * 16 + (j < 4) ? (j + 1) * 4 : (j - 3) * 8;
- */
- neg %g2
- and %g1, 0xf, %g1
- add %g2, 41, %g2
-1:
- cmp %g2, 6
- bcs,a 2f
- cmp %g2, 4
- add %g1, 16, %g1
- b 1b
- sub %g2, 6, %g2
-2:
- bcs,a 3f
- inc %g2
- sub %g2, 3, %g2
- b 2f
- sll %g2, 3, %g2
-3:
- sll %g2, 2, %g2
-2:
- PRE_RETL
- retl
- add %g1, %g2, %o0
-52:
-/* o0 = g1 + g7 - (g2 / 8) * 32 + (x & 3) * 8 */
- and %g2, 0xfffffffffffffff8, %g4
- and %g2, 3, %g2
- sll %g4, 2, %g4
- sll %g2, 3, %g2
- add %g2, %g4, %g2
- b,a 1b
-53:
-/* o0 = o3 + (o2 & 15) - (g2 & 8) - (g2 & 3) * 2 */
- and %g2, 3, %g4
- and %g2, 0xfffffffffffffff8, %g2
- sll %g4, 1, %g4
- add %g2, %g4, %g2
- and %o2, 0xf, %o0
- add %o0, %o3, %o0
- PRE_RETL
- retl
- sub %o0, %g2, %o0
-54:
-/* o0 = o3 + (o2 & 15) - (g2 / 4) * 2 - (g2 & 1) */
- srl %g2, 2, %o4
- and %g2, 1, %o1
- sll %o4, 1, %o4
- and %o2, 0xf, %o2
- sub %o3, %o1, %o3
- sub %o2, %o4, %o2
- PRE_RETL
- retl
- add %o2, %o3, %o0
-55:
-/* o0 = (o2 & 1) + (27 - g2)/4 * 2 + ((27 - g2) & 1) */
- neg %g2
- and %o2, 1, %o2
- add %g2, 27, %g2
- srl %g2, 2, %o1
- and %g2, 1, %g2
- sll %o1, 1, %o1
- add %o2, %g2, %o0
- PRE_RETL
- retl
- add %o0, %o1, %o0
+++ /dev/null
-/* copy_user.S: Sparc optimized copy_to_user code.
- *
- * Copyright(C) 1995 Linus Torvalds
- * Copyright(C) 1996 David S. Miller
- * Copyright(C) 1996 Eddie C. Dost
- * Copyright(C) 1996,1997 Jakub Jelinek
- *
- * derived from:
- * e-mail between David and Eddie.
- *
- * Returns 0 if successful, otherwise count of bytes not copied yet
- *
- * FIXME: This code should be optimized for sparc64... -jj
- */
-
-#include <asm/ptrace.h>
-#include <asm/head.h>
-#include <asm/asi.h>
-
-#define PRE_RETL sethi %uhi(KERNBASE), %g4; sllx %g4, 32, %g4;
-
-#define EX(x,y,a,b,z) \
-98: x,y; \
- .section .fixup,z##alloc,z##execinstr; \
- .align 4; \
-99: PRE_RETL \
- retl; \
- a, b, %o0; \
- .section __ex_table,z##alloc; \
- .align 8; \
- .xword 98b, 99b; \
- .text; \
- .align 4
-
-#define EX2(x,y,c,d,e,a,b,z) \
-98: x,y; \
- .section .fixup,z##alloc,z##execinstr; \
- .align 4; \
-99: c, d, e; \
- PRE_RETL \
- retl; \
- a, b, %o0; \
- .section __ex_table,z##alloc; \
- .align 8; \
- .xword 98b, 99b; \
- .text; \
- .align 4
-
-#define EXO2(x,y,z) \
-98: x,##y; \
- .section __ex_table,z##alloc; \
- .align 8; \
- .xword 98b, 97f; \
- .text; \
- .align 4
-
-#define EXT(start,end,handler,z) \
- .section __ex_table,z##alloc; \
- .align 8; \
- .xword start, 0, end, handler; \
- .text; \
- .align 4
-
-/* Please do not change following macros unless you change logic used
- * in .fixup at the end of this file as well
- */
-
-/* Both these macros have to start with exactly the same insn */
-#define MOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
- ldd [%src + offset + 0x00], %t0; \
- ldd [%src + offset + 0x08], %t2; \
- ldd [%src + offset + 0x10], %t4; \
- ldd [%src + offset + 0x18], %t6; \
- sta %t0, [%dst + offset + 0x00] %asi; \
- sta %t1, [%dst + offset + 0x04] %asi; \
- sta %t2, [%dst + offset + 0x08] %asi; \
- sta %t3, [%dst + offset + 0x0c] %asi; \
- sta %t4, [%dst + offset + 0x10] %asi; \
- sta %t5, [%dst + offset + 0x14] %asi; \
- sta %t6, [%dst + offset + 0x18] %asi; \
- sta %t7, [%dst + offset + 0x1c] %asi;
-
-#define MOVE_BIGALIGNCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
- ldd [%src + offset + 0x00], %t0; \
- ldd [%src + offset + 0x08], %t2; \
- ldd [%src + offset + 0x10], %t4; \
- ldd [%src + offset + 0x18], %t6; \
- stda %t0, [%dst + offset + 0x00] %asi; \
- stda %t2, [%dst + offset + 0x08] %asi; \
- stda %t4, [%dst + offset + 0x10] %asi; \
- stda %t6, [%dst + offset + 0x18] %asi;
-
-#define MOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) \
- ldd [%src - offset - 0x10], %t0; \
- ldd [%src - offset - 0x08], %t2; \
- sta %t0, [%dst - offset - 0x10] %asi; \
- sta %t1, [%dst - offset - 0x0c] %asi; \
- sta %t2, [%dst - offset - 0x08] %asi; \
- sta %t3, [%dst - offset - 0x04] %asi;
-
-#define MOVE_HALFCHUNK(src, dst, offset, t0, t1, t2, t3) \
- lduh [%src + offset + 0x00], %t0; \
- lduh [%src + offset + 0x02], %t1; \
- lduh [%src + offset + 0x04], %t2; \
- lduh [%src + offset + 0x06], %t3; \
- stha %t0, [%dst + offset + 0x00] %asi; \
- stha %t1, [%dst + offset + 0x02] %asi; \
- stha %t2, [%dst + offset + 0x04] %asi; \
- stha %t3, [%dst + offset + 0x06] %asi;
-
-#define MOVE_SHORTCHUNK(src, dst, offset, t0, t1) \
- ldub [%src - offset - 0x02], %t0; \
- ldub [%src - offset - 0x01], %t1; \
- stba %t0, [%dst - offset - 0x02] %asi; \
- stba %t1, [%dst - offset - 0x01] %asi;
-
- .text
- .align 4
-
- .globl __copy_to_user
-dword_align:
- andcc %o1, 1, %g0
- be 4f
- andcc %o1, 2, %g0
-
- ldub [%o1], %g2
- add %o1, 1, %o1
- EXO2(stba %g2, [%o0] %asi,#)
- sub %o2, 1, %o2
- bne 3f
- add %o0, 1, %o0
-
- lduh [%o1], %g2
- add %o1, 2, %o1
- EXO2(stha %g2, [%o0] %asi,#)
- sub %o2, 2, %o2
- ba,pt %xcc, 3f
- add %o0, 2, %o0
-4:
- lduh [%o1], %g2
- add %o1, 2, %o1
- EXO2(stha %g2, [%o0] %asi,#)
- sub %o2, 2, %o2
- ba,pt %xcc, 3f
- add %o0, 2, %o0
-
-__copy_to_user: /* %o0=dst %o1=src %o2=len */
- wr %g0, ASI_S, %asi
- xor %o0, %o1, %o4
-1:
- andcc %o4, 3, %o5
-2:
- bne,pn %icc, cannot_optimize
- cmp %o2, 15
-
- bleu,pn %xcc, short_aligned_end
- andcc %o1, 3, %g0
-
- bne,pn %icc, dword_align
-3:
- andcc %o1, 4, %g0
-
- be,pt %icc, 2f
- mov %o2, %g1
-
- ld [%o1], %o4
- sub %g1, 4, %g1
- EXO2(sta %o4, [%o0] %asi,#)
- add %o1, 4, %o1
- add %o0, 4, %o0
-2:
- andcc %g1, 0xffffffffffffff80, %g7
- be,pn %xcc, 3f
- andcc %o0, 4, %g0
-
- be,pn %icc, ldd_std + 4
-5:
- MOVE_BIGCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5)
- MOVE_BIGCHUNK(o1, o0, 0x20, o2, o3, o4, o5, g2, g3, g4, g5)
- MOVE_BIGCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5)
- MOVE_BIGCHUNK(o1, o0, 0x60, o2, o3, o4, o5, g2, g3, g4, g5)
-80:
- EXT(5b, 80b, 50f,#)
- subcc %g7, 128, %g7
- add %o1, 128, %o1
- bne,pt %xcc, 5b
- add %o0, 128, %o0
-3:
- andcc %g1, 0x70, %g7
- be,pn %icc, copy_user_table_end
- andcc %g1, 8, %g0
-100:
- rd %pc, %o5
- srl %g7, 1, %o4
- add %g7, %o4, %o4
- add %o1, %g7, %o1
- sub %o5, %o4, %o5
- jmpl %o5 + (copy_user_table_end - 100b), %g0
- add %o0, %g7, %o0
-
-copy_user_table:
- MOVE_LASTCHUNK(o1, o0, 0x60, g2, g3, g4, g5)
- MOVE_LASTCHUNK(o1, o0, 0x50, g2, g3, g4, g5)
- MOVE_LASTCHUNK(o1, o0, 0x40, g2, g3, g4, g5)
- MOVE_LASTCHUNK(o1, o0, 0x30, g2, g3, g4, g5)
- MOVE_LASTCHUNK(o1, o0, 0x20, g2, g3, g4, g5)
- MOVE_LASTCHUNK(o1, o0, 0x10, g2, g3, g4, g5)
- MOVE_LASTCHUNK(o1, o0, 0x00, g2, g3, g4, g5)
-copy_user_table_end:
- EXT(copy_user_table, copy_user_table_end, 51f,#)
- be,pt %icc, copy_user_last7
- andcc %g1, 4, %g0
-
- ldd [%o1], %g2
- add %o0, 8, %o0
- add %o1, 8, %o1
- EX(sta %g2, [%o0 - 0x08] %asi, and %g1, 0xf,#)
- EX2(sta %g3, [%o0 - 0x04] %asi, and %g1, 0xf, %g1, sub %g1, 4,#)
-copy_user_last7:
- be,pn %icc, 1f
- andcc %g1, 2, %g0
-
- ld [%o1], %g2
- add %o1, 4, %o1
- EX(sta %g2, [%o0] %asi, and %g1, 7,#)
- add %o0, 4, %o0
-1:
- be,pn %icc, 1f
- andcc %g1, 1, %g0
-
- lduh [%o1], %g2
- add %o1, 2, %o1
- EX(stha %g2, [%o0] %asi, and %g1, 3,#)
- add %o0, 2, %o0
-1:
- be,pn %icc, 1f
- nop
-
- ldub [%o1], %g2
- EX(stba %g2, [%o0] %asi, add %g0, 1,#)
-1:
- PRE_RETL
- retl
- clr %o0
-
-ldd_std:
- MOVE_BIGALIGNCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5)
- MOVE_BIGALIGNCHUNK(o1, o0, 0x20, o2, o3, o4, o5, g2, g3, g4, g5)
- MOVE_BIGALIGNCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5)
- MOVE_BIGALIGNCHUNK(o1, o0, 0x60, o2, o3, o4, o5, g2, g3, g4, g5)
-81:
- EXT(ldd_std, 81b, 52f,#)
- subcc %g7, 128, %g7
- add %o1, 128, %o1
- bne,pt %xcc, ldd_std
- add %o0, 128, %o0
-
- andcc %g1, 0x70, %g7
- be,pn %icc, copy_user_table_end
- andcc %g1, 8, %g0
-101:
- rd %pc, %o5
- srl %g7, 1, %o4
- add %g7, %o4, %o4
- add %o1, %g7, %o1
- sub %o5, %o4, %o5
- jmpl %o5 + (copy_user_table_end - 101b), %g0
- add %o0, %g7, %o0
-
-cannot_optimize:
- bleu short_end
- cmp %o5, 2
-
- bne byte_chunk
- and %o2, 0xfffffffffffffff0, %o3
-
- andcc %o1, 1, %g0
- be 10f
- nop
-
- ldub [%o1], %g2
- add %o1, 1, %o1
- EXO2(stba %g2, [%o0] %asi,#)
- sub %o2, 1, %o2
- andcc %o2, 0xfffffffffffffff0, %o3
- be short_end
- add %o0, 1, %o0
-10:
- MOVE_HALFCHUNK(o1, o0, 0x00, g2, g3, g4, g5)
- MOVE_HALFCHUNK(o1, o0, 0x08, g2, g3, g4, g5)
-82:
- EXT(10b, 82b, 53f,#)
- subcc %o3, 0x10, %o3
- add %o1, 0x10, %o1
- bne 10b
- add %o0, 0x10, %o0
- ba,pt %xcc, 2f
- and %o2, 0xe, %o3
-
-byte_chunk:
- MOVE_SHORTCHUNK(o1, o0, -0x02, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, -0x04, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, -0x06, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, -0x08, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, -0x0a, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, -0x0c, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, -0x0e, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, -0x10, g2, g3)
-83:
- EXT(byte_chunk, 83b, 54f,#)
- subcc %o3, 0x10, %o3
- add %o1, 0x10, %o1
- bne,pt %xcc, byte_chunk
- add %o0, 0x10, %o0
-
-short_end:
- and %o2, 0xe, %o3
-2:
- rd %pc, %o5
- sll %o3, 3, %o4
- add %o0, %o3, %o0
- sub %o5, %o4, %o5
- add %o1, %o3, %o1
- jmpl %o5 + (short_table_end - 2b), %g0
- andcc %o2, 1, %g0
-84:
- MOVE_SHORTCHUNK(o1, o0, 0x0c, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, 0x0a, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, 0x08, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, 0x06, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, 0x04, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, 0x02, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, 0x00, g2, g3)
-short_table_end:
- EXT(84b, short_table_end, 55f,#)
- be 1f
- nop
- ldub [%o1], %g2
- EX(stba %g2, [%o0] %asi, add %g0, 1,#)
-1:
- PRE_RETL
- retl
- clr %o0
-
-short_aligned_end:
- bne short_end
- andcc %o2, 8, %g0
-
- be 1f
- andcc %o2, 4, %g0
-
- ld [%o1 + 0x00], %g2
- ld [%o1 + 0x04], %g3
- add %o1, 8, %o1
- EXO2(sta %g2, [%o0 + 0x00] %asi,#)
- EX(sta %g3, [%o0 + 0x04] %asi, sub %o2, 4,#)
- add %o0, 8, %o0
-1:
- ba,pt %xcc, copy_user_last7
- mov %o2, %g1
-
- .section .fixup,#alloc,#execinstr
- .align 4
-97:
- PRE_RETL
- retl
- mov %o2, %o0
-/* exception routine sets %g2 to (broken_insn - first_insn)>>2 */
-50:
-/* This magic counts how many bytes are left when crash in MOVE_BIGCHUNK
- * happens. This is derived from the amount ldd reads, st stores, etc.
- * x = g2 % 12;
- * o0 = g1 + g7 - ((g2 / 12) * 32 + (x < 4) ? x * 8 : (x - 4) * 4)
- */
- cmp %g2, 12
- bcs 1f
- cmp %g2, 24
- bcs 2f
- cmp %g2, 36
- bcs 3f
- nop
- sub %g2, 12, %g2
- sub %g7, 32, %g7
-3:
- sub %g2, 12, %g2
- sub %g7, 32, %g7
-2:
- sub %g2, 12, %g2
- sub %g7, 32, %g7
-1:
- cmp %g2, 4
- bcs,a 1f
- sll %g2, 3, %g2
- sub %g2, 4, %g2
- sll %g2, 2, %g2
-1:
- and %g1, 0x7f, %o0
- add %o0, %g7, %o0
- PRE_RETL
- retl
- sub %o0, %g2, %o0
-51:
-/* i = 41 - g2; j = i % 6;
- * o0 = (g1 & 15) + (i / 6) * 16 + (j < 4) ? (j + 1) * 4 : (j - 3) * 8;
- */
- neg %g2
- and %g1, 0xf, %g1
- add %g2, 41, %g2
-1:
- cmp %g2, 6
- bcs,a 2f
- cmp %g2, 4
- add %g1, 16, %g1
- b 1b
- sub %g2, 6, %g2
-2:
- bcs,a 3f
- inc %g2
- sub %g2, 3, %g2
- b 2f
- sll %g2, 3, %g2
-3:
- sll %g2, 2, %g2
-2:
- PRE_RETL
- retl
- add %g1, %g2, %o0
-52:
-/* o0 = g1 + g7 - (g2 / 8) * 32 + (x & 3) * 8 */
- and %g2, 0xfffffffffffffff8, %g4
- and %g2, 3, %g2
- sll %g4, 2, %g4
- sll %g2, 3, %g2
- add %g2, %g4, %g2
- b,a 1b
-53:
-/* o0 = o3 + (o2 & 15) - (g2 & 8) - (g2 & 3) * 2 */
- and %g2, 3, %g4
- and %g2, 0xfffffffffffffff8, %g2
- sll %g4, 1, %g4
- add %g2, %g4, %g2
- and %o2, 0xf, %o0
- add %o0, %o3, %o0
- PRE_RETL
- retl
- sub %o0, %g2, %o0
-54:
-/* o0 = o3 + (o2 & 15) - (g2 / 4) * 2 - (g2 & 1) */
- srl %g2, 2, %o4
- and %g2, 1, %o1
- sll %o4, 1, %o4
- and %o2, 0xf, %o2
- sub %o3, %o1, %o3
- sub %o2, %o4, %o2
- PRE_RETL
- retl
- add %o2, %o3, %o0
-55:
-/* o0 = (o2 & 1) + (27 - g2)/4 * 2 + ((27 - g2) & 1) */
- neg %g2
- and %o2, 1, %o2
- add %g2, 27, %g2
- srl %g2, 2, %o1
- and %g2, 1, %g2
- sll %o1, 1, %o1
- add %o2, %g2, %o0
- PRE_RETL
- retl
- add %o0, %o1, %o0
+++ /dev/null
-/* memcpy.S: Sparc optimized memcpy, bcopy and memmove code
- * Hand optimized from GNU libc's memcpy, bcopy and memmove
- * for UltraSparc
- * Copyright (C) 1991,1996 Free Software Foundation
- * Copyright (C) 1995 Linus Torvalds (Linus.Torvalds@helsinki.fi)
- * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
- * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
- */
-
-#include <asm/asi.h>
-#include <asm/head.h>
-
-#ifdef __KERNEL__
-
-#define FUNC(x) \
- .globl x; \
- .type x,@function; \
- .align 4; \
-x:
-
-#define FASTER_ALIGNED
-
-/* In kernel these functions don't return a value.
- * One should use macros in asm/string.h for that purpose.
- * We return 0, so that bugs are more apparent.
- */
-#define SETUP_RETL
-#define PRE_RETL sethi %uhi(KERNBASE), %g4; clr %o0
-#define RETL_INSN sllx %g4, 32, %g4
-
-#else
-
-/* libc */
-
-#define FASTER_ALIGNED
-
-#ifdef DEBUG
-#define FUNC(x) \
- .globl jj##x##1; \
- .type jj##x##1,@function; \
- .align 4; \
-jj##x##1:
-#else
-#include "DEFS.h"
-#endif
-
-#define SETUP_RETL mov %o0, %g6
-#define PRE_RETL
-#define RETL_INSN mov %g6, %o0
-
-#endif
-
-#define MOVE_BIGCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
- ldd [%src + offset + 0x00], %t0; \
- ldd [%src + offset + 0x08], %t2; \
- ldd [%src + offset + 0x10], %t4; \
- ldd [%src + offset + 0x18], %t6; \
- stw %t0, [%dst + offset + 0x00]; \
- stw %t1, [%dst + offset + 0x04]; \
- stw %t2, [%dst + offset + 0x08]; \
- stw %t3, [%dst + offset + 0x0c]; \
- stw %t4, [%dst + offset + 0x10]; \
- stw %t5, [%dst + offset + 0x14]; \
- stw %t6, [%dst + offset + 0x18]; \
- stw %t7, [%dst + offset + 0x1c];
-
-#define MOVE_BIGALIGNCHUNK(src, dst, offset, t0, t1, t2, t3, t4, t5, t6, t7) \
- ldx [%src + offset + 0x00], %t0; \
- ldx [%src + offset + 0x08], %t1; \
- ldx [%src + offset + 0x10], %t2; \
- ldx [%src + offset + 0x18], %t3; \
- ldx [%src + offset + 0x20], %t4; \
- ldx [%src + offset + 0x28], %t5; \
- ldx [%src + offset + 0x30], %t6; \
- ldx [%src + offset + 0x38], %t7; \
- stx %t0, [%dst + offset + 0x00]; \
- stx %t1, [%dst + offset + 0x08]; \
- stx %t2, [%dst + offset + 0x10]; \
- stx %t3, [%dst + offset + 0x18]; \
- stx %t4, [%dst + offset + 0x20]; \
- stx %t5, [%dst + offset + 0x28]; \
- stx %t6, [%dst + offset + 0x30]; \
- stx %t7, [%dst + offset + 0x38];
-
-#define MOVE_LASTCHUNK(src, dst, offset, t0, t1, t2, t3) \
- ldd [%src - offset - 0x10], %t0; \
- ldd [%src - offset - 0x08], %t2; \
- stw %t0, [%dst - offset - 0x10]; \
- stw %t1, [%dst - offset - 0x0c]; \
- stw %t2, [%dst - offset - 0x08]; \
- stw %t3, [%dst - offset - 0x04];
-
-#define MOVE_LASTALIGNCHUNK(src, dst, offset, t0, t1) \
- ldx [%src - offset - 0x10], %t0; \
- ldx [%src - offset - 0x08], %t1; \
- stx %t0, [%dst - offset - 0x10]; \
- stx %t1, [%dst - offset - 0x08];
-
-#define MOVE_SHORTCHUNK(src, dst, offset, t0, t1) \
- ldub [%src - offset - 0x02], %t0; \
- ldub [%src - offset - 0x01], %t1; \
- stb %t0, [%dst - offset - 0x02]; \
- stb %t1, [%dst - offset - 0x01];
-
- .text
- .align 4
-
-FUNC(bcopy)
-
- mov %o0, %o3
- mov %o1, %o0
- mov %o3, %o1
- brgez,a,pt %o2, 1f
- cmp %o0, %o1
-
- retl
- nop ! Only bcopy returns here and it retuns void...
-
-#ifdef __KERNEL__
-FUNC(amemmove)
-FUNC(__memmove)
-#endif
-FUNC(memmove)
-
- cmp %o0, %o1
-1:
- SETUP_RETL
- bleu,pt %xcc, 9f
- sub %o0, %o1, %o4
-
- add %o1, %o2, %o3
- cmp %o3, %o0
- bleu,pt %xcc, 0f
- andcc %o4, 3, %o5
-
- add %o1, %o2, %o1
- add %o0, %o2, %o0
- sub %o1, 1, %o1
- sub %o0, 1, %o0
-
-1:
- ldub [%o1], %o4
- subcc %o2, 1, %o2
- sub %o1, 1, %o1
- stb %o4, [%o0]
- bne,pt %icc, 1b
- sub %o0, 1, %o0
-
- PRE_RETL
- retl
- RETL_INSN
-
-#ifdef __KERNEL__
-FUNC(__memcpy)
-#endif
-FUNC(memcpy) /* %o0=dst %o1=src %o2=len */
-
- sub %o0, %o1, %o4
- SETUP_RETL
-9:
- andcc %o4, 3, %o5
-0:
- bne,pn %icc, 86f
- cmp %o2, 15
-
- bleu,pn %xcc, 90f
- andcc %o1, 3, %g0
-
- be,a,pt %icc, 3f ! check if we need to align
- andcc %o1, 4, %g0
-
- andcc %o1, 1, %g0
- be,pn %icc, 4f
- andcc %o1, 2, %g0
-
- ldub [%o1], %g2
- add %o1, 1, %o1
- sub %o2, 1, %o2
- stb %g2, [%o0]
- bne,pn %icc, 5f
- add %o0, 1, %o0
-4:
- lduh [%o1], %g2
- add %o1, 2, %o1
- sub %o2, 2, %o2
- sth %g2, [%o0]
- add %o0, 2, %o0
-5:
- andcc %o1, 4, %g0
-3:
- be,pn %icc, 2f
- mov %o2, %g1
-
- lduw [%o1], %o4
- sub %g1, 4, %g1
- stw %o4, [%o0]
- add %o1, 4, %o1
- add %o0, 4, %o0
-2:
- andcc %g1, -128, %g7
- be,pn %xcc, 3f
- andcc %o0, 4, %g0
-
- be,a,pn %icc, 82f + 4
- ldx [%o1], %o2
-5:
- MOVE_BIGCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5)
- MOVE_BIGCHUNK(o1, o0, 0x20, o2, o3, o4, o5, g2, g3, g4, g5)
- MOVE_BIGCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5)
- MOVE_BIGCHUNK(o1, o0, 0x60, o2, o3, o4, o5, g2, g3, g4, g5)
- subcc %g7, 128, %g7
- add %o1, 128, %o1
- bne,pt %xcc, 5b
- add %o0, 128, %o0
-3:
- andcc %g1, 0x70, %g7
- be,pn %icc, 80f
- andcc %g1, 8, %g0
-79:
- rd %pc, %o5
- srl %g7, 1, %o4
- add %g7, %o4, %o4
- add %o1, %g7, %o1
- sub %o5, %o4, %o5
- jmpl %o5 + %lo(80f-79b), %g0
- add %o0, %g7, %o0
-
- MOVE_LASTCHUNK(o1, o0, 0x60, g2, g3, g4, g5)
- MOVE_LASTCHUNK(o1, o0, 0x50, g2, g3, g4, g5)
- MOVE_LASTCHUNK(o1, o0, 0x40, g2, g3, g4, g5)
- MOVE_LASTCHUNK(o1, o0, 0x30, g2, g3, g4, g5)
- MOVE_LASTCHUNK(o1, o0, 0x20, g2, g3, g4, g5)
- MOVE_LASTCHUNK(o1, o0, 0x10, g2, g3, g4, g5)
- MOVE_LASTCHUNK(o1, o0, 0x00, g2, g3, g4, g5)
-
-80: /* memcpy_table_end */
- be,pt %icc, 81f
- andcc %g1, 4, %g0
-
- ldd [%o1], %g2
- add %o0, 8, %o0
- stw %g2, [%o0 - 0x08]
- add %o1, 8, %o1
- stw %g3, [%o0 - 0x04]
-
-81: /* memcpy_last7 */
-
- be,pt %icc, 1f
- andcc %g1, 2, %g0
-
- lduw [%o1], %g2
- add %o1, 4, %o1
- stw %g2, [%o0]
- add %o0, 4, %o0
-1:
- be,pt %icc, 1f
- andcc %g1, 1, %g0
-
- lduh [%o1], %g2
- add %o1, 2, %o1
- sth %g2, [%o0]
- add %o0, 2, %o0
-1:
- be,pt %icc, 1f
- nop
-
- ldub [%o1], %g2
- stb %g2, [%o0]
-1:
- PRE_RETL
- retl
- RETL_INSN
-
-82: /* ldx_stx */
- MOVE_BIGALIGNCHUNK(o1, o0, 0x00, o2, o3, o4, o5, g2, g3, g4, g5)
- MOVE_BIGALIGNCHUNK(o1, o0, 0x40, o2, o3, o4, o5, g2, g3, g4, g5)
- subcc %g7, 128, %g7
- add %o1, 128, %o1
- bne,pt %xcc, 82b
- add %o0, 128, %o0
-
-#ifndef FASTER_ALIGNED
-
- andcc %g1, 0x70, %g7
- be,pn %icc, 80b
- andcc %g1, 8, %g0
-83:
- rd %pc, %o5
- srl %g7, 1, %o4
- add %g7, %o4, %o4
- add %o1, %g7, %o1
- sub %o5, %o4, %o5
- jmpl %o5 + %lo(80b - 83b), %g0
- add %o0, %g7, %o0
-
-#else /* FASTER_ALIGNED */
-
- andcc %g1, 0x70, %g7
- be,pn %icc, 84f
- andcc %g1, 8, %g0
-83:
- rd %pc, %o5
- add %o1, %g7, %o1
- sub %o5, %g7, %o5
- jmpl %o5 + %lo(84f - 83b), %g0
- add %o0, %g7, %o0
-
- MOVE_LASTALIGNCHUNK(o1, o0, 0x60, g2, g3)
- MOVE_LASTALIGNCHUNK(o1, o0, 0x50, g2, g3)
- MOVE_LASTALIGNCHUNK(o1, o0, 0x40, g2, g3)
- MOVE_LASTALIGNCHUNK(o1, o0, 0x30, g2, g3)
- MOVE_LASTALIGNCHUNK(o1, o0, 0x20, g2, g3)
- MOVE_LASTALIGNCHUNK(o1, o0, 0x10, g2, g3)
- MOVE_LASTALIGNCHUNK(o1, o0, 0x00, g2, g3)
-
-84: /* amemcpy_table_end */
- be,pt %icc, 85f
- andcc %g1, 4, %g0
-
- ldx [%o1], %g2
- add %o1, 8, %o1
- stx %g2, [%o0]
- add %o0, 8, %o0
-85: /* amemcpy_last7 */
- be,pt %icc, 1f
- andcc %g1, 2, %g0
-
- lduw [%o1], %g2
- add %o1, 4, %o1
- stw %g2, [%o0]
- add %o0, 4, %o0
-1:
- be,pt %icc, 1f
- andcc %g1, 1, %g0
-
- lduh [%o1], %g2
- add %o1, 2, %o1
- sth %g2, [%o0]
- add %o0, 2, %o0
-1:
- be,pt %icc, 1f
- nop
-
- ldub [%o1], %g2
- stb %g2, [%o0]
-1:
- PRE_RETL
- retl
- RETL_INSN
-
-#endif /* FASTER_ALIGNED */
-
-86: /* non_aligned */
- cmp %o2, 15
- bleu,pn %xcc, 88f
-
- andcc %o0, 3, %g0
- be,pn %icc, 61f
- andcc %o0, 1, %g0
- be,pn %icc, 60f
- andcc %o0, 2, %g0
-
- ldub [%o1], %g5
- add %o1, 1, %o1
- stb %g5, [%o0]
- sub %o2, 1, %o2
- bne,pn %icc, 61f
- add %o0, 1, %o0
-60:
- ldub [%o1], %g3
- add %o1, 2, %o1
- stb %g3, [%o0]
- sub %o2, 2, %o2
- ldub [%o1 - 1], %g3
- add %o0, 2, %o0
- stb %g3, [%o0 - 1]
-61:
- and %o1, 3, %g2
- and %o2, 0xc, %g3
- and %o1, -4, %o1
- cmp %g3, 4
- sll %g2, 3, %g4
- mov 32, %g2
- be,pn %icc, 4f
- sub %g2, %g4, %g7
-
- blu,pn %icc, 3f
- cmp %g3, 0x8
-
- be,pn %icc, 2f
- srl %o2, 2, %g3
-
- lduw [%o1], %o3
- add %o0, -8, %o0
- lduw [%o1 + 4], %o4
- ba,pt %xcc, 8f
- add %g3, 1, %g3
-2:
- lduw [%o1], %o4
- add %o0, -12, %o0
- lduw [%o1 + 4], %o5
- add %g3, 2, %g3
- ba,pt %xcc, 9f
- add %o1, -4, %o1
-3:
- lduw [%o1], %g1
- add %o0, -4, %o0
- lduw [%o1 + 4], %o3
- srl %o2, 2, %g3
- ba,pt %xcc, 7f
- add %o1, 4, %o1
-4:
- lduw [%o1], %o5
- cmp %o2, 7
- lduw [%o1 + 4], %g1
- srl %o2, 2, %g3
- bleu,pn %xcc, 10f
- add %o1, 8, %o1
-
- lduw [%o1], %o3
- add %g3, -1, %g3
-5:
- sll %o5, %g4, %g2
- srl %g1, %g7, %g5
- or %g2, %g5, %g2
- stw %g2, [%o0]
-7:
- lduw [%o1 + 4], %o4
- sll %g1, %g4, %g2
- srl %o3, %g7, %g5
- or %g2, %g5, %g2
- stw %g2, [%o0 + 4]
-8:
- lduw [%o1 + 8], %o5
- sll %o3, %g4, %g2
- srl %o4, %g7, %g5
- or %g2, %g5, %g2
- stw %g2, [%o0 + 8]
-9:
- lduw [%o1 + 12], %g1
- sll %o4, %g4, %g2
- srl %o5, %g7, %g5
- addcc %g3, -4, %g3
- or %g2, %g5, %g2
- add %o1, 16, %o1
- stw %g2, [%o0 + 12]
- add %o0, 16, %o0
- bne,a,pt %xcc, 5b
- lduw [%o1], %o3
-10:
- sll %o5, %g4, %g2
- srl %g1, %g7, %g5
- srl %g7, 3, %g3
- or %g2, %g5, %g2
- sub %o1, %g3, %o1
- andcc %o2, 2, %g0
- stw %g2, [%o0]
- be,pt %icc, 1f
- andcc %o2, 1, %g0
-
- ldub [%o1], %g2
- add %o1, 2, %o1
- stb %g2, [%o0 + 4]
- add %o0, 2, %o0
- ldub [%o1 - 1], %g2
- stb %g2, [%o0 + 3]
-1:
- be,pt %icc, 1f
- nop
-
- ldub [%o1], %g2
- stb %g2, [%o0 + 4]
-1:
- PRE_RETL
- retl
- RETL_INSN
-
-88: /* short_end */
-
- and %o2, 0xe, %o3
-20:
- rd %pc, %o5
- sll %o3, 3, %o4
- add %o0, %o3, %o0
- sub %o5, %o4, %o5
- add %o1, %o3, %o1
- jmpl %o5 + %lo(89f - 20b), %g0
- andcc %o2, 1, %g0
-
- MOVE_SHORTCHUNK(o1, o0, 0x0c, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, 0x0a, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, 0x08, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, 0x06, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, 0x04, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, 0x02, g2, g3)
- MOVE_SHORTCHUNK(o1, o0, 0x00, g2, g3)
-
-89: /* short_table_end */
-
- be,pt %icc, 1f
- nop
-
- ldub [%o1], %g2
- stb %g2, [%o0]
-1:
- PRE_RETL
- retl
- RETL_INSN
-
-90: /* short_aligned_end */
- bne,pn %xcc, 88b
- andcc %o2, 8, %g0
-
- be,pt %icc, 1f
- andcc %o2, 4, %g0
-
- lduw [%o1 + 0x00], %g2
- lduw [%o1 + 0x04], %g3
- add %o1, 8, %o1
- stw %g2, [%o0 + 0x00]
- stw %g3, [%o0 + 0x04]
- add %o0, 8, %o0
-1:
- ba,pt %xcc, 81b
- mov %o2, %g1
+++ /dev/null
-/* linux/arch/sparc64/lib/memset.S: Sparc optimized memset, bzero and clear_user code
- * Copyright (C) 1991,1996 Free Software Foundation
- * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
- *
- * Returns 0, if ok, and number of bytes not yet set if exception
- * occurs and we were called as clear_user.
- */
-
-#include <asm/asi.h>
-#include <asm/ptrace.h>
-
-#define EX(x,y,a,b,z) \
-98: x,y; \
- .section .fixup,z##alloc,z##execinstr; \
- .align 4; \
-99: ba,pt %xcc, 30f; \
- a, b, %o0; \
- .section __ex_table,z##alloc; \
- .align 8; \
- .xword 98b, 99b; \
- .text; \
- .align 4
-
-#define EXT(start,end,handler,z) \
- .section __ex_table,z##alloc; \
- .align 8; \
- .xword start, 0, end, handler; \
- .text; \
- .align 4
-
-/* Please don't change these macros, unless you change the logic
- * in the .fixup section below as well.
- * Store 64 bytes at (BASE + OFFSET) using value SOURCE.
- */
-#define ZERO_BIG_BLOCK(base, offset, source) \
- stxa source, [base + offset + 0x00] %asi; \
- stxa source, [base + offset + 0x08] %asi; \
- stxa source, [base + offset + 0x10] %asi; \
- stxa source, [base + offset + 0x18] %asi; \
- stxa source, [base + offset + 0x20] %asi; \
- stxa source, [base + offset + 0x28] %asi; \
- stxa source, [base + offset + 0x30] %asi; \
- stxa source, [base + offset + 0x38] %asi;
-
-#define ZERO_LAST_BLOCKS(base, offset, source) \
- stxa source, [base - offset - 0x38] %asi; \
- stxa source, [base - offset - 0x30] %asi; \
- stxa source, [base - offset - 0x28] %asi; \
- stxa source, [base - offset - 0x20] %asi; \
- stxa source, [base - offset - 0x18] %asi; \
- stxa source, [base - offset - 0x10] %asi; \
- stxa source, [base - offset - 0x08] %asi; \
- stxa source, [base - offset - 0x00] %asi;
-
- .text
- .align 4
-
- .globl __bzero, __memset, __bzero_noasi
- .globl memset, __memset_start, __memset_end
-__memset_start:
-__memset:
-memset:
- and %o1, 0xff, %g3
- sll %g3, 8, %g2
- or %g3, %g2, %g3
- sll %g3, 16, %g2
- or %g3, %g2, %g3
- mov %o2, %o1
- wr %g0, ASI_P, %asi
- sllx %g3, 32, %g2
- ba,pt %xcc, 1f
- or %g3, %g2, %g3
-__bzero:
- wr %g0, ASI_P, %asi
-__bzero_noasi:
- mov %g0, %g3
-1:
- cmp %o1, 7
- bleu,pn %xcc, 7f
- andcc %o0, 3, %o2
-
- be,a,pt %icc, 4f
- andcc %o0, 4, %g0
-
- cmp %o2, 3
- be,pn %icc, 2f
- EX(stba %g3, [%o0] %asi, sub %o1, 0,#)
-
- cmp %o2, 2
- be,pt %icc, 2f
- EX(stba %g3, [%o0 + 0x01] %asi, sub %o1, 1,#)
-
- EX(stba %g3, [%o0 + 0x02] %asi, sub %o1, 2,#)
-2:
- sub %o2, 4, %o2
- sub %o0, %o2, %o0
- add %o1, %o2, %o1
- andcc %o0, 4, %g0
-4:
- be,a,pt %icc, 2f
- andncc %o1, 0x7f, %o3
-
- EX(sta %g3, [%o0] %asi, sub %o1, 0,#)
- sub %o1, 4, %o1
- add %o0, 4, %o0
- andncc %o1, 0x7f, %o3 ! Now everything is 8 aligned and o1 is len to run
-2:
- be,pn %xcc, 9f
- andcc %o1, 0x78, %o2
-10:
- ZERO_BIG_BLOCK(%o0, 0x00, %g3)
- subcc %o3, 128, %o3
- ZERO_BIG_BLOCK(%o0, 0x40, %g3)
-11:
- EXT(10b, 11b, 20f,#)
- bne,pt %xcc, 10b
- add %o0, 128, %o0
-
- tst %o2
-9:
- be,pn %xcc, 13f
- andcc %o1, 7, %o1
-14:
- rd %pc, %o4
- srl %o2, 1, %o3
- sub %o4, %o3, %o4
- jmpl %o4 + (13f - 14b), %g0
- add %o0, %o2, %o0
-12:
- ZERO_LAST_BLOCKS(%o0, 0x48, %g3)
- ZERO_LAST_BLOCKS(%o0, 0x08, %g3)
-13:
- be,pn %icc, 8f
- andcc %o1, 4, %g0
-
- be,pn %icc, 1f
- andcc %o1, 2, %g0
-
- EX(sta %g3, [%o0] %asi, and %o1, 7,#)
- add %o0, 4, %o0
-1:
- be,pn %icc, 1f
- andcc %o1, 1, %g0
-
- EX(stha %g3, [%o0] %asi, and %o1, 3,#)
- add %o0, 2, %o0
-1:
- bne,a,pn %icc, 8f
- EX(stba %g3, [%o0] %asi, and %o1, 1,#)
-8:
- retl
- clr %o0
-7:
- be,pn %icc, 13b
- orcc %o1, 0, %g0
-
- be,pn %icc, 0f
-8:
- add %o0, 1, %o0
- subcc %o1, 1, %o1
- bne,a,pt %icc, 8b
- EX(stba %g3, [%o0 - 1] %asi, add %o1, 1,#)
-0:
- retl
- clr %o0
-__memset_end:
-
- .section .fixup,#alloc,#execinstr
- .align 4
-20:
- cmp %g2, 8
- bleu,pn %xcc, 1f
- and %o1, 0x7f, %o1
- sub %g2, 9, %g2
- add %o3, 64, %o3
-1:
- sll %g2, 3, %g2
- add %o3, %o1, %o0
- ba,pt %xcc, 30f
- sub %o0, %g2, %o0
-21:
- mov 8, %o0
- and %o1, 7, %o1
- sub %o0, %g2, %o0
- sll %o0, 3, %o0
- ba,pt %xcc, 30f
- add %o0, %o1, %o0
-30:
-/* %o4 is faulting address, %o5 is %pc where fault occured */
- save %sp, -160, %sp
- mov %i5, %o0
- mov %i7, %o1
- call lookup_fault
- mov %i4, %o2
- ret
- restore
-# $Id: Makefile,v 1.1 1996/12/26 10:24:22 davem Exp $
+# $Id: Makefile,v 1.3 1997/06/27 14:53:38 jj Exp $
# Makefile for the linux Sparc64-specific parts of the memory manager.
#
# Note! Dependencies are done automagically by 'make dep', which also
#
# Note 2! The CFLAGS definition is now in the main makefile...
+.S.s:
+ $(CPP) -D__ASSEMBLY__ -ansi $< -o $*.s
+
+.S.o:
+ $(CC) -D__ASSEMBLY__ -ansi -c $< -o $*.o
+
O_TARGET := mm.o
-O_OBJS := fault.o init.o generic.o asyncd.o extable.o
+O_OBJS := ultra.o fault.o init.o generic.o asyncd.o extable.o modutil.o
include $(TOPDIR)/Rules.make
-/* $Id: fault.c,v 1.12 1997/06/13 14:02:52 davem Exp $
+/* $Id: fault.c,v 1.17 1997/07/04 01:41:10 davem Exp $
* arch/sparc64/mm/fault.c: Page fault handlers for the 64-bit Sparc.
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
return 0;
}
-/* #define FAULT_TRACER */
-/* #define FAULT_TRACER_VERBOSE */
+/* #define DEBUG_EXCEPTIONS */
-#ifdef FAULT_TRACER
-/* Set and clear this elsewhere at critical moment, for oodles of debugging fun. */
-int fault_trace_enable = 0;
-#endif
-
-#include <asm/hardirq.h>
-
-asmlinkage void do_sparc64_fault(struct pt_regs *regs, int text_fault, int write,
- unsigned long address, unsigned long tag,
- unsigned long sfsr)
+asmlinkage void do_sparc64_fault(struct pt_regs *regs, unsigned long address, int write)
{
+ struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
- struct task_struct *tsk = current;
- struct mm_struct *mm = tsk->mm;
- unsigned long fixup;
- unsigned long g2;
- int from_user = !(regs->tstate & TSTATE_PRIV);
-#ifdef FAULT_TRACER
- static unsigned long last_addr = 0;
- static int rcnt = 0;
-
- if(fault_trace_enable) {
-#ifdef FAULT_TRACER_VERBOSE
- printk("FAULT(PC[%016lx],t[%d],w[%d],addr[%016lx])...",
- regs->tpc, text_fault, write, address);
-#else
- printk("F[%016lx:%016lx:w(%d)", regs->tpc, address, write);
-#endif
- if(address == last_addr) {
- if(rcnt++ > 15) {
- printk("Wheee lotsa bogus faults, something wrong, "
- "spinning\n");
- printk("pctx[%016lx]sctx[%016lx]mmctx[%016lx]DS(%x)"
- "tctx[%016lx] flgs[%016lx]\n",
- spitfire_get_primary_context(),
- spitfire_get_secondary_context(),
- mm->context, (unsigned)current->tss.current_ds,
- current->tss.ctx, current->tss.flags);
- __asm__ __volatile__("flushw");
- printk("o7[%016lx] i7[%016lx]\n",
- regs->u_regs[UREG_I7],
- ((struct reg_window *)(regs->u_regs[UREG_FP]+STACK_BIAS))->ins[7]);
- sti();
- while(1)
- barrier();
- }
- } else rcnt = 0;
- last_addr = address;
- }
-#endif
- lock_kernel ();
+ lock_kernel();
down(&mm->mmap_sem);
vma = find_vma(mm, address);
if(!vma)
*/
bad_area:
up(&mm->mmap_sem);
- /* Is this in ex_table? */
+
+ {
+ unsigned long g2 = regs->u_regs[UREG_G2];
+
+ /* Is this in ex_table? */
+ if (regs->tstate & TSTATE_PRIV) {
+ unsigned char asi = ASI_P;
+ unsigned int insn;
+ unsigned long fixup;
+
+ insn = *(unsigned int *)regs->tpc;
+ if ((insn & 0xc0800000) == 0xc0800000) {
+ if (insn & 0x2000)
+ asi = (regs->tstate >> 24);
+ else
+ asi = (insn >> 5);
+ }
- g2 = regs->u_regs[UREG_G2];
- if (!from_user && (fixup = search_exception_table (regs->tpc, &g2))) {
- printk("Exception: PC<%016lx> faddr<%016lx>\n", regs->tpc, address);
- printk("EX_TABLE: insn<%016lx> fixup<%016lx> g2<%016lx>\n",
- regs->tpc, fixup, g2);
- regs->tpc = fixup;
- regs->tnpc = regs->tpc + 4;
- regs->u_regs[UREG_G2] = g2;
- goto out;
- }
- if(from_user) {
-#if 1
- unsigned long cpc;
- __asm__ __volatile__("mov %%i7, %0" : "=r" (cpc));
- printk("[%s:%d] SIGSEGV pc[%016lx] addr[%016lx] w[%d] sfsr[%016lx] "
- "caller[%016lx]\n", current->comm, current->pid, regs->tpc,
- address, write, sfsr, cpc);
+ /* Look in asi.h: All _S asis have LS bit set */
+ if ((asi & 0x1) &&
+ (fixup = search_exception_table (regs->tpc, &g2))) {
+#ifdef DEBUG_EXCEPTIONS
+ printk("Exception: PC<%016lx> faddr<%016lx>\n",
+ regs->tpc, address);
+ printk("EX_TABLE: insn<%016lx> fixup<%016lx> "
+ "g2<%016lx>\n", regs->tpc, fixup, g2);
#endif
- tsk->tss.sig_address = address;
- tsk->tss.sig_desc = SUBSIG_NOMAPPING;
- send_sig(SIGSEGV, tsk, 1);
- goto out;
+ regs->tpc = fixup;
+ regs->tnpc = regs->tpc + 4;
+ regs->u_regs[UREG_G2] = g2;
+ goto out;
+ }
+ } else {
+ current->tss.sig_address = address;
+ current->tss.sig_desc = SUBSIG_NOMAPPING;
+ send_sig(SIGSEGV, current, 1);
+ goto out;
+ }
+ unhandled_fault (address, current, regs);
}
- unhandled_fault (address, tsk, regs);
out:
unlock_kernel();
-#ifdef FAULT_TRACER
- if(fault_trace_enable) {
-#ifdef FAULT_TRACER_VERBOSE
- printk(" done\n");
-#else
- printk("]");
-#endif
- }
-#endif
}
+void fixup_dcache_alias(struct vm_area_struct *vma, unsigned long address, pte_t pte)
+{
+ struct vm_area_struct *vmaring;
+ struct inode *inode;
+ unsigned long vaddr, offset, start;
+ pgd_t *pgdp;
+ pmd_t *pmdp;
+ pte_t *ptep;
+ int alias_found = 0;
+
+ inode = vma->vm_inode;
+ if(!inode)
+ return;
+
+ offset = (address & PAGE_MASK) - vma->vm_start;
+ vmaring = inode->i_mmap;
+ do {
+ vaddr = vmaring->vm_start + offset;
+
+ /* This conditional is misleading... */
+ if((vaddr ^ address) & PAGE_SIZE) {
+ alias_found++;
+ start = vmaring->vm_start;
+ while(start < vmaring->vm_end) {
+ pgdp = pgd_offset(vmaring->vm_mm, start);
+ if(!pgdp) goto next;
+ pmdp = pmd_offset(pgdp, start);
+ if(!pmdp) goto next;
+ ptep = pte_offset(pmdp, start);
+ if(!ptep) goto next;
+
+ if(pte_val(*ptep) & _PAGE_PRESENT) {
+ flush_cache_page(vmaring, start);
+ *ptep = __pte(pte_val(*ptep) &
+ ~(_PAGE_CV));
+ flush_tlb_page(vmaring, start);
+ }
+ next:
+ start += PAGE_SIZE;
+ }
+ }
+ } while((vmaring = vmaring->vm_next_share) != NULL);
+
+ if(alias_found && (pte_val(pte) & _PAGE_CV)) {
+ pgdp = pgd_offset(vma->vm_mm, address);
+ pmdp = pmd_offset(pgdp, address);
+ ptep = pte_offset(pmdp, address);
+ flush_cache_page(vma, address);
+ *ptep = __pte(pte_val(*ptep) & ~(_PAGE_CV));
+ flush_tlb_page(vma, address);
+ }
+}
-/* $Id: generic.c,v 1.1 1996/12/26 10:24:23 davem Exp $
+/* $Id: generic.c,v 1.2 1997/07/01 09:11:42 jj Exp $
* generic.c: Generic Sparc mm routines that are not dependent upon
* MMU type but are Sparc specific.
*
if (end > PMD_SIZE)
end = PMD_SIZE;
do {
- pte_t oldpage = *pte;
- pte_clear(pte);
- set_pte(pte, mk_pte_io(offset, prot, space));
- forget_pte(oldpage);
- address += PAGE_SIZE;
+ pte_t oldpage;
+ pte_t entry;
+ unsigned long curend = address + PAGE_SIZE;
+
+ entry = mk_pte_io(offset, prot, space);
offset += PAGE_SIZE;
- pte++;
+ if (!(address & 0xffff)) {
+ if (!(address & 0x3fffff) && !(offset & 0x3fffff) && end >= address + 0x400000) {
+ entry = mk_pte_io(offset, __pgprot(pgprot_val (prot) | _PAGE_SZ4MB), space);
+ curend = address + 0x400000;
+ offset += 0x400000 - PAGE_SIZE;
+ } else if (!(address & 0x7ffff) && !(offset & 0x7ffff) && end >= address + 0x80000) {
+ entry = mk_pte_io(offset, __pgprot(pgprot_val (prot) | _PAGE_SZ512K), space);
+ curend = address + 0x80000;
+ offset += 0x80000 - PAGE_SIZE;
+ } else if (!(offset & 0xffff) && end >= address + 0x10000) {
+ entry = mk_pte_io(offset, __pgprot(pgprot_val (prot) | _PAGE_SZ64K), space);
+ curend = address + 0x10000;
+ offset += 0x10000 - PAGE_SIZE;
+ }
+ }
+ do {
+ oldpage = *pte;
+ pte_clear(pte);
+ set_pte(pte, entry);
+ forget_pte(oldpage);
+ address += PAGE_SIZE;
+ pte++;
+ } while (address < curend);
} while (address < end);
}
-/* $Id: init.c,v 1.30 1997/06/06 10:56:21 jj Exp $
+/* $Id: init.c,v 1.39 1997/07/07 02:50:57 davem Exp $
* arch/sparc64/mm/init.c
*
* Copyright (C) 1996,1997 David S. Miller (davem@caip.rutgers.edu)
* Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
+#include <linux/config.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/blk.h>
#include <linux/swap.h>
+#include <asm/head.h>
#include <asm/system.h>
#include <asm/page.h>
#include <asm/pgtable.h>
unsigned long tlb_context_cache = CTX_FIRST_VERSION;
/* References to section boundaries */
-extern char __init_begin, __init_end, etext, __p1275_loc, __bss_start;
+extern char __init_begin, __init_end, etext, __bss_start;
/*
* BAD_PAGE is the page that is used for page faults when linux
pte_t *__bad_pte(void)
{
memset((void *) &empty_bad_pte_table, 0, PAGE_SIZE);
- return (pte_t *) (((unsigned long)&empty_bad_pte_table) + phys_base);
+ return (pte_t *) (((unsigned long)&empty_bad_pte_table)
+ - ((unsigned long)&empty_zero_page) + phys_base + PAGE_OFFSET);
}
pte_t __bad_page(void)
{
memset((void *) &empty_bad_page, 0, PAGE_SIZE);
- return pte_mkdirty(mk_pte((((unsigned long) &empty_bad_page)+phys_base),
+ return pte_mkdirty(mk_pte((((unsigned long) &empty_bad_page)
+ - ((unsigned long)&empty_zero_page) + phys_base + PAGE_OFFSET),
PAGE_SHARED));
}
};
#define MAX_TRANSLATIONS 64
-static void inherit_prom_mappings(void)
+static inline void inherit_prom_mappings(void)
{
struct linux_prom_translation transl[MAX_TRANSLATIONS];
pgd_t *pgdp;
}
}
-static void inherit_locked_prom_mappings(void)
+int prom_itlb_ent, prom_dtlb_ent;
+unsigned long prom_itlb_tag, prom_itlb_data;
+unsigned long prom_dtlb_tag, prom_dtlb_data;
+
+static inline void inherit_locked_prom_mappings(void)
{
int i;
int dtlb_seen = 0;
data = spitfire_get_dtlb_data(i);
if(!dtlb_seen && (data & _PAGE_L)) {
unsigned long tag = spitfire_get_dtlb_tag(i);
+ prom_dtlb_ent = i;
+ prom_dtlb_tag = tag;
+ prom_dtlb_data = data;
__asm__ __volatile__("stxa %%g0, [%0] %1"
: : "r" (TLB_TAG_ACCESS), "i" (ASI_DMMU));
membar("#Sync");
data = spitfire_get_itlb_data(i);
if(!itlb_seen && (data & _PAGE_L)) {
unsigned long tag = spitfire_get_itlb_tag(i);
+ prom_itlb_ent = i;
+ prom_itlb_tag = tag;
+ prom_itlb_data = data;
__asm__ __volatile__("stxa %%g0, [%0] %1"
: : "r" (TLB_TAG_ACCESS), "i" (ASI_IMMU));
membar("#Sync");
}
}
+/* Give PROM back his world, done during reboots... */
+void prom_reload_locked(void)
+{
+ __asm__ __volatile__("stxa %0, [%1] %2"
+ : : "r" (prom_dtlb_tag), "r" (TLB_TAG_ACCESS),
+ "i" (ASI_DMMU));
+ membar("#Sync");
+ spitfire_put_dtlb_data(prom_dtlb_ent, prom_dtlb_data);
+ membar("#Sync");
+
+ __asm__ __volatile__("stxa %0, [%1] %2"
+ : : "r" (prom_itlb_tag), "r" (TLB_TAG_ACCESS),
+ "i" (ASI_IMMU));
+ membar("#Sync");
+ spitfire_put_itlb_data(prom_itlb_ent, prom_itlb_data);
+ membar("#Sync");
+}
+
+/* If not locked, zap it. */
+void flush_tlb_all(void)
+{
+ unsigned long flags;
+ int i;
+
+ save_flags(flags); cli();
+ for(i = 0; i < 64; i++) {
+ if(!(spitfire_get_dtlb_data(i) & _PAGE_L)) {
+ __asm__ __volatile__("stxa %%g0, [%0] %1"
+ : /* no outputs */
+ : "r" (TLB_TAG_ACCESS), "i" (ASI_DMMU));
+ membar("#Sync");
+ spitfire_put_dtlb_data(i, 0x0UL);
+ membar("#Sync");
+ }
+ if(!(spitfire_get_itlb_data(i) & _PAGE_L)) {
+ __asm__ __volatile__("stxa %%g0, [%0] %1"
+ : /* no outputs */
+ : "r" (TLB_TAG_ACCESS), "i" (ASI_IMMU));
+ membar("#Sync");
+ spitfire_put_itlb_data(i, 0x0UL);
+ membar("#Sync");
+ }
+ }
+ restore_flags(flags);
+}
+
+void get_new_mmu_context(struct mm_struct *mm, unsigned long ctx)
+{
+ if((ctx & ~(CTX_VERSION_MASK)) == 0) {
+ flush_tlb_all();
+ ctx = (ctx & CTX_VERSION_MASK) + CTX_FIRST_VERSION;
+ if(ctx == 1)
+ ctx = CTX_FIRST_VERSION;
+ }
+ tlb_context_cache = ctx + 1;
+ mm->context = ctx;
+}
+
__initfunc(static void
allocate_ptable_skeleton(unsigned long start, unsigned long end))
{
{
extern unsigned long phys_base;
extern void setup_tba(unsigned long kpgdir);
- extern void __bfill64(void *, unsigned long);
- pgd_t *pgdp;
+ extern void __bfill64(void *, unsigned long *);
pmd_t *pmdp;
- pte_t *ptep, pte;
int i;
-
- /* Must create 2nd locked DTLB entry if physical ram starts at
- * 4MB absolute or higher, kernel image has been placed in the
- * right place at PAGE_OFFSET but references to start_mem and pages
- * will be to the perfect alias mapping, so set it up now.
+ unsigned long alias_base = phys_base + PAGE_OFFSET;
+ unsigned long pt;
+ unsigned long flags;
+ unsigned long shift = alias_base - ((unsigned long)&empty_zero_page);
+
+ /* We assume physical memory starts at some 4mb multiple,
+ * if this were not true we wouldn't boot up to this point
+ * anyways.
*/
- if(phys_base >= (4 * 1024 * 1024)) {
- unsigned long alias_base = phys_base + PAGE_OFFSET;
- unsigned long pte;
- unsigned long flags;
-
- /* We assume physical memory starts at some 4mb multiple,
- * if this were not true we wouldn't boot up to this point
- * anyways.
- */
- pte = phys_base | _PAGE_VALID | _PAGE_SZ4MB;
- pte |= _PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_L | _PAGE_W;
- save_flags(flags); cli();
- __asm__ __volatile__("
- stxa %1, [%0] %3
- stxa %2, [%5] %4
- membar #Sync
- flush %%g4
- nop
- nop
- nop"
- : /* No outputs */
- : "r" (TLB_TAG_ACCESS), "r" (alias_base), "r" (pte),
- "i" (ASI_DMMU), "i" (ASI_DTLB_DATA_ACCESS), "r" (61 << 3)
- : "memory");
- restore_flags(flags);
-
- /* Now set kernel pgd to upper alias so physical page computations
- * work.
- */
- init_mm.pgd += (phys_base / (sizeof(pgd_t *)));
- }
+ pt = phys_base | _PAGE_VALID | _PAGE_SZ4MB;
+ pt |= _PAGE_CP | _PAGE_CV | _PAGE_P | _PAGE_L | _PAGE_W;
+ save_flags(flags); cli();
+ __asm__ __volatile__("
+ stxa %1, [%0] %3
+ stxa %2, [%5] %4
+ membar #Sync
+ flush %%g6
+ nop
+ nop
+ nop"
+ : /* No outputs */
+ : "r" (TLB_TAG_ACCESS), "r" (alias_base), "r" (pt),
+ "i" (ASI_DMMU), "i" (ASI_DTLB_DATA_ACCESS), "r" (61 << 3)
+ : "memory");
+ restore_flags(flags);
+
+ /* Now set kernel pgd to upper alias so physical page computations
+ * work.
+ */
+ init_mm.pgd += ((shift) / (sizeof(pgd_t *)));
- null_pmd_table = __pa(((unsigned long)&empty_null_pmd_table) + phys_base);
- null_pte_table = __pa(((unsigned long)&empty_null_pte_table) + phys_base);
+ null_pmd_table = __pa(((unsigned long)&empty_null_pmd_table) + shift);
+ null_pte_table = __pa(((unsigned long)&empty_null_pte_table) + shift);
pmdp = (pmd_t *) &empty_null_pmd_table;
for(i = 0; i < 1024; i++)
memset((void *) &empty_null_pte_table, 0, PAGE_SIZE);
/* Now can init the kernel/bad page tables. */
- __bfill64((void *)swapper_pg_dir, null_pmd_table);
- __bfill64((void *)&empty_bad_pmd_table, null_pte_table);
+ __bfill64((void *)swapper_pg_dir, &null_pmd_table);
+ __bfill64((void *)&empty_bad_pmd_table, &null_pte_table);
/* We use mempool to create page tables, therefore adjust it up
* such that __pa() macros etc. work.
*/
- mempool = PAGE_ALIGN(start_mem) + phys_base;
+ mempool = PAGE_ALIGN(start_mem) + shift;
/* FIXME: This should be done much nicer.
* Just now we allocate 64M for each.
allocate_ptable_skeleton(IOBASE_VADDR, IOBASE_VADDR + 0x4000000);
allocate_ptable_skeleton(DVMA_VADDR, DVMA_VADDR + 0x4000000);
inherit_prom_mappings();
- allocate_ptable_skeleton(0, 0x8000 + PAGE_SIZE);
-
- /* Map prom interface page. */
- pgdp = pgd_offset(init_task.mm, 0x8000);
- pmdp = pmd_offset(pgdp, 0x8000);
- ptep = pte_offset(pmdp, 0x8000);
- pte = mk_pte(((unsigned long)&__p1275_loc)+phys_base, PAGE_KERNEL);
- set_pte(ptep, pte);
-
+
/* Ok, we can use our TLB miss and window trap handlers safely. */
setup_tba((unsigned long)init_mm.pgd);
- /* Kill locked PROM interface page mapping, the mapping will
- * re-enter on the next PROM interface call via our TLB miss
- * handlers.
- */
- spitfire_flush_dtlb_primary_page(0x8000);
- membar("#Sync");
- spitfire_flush_itlb_primary_page(0x8000);
- membar("#Sync");
-
/* Really paranoid. */
- flushi(PAGE_OFFSET);
+ flushi((long)&empty_zero_page);
membar("#Sync");
/* Cleanup the extra locked TLB entry we created since we have the
* nice TLB miss handlers of ours installed now.
*/
- if(phys_base >= (4 * 1024 * 1024)) {
- /* We only created DTLB mapping of this stuff. */
- spitfire_flush_dtlb_nucleus_page(phys_base + PAGE_OFFSET);
- membar("#Sync");
-
- /* Paranoid */
- flushi(PAGE_OFFSET);
- membar("#Sync");
- }
+ /* We only created DTLB mapping of this stuff. */
+ spitfire_flush_dtlb_nucleus_page(alias_base);
+ membar("#Sync");
- inherit_locked_prom_mappings();
+ /* Paranoid */
+ flushi((long)&empty_zero_page);
+ membar("#Sync");
+ inherit_locked_prom_mappings();
+
flush_tlb_all();
-
+
start_mem = free_area_init(PAGE_ALIGN(mempool), end_mem);
return device_scan (PAGE_ALIGN (start_mem));
int codepages = 0;
int datapages = 0;
int initpages = 0;
- int prompages = 0;
unsigned long tmp2, addr;
- unsigned long data_end;
+ unsigned long alias_base = phys_base + PAGE_OFFSET - (long)(&empty_zero_page);
end_mem &= PAGE_MASK;
max_mapnr = MAP_NR(end_mem);
}
taint_real_pages(start_mem, end_mem);
- data_end = start_mem - phys_base;
for (addr = PAGE_OFFSET; addr < end_mem; addr += PAGE_SIZE) {
if(PageReserved(mem_map + MAP_NR(addr))) {
- if ((addr < (unsigned long) &etext) && (addr >= PAGE_OFFSET))
+ if ((addr < ((unsigned long) &etext) + alias_base) && (addr >= alias_base))
codepages++;
- else if((addr >= (unsigned long)&__init_begin && addr < (unsigned long)&__init_end))
+ else if((addr >= ((unsigned long)&__init_begin) + alias_base)
+ && (addr < ((unsigned long)&__init_end) + alias_base))
initpages++;
- else if((addr >= (unsigned long)&__p1275_loc && addr < (unsigned long)&__bss_start))
- prompages++;
- else if((addr < data_end) && (addr >= PAGE_OFFSET))
+ else if((addr < start_mem) && (addr >= alias_base))
datapages++;
continue;
}
tmp2 = nr_free_pages << PAGE_SHIFT;
- printk("Memory: %luk available (%dk kernel code, %dk data, %dk init, %dk prom) [%016lx,%016lx]\n",
+ printk("Memory: %luk available (%dk kernel code, %dk data, %dk init) [%016lx,%016lx]\n",
tmp2 >> 10,
codepages << (PAGE_SHIFT-10),
datapages << (PAGE_SHIFT-10),
initpages << (PAGE_SHIFT-10),
- prompages << (PAGE_SHIFT-10),
PAGE_OFFSET, end_mem);
min_free_pages = nr_free_pages >> 7;
min_free_pages = 16;
free_pages_low = min_free_pages + (min_free_pages >> 1);
free_pages_high = min_free_pages + min_free_pages;
-
-#if 0
- printk("Testing fault handling...\n");
- *(char *)0x00000deadbef0000UL = 0;
-#endif
}
void free_initmem (void)
addr = (unsigned long)(&__init_begin);
for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) {
- unsigned long page = addr;
-
- if(page < ((unsigned long)__va(phys_base)))
- page += phys_base;
+ unsigned long page = addr + (long)__va(phys_base)
+ - (long)(&empty_zero_page);
mem_map[MAP_NR(page)].flags &= ~(1 << PG_reserved);
atomic_set(&mem_map[MAP_NR(page)].count, 1);
--- /dev/null
+/* $Id: modutil.c,v 1.1 1997/06/27 14:53:35 jj Exp $
+ * arch/sparc64/mm/modutil.c
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Based upon code written by Linus Torvalds and others.
+ */
+
+#include <linux/malloc.h>
+#include <linux/vmalloc.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/vaddrs.h>
+
+static struct vm_struct * modvmlist = NULL;
+
+void module_unmap (void * addr)
+{
+ struct vm_struct **p, *tmp;
+
+ if (!addr)
+ return;
+ if ((PAGE_SIZE-1) & (unsigned long) addr) {
+ printk("Trying to vfree() bad address (%p)\n", addr);
+ return;
+ }
+ for (p = &modvmlist ; (tmp = *p) ; p = &tmp->next) {
+ if (tmp->addr == addr) {
+ *p = tmp->next;
+ vmfree_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size);
+ kfree(tmp);
+ return;
+ }
+ }
+ printk("Trying to unmap nonexistent module vm area (%p)\n", addr);
+}
+
+void * module_map (unsigned long size)
+{
+ void * addr;
+ struct vm_struct **p, *tmp, *area;
+
+ size = PAGE_ALIGN(size);
+ if (!size || size > MODULES_LEN) return NULL;
+
+ addr = (void *) MODULES_VADDR;
+ for (p = &modvmlist; (tmp = *p) ; p = &tmp->next) {
+ if (size + (unsigned long) addr < (unsigned long) tmp->addr)
+ break;
+ addr = (void *) (tmp->size + (unsigned long) tmp->addr);
+ }
+ if ((unsigned long) addr + size >= MODULES_END) return NULL;
+
+ area = (struct vm_struct *) kmalloc(sizeof(*area), GFP_KERNEL);
+ if (!area) return NULL;
+ area->size = size + PAGE_SIZE;
+ area->addr = addr;
+ area->next = *p;
+ *p = area;
+
+ if (vmalloc_area_pages(VMALLOC_VMADDR(addr), size)) {
+ vfree(addr);
+ return NULL;
+ }
+ return addr;
+}
--- /dev/null
+/* $Id: ultra.S,v 1.6 1997/06/30 10:31:43 jj Exp $
+ * ultra.S: Don't expand these all over the place...
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#include <asm/asi.h>
+#include <asm/spitfire.h>
+
+ /* All callers check mm->context != NO_CONTEXT for us. */
+ .text
+ .align 32
+ .globl __flush_tlb_mm, __flush_tlb_range, __flush_tlb_page
+__flush_tlb_mm: /* %o0 == (mm->context & 0x1fff) */
+ rdpr %otherwin, %g1
+ brz,pt %g1, 1f
+ mov %o7, %g3
+ call __flushw_user
+ clr %g2
+1: rdpr %pil, %g1
+9: mov SECONDARY_CONTEXT, %g7
+ wrpr %g0, 15, %pil
+
+ ldxa [%g7] ASI_DMMU, %g2
+ cmp %g2, %o0
+ be,pt %icc, 1f
+ mov 0x50, %g3
+ stxa %o0, [%g7] ASI_DMMU
+1: stxa %g0, [%g3] ASI_DMMU_DEMAP
+ be,pt %icc, 1f
+ stxa %g0, [%g3] ASI_IMMU_DEMAP
+
+ stxa %g2, [%g7] ASI_DMMU
+1: wrpr %g1, 0x0, %pil
+ retl
+ flush %g6
+__flush_tlb_range: /* %o0 == (mm->context & 0x1fff), %o1 == start, %o2 == end */
+ sethi %hi(8192 - 1), %g5
+ or %g5, %lo(8192 - 1), %g5
+ andn %o1, %g5, %o1
+ andn %o2, %g5, %o2
+
+ sub %o2, %o1, %o3
+ add %g5, 1, %g5
+ orcc %o1, 0x50, %o1
+ srlx %o3, 13, %o4
+ rdpr %otherwin, %g1
+ brz,pt %g1, 1f
+ mov %o7, %g3
+ call __flushw_user
+
+ clr %g2
+1: cmp %o4, 96
+ bgu,pn %icc, 9b
+ rdpr %pil, %g1
+ mov SECONDARY_CONTEXT, %g7
+ wrpr %g0, 15, %pil
+ ldxa [%g7] ASI_DMMU, %g2
+ cmp %g2, %o0
+
+ be,pt %icc, 1f
+ sub %o3, %g5, %o3
+ stxa %o0, [%g7] ASI_DMMU
+1: stxa %g0, [%o1 + %o3] ASI_DMMU_DEMAP
+ stxa %g0, [%o1 + %o3] ASI_IMMU_DEMAP
+ brnz,pt %o3, 1b
+ sub %o3, %g5, %o3
+ nop
+
+ be,pt %icc, 1f
+ wrpr %g1, 0x0, %pil
+ stxa %g2, [%g7] ASI_DMMU
+1: retl
+ flush %g6
+
+ .align 32
+__flush_tlb_page: /* %o0 == (mm->context & 0x1fff), %o1 == page & PAGE_MASK */
+ rdpr %otherwin, %g1
+ brz,pt %g1, 1f
+ mov %o7, %g3
+ call __flushw_user
+ clr %g2
+1: rdpr %pil, %g1
+ mov SECONDARY_CONTEXT, %g7
+ wrpr %g0, 15, %pil
+
+ ldxa [%g7] ASI_DMMU, %g2
+ cmp %g2, %o0
+ be,pt %icc, 1f
+ or %o1, 0x10, %g3
+ stxa %o0, [%g7] ASI_DMMU
+1: stxa %g0, [%g3] ASI_DMMU_DEMAP
+ be,pt %icc, 1f
+ stxa %g0, [%g3] ASI_IMMU_DEMAP
+ stxa %g2, [%g7] ASI_DMMU
+1: wrpr %g1, 0x0, %pil
+ retl
+ flush %g6
+
+
+
-/* $Id: bootstr.c,v 1.3 1997/03/04 16:27:06 jj Exp $
+/* $Id: bootstr.c,v 1.4 1997/06/17 13:25:35 jj Exp $
* bootstr.c: Boot string/argument acquisition from the PROM.
*
* Copyright(C) 1995 David S. Miller (davem@caip.rutgers.edu)
*/
#include <linux/string.h>
+#include <linux/init.h>
#include <asm/oplib.h>
#define BARG_LEN 256
-static char barg_buf[BARG_LEN];
-static char fetched = 0;
+int bootstr_len __initdata = BARG_LEN;
+static int bootstr_valid __initdata = 0;
+static char bootstr_buf[BARG_LEN] __initdata = { 0 };
-char *
-prom_getbootargs(void)
+__initfunc(char *
+prom_getbootargs(void))
{
/* This check saves us from a panic when bootfd patches args. */
- if (fetched) return barg_buf;
- prom_getstring(prom_chosen_node, "bootargs", barg_buf, BARG_LEN);
- fetched = 1;
- return barg_buf;
+ if (bootstr_valid) return bootstr_buf;
+ prom_getstring(prom_chosen_node, "bootargs", bootstr_buf, BARG_LEN);
+ bootstr_valid = 1;
+ return bootstr_buf;
}
-/* $Id: misc.c,v 1.6 1997/04/10 05:13:05 davem Exp $
+/* $Id: misc.c,v 1.7 1997/07/05 09:52:51 davem Exp $
* misc.c: Miscellaneous prom functions that don't belong
* anywhere else.
*
* Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
*/
+#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
prom_cmdline(void)
{
extern void kernel_enter_debugger(void);
- extern void install_obp_ticker(void);
- extern void install_linux_ticker(void);
+ /* extern void install_obp_ticker(void); */
+ /* extern void install_linux_ticker(void); */
unsigned long flags;
/* kernel_enter_debugger(); */
-/* $Id: p1275.c,v 1.8 1997/04/03 09:29:21 davem Exp $
+/* $Id: p1275.c,v 1.10 1997/06/27 04:18:30 davem Exp $
* p1275.c: Sun IEEE 1275 PROM low level interface routines
*
* Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
#include <asm/spitfire.h>
#include <asm/pstate.h>
-/* If you change layout of this structure, please change the prom_doit
- function below as well. */
-typedef struct {
- unsigned prom_doit_code [24]; /* 0x8000 */
- long prom_sync_routine; /* 0x8060 */
- void (*prom_cif_handler)(long *); /* 0x8068 */
- unsigned long prom_cif_stack; /* 0x8070 */
- unsigned long prom_args [23]; /* 0x8078 */
- char prom_buffer [7888];
-} at0x8000;
+struct {
+ long prom_sync_routine; /* 0x00 */
+ void (*prom_cif_handler)(long *); /* 0x08 */
+ unsigned long prom_cif_stack; /* 0x10 */
+ unsigned long prom_args [23]; /* 0x18 */
+ char prom_buffer [3000];
+} p1275buf;
-static void (*prom_do_it)(void);
-
-void prom_cif_interface (void) __attribute__ ((__section__ (".p1275")));
-
-/* At most 14 insns */
void prom_cif_interface (void)
{
__asm__ __volatile__ ("
- sethi %%hi(0x8000), %%o0
- ldx [%%o0 + 0x070], %%o1 ! prom_cif_stack
+ mov %0, %%o0
+ ldx [%%o0 + 0x010], %%o1 ! prom_cif_stack
save %%o1, -0xc0, %%sp
- ldx [%%i0 + 0x068], %%l2 ! prom_cif_handler
+ ldx [%%i0 + 0x008], %%l2 ! prom_cif_handler
rdpr %%pstate, %%l4
mov %%g4, %%l0
mov %%g6, %%l1
- wrpr %%l4, %0, %%pstate ! turn on address masking
+ wrpr %%l4, %1, %%pstate ! turn on address masking
call %%l2
- or %%i0, 0x078, %%o0 ! prom_args
+ add %%i0, 0x018, %%o0 ! prom_args
wrpr %%l4, 0, %%pstate ! put pstate back
mov %%l0, %%g4
ret
restore %%l1, 0, %%g6
save %%sp, -0xc0, %%sp ! If you change the offset of the save
rdpr %%pstate, %%l4 ! here, please change the 0x8038
- andn %%l4, %0, %%l3 ! constant below as well
+ andn %%l4, %1, %%l3 ! constant below as well
wrpr %%l3, %%pstate
- ldx [%%o0 + 0x060], %%l2
+ ldx [%%o0 + 0x000], %%l2
call %%l2
nop
wrpr %%l4, 0, %%pstate
ret
restore
- " : : "i" (PSTATE_AM));
+ " : : "r" (&p1275buf), "i" (0 /* PSTATE_AM */));
}
long p1275_cmd (char *service, long fmt, ...)
va_list list;
long attrs, x;
long ctx = 0;
- at0x8000 *low = (at0x8000 *)(0x8000);
- p = low->prom_buffer;
+ p = p1275buf.prom_buffer;
save_and_cli(flags);
ctx = spitfire_get_primary_context ();
if (ctx) {
flushw_user ();
spitfire_set_primary_context (0);
}
- low->prom_args[0] = (unsigned long)p; /* service */
+ p1275buf.prom_args[0] = (unsigned long)p; /* service */
strcpy (p, service);
p = (char *)(((long)(strchr (p, 0) + 8)) & ~7);
- low->prom_args[1] = nargs = (fmt & 0x0f); /* nargs */
- low->prom_args[2] = nrets = ((fmt & 0xf0) >> 4); /* nrets */
+ p1275buf.prom_args[1] = nargs = (fmt & 0x0f); /* nargs */
+ p1275buf.prom_args[2] = nrets = ((fmt & 0xf0) >> 4); /* nrets */
attrs = fmt >> 8;
va_start(list, fmt);
for (i = 0; i < nargs; i++, attrs >>= 3) {
switch (attrs & 0x7) {
case P1275_ARG_NUMBER:
- low->prom_args[i + 3] = (unsigned)va_arg(list, long); break;
+ p1275buf.prom_args[i + 3] = (unsigned)va_arg(list, long); break;
case P1275_ARG_IN_STRING:
strcpy (p, va_arg(list, char *));
- low->prom_args[i + 3] = (unsigned long)p;
+ p1275buf.prom_args[i + 3] = (unsigned long)p;
p = (char *)(((long)(strchr (p, 0) + 8)) & ~7);
break;
case P1275_ARG_OUT_BUF:
(void) va_arg(list, char *);
- low->prom_args[i + 3] = (unsigned long)p;
+ p1275buf.prom_args[i + 3] = (unsigned long)p;
x = va_arg(list, long);
i++; attrs >>= 3;
p = (char *)(((long)(p + (int)x + 7)) & ~7);
- low->prom_args[i + 3] = x;
+ p1275buf.prom_args[i + 3] = x;
break;
case P1275_ARG_IN_BUF:
q = va_arg(list, char *);
- low->prom_args[i + 3] = (unsigned long)p;
+ p1275buf.prom_args[i + 3] = (unsigned long)p;
x = va_arg(list, long);
i++; attrs >>= 3;
memcpy (p, q, (int)x);
p = (char *)(((long)(p + (int)x + 7)) & ~7);
- low->prom_args[i + 3] = x;
+ p1275buf.prom_args[i + 3] = x;
break;
case P1275_ARG_OUT_32B:
(void) va_arg(list, char *);
- low->prom_args[i + 3] = (unsigned long)p;
+ p1275buf.prom_args[i + 3] = (unsigned long)p;
p += 32;
break;
case P1275_ARG_IN_FUNCTION:
- low->prom_args[i + 3] = 0x8038;
- low->prom_sync_routine = va_arg(list, long); break;
+ p1275buf.prom_args[i + 3] = (unsigned long)prom_cif_interface + 0x38;
+ p1275buf.prom_sync_routine = va_arg(list, long); break;
}
}
va_end(list);
-
- (*prom_do_it)();
+
+ prom_cif_interface();
attrs = fmt >> 8;
va_start(list, fmt);
case P1275_ARG_OUT_BUF:
p = va_arg(list, char *);
x = va_arg(list, long);
- memcpy (p, (char *)(low->prom_args[i + 3]), (int)x);
+ memcpy (p, (char *)(p1275buf.prom_args[i + 3]), (int)x);
i++; attrs >>= 3;
break;
case P1275_ARG_OUT_32B:
p = va_arg(list, char *);
- memcpy (p, (char *)(low->prom_args[i + 3]), 32);
+ memcpy (p, (char *)(p1275buf.prom_args[i + 3]), 32);
break;
}
}
va_end(list);
- x = low->prom_args [nargs + 3];
+ x = p1275buf.prom_args [nargs + 3];
if (ctx)
spitfire_set_primary_context (ctx);
void prom_cif_init(void *cif_handler, void *cif_stack)
{
- at0x8000 *low = (at0x8000 *)(0x8000);
-
- low->prom_cif_handler = (void (*)(long *))cif_handler;
- low->prom_cif_stack = (unsigned long)cif_stack;
- prom_do_it = (void (*)(void))(0x8000);
+ p1275buf.prom_cif_handler = (void (*)(long *))cif_handler;
+ p1275buf.prom_cif_stack = (unsigned long)cif_stack;
}
SECTIONS
{
- empty_zero_page = 0xfffff80000000000;
- swapper_pg_dir = 0xfffff80000002000;
+ empty_zero_page = 0x0000000000400000;
+ swapper_pg_dir = 0x0000000000402000;
. = 0x4000;
- .text 0xfffff80000004000 :
+ .text 0x0000000000404000 :
{
*(.text)
*(.gnu.warning)
.data.init : { *(.data.init) }
. = ALIGN(8192);
__init_end = .;
- __p1275_loc = .;
- .p1275 :
- {
- *(.p1275)
- . = ALIGN(8192);
- }
__bss_start = .;
.sbss : { *(.sbss) *(.scommon) }
.bss :
raw_cmd->flags |= FD_RAW_HARDFAILURE;
} else {
raw_cmd->reply_count = inr;
+ if(raw_cmd->reply_count > MAX_REPLIES)
+ raw_cmd->reply_count=0;
for (i=0; i< raw_cmd->reply_count; i++)
raw_cmd->reply[i] = reply_buffer[i];
poll_aux_status_nosleep();
#endif /* INITIALIZE_DEVICE */
outb_p(KBD_CCMD_MOUSE_DISABLE, KBD_CNTL_REG); /* Disable Aux device */
- aux_write_dev_nosleep(AUX_INTS_OFF); /* Disable controller interrupts */
+ poll_aux_status_nosleep();
+ outb_p(KBD_CCMD_WRITE_MODE, KBD_CNTL_REG);
+ poll_aux_status_nosleep();
+ outb_p(AUX_INTS_OFF, KBD_DATA_REG);
}
return 0;
}
#define PTY_MAGIC 0x5001
-#define PTY_BUF_SIZE PAGE_SIZE/2
-
-/*
- * tmp_buf is used as a temporary buffer by pty_write. We need to
- * lock it in case the copy_from_user blocks while swapping in a page,
- * and some other program tries to do a pty write at the same time.
- * Since the lock will only come under contention when the system is
- * swapping and available memory is low, it makes sense to share one
- * buffer across all the PTY's, since it significantly saves memory if
- * large numbers of PTY's are open.
- */
-static unsigned char *tmp_buf;
-static struct semaphore tmp_buf_sem = MUTEX;
-
static struct tty_driver pty_driver, pty_slave_driver;
static struct tty_driver old_pty_driver, old_pty_slave_driver;
static int pty_refcount;
set_bit(TTY_THROTTLED, &tty->flags);
}
+/*
+ * WSH 05/24/97: modified to
+ * (1) use space in tty->flip instead of a shared temp buffer
+ * The flip buffers aren't being used for a pty, so there's lots
+ * of space available. The buffer is protected by a per-pty
+ * semaphore that should almost never come under contention.
+ * (2) avoid redundant copying for cases where count >> receive_room
+ * N.B. Calls from user space may now return an error code instead of
+ * a count.
+ */
static int pty_write(struct tty_struct * tty, int from_user,
const unsigned char *buf, int count)
{
struct tty_struct *to = tty->link;
- int c=0, n, r;
+ int c=0, n;
char *temp_buffer;
if (!to || tty->stopped)
return 0;
-
+
if (from_user) {
- down(&tmp_buf_sem);
- temp_buffer = tmp_buf +
- ((tty->driver.subtype-1) * PTY_BUF_SIZE);
+ down(&tty->flip.pty_sem);
+ temp_buffer = &tty->flip.char_buf[0];
while (count > 0) {
- n = MIN(count, PTY_BUF_SIZE);
+ /* check space so we don't copy needlessly */
+ n = MIN(count, to->ldisc.receive_room(to));
+ if (!n) break;
+
+ n = MIN(n, PTY_BUF_SIZE);
n -= copy_from_user(temp_buffer, buf, n);
if (!n) {
if (!c)
c = -EFAULT;
break;
}
- r = to->ldisc.receive_room(to);
- if (r <= 0)
- break;
- n = MIN(n, r);
- to->ldisc.receive_buf(to, temp_buffer, 0, n);
- buf += n; c+= n;
+
+ /* check again in case the buffer filled up */
+ n = MIN(n, to->ldisc.receive_room(to));
+ if (!n) break;
+ buf += n;
+ c += n;
count -= n;
+ to->ldisc.receive_buf(to, temp_buffer, 0, n);
}
- up(&tmp_buf_sem);
+ up(&tty->flip.pty_sem);
} else {
c = MIN(count, to->ldisc.receive_room(to));
to->ldisc.receive_buf(to, buf, 0, c);
return to->ldisc.receive_room(to);
}
+/*
+ * WSH 05/24/97: Modified for asymmetric MASTER/SLAVE behavior
+ * The chars_in_buffer() value is used by the ldisc select() function
+ * to hold off writing when chars_in_buffer > WAKEUP_CHARS (== 256).
+ * The pty driver chars_in_buffer() Master/Slave must behave differently:
+ *
+ * The Master side needs to allow typed-ahead commands to accumulate
+ * while being canonicalized, so we report "our buffer" as empty until
+ * some threshold is reached, and then report the count. (Any count >
+ * WAKEUP_CHARS is regarded by select() as "full".) To avoid deadlock
+ * the count returned must be 0 if no canonical data is available to be
+ * read. (The N_TTY ldisc.chars_in_buffer now knows this.)
+ *
+ * The Slave side passes all characters in raw mode to the Master side's
+ * buffer where they can be read immediately, so in this case we can
+ * return the true count in the buffer.
+ */
static int pty_chars_in_buffer(struct tty_struct *tty)
{
struct tty_struct *to = tty->link;
+ int count;
if (!to || !to->ldisc.chars_in_buffer)
return 0;
- return to->ldisc.chars_in_buffer(to);
+ /* The ldisc must report 0 if no characters available to be read */
+ count = to->ldisc.chars_in_buffer(to);
+
+ if (tty->driver.subtype == PTY_TYPE_SLAVE) return count;
+
+ /* Master side driver ... if the other side's read buffer is less than
+ * half full, return 0 to allow writers to proceed; otherwise return
+ * the count. This leaves a comfortable margin to avoid overflow,
+ * and still allows half a buffer's worth of typed-ahead commands.
+ */
+ return ((count < N_TTY_BUF_SIZE/2) ? 0 : count);
}
static void pty_flush_buffer(struct tty_struct *tty)
pty = pty_state + line;
tty->driver_data = pty;
- if (!tmp_buf) {
- unsigned long page = __get_free_page(GFP_KERNEL);
- if (!tmp_buf) {
- retval = -ENOMEM;
- if (!page)
- goto out;
- tmp_buf = (unsigned char *) page;
- memset((void *) page, 0, PAGE_SIZE);
- } else
- free_page(page);
- }
retval = -EIO;
if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
goto out;
old_pty_slave_driver.num = (NR_PTYS > 64) ? 64 : NR_PTYS;
old_pty_slave_driver.other = &old_pty_driver;
- tmp_buf = 0;
-
if (tty_register_driver(&pty_driver))
panic("Couldn't register pty driver");
if (tty_register_driver(&pty_slave_driver))
*/
#include <linux/utsname.h>
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/string.h>
unsigned long count)
{
struct wait_queue wait = { current, NULL };
- int retval;
+ int retval = 0;
if (count < sizeof(unsigned long))
return -EINVAL;
/* -*- linux-c -*-
*
- * $Id: sysrq.c,v 1.2 1997/05/31 18:33:11 mj Exp $
+ * $Id: sysrq.c,v 1.3 1997/06/18 09:42:12 mj Exp $
*
* Linux Magic System Request Key Hacks
*
show_mem();
break;
case 2 ... 11: /* 0-9 -- set console logging level */
- key -= 2;
+ key--;
if (key == 10)
key = 0;
orig_log_level = key;
tty->flip.flag_buf_ptr = tty->flip.flag_buf;
tty->flip.tqueue.routine = flush_to_ldisc;
tty->flip.tqueue.data = tty;
+ tty->flip.pty_sem = MUTEX;
}
/*
func_scr_writew((func_scr_readw(org) & 0xff00) | c, org);
}
}
+#ifdef CONFIG_FB_CONSOLE
+ if (currcons == fg_console)
+ /* Horribly inefficient if count < screen size. */
+ update_screen(currcons);
+#endif
written = buf - buf0;
file->f_pos += written;
RETURN( written );
*
*/
#include <linux/module.h>
+#include <linux/config.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/stddef.h>
bool 'Soundmodem support for 2400 baud AFSK modulation (8MHz crystal)' CONFIG_SOUNDMODEM_AFSK2400_8
bool 'Soundmodem support for 4800 baud HAPN-1 modulation' CONFIG_SOUNDMODEM_HAPN4800
bool 'Soundmodem support for 9600 baud FSK G3RUH modulation' CONFIG_SOUNDMODEM_FSK9600
- #bool 'Soundmodem support for 2666 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK2666
- #bool 'Soundmodem support for 4800 baud PSK modulation' CONFIG_SOUNDMODEM_PSK4800
+ if [ -f drivers/net/soundmodem/sm_afsk2666.c ]; then
+ bool 'Soundmodem support for 2666 baud AFSK modulation' CONFIG_SOUNDMODEM_AFSK2666
+ fi
+ if [ -f drivers/net/soundmodem/sm_psk4800.c ]; then
+ bool 'Soundmodem support for 4800 baud PSK modulation' CONFIG_SOUNDMODEM_PSK4800
+ fi
fi
fi
tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP
#include <linux/version.h>
#endif
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
* - Jay Schulist <Jay.Schulist@spacs.k12.wi.us>
*/
+#include <linux/config.h>
#ifdef CONFIG_COPS_DAYNA
* - Jay Schulist <Jay.Schulist@spacs.k12.wi.us>
*/
+#include <linux/config.h>
#ifdef CONFIG_COPS_TANGENT
#include <asm/io.h>
#include <asm/dma.h>
-#include <linux/config.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/ioport.h>
-/* $Id: cgsix.c,v 1.32 1997/06/14 15:26:08 davem Exp $
+/* $Id: cgsix.c,v 1.33 1997/07/01 09:12:05 jj Exp $
* cgsix.c: cgsix frame buffer driver
*
* Copyright (C) 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
{
uint size, page, r, map_size;
unsigned long map_offset = 0;
-
+
size = vma->vm_end - vma->vm_start;
if (vma->vm_offset & ~PAGE_MASK)
return -ENXIO;
+#ifdef __sparc_v9__
+ /* Try to align RAM */
+#define ALIGNMENT 0x80000
+ map_offset = vma->vm_offset + size;
+ if (vma->vm_offset <= CG6_RAM && map_offset >= CG6_RAM + fb->type.fb_size) {
+ struct vm_area_struct *vmm = find_vma(current->mm, vma->vm_start);
+ int alignment = ALIGNMENT - ((vma->vm_start + CG6_RAM - vma->vm_offset) & (ALIGNMENT - 1));
+ int sz = 0, fbsz;
+
+ if (alignment == ALIGNMENT) alignment = 0;
+ fbsz = ((fb->type.fb_size + ALIGNMENT - 1) & ~(ALIGNMENT - 1));
+ if (map_offset < CG6_RAM + fbsz)
+ sz = fbsz - map_offset + CG6_RAM;
+ if ((sz || alignment) && (!vmm || vmm->vm_start >= vma->vm_end + sz + alignment)) {
+ vma->vm_start += alignment;
+ vma->vm_end += alignment + sz;
+ }
+ }
+#undef ALIGNMENT
+#endif
+
/* To stop the swapper from even considering these pages */
vma->vm_flags |= FB_MMAP_VM_FLAGS;
struct openpromio *opp;
unsigned long flags;
int bufsize, len, error = 0;
+ extern char saved_command_line[];
if (cmd == OPROMSETOPT)
bufsize = getstrings((void *)arg, &opp);
case OPROMNXTOPT:
case OPROMNXTPROP:
save_and_cli(flags);
- buf = prom_nextprop(node, opp->oprom_array);
+ buf = prom_nextprop(node, opp->oprom_array, buffer);
restore_flags(flags);
len = strlen(buf);
break;
case OPROMGETBOOTARGS:
- save_and_cli(flags);
- buf = prom_getbootargs();
- restore_flags(flags);
+ buf = saved_command_line;
len = strlen(buf);
unsigned long flags;
int error, node, len;
char *str, *tmp;
+ char buffer[64];
switch (cmd) {
case OPIOCGET:
return error;
save_and_cli(flags);
- tmp = prom_nextprop(op.op_nodeid,str);
+ tmp = prom_nextprop(op.op_nodeid,str,buffer);
restore_flags(flags);
if (tmp) {
-/* $Id: sunfb.c,v 1.24 1997/06/06 10:56:24 jj Exp $
+/* $Id: sunfb.c,v 1.25 1997/07/01 09:12:06 jj Exp $
* sunfb.c: Sun generic frame buffer support.
*
* Copyright (C) 1995, 1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
v = (*fb->mmap)(inode, file, vma, fb->base, fb);
if (v) return v;
+ vma->vm_flags |= VM_IO;
if (!fb->mmaped) {
fb->mmaped = 1;
if (!minor) {
if(ch == SKBD_RESET) {
kbd_reset_pending = 1;
- return;
+ goto out;
}
if(ch == SKBD_LYOUT) {
kbd_layout_pending = 1;
- return;
+ goto out;
}
if(kbd_reset_pending) {
sunkbd_type = ch;
kbd_reset_pending = 0;
if(ch == SUNKBD_TYPE4)
send_cmd(SKBDCMD_GLAYOUT);
- return;
+ goto out;
} else if(kbd_layout_pending) {
sunkbd_layout = ch;
kbd_layout_pending = 0;
- return;
+ goto out;
} else if(ch == SKBD_ALLUP) {
del_timer (&auto_repeat_timer);
memset(key_down, 0, sizeof(key_down));
compute_shiftstate();
- return;
+ goto out;
}
#ifdef SKBD_DEBUG
if(ch == 0x7f)
} else {
keycode = ch;
}
- add_keyboard_randomness(keycode);
- mark_bh(KEYBOARD_BH);
do_poke_blanked_console = 1;
mark_bh(CONSOLE_BH);
+ add_keyboard_randomness(keycode);
+
kbd = kbd_table + fg_console;
tty = ttytab[fg_console];
if((raw_mode = (kbd->kbdmode == VC_RAW))) {
}
if(raw_mode)
- return;
+ goto out;
if(kbd->kbdmode == VC_MEDIUMRAW) {
put_queue(keycode + up_flag);
- return;
+ goto out;
}
/*
compute_shiftstate();
}
}
+out:
+ mark_bh(KEYBOARD_BH);
}
static void put_queue(int ch)
ctr = 0;
wait_for_synchron = 1;
if(mouse_baud_changing == 1) {
- printk("sunmouse: Successfully adjusted to %d baud.\n",
+ printk(KERN_DEBUG "sunmouse: Successfully adjusted to %d baud.\n",
mouse_baud);
mouse_baud_changing = 0;
}
-/* $Id: sunserial.c,v 1.42 1997/05/26 20:10:20 davem Exp $
+/* $Id: sunserial.c,v 1.43 1997/07/05 09:53:23 davem Exp $
* serial.c: Serial port driver for the Sparc.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
*/
printk("\n");
flush_user_windows();
+#ifndef __sparc_v9__
if((((unsigned long)linux_dbvec)>=DEBUG_FIRSTVADDR) &&
(((unsigned long)linux_dbvec)<=DEBUG_LASTVADDR))
sp_enter_debugger();
else
+#endif
prom_cmdline();
/* XXX We want to notify the keyboard driver that all
mark_bh(SERIAL_BH);
}
+#ifndef __sparc_v9__
extern void breakpoint(void); /* For the KGDB frame character */
+#endif
static _INLINE_ void receive_chars(struct sun_serial *info, struct pt_regs *regs)
{
/* It is a 'keyboard interrupt' ;-) */
wake_up(&keypress_wait);
}
+#ifndef __sparc_v9__
/* Look for kgdb 'stop' character, consult the gdb
* documentation for remote target debugging and
* arch/sparc/kernel/sparc-stub.c to see how all this works.
breakpoint();
return;
}
-
+#endif
if(!tty)
return;
static void show_serial_version(void)
{
- char *revision = "$Revision: 1.42 $";
+ char *revision = "$Revision: 1.43 $";
char *version, *p;
version = strchr(revision, ' ');
*/
+#include <linux/config.h>
+
/*
Define types for some of the structures that interface with the rest
of the Linux Kernel and SCSI Subsystem.
int amiga7xx_detect(Scsi_Host_Template *tpnt)
{
static unsigned char called = 0;
- int key;
+ int key, clock;
int num = 0;
unsigned long address;
long long options;
clock = 50000000; /* 50MHz SCSI Clock */
ncr53c7xx_init(tpnt, 0, 710, (u32)(unsigned char *)(address + 0x40000),
- 0, IRQ_AMIGA_PORTS, DMA_NONE,
- options, clock);
+ 0, IRQ_AMIGA_PORTS, DMA_NONE, options, clock);
zorro_config_board(key, 0);
num++;
clock = 50000000; /* 50MHz SCSI Clock */
- ncr53c7xx_init(tpnt, 0, 710, (u32)(unsigned char *)ZTWO_VADDR(0xDD0040),
- 0, IRQ_AMIGA_PORTS, DMA_NONE,
- options, clock);
+ ncr53c7xx_init(tpnt, 0, 710,
+ (u32)(unsigned char *)ZTWO_VADDR(0xDD0040),
+ 0, IRQ_AMIGA_PORTS, DMA_NONE, options, clock);
num++;
}
#endif
#ifdef CONFIG_A4091_SCSI
while ( (key = zorro_find(MANUF_COMMODORE, PROD_A4091, 0, 0)) ||
- (key = zorro_find(MANUF_COMMODORE2, PROD_A4091_2, 0, 0)) )
+ (key = zorro_find(MANUF_COMMODORE2, PROD_A4091_2, 0, 0)) )
{
cd = zorro_get_board(key);
address = (unsigned long)kernel_map((unsigned long)cd->cd_BoardAddr,
* [Curtin-1-08-STABLE]
*/
+#include <linux/config.h>
+
/* The following #define is to avoid a clash with hosts.c */
#define PPA_CODE 1
#include "ppa.h"
+#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#ifndef _DEV_TABLE_H_
#define _DEV_TABLE_H_
+#include <linux/config.h>
/*
* Sound card numbers 27 to 999. (1 to 26 are defined in soundcard.h)
old_bh = affs_find_entry(old_dir,old_name,old_len,&old_ino);
if (!old_bh)
goto end_rename;
- old_inode = __iget(old_dir->i_sb,old_ino,0);
+ old_inode = __iget(old_dir->i_sb,old_ino);
if (!old_inode)
goto end_rename;
new_bh = affs_find_entry(new_dir,new_name,new_len,&new_ino);
if (new_bh) {
- new_inode = __iget(new_dir->i_sb,new_ino,0);
+ new_inode = __iget(new_dir->i_sb,new_ino);
if (!new_inode) { /* What does this mean? */
affs_brelse(new_bh);
new_bh = NULL;
#define AUTOFS_HASH_SIZE 67
-typedef u32 autofs_hash_t; /* Type returned by autofs_hash() */
-
struct autofs_dir_ent {
- autofs_hash_t hash;
+ int hash;
struct autofs_dir_ent *next;
struct autofs_dir_ent **back;
char *name;
struct wait_queue *queue;
struct autofs_wait_queue *next;
/* We use the following to see what we are waiting for */
- autofs_hash_t hash;
+ int hash;
int len;
char *name;
/* This is for status reporting upon return */
/* Hash operations */
-autofs_hash_t autofs_hash(const char *,int);
void autofs_initialize_hash(struct autofs_dirhash *);
-struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *,autofs_hash_t,const char *,int);
+struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *,struct qstr *);
void autofs_hash_insert(struct autofs_dirhash *,struct autofs_dir_ent *);
void autofs_hash_delete(struct autofs_dir_ent *);
struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *,off_t *);
/* Queue management functions */
-int autofs_wait(struct autofs_sb_info *,autofs_hash_t,const char *,int);
+int autofs_wait(struct autofs_sb_info *,struct qstr *);
int autofs_wait_release(struct autofs_sb_info *,unsigned long,int);
void autofs_catatonic_mode(struct autofs_sb_info *);
return 1;
}
-static int autofs_dir_lookup(struct inode *dir, const char *name, int len,
- struct inode **result)
+/*
+ * No entries except for "." and "..", both of which are handled by the VFS layer
+ */
+static int autofs_dir_lookup(struct inode *dir, struct qstr *str, struct inode **result)
{
- *result = dir;
- if (!len)
- return 0;
- if (name[0] == '.') {
- if (len == 1)
- return 0;
- if (name[1] == '.' && len == 2) {
- /* Return the root directory */
- *result = iget(dir->i_sb,AUTOFS_ROOT_INO);
- iput(dir);
- return 0;
- }
- }
- *result = NULL;
- iput(dir);
return -ENOENT; /* No other entries */
}
return (jiffies - ent->last_usage >= timeout) ? ent : NULL;
}
-/* Adapted from the Dragon Book, page 436 */
-/* This particular hashing algorithm requires autofs_hash_t == u32 */
-autofs_hash_t autofs_hash(const char *name, int len)
-{
- autofs_hash_t h = 0;
- while ( len-- ) {
- h = (h << 4) + (unsigned char) (*name++);
- h ^= ((h & 0xf0000000) >> 24);
- }
- return h;
-}
-
void autofs_initialize_hash(struct autofs_dirhash *dh) {
memset(&dh->h, 0, AUTOFS_HASH_SIZE*sizeof(struct autofs_dir_ent *));
dh->expiry_head.exp_next = dh->expiry_head.exp_prev =
&dh->expiry_head;
}
-struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *dh, autofs_hash_t hash, const char *name, int len)
+struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *dh, struct qstr *name)
{
struct autofs_dir_ent *dhn;
- DPRINTK(("autofs_hash_lookup: hash = 0x%08x, name = ", hash));
- autofs_say(name,len);
+ DPRINTK(("autofs_hash_lookup: hash = 0x%08x, name = ", name->hash));
+ autofs_say(name->name,name->len);
- for ( dhn = dh->h[hash % AUTOFS_HASH_SIZE] ; dhn ; dhn = dhn->next ) {
- if ( hash == dhn->hash &&
- len == dhn->len &&
- !memcmp(name, dhn->name, len) )
+ for ( dhn = dh->h[(unsigned) name->hash % AUTOFS_HASH_SIZE] ; dhn ; dhn = dhn->next ) {
+ if ( name->hash == dhn->hash &&
+ name->len == dhn->len &&
+ !memcmp(name->name, dhn->name, name->len) )
break;
}
autofs_init_usage(dh,ent);
- dhnp = &dh->h[ent->hash % AUTOFS_HASH_SIZE];
+ dhnp = &dh->h[(unsigned) ent->hash % AUTOFS_HASH_SIZE];
ent->next = *dhnp;
ent->back = dhnp;
*dhnp = ent;
s->s_magic = AUTOFS_SUPER_MAGIC;
s->s_op = &autofs_sops;
unlock_super(s);
- if (!(s->s_mounted = iget(s, AUTOFS_ROOT_INO))) {
+ s->s_root = d_alloc_root(iget(s, AUTOFS_ROOT_INO), NULL);
+ if (!s->s_root) {
s->s_dev = 0;
kfree(sbi);
printk("autofs: get root inode failed\n");
return NULL;
}
- if ( parse_options(data,&pipefd,&s->s_mounted->i_uid,&s->s_mounted->i_gid,&sbi->oz_pgrp,&minproto,&maxproto) ) {
- iput(s->s_mounted);
+ if ( parse_options(data,&pipefd,&s->s_root->d_inode->i_uid,&s->s_root->d_inode->i_gid,&sbi->oz_pgrp,&minproto,&maxproto) ) {
+ d_delete(s->s_root);
s->s_dev = 0;
kfree(sbi);
printk("autofs: called with bogus options\n");
}
if ( minproto > AUTOFS_PROTO_VERSION || maxproto < AUTOFS_PROTO_VERSION ) {
- iput(s->s_mounted);
+ d_delete(s->s_root);
s->s_dev = 0;
kfree(sbi);
printk("autofs: kernel does not match daemon version\n");
} else {
printk("autofs: could not open pipe file descriptor\n");
}
- iput(s->s_mounted);
+ d_delete(s->s_root);
s->s_dev = 0;
kfree(sbi);
MOD_DEC_USE_COUNT;
return;
}
- inode->i_uid = inode->i_sb->s_mounted->i_uid;
- inode->i_gid = inode->i_sb->s_mounted->i_gid;
+ inode->i_uid = inode->i_sb->s_root->d_inode->i_uid;
+ inode->i_gid = inode->i_sb->s_root->d_inode->i_gid;
if ( ino >= AUTOFS_FIRST_SYMLINK && ino < AUTOFS_FIRST_DIR_INO ) {
/* Symlink inode - should be in symlink list */
#include "autofs_i.h"
static int autofs_root_readdir(struct inode *,struct file *,void *,filldir_t);
-static int autofs_root_lookup(struct inode *,const char *,int,struct inode **);
-static int autofs_root_symlink(struct inode *,const char *,int,const char *);
-static int autofs_root_unlink(struct inode *,const char *,int);
-static int autofs_root_rmdir(struct inode *,const char *,int);
-static int autofs_root_mkdir(struct inode *,const char *,int,int);
+static int autofs_root_lookup(struct inode *,struct qstr *,struct inode **);
+static int autofs_root_symlink(struct inode *,struct dentry *,const char *);
+static int autofs_root_unlink(struct inode *,struct dentry *);
+static int autofs_root_rmdir(struct inode *,struct dentry *);
+static int autofs_root_mkdir(struct inode *,struct dentry *,int);
static int autofs_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long);
static struct file_operations autofs_root_operations = {
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
return 0;
}
-static int autofs_root_lookup(struct inode *dir, const char *name, int len,
- struct inode **result)
+static int autofs_root_lookup(struct inode *dir, struct qstr *str, struct inode **result)
{
struct autofs_sb_info *sbi;
struct autofs_dir_ent *ent;
struct inode *res;
- autofs_hash_t hash;
int status, oz_mode;
DPRINTK(("autofs_root_lookup: name = "));
- autofs_say(name,len);
+ autofs_say(str->name,str->len);
*result = NULL;
if (!dir)
return -ENOENT;
- if (!S_ISDIR(dir->i_mode)) {
- iput(dir);
+ if (!S_ISDIR(dir->i_mode))
return -ENOTDIR;
- }
-
- /* Handle special cases: . and ..; since this is a root directory,
- they both point to the inode itself */
- *result = dir;
- if (!len)
- return 0;
- if (name[0] == '.') {
- if (len == 1)
- return 0;
- if (name[1] == '.' && len == 2)
- return 0;
- }
*result = res = NULL;
sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
- hash = autofs_hash(name,len);
-
oz_mode = autofs_oz_mode(sbi);
DPRINTK(("autofs_lookup: pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d\n", current->pid, current->pgrp, sbi->catatonic, oz_mode));
do {
- while ( !(ent = autofs_hash_lookup(&sbi->dirhash,hash,name,len)) ) {
+ while ( !(ent = autofs_hash_lookup(&sbi->dirhash,str)) ) {
DPRINTK(("lookup failed, pid = %u, pgrp = %u\n", current->pid, current->pgrp));
- if ( oz_mode ) {
- iput(dir);
+ if ( oz_mode )
return -ENOENT;
- } else {
- status = autofs_wait(sbi,hash,name,len);
- DPRINTK(("autofs_wait returned %d\n", status));
- if ( status ) {
- iput(dir);
- return status;
- }
- }
+ up(&dir->i_sem);
+ status = autofs_wait(sbi,str);
+ down(&dir->i_sem);
+ DPRINTK(("autofs_wait returned %d\n", status));
+ if ( status )
+ return status;
}
DPRINTK(("lookup successful, inode = %08x\n", (unsigned int)ent->ino));
if (!(res = iget(dir->i_sb,ent->ino))) {
printk("autofs: iget returned null!\n");
- iput(dir);
return -EACCES;
}
if ( !oz_mode && S_ISDIR(res->i_mode) && res->i_sb == dir->i_sb ) {
/* Not a mount point yet, call 1-800-DAEMON */
DPRINTK(("autofs: waiting on non-mountpoint dir, inode = %lu, pid = %u, pgrp = %u\n", res->i_ino, current->pid, current->pgrp));
- iput(res);
res = NULL;
- status = autofs_wait(sbi,hash,name,len);
- if ( status ) {
- iput(dir);
+ up(&dir->i_sem);
+ status = autofs_wait(sbi,str);
+ down(&dir->i_sem);
+ if ( status )
return status;
- }
}
} while(!res);
autofs_update_usage(&sbi->dirhash,ent);
*result = res;
- iput(dir);
return 0;
}
-static int autofs_root_symlink(struct inode *dir, const char *name, int len, const char *symname)
+static int autofs_root_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
{
struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
struct autofs_dirhash *dh = &sbi->dirhash;
- autofs_hash_t hash = autofs_hash(name,len);
struct autofs_dir_ent *ent;
unsigned int n;
int slsize;
DPRINTK(("autofs_root_symlink: %s <- ", symname));
autofs_say(name,len);
- if ( !autofs_oz_mode(sbi) ) {
- iput(dir);
+ if ( !autofs_oz_mode(sbi) )
return -EPERM;
- }
- if ( autofs_hash_lookup(dh,hash,name,len) ) {
- iput(dir);
+
+ if ( autofs_hash_lookup(dh, &dentry->d_name) )
return -EEXIST;
- }
+
n = find_first_zero_bit(sbi->symlink_bitmap,AUTOFS_MAX_SYMLINKS);
- if ( n >= AUTOFS_MAX_SYMLINKS ) {
- iput(dir);
+ if ( n >= AUTOFS_MAX_SYMLINKS )
return -ENOSPC;
- }
+
set_bit(n,sbi->symlink_bitmap);
sl = &sbi->symlink[n];
sl->len = strlen(symname);
sl->data = kmalloc(slsize = sl->len+1, GFP_KERNEL);
if ( !sl->data ) {
clear_bit(n,sbi->symlink_bitmap);
- iput(dir);
return -ENOSPC;
}
+
ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL);
if ( !ent ) {
kfree(sl->data);
clear_bit(n,sbi->symlink_bitmap);
- iput(dir);
return -ENOSPC;
}
- ent->name = kmalloc(len, GFP_KERNEL);
+
+ ent->name = kmalloc(dentry->d_name.len, GFP_KERNEL);
if ( !ent->name ) {
kfree(sl->data);
kfree(ent);
clear_bit(n,sbi->symlink_bitmap);
- iput(dir);
return -ENOSPC;
}
+
memcpy(sl->data,symname,slsize);
sl->mtime = CURRENT_TIME;
ent->ino = AUTOFS_FIRST_SYMLINK + n;
- ent->hash = hash;
- memcpy(ent->name,name,ent->len = len);
+ ent->hash = dentry->d_name.hash;
+ memcpy(ent->name, dentry->d_name.name,ent->len = dentry->d_name.len);
autofs_hash_insert(dh,ent);
- iput(dir);
+ d_instantiate(dentry, iget(dir->i_sb,ent->ino), 0);
return 0;
}
-static int autofs_root_unlink(struct inode *dir, const char *name, int len)
+static int autofs_root_unlink(struct inode *dir, struct dentry *dentry)
{
struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
struct autofs_dirhash *dh = &sbi->dirhash;
- autofs_hash_t hash = autofs_hash(name,len);
struct autofs_dir_ent *ent;
unsigned int n;
- iput(dir); /* Nothing below can sleep */
-
if ( !autofs_oz_mode(sbi) )
return -EPERM;
- ent = autofs_hash_lookup(dh,hash,name,len);
+ ent = autofs_hash_lookup(dh, &dentry->d_name);
if ( !ent )
return -ENOENT;
autofs_hash_delete(ent);
clear_bit(n,sbi->symlink_bitmap);
kfree(sbi->symlink[n].data);
+ d_delete(dentry);
return 0;
}
-static int autofs_root_rmdir(struct inode *dir, const char *name, int len)
+static int autofs_root_rmdir(struct inode *dir, struct dentry *dentry)
{
struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
struct autofs_dirhash *dh = &sbi->dirhash;
- autofs_hash_t hash = autofs_hash(name,len);
struct autofs_dir_ent *ent;
- if ( !autofs_oz_mode(sbi) ) {
- iput(dir);
+ if ( !autofs_oz_mode(sbi) )
return -EPERM;
- }
- ent = autofs_hash_lookup(dh,hash,name,len);
- if ( !ent ) {
- iput(dir);
+
+ ent = autofs_hash_lookup(dh, &dentry->d_name);
+ if ( !ent )
return -ENOENT;
- }
- if ( (unsigned int)ent->ino < AUTOFS_FIRST_DIR_INO ) {
- iput(dir);
+
+ if ( (unsigned int)ent->ino < AUTOFS_FIRST_DIR_INO )
return -ENOTDIR; /* Not a directory */
- }
+
autofs_hash_delete(ent);
dir->i_nlink--;
- iput(dir);
+ d_delete(dentry);
return 0;
}
-static int autofs_root_mkdir(struct inode *dir, const char *name, int len, int mode)
+static int autofs_root_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
struct autofs_dirhash *dh = &sbi->dirhash;
- autofs_hash_t hash = autofs_hash(name,len);
struct autofs_dir_ent *ent;
- if ( !autofs_oz_mode(sbi) ) {
- iput(dir);
+ if ( !autofs_oz_mode(sbi) )
return -EPERM;
- }
- ent = autofs_hash_lookup(dh,hash,name,len);
- if ( ent ) {
- iput(dir);
+
+ ent = autofs_hash_lookup(dh, &dentry->d_name);
+ if ( ent )
return -EEXIST;
- }
+
if ( sbi->next_dir_ino < AUTOFS_FIRST_DIR_INO ) {
printk("autofs: Out of inode numbers -- what the heck did you do??\n");
- iput(dir);
return -ENOSPC;
}
+
ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL);
- if ( !ent ) {
- iput(dir);
+ if ( !ent )
return -ENOSPC;
- }
- ent->name = kmalloc(len, GFP_KERNEL);
+
+ ent->name = kmalloc(dentry->d_name.len, GFP_KERNEL);
if ( !ent->name ) {
kfree(ent);
- iput(dir);
return -ENOSPC;
}
- ent->hash = hash;
- memcpy(ent->name, name, ent->len = len);
+
+ ent->hash = dentry->d_name.hash;
+ memcpy(ent->name, dentry->d_name.name, ent->len = dentry->d_name.len);
ent->ino = sbi->next_dir_ino++;
autofs_hash_insert(dh,ent);
dir->i_nlink++;
- iput(dir);
+ d_instantiate(dentry, iget(dir->i_sb,ent->ino), D_DIR);
return 0;
}
struct autofs_symlink *sl;
int len;
- if (!S_ISLNK(inode->i_mode)) {
- iput(inode);
- return -EINVAL;
- }
sl = (struct autofs_symlink *)inode->u.generic_ip;
len = sl->len;
if (len > buflen) len = buflen;
copy_to_user(buffer,sl->data,len);
- iput(inode);
return len;
}
+static struct dentry * autofs_follow_link(struct inode *inode, struct dentry *base)
+{
+ struct autofs_symlink *sl;
+
+ sl = (struct autofs_symlink *)inode->u.generic_ip;
+ return lookup_dentry(sl->data, base, 1);
+}
+
struct inode_operations autofs_symlink_inode_operations = {
NULL, /* file operations */
NULL, /* create */
NULL, /* mknod */
NULL, /* rename */
autofs_readlink, /* readlink */
+ autofs_follow_link, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
autofs_catatonic_mode(sbi);
}
-int autofs_wait(struct autofs_sb_info *sbi, autofs_hash_t hash, const char *name, int len)
+int autofs_wait(struct autofs_sb_info *sbi, struct qstr * name)
{
struct autofs_wait_queue *wq;
int status;
for ( wq = sbi->queues ; wq ; wq = wq->next ) {
- if ( wq->hash == hash &&
- wq->len == len &&
- wq->name && !memcmp(wq->name,name,len) )
+ if ( wq->hash == name->hash &&
+ wq->len == name->len &&
+ wq->name && !memcmp(wq->name,name->name,name->len) )
break;
}
if ( !wq )
return -ENOMEM;
- wq->name = kmalloc(len,GFP_KERNEL);
+ wq->name = kmalloc(name->len,GFP_KERNEL);
if ( !wq->name ) {
kfree(wq);
return -ENOMEM;
}
wq->wait_queue_token = autofs_next_wait_queue++;
init_waitqueue(&wq->queue);
- wq->hash = hash;
- wq->len = len;
+ wq->hash = name->hash;
+ wq->len = name->len;
wq->status = -EINTR; /* Status return if interrupted */
- memcpy(wq->name, name, len);
+ memcpy(wq->name, name->name, name->len);
wq->next = sbi->queues;
sbi->queues = wq;
{
elf_caddr_t *argv;
elf_caddr_t *envp;
- elf_addr_t *sp;
+ elf_addr_t *sp, *csp;
/*
- * Force 16 byte alignment here for generality.
+ * Force 16 byte _final_ alignment here for generality.
*/
sp = (elf_addr_t *) (~15UL & (unsigned long) p);
-#ifdef __sparc__
-{
- elf_addr_t *csp;
csp = sp;
csp -= exec ? DLINFO_ITEMS*2 : 2;
csp -= envc+1;
csp -= argc+1;
- if (!(((unsigned long) csp) & 4))
- sp--;
-}
-#endif
+ csp -= (!ibcs ? 3 : 1); /* argc itself */
+ if ((unsigned long)csp & 15UL) {
+ sp -= (16UL - ((unsigned long)csp & 15UL)) / sizeof(*sp);
+ }
/*
* Put the ELF interpreter info on the stack
if (exec) {
sp -= 11*2;
- NEW_AUX_ENT (0, AT_PHDR, load_addr + exec->e_phoff);
- NEW_AUX_ENT (1, AT_PHENT, sizeof (struct elf_phdr));
- NEW_AUX_ENT (2, AT_PHNUM, exec->e_phnum);
- NEW_AUX_ENT (3, AT_PAGESZ, ELF_EXEC_PAGESIZE);
- NEW_AUX_ENT (4, AT_BASE, interp_load_addr);
- NEW_AUX_ENT (5, AT_FLAGS, 0);
- NEW_AUX_ENT (6, AT_ENTRY, (elf_addr_t) exec->e_entry);
- NEW_AUX_ENT (7, AT_UID, (elf_addr_t) current->uid);
- NEW_AUX_ENT (8, AT_EUID, (elf_addr_t) current->euid);
- NEW_AUX_ENT (9, AT_GID, (elf_addr_t) current->gid);
- NEW_AUX_ENT (10, AT_EGID, (elf_addr_t) current->egid);
+ NEW_AUX_ENT (0, AT_PHDR, load_addr + exec->e_phoff);
+ NEW_AUX_ENT (1, AT_PHENT, sizeof (struct elf_phdr));
+ NEW_AUX_ENT (2, AT_PHNUM, exec->e_phnum);
+ NEW_AUX_ENT (3, AT_PAGESZ, ELF_EXEC_PAGESIZE);
+ NEW_AUX_ENT (4, AT_BASE, interp_load_addr);
+ NEW_AUX_ENT (5, AT_FLAGS, 0);
+ NEW_AUX_ENT (6, AT_ENTRY, (elf_addr_t) exec->e_entry);
+ NEW_AUX_ENT (7, AT_UID, (elf_addr_t) current->uid);
+ NEW_AUX_ENT (8, AT_EUID, (elf_addr_t) current->euid);
+ NEW_AUX_ENT (9, AT_GID, (elf_addr_t) current->gid);
+ NEW_AUX_ENT (10, AT_EGID, (elf_addr_t) current->egid);
}
#undef NEW_AUX_ENT
* a specified wrapper. This should obsolete binfmt_java, binfmt_em86 and
* binfmt_mz.
*
- * 25.4.97 first version
- * [...]
- * 19.5.97 cleanup
+ * 1997-04-25 first version
+ * [...]
+ * 1997-05-19 cleanup
+ * 1997-06-26 hpa: pass the real filename rather than argv[0]
+ * 1997-06-30 minor cleanup
*/
#include <linux/module.h>
*ep = e->next;
entry_proc_cleanup(e);
kfree(e);
- MOD_DEC_USE_COUNT;
}
write_unlock(&entries_lock);
}
entries = entries->next;
entry_proc_cleanup(e);
kfree(e);
- MOD_DEC_USE_COUNT;
}
write_unlock(&entries_lock);
}
char *iname_addr = iname, *p;
int retval, fmt_flags = 0;
- MOD_INC_USE_COUNT;
if (!enabled) {
retval = -ENOEXEC;
goto _ret;
/* Build args for interpreter */
if ((fmt_flags & ENTRY_STRIP_EXT) &&
- (p = strrchr(bprm->filename, '.'))) {
+ (p = strrchr(bprm->filename, '.')))
*p = '\0';
- remove_arg_zero(bprm);
- bprm->p = copy_strings(1, &bprm->filename, bprm->page, bprm->p, 2);
- bprm->argc++;
- }
+ remove_arg_zero(bprm);
+ bprm->p = copy_strings(1, &bprm->filename, bprm->page, bprm->p, 2);
+ bprm->argc++;
bprm->p = copy_strings(1, &iname_addr, bprm->page, bprm->p, 2);
bprm->argc++;
if (!bprm->p) {
if ((retval = prepare_binprm(bprm)) >= 0)
retval = search_binary_handler(bprm, regs);
_ret:
- MOD_DEC_USE_COUNT;
return retval;
}
struct binfmt_entry *e;
int memsize, cnt = count - 1, err = 0;
- MOD_INC_USE_COUNT;
/* some sanity checks */
- if ((count < 11) || (count > 256)) {
- err = -EINVAL;
- goto _err;
- }
+ if ((count < 11) || (count > 256))
+ return -EINVAL;
memsize = sizeof(struct binfmt_entry) + count;
- if (!(e = (struct binfmt_entry *) kmalloc(memsize, GFP_USER))) {
- err = -ENOMEM;
- goto _err;
- }
+ if (!(e = (struct binfmt_entry *) kmalloc(memsize, GFP_USER)))
+ return -ENOMEM;
sp = buffer + 1;
del = buffer[0];
!(e->proc_name) || !(e->interpreter) ||
entry_proc_setup(e)) {
kfree(e);
- err = -EINVAL;
- goto _err;
+ return -EINVAL;
}
write_lock(&entries_lock);
write_unlock(&entries_lock);
return count;
-_err:
- MOD_DEC_USE_COUNT;
- return err;
}
/*
char *dp;
int elen, i;
- MOD_INC_USE_COUNT;
#ifndef VERBOSE_STATUS
if (data) {
read_lock(&entries_lock);
*eof = (elen <= count) ? 1 : 0;
*start = page + off;
- MOD_DEC_USE_COUNT;
return elen;
}
struct binfmt_entry *e;
int res = count;
- MOD_INC_USE_COUNT;
if (((buffer[0] == '1') || (buffer[0] == '0')) &&
((count == 1) || ((count == 2) && (buffer[1] == '\n')))) {
if (data) {
} else {
res = -EINVAL;
}
- MOD_DEC_USE_COUNT;
return res;
}
unregister_binfmt(&misc_format);
remove_proc_entry("register", bm_dir);
remove_proc_entry("status", bm_dir);
+ clear_entries();
remove_proc_entry("sys/fs/binfmt_misc", NULL);
}
#endif
next->b_count--;
}
}
+ run_task_queue(&tq_disk);
#ifdef DEBUG
if (ncount) printk("sync_old_buffers: %d dirty buffers not on dirty list\n", ncount);
printk("Wrote %d/%d buffers\n", nwritten, ndirty);
* (C) 1997 Thomas Schoebel-Theuer
*/
-/* The new dcache is exclusively called from the VFS, not from
- * the specific fs'es any more. Despite having the same name as in the
- * old code, it has less to do with it.
- *
- * It serves many purposes:
- *
- * 1) Any inode that has been retrieved with lookup() and is in use
- * (i_count>0), has access to its full absolute path name, by going
- * to inode->i_dentry and then recursively following the entry->d_parent
- * chain. Use d_path() as predefined method for that.
- * You may find out the corresponding inode belonging to
- * a dentry by calling d_inode(). This can be used as an easy way for
- * determining .. and its absolute pathname, an old UNIX problem that
- * deserved a solution for a long time.
- * Note that hardlinked inodes may have multiple dentries assigned to
- * (via the d_next chain), reflecting multiple alias pathnames.
- *
- * 2) If not disabled by filesystem types specifying FS_NO_DCACHE,
- * the dentries of unused (aged) inodes are retained for speeding up
- * lookup()s, by allowing hashed inquiry starting from the dentry of
- * the parent directory.
- *
- * 3) It can remeber so-called "negative entries", that is dentries for
- * pathnames that are known to *not* exist, so unneccessary repeated
- * lookup()s for non-existant names can be saved.
- *
- * 4) It provides a means for keeping deleted files (inode->i_nlink==0)
- * accessible in the so-called *basket*. Inodes in the basket have been
- * removed with unlink() while being in use (i_count>0), so they would
- * normally use up space on the disk and be accessile through their
- * filedescriptor, but would not be accessible for lookup() any more.
- * The basket simply keeps such files in the dcache (for potential
- * dcache lookup) until they are either eventually removed completely,
- * or transferred to the second-level basket, the so-called *ibasket*.
- * The ibasket is implemented in the new inode code, on request of
- * filesystem types that have the flag FS_IBASKET set, and proliferates
- * the unlinked files when i_count has gone to zero, at least as long
- * as there is space on the disk and enough inodes remain available
- * and no umount() has started.
- *
- * 5) Preliminary dentries can be added by readdir(). While normal dentries
- * directly point to the inode via u.d_inode only the inode number is
- * known from readdir(), but not more. They can be converted to
- * normal dentries by using d_inode().
- */
-
/*
* Notes on the allocation strategy:
*
- * The dcache is a full slave cache of the inodes. Whenever an inode
- * is cleared, all the dentries associated with it will recursively
- * disappear. dentries have no own reference counting; this has to
- * be obeyed for SMP.
- * If directories could go out of inode cache while
- * successors are alive, this would interrupt the d_parent chain of
- * the live successors. To prevent this without using zombies, all
- * directories are thus prevented from __iput() as long as successors
- * are alive.
+ * The dcache is a master of the icache - whenever a dcache entry
+ * exists, the inode will always exist. "iput()" is done either when
+ * the dcache entry is deleted or garbage collected.
*/
-#include <linux/config.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/malloc.h>
/* this should be removed after the beta phase */
-/* #define DEBUG */
+/*#define DEBUG*/
/*#undef DEBUG*/
-/* #define DEBUG_DDIR_COUNT */
-
-#define D_HASHSIZE 64
+/*#define DEBUG_DDIR_COUNT*/
-/* local flags for d_flag */
-#define D_DIR 32
-#define D_HASHED 64
-#define D_ZOMBIE 128
-#define D_PRELIMINARY 256
-#define D_INC_DDIR 512
-
-/* local flags for d_del() */
-#define D_RECURSIVE 4
-#define D_NO_FREE 8
-
-/* This is only used for directory dentries. Think of it as an extension
- * of the dentry.
- * It is defined as separate struct, so it uses up space only
- * where necessary.
- */
-struct ddir {
- struct dentry * dd_hashtable[D_HASHSIZE];
- struct dentry * dd_neglist;
- struct dentry * dd_basketlist;
- struct dentry * dd_zombielist;
- unsigned short dd_alloced; /* # d_alloc()ed, but not yet d_add()ed */
- unsigned short dd_hashed; /* # of entries in hashtable */
- unsigned short dd_true_hashed; /* # non-preliminaries in hashtable */
- unsigned short dd_negs; /* # of negative entries */
-};
+void printpath(struct dentry * entry);
DEF_INSERT(alias,struct dentry,d_next,d_prev)
DEF_REMOVE(alias,struct dentry,d_next,d_prev)
DEF_INSERT(hash,struct dentry,d_hash_next,d_hash_prev)
DEF_REMOVE(hash,struct dentry,d_hash_next,d_hash_prev)
-DEF_INSERT(basket,struct dentry,d_basket_next,d_basket_prev)
-DEF_REMOVE(basket,struct dentry,d_basket_next,d_basket_prev)
-
struct dentry * the_root = NULL;
+/*
+ * This is the single most critical data structure when it comes
+ * to the dcache: the hashtable for lookups. Somebody should try
+ * to make this good - I've just made it work.
+ *
+ * This hash-function tries to avoid losing too many bits of hash
+ * information, yet avoid using a prime hash-size or similar.
+ */
+#define D_HASHBITS 10
+#define D_HASHSIZE (1UL << D_HASHBITS)
+#define D_HASHMASK (D_HASHSIZE-1)
+struct dentry * dentry_hashtable[D_HASHSIZE];
+
+static inline unsigned long dentry_hash(struct dentry *dir, int name_hash)
+{
+ unsigned long hash = name_hash + (unsigned long) dir;
+ hash = hash ^ (hash >> D_HASHBITS) ^ (hash >> D_HASHBITS*2);
+ return hash & D_HASHMASK;
+}
+
unsigned long name_cache_init(unsigned long mem_start, unsigned long mem_end)
{
return mem_start;
if(cnt % 1000 == 0)
printk("------%d allocated: %d: %d %d %d\n", inodes_stat, cnt,
x_alloc, x_freed, x_free);
- if(cnt>=20000) panic("stop");
+ if (cnt>=20000) panic("stop");
}
#if 0
{
int i;
for(i = cnt-1; i>=0; i--)
- if(tst[i] == ptr)
+ if (tst[i] == ptr)
return i;
return -1;
}
void LOG(char * txt, struct dentry * entry)
{
static int count = 0;
- if(entry) {
+ if (entry) {
TST(txt,entry);
}
- if(count) {
+ if (count) {
count--;
printk("%s: entry=%p\n", txt, entry);
}
}
#ifdef DEBUG_DDIR_COUNT
-static struct ddir * d_dir(struct dentry * entry);
void recursive_test(struct dentry * entry)
{
- int i;
- struct ddir * ddir = d_dir(entry);
- int sons = 0;
-
- if(ddir->dd_zombielist)
- sons++;
- for(i=0; i < D_HASHSIZE; i++) {
- struct dentry ** base = &ddir->dd_hashtable[i];
- struct dentry * tmp = *base;
- if(tmp) do {
- TST("__clear",tmp);
- if(!(tmp->d_flag & D_HASHED)) {
- printk("VFS: dcache entry not hashed!\n");
- printpath(*base); printk("\n");
- printpath(tmp);
- }
- if(!(tmp->d_flag & D_PRELIMINARY))
- sons++;
- if(tmp->d_flag & D_DIR)
- recursive_test(tmp);
- tmp = tmp->d_hash_next;
- } while(tmp && tmp != *base);
- }
- if(!sons && !(entry->d_flag & D_PRELIMINARY) && entry->u.d_inode) {
- struct inode * inode = entry->u.d_inode;
- if(!atomic_read(&inode->i_count)) {
- if(!(inode->i_status & 1/*ST_AGED*/)) {
- printpath(entry);
- printk(" is not aged!\n");
- }
- if(inode->i_ddir_count) {
- printpath(entry);
- printk(" has ddir_count blockage!\n");
- }
- }
- }
}
#else
#define recursive_test(e) /*nothing*/
void printpath(struct dentry * entry)
{
- if(!IS_ROOT(entry))
+ if (!IS_ROOT(entry))
printpath(entry->d_parent);
printk("/%s", entry->d_name.name);
}
-static inline long has_sons(struct ddir * ddir)
-{
- return ((ddir->dd_alloced | ddir->dd_hashed) ||
- ddir->dd_neglist ||
- ddir->dd_basketlist ||
- ddir->dd_zombielist);
-}
-
-static inline int has_true_sons(struct ddir * ddir)
-{
- return (ddir->dd_alloced | ddir->dd_true_hashed);
-}
-
-/* Only hold the i_ddir_count pseudo refcount when neccessary (i.e. when
- * they have true_sons), to prevent keeping too much dir inodes in use.
- */
-static inline void inc_ddir(struct dentry * entry, struct inode * inode)
-{
- if(!(entry->d_flag & D_INC_DDIR)) {
- entry->d_flag |= D_INC_DDIR;
-#ifdef DEBUG
- if(inode->i_ddir_count) {
- printpath(entry);
- printk(" ddir_count=%d\n", inode->i_ddir_count);
- }
-#endif
- inode->i_ddir_count++;
- _get_inode(inode);
- }
-}
-
-static inline blocking void dec_ddir(struct dentry * entry, struct inode * inode)
+void d_free(struct dentry *dentry)
{
- if(entry->d_flag & D_INC_DDIR) {
- entry->d_flag &= ~D_INC_DDIR;
- inode->i_ddir_count--;
- if(!inode->i_ddir_count)
- __iput(inode);
+ if (dentry) {
+ kfree(dentry->d_name.name);
+ kfree(dentry);
}
}
-/* Do not inline this many times. */
-static void d_panic(void)
-{
- panic("VFS: dcache directory corruption");
-}
-
-/*
- * IF this is a directory, the ddir has been allocated right
- * after the dentry.
- */
-static inline struct ddir * d_dir(struct dentry * entry)
-{
- if(!(entry->d_flag & D_DIR))
- d_panic();
- return (struct ddir *) (entry+1);
-}
-
#define NAME_ALLOC_LEN(len) ((len+16) & ~15)
-struct dentry * d_alloc(struct dentry * parent, int len, int isdir)
+struct dentry * d_alloc(struct dentry * parent, struct qstr *name, int isdir)
{
+ char *str;
struct dentry *res;
- int size = sizeof(struct dentry);
- int flag = 0;
- if (isdir) {
- size += sizeof(struct ddir);
- flag = D_DIR;
- }
- res = kmalloc(size, GFP_KERNEL);
+ res = kmalloc(sizeof(struct dentry), GFP_KERNEL);
if (!res)
return NULL;
- memset(res, 0, size);
- res->d_flag = flag;
- res->d_name.name = kmalloc(NAME_ALLOC_LEN(len), GFP_KERNEL);
- if (!res->d_name.name) {
+ str = kmalloc(NAME_ALLOC_LEN(name->len), GFP_KERNEL);
+ if (!str) {
kfree(res);
return NULL;
}
- res->d_name.len = len;
+ memcpy(str, name->name, name->len);
+ str[name->len] = 0;
+
+ memset(res, 0, sizeof(struct dentry));
+
res->d_parent = parent;
- if(parent) {
- struct ddir * pdir = d_dir(parent);
-#ifdef DEBUG
- if(pdir->dd_alloced > 1 && !IS_ROOT(parent)) {
- printpath(parent);
- printk(" dd_alloced=%d\n", pdir->dd_alloced);
- }
-#endif
- pdir->dd_alloced++;
- }
+ res->d_mounts = res;
+ res->d_covers = res;
+ res->d_flag = isdir ? D_DIR : 0;
+
+ res->d_name.name = str;
+ res->d_name.len = name->len;
+ res->d_name.hash = name->hash;
+
#ifdef DEBUG
x_alloc++;
#endif
return res;
}
-extern blocking struct dentry * d_alloc_root(struct inode * root_inode)
+extern blocking struct dentry * d_alloc_root(struct inode * root_inode, struct dentry *old_root)
{
- struct dentry * res = the_root;
+ struct dentry *res;
+ struct qstr name = { "/", 1, 0 }; /* dummy qstr */
- if(res) {
- d_del(res, D_NO_CLEAR_INODE); /* invalidate everything beyond */
- } else {
- struct ddir * ddir;
-
- the_root = res = d_alloc(NULL, 0, 1);
- LOG("d_alloc_root", res);
- res->d_parent = res;
- res->d_name.name[0]='\0';
- ddir = d_dir(res);
- ddir->dd_alloced = 999; /* protect from deletion */
- }
- insert_alias(&root_inode->i_dentry, res);
- root_inode->i_dent_count++;
- root_inode->i_ddir_count++;
- res->u.d_inode = root_inode;
+ if (!root_inode)
+ return NULL;
+ res = d_alloc(NULL, &name, 1);
+ LOG("d_alloc_root", res);
+ res->d_parent = res;
+ d_instantiate(res, root_inode, D_DIR);
return res;
}
-static inline unsigned long d_hash(char first, char last)
-{
- return ((unsigned long)first ^ ((unsigned long)last << 4)) & (D_HASHSIZE-1);
-}
-
-static inline struct dentry ** d_base_entry(struct ddir * pdir, struct dentry * entry)
+static inline struct dentry ** d_base_qstr(struct dentry * parent, struct qstr * s)
{
- return &pdir->dd_hashtable[d_hash(entry->d_name.name[0],
- entry->d_name.name[entry->d_name.len-1])];
+ return dentry_hashtable + dentry_hash(parent, s->hash);
}
-static inline struct dentry ** d_base_qstr(struct ddir * pdir,
- struct qstr * s1,
- struct qstr * s2)
+static inline struct dentry ** d_base_entry(struct dentry * pdir, struct dentry * entry)
{
- unsigned long hash;
-
- if(s2 && s2->len) {
- hash = d_hash(s1->name[0], s2->name[s2->len-1]);
- } else {
- hash = d_hash(s1->name[0], s1->name[s1->len-1]);
- }
- return &pdir->dd_hashtable[hash];
+ return d_base_qstr(pdir, &entry->d_name);
}
static /*inline*/ blocking void _d_remove_from_parent(struct dentry * entry,
- struct ddir * pdir,
- struct inode * inode,
- int flags)
+ struct dentry * parent)
{
- if(entry->d_flag & D_HASHED) {
- struct dentry ** base = d_base_entry(pdir, entry);
+ if (entry->d_flag & D_HASHED) {
+ struct dentry ** base = d_base_entry(parent, entry);
remove_hash(base, entry);
entry->d_flag &= ~D_HASHED;
- pdir->dd_hashed--;
- if(!(entry->d_flag & D_PRELIMINARY)) {
- pdir->dd_true_hashed--;
- if(!inode) {
-#ifdef DEBUG
- if(!entry->d_next || !entry->d_prev) {
- printpath(entry);
- printk(" flags=%x d_flag=%x negs=%d "
- "hashed=%d\n", flags, entry->d_flag,
- pdir->dd_negs, pdir->dd_hashed);
- }
-#endif
- remove_alias(&pdir->dd_neglist, entry);
- pdir->dd_negs--;
- }
- }
- } else if(!(entry->d_flag & D_ZOMBIE)) {
-#ifdef DEBUG
- if(!pdir->dd_alloced) printk("dd_alloced is 0!\n");
-#endif
- pdir->dd_alloced--;
- }
- if(entry->d_flag & D_BASKET) {
- remove_basket(&pdir->dd_basketlist, entry);
- entry->d_flag &= ~D_BASKET;
- }
-}
-
-/* Theoretically, zombies should never or extremely seldom appear,
- * so this code is nearly superfluous.
- * A way to get zombies is while using inodes (i_count>0), unlink()
- * them as well as rmdir() the parent dir => the parent dir becomes a zombie.
- * Zombies are *not* in the hashtable, because somebody could re-creat()
- * that filename in it's parent dir again.
- * Besides coding errors during beta phase, when forcing an umount()
- * (e.g. at shutdown time), inodes could be in use such that the parent
- * dir is cleared, resulting also in zombies.
- */
-static /*inline*/ void _d_handle_zombie(struct dentry * entry,
- struct ddir * ddir,
- struct ddir * pdir)
-{
- if(entry->d_flag & D_DIR) {
- if(entry->d_flag & D_ZOMBIE) {
- if(!has_sons(ddir)) {
- entry->d_flag &= ~D_ZOMBIE;
- remove_hash(&pdir->dd_zombielist, entry);
- if(!pdir->dd_zombielist &&
- (entry->d_parent->d_flag & D_ZOMBIE)) {
- d_del(entry->d_parent, D_NORMAL);
- }
- }
- } else if(has_sons(ddir)) {
- entry->d_flag |= D_ZOMBIE;
- insert_hash(&pdir->dd_zombielist, entry);
-
- /* This condition is no longer a bug, with the removal
- * of recursive_clear() this happens naturally during
- * an unmount attempt of a filesystem which is busy.
- */
-#if 0
- /* Not sure when this message should show up... */
- if(!IS_ROOT(entry)) {
- printk("VFS: clearing dcache directory "
- "with successors\n");
-#ifdef DEBUG
- printpath(entry);
- printk(" d_flag=%x alloced=%d negs=%d hashed=%d "
- "basket=%p zombies=%p\n",
- entry->d_flag, ddir->dd_alloced,
- ddir->dd_negs, ddir->dd_hashed,
- ddir->dd_basketlist, ddir->dd_zombielist);
-#endif
- }
-#endif
- }
- }
-}
-
-static /*inline*/ blocking void _d_del(struct dentry * entry,
- int flags)
-{
- struct ddir * ddir = NULL;
- struct ddir * pdir;
- struct inode * inode = entry->d_flag & D_PRELIMINARY ? NULL : entry->u.d_inode;
-
-#ifdef DEBUG
- if(inode)
- xcheck("_d_del", inode);
-#endif
- if(!entry->d_parent) {
- printk("VFS: dcache parent is NULL\n");
- return;
- }
- pdir = d_dir(entry->d_parent);
- if(!IS_ROOT(entry))
- _d_remove_from_parent(entry, pdir, inode, flags);
-
- /* This may block, be careful! _d_remove_from_parent() is
- * thus called before.
- */
- if(entry->d_flag & D_DIR)
- ddir = d_dir(entry);
- if(IS_ROOT(entry))
- return;
-
- if(flags & D_NO_FREE) {
- /* Make it re-d_add()able */
- pdir->dd_alloced++;
- entry->d_flag &= D_DIR;
- } else
- _d_handle_zombie(entry, ddir, pdir);
-
- /* This dec_ddir() must occur after zombie handling. */
- if(!has_true_sons(pdir))
- dec_ddir(entry->d_parent, entry->d_parent->u.d_inode);
-
- entry->u.d_inode = NULL;
- if(inode) {
- remove_alias(&inode->i_dentry, entry);
- inode->i_dent_count--;
- if (entry->d_flag & D_DIR)
- dec_ddir(entry, inode);
-
- if(!(flags & D_NO_CLEAR_INODE) &&
- !(atomic_read(&inode->i_count) +
- inode->i_ddir_count +
- inode->i_dent_count)) {
-#ifdef DEBUG
- printk("#");
-#endif
- /* This may block also. */
- _clear_inode(inode, 0, 0);
- }
- }
- if(!(flags & D_NO_FREE) && !(entry->d_flag & D_ZOMBIE)) {
- kfree(entry->d_name.name);
- kfree(entry);
-#ifdef DEBUG
- x_freed++;
-#endif
}
-#ifdef DEBUG
- x_free++;
-#endif
-}
-
-blocking void d_del(struct dentry * entry, int flags)
-{
- if(!entry)
- return;
- LOG("d_clear", entry);
- _d_del(entry, flags);
}
-static inline struct dentry * __dlookup(struct dentry ** base,
- struct qstr * name,
- struct qstr * appendix)
+static inline struct dentry * __dlookup(struct dentry * base, struct dentry * parent, struct qstr * name)
{
- struct dentry * tmp = *base;
-
- if(tmp && name->len) {
- int totallen = name->len;
+ if (base) {
+ struct dentry * tmp = base;
+ int len = name->len;
+ int hash = name->hash;
+ const unsigned char *str = name->name;
- if(appendix)
- totallen += appendix->len;
do {
- if(tmp->d_name.len == totallen &&
- !(tmp->d_flag & D_DUPLICATE) &&
- !strncmp(tmp->d_name.name, name->name, name->len) &&
- (!appendix || !strncmp(tmp->d_name.name+name->len,
- appendix->name, appendix->len)))
+ if (tmp->d_name.hash == hash &&
+ tmp->d_name.len == len &&
+ tmp->d_parent == parent &&
+ !(tmp->d_flag & D_DUPLICATE) &&
+ !memcmp(tmp->d_name.name, str, len))
return tmp;
tmp = tmp->d_hash_next;
- } while(tmp != *base);
+ } while(tmp != base);
}
return NULL;
}
-struct dentry * d_lookup(struct inode * dir,
- struct qstr * name,
- struct qstr * appendix)
+struct dentry * d_lookup(struct dentry * dir, struct qstr * name)
{
- if(dir->i_dentry) {
- struct ddir * ddir = d_dir(dir->i_dentry);
- struct dentry ** base = d_base_qstr(ddir, name, appendix);
+ struct dentry ** base = d_base_qstr(dir, name);
- return __dlookup(base, name, appendix);
- }
- return NULL;
+ return __dlookup(*base, dir, name);
}
static /*inline*/ blocking void _d_insert_to_parent(struct dentry * entry,
- struct ddir * pdir,
+ struct dentry * parent,
struct inode * inode,
- struct qstr * ininame,
int flags)
{
struct dentry ** base;
- struct dentry * parent = entry->d_parent;
-#ifdef DEBUG
- if(!pdir->dd_alloced)
- printk("dd_alloced is 0!\n");
-#endif
- base = d_base_qstr(pdir, ininame, NULL);
- if(!(flags & (D_NOCHECKDUP|D_DUPLICATE)) &&
- __dlookup(base, ininame, NULL)) {
- d_del(entry, D_NO_CLEAR_INODE);
- return;
- }
- if(entry->d_flag & D_HASHED) {
+ base = d_base_qstr(parent, &entry->d_name);
+ if (entry->d_flag & D_HASHED) {
printk("VFS: dcache entry is already hashed\n");
return;
}
- if(!(flags & D_PRELIMINARY))
- pdir->dd_true_hashed++;
- pdir->dd_hashed++;
insert_hash(base, entry);
entry->d_flag |= D_HASHED;
- pdir->dd_alloced--;
- if(flags & D_BASKET)
- insert_basket(&pdir->dd_basketlist, entry);
+}
-#ifdef DEBUG
- if(inode && inode->i_dentry && (entry->d_flag & D_DIR)) {
- struct dentry * tmp = inode->i_dentry;
- printk("Auweia inode=%p entry=%p (%p %p %s)\n",
- inode, entry, parent->u.d_inode, parent, parent->d_name.name);
- printk("entry path="); printpath(entry); printk("\n");
- do {
- TST("auweia",tmp);
- printk("alias path="); printpath(tmp); printk("\n");
- tmp = tmp->d_next;
- } while(tmp != inode->i_dentry);
- printk("\n");
+/*
+ * Fill in inode information in the entry.
+ *
+ * This turns negative dentries into productive full members
+ * of society.
+ *
+ * NOTE! This assumes that the inode count has been incremented
+ * (or otherwise set) by the caller to indicate that it is now
+ * in use by the dcache..
+ */
+void d_instantiate(struct dentry *entry, struct inode * inode, int flags)
+{
+ entry->d_flag = (entry->d_flag & ~D_NEGATIVE) | flags;
+
+ if (inode && !(flags & D_NEGATIVE)) {
+ if (entry->d_flag & D_DIR) {
+ if (inode->i_dentry) {
+ printk("VFS: creating dcache directory alias\n");
+ return;
+ }
+ }
+ insert_alias(&inode->i_dentry, entry);
}
-#endif
- if(has_true_sons(pdir))
- inc_ddir(parent, parent->u.d_inode);
- if(!inode && !(flags & D_PRELIMINARY)) {
- insert_alias(&pdir->dd_neglist, entry);
- pdir->dd_negs++;
-
- /* Don't allow the negative list to grow too much ... */
- while(pdir->dd_negs > (pdir->dd_true_hashed >> 1) + 5)
- d_del(pdir->dd_neglist->d_prev, D_REMOVE);
+
+ entry->d_inode = inode;
+}
+
+/*
+ * Remove the inode from the dentry.. This removes
+ * it from the parent hashes but otherwise leaves it
+ * around - it may be a "zombie", part of a path
+ * that is still in use...
+ *
+ * "The Night of the Living Dead IV - the Dentry"
+ */
+void d_delete(struct dentry * dentry)
+{
+ struct inode * inode = dentry->d_inode;
+
+ _d_remove_from_parent(dentry, dentry->d_parent);
+ if (inode) {
+ remove_alias(&inode->i_dentry, dentry);
+ dentry->d_inode = NULL;
+ iput(inode);
}
}
-blocking void d_add(struct dentry * entry, struct inode * inode,
- struct qstr * ininame, int flags)
+blocking void d_add(struct dentry * entry, struct inode * inode, int flags)
{
struct dentry * parent = entry->d_parent;
- struct qstr dummy;
- struct ddir * pdir;
#ifdef DEBUG
- if(inode)
+ if (inode)
xcheck("d_add", inode);
- if(IS_ROOT(entry)) {
+ if (IS_ROOT(entry)) {
printk("VFS: d_add for root dentry ");
printpath(entry);
printk(" -> ");
- if(ininame)
- printk("%s", ininame->name);
printk("\n");
return;
}
- if(!parent)
+ if (!parent)
panic("d_add with parent==NULL");
LOG("d_add", entry);
#endif
- if(ininame) {
- if(ininame->len != entry->d_name.len) {
- printk("VFS: d_add with wrong string length");
- entry->d_name.len = ininame->len; /* kludge */
- }
- memcpy(entry->d_name.name, ininame->name, ininame->len);
- entry->d_name.name[ininame->len] = '\0';
- } else {
- dummy.name = entry->d_name.name;
- dummy.len = entry->d_name.len;
- ininame = &dummy;
- }
if(entry->d_flag & D_HASHED)
printk("VFS: d_add of already added dcache entry\n");
- pdir = d_dir(parent);
- _d_insert_to_parent(entry, pdir, inode, ininame, flags);
- entry->d_flag |= flags;
- if(inode && !(flags & D_PRELIMINARY)) {
- if(entry->d_flag & D_DIR) {
- if(inode->i_dentry) {
- printk("VFS: creating dcache directory alias\n");
- return;
- }
- }
- insert_alias(&inode->i_dentry, entry);
- inode->i_dent_count++;
- }
- entry->u.d_inode = inode;
+ _d_insert_to_parent(entry, parent, inode, flags);
+ d_instantiate(entry, inode, flags);
}
-blocking struct dentry * d_entry(struct dentry * parent,
- struct qstr * name,
- struct inode * inode)
+static inline void alloc_new_name(struct dentry * entry, struct qstr *newname)
{
- struct ddir * pdir = d_dir(parent);
- struct dentry ** base = d_base_qstr(pdir, name, NULL);
- struct dentry * found = __dlookup(base, name, NULL);
-
- if(!found) {
- int isdir = (inode && S_ISDIR(inode->i_mode));
-
- found = d_alloc(parent, name->len, isdir);
- if(found) {
- d_add(found, inode, name,
- isdir ? (D_DIR|D_NOCHECKDUP) : D_NOCHECKDUP);
- } else
- printk("VFS: problem with d_alloc\n");
- }
- return found;
-}
+ int len = newname->len;
+ int hash = newname->hash;
+ char *name = (char *) entry->d_name.name;
-blocking void d_entry_preliminary(struct dentry * parent,
- struct qstr * name,
- unsigned long ino)
-{
- struct ddir * pdir = d_dir(parent);
- struct dentry ** base = d_base_qstr(pdir, name, NULL);
- struct dentry * found = __dlookup(base, name, NULL);
-
- if(!found && ino) {
- struct dentry * new = d_alloc(parent, name->len, 0);
-
- if(new) {
- d_add(new, NULL, name, D_PRELIMINARY|D_NOCHECKDUP);
- new->u.d_ino = ino;
- } else
- printk("VFS: problem with d_alloc\n");
+ if (NAME_ALLOC_LEN(len) != NAME_ALLOC_LEN(entry->d_name.len)) {
+ name = kmalloc(NAME_ALLOC_LEN(len), GFP_KERNEL);
+ if (!name)
+ printk("out of memory for dcache\n");
+ kfree(entry->d_name.name);
+ entry->d_name.name = name;
}
-}
-
-static inline void alloc_new_name(struct dentry * entry, int len)
-{
- int alloc_len = NAME_ALLOC_LEN(len);
- char *name;
-
- if (alloc_len == NAME_ALLOC_LEN(entry->d_name.len))
- return;
- name = kmalloc(alloc_len, GFP_KERNEL);
- if (!name)
- printk("out of memory for dcache\n");
- kfree(entry->d_name.name);
- entry->d_name.name = name;
+ memcpy(name, newname->name, len);
+ name[len] = 0;
+ entry->d_name.len = len;
+ entry->d_name.hash = hash;
}
static inline void d_remove_old_parent(struct dentry * entry)
{
- struct ddir * pdir;
+ struct dentry * parent;
struct inode * inode;
- pdir = d_dir(entry->d_parent);
- inode = entry->u.d_inode;
- _d_remove_from_parent(entry, pdir, inode, D_NO_CLEAR_INODE);
+ parent = entry->d_parent;
+ inode = entry->d_inode;
+ _d_remove_from_parent(entry, parent);
}
-static inline void d_add_new_parent(struct dentry * entry, struct inode * new_parent)
+static inline void d_add_new_parent(struct dentry * entry, struct dentry * parent)
{
- struct ddir * pdir;
struct inode * inode;
- pdir = d_dir(entry->d_parent = new_parent->i_dentry);
- inode = entry->u.d_inode;
+ entry->d_parent = parent;
+ inode = entry->d_inode;
- _d_insert_to_parent(entry, pdir, inode, &entry->d_name, entry->d_flag);
+ _d_insert_to_parent(entry, parent, inode, entry->d_flag);
}
-blocking void d_move(struct dentry * entry, struct inode * newdir,
- struct qstr * newname, struct qstr * newapp)
+blocking void d_move(struct dentry * entry, struct dentry * newdir, struct qstr * newname)
{
struct inode * inode;
- int len;
int flags;
- if(!entry)
+ if (!entry)
return;
- inode = entry->u.d_inode;
+ inode = entry->d_inode;
flags = entry->d_flag;
- if((flags & D_PRELIMINARY) || !inode) {
- if(!(flags & D_PRELIMINARY))
- printk("VFS: trying to move negative dcache entry\n");
- d_del(entry, D_NO_CLEAR_INODE);
- return;
+ if (!inode) {
+ printk("VFS: moving negative dcache entry\n");
}
-#if 0
-printk("d_move %p '%s' -> '%s%s' dent_count=%d\n", inode, entry->d_name.name,
- newname->name, newapp ? newapp->name : "", inode->i_dent_count);
-#endif
- if(flags & D_ZOMBIE) {
+
+ if (flags & D_ZOMBIE) {
printk("VFS: moving zombie entry\n");
}
d_remove_old_parent(entry);
-
- len = newname->len;
- if(newapp) {
- len += newapp->len;
- flags |= D_BASKET;
- } else
- flags &= ~D_BASKET;
- alloc_new_name(entry, len);
- memcpy(entry->d_name.name, newname->name, newname->len);
- if(newapp)
- memcpy(entry->d_name.name+newname->len, newapp->name, newapp->len);
- entry->d_name.name[len] = '\0';
- entry->d_name.len = len;
-
+ alloc_new_name(entry, newname);
d_add_new_parent(entry, newdir);
}
-int d_path(struct dentry * entry, struct inode * chroot, char * buf)
+int d_path(struct dentry * entry, struct dentry * chroot, char * buf)
{
- if(IS_ROOT(entry) || (chroot && entry->u.d_inode == chroot &&
- !(entry->d_flag & D_PRELIMINARY))) {
+ if (IS_ROOT(entry) || (chroot && entry == chroot)) {
*buf = '/';
return 1;
} else {
int len = d_path(entry->d_parent, chroot, buf);
buf += len;
- if(len > 1) {
+ if (len > 1) {
*buf++ = '/';
len++;
}
return len + entry->d_name.len;
}
}
-
-struct dentry * d_basket(struct dentry * dir_entry)
-{
- if(dir_entry && (dir_entry->d_flag & D_DIR)) {
- struct ddir * ddir = d_dir(dir_entry);
-
- return ddir->dd_basketlist;
- } else
- return NULL;
-}
-
-int d_isbasket(struct dentry * entry)
-{
- return entry->d_flag & D_BASKET;
-}
-
-blocking struct inode * d_inode(struct dentry ** changing_entry)
-{
- struct dentry * entry = *changing_entry;
- struct inode * inode;
-
-#ifdef CONFIG_DCACHE_PRELOAD
- if(entry->d_flag & D_PRELIMINARY) {
- struct qstr name = { entry->d_name.name, entry->d_name.len };
- struct ddir * pdir = d_dir(entry->d_parent);
- struct dentry ** base = d_base_qstr(pdir, &name, NULL);
- struct dentry * found;
- unsigned long ino;
- struct inode * dir = entry->d_parent->u.d_inode;
- TST("d_inode",entry);
- ino = entry->u.d_ino;
- if(!dir)
- d_panic();
-
- /* Prevent concurrent d_lookup()s or d_inode()s before
- * giving up vfs_lock. This just removes from the parent,
- * but does not deallocate it.
- */
-
- /* !!!!!!! Aiee, here is an unresolved race if somebody
- * unlink()s the inode during the iget(). The problem is
- * that we need to synchronize externally. Proposed solution:
- * put a rw_lock (read-mode) on the parent dir for each
- * iget(), lookup() and so on, and a write-mode lock for
- * everything that changes the dir (e.g. unlink()), and do
- * this consistently everywhere in the generic VFS (not in
- * the concrete filesystems). This should kill similar
- * races everywhere, with a single clean concept.
- * Later, the synchronization stuff can be cleaned out
- * of the concrete fs'es.
- */
- d_del(entry, D_NO_CLEAR_INODE|D_NO_FREE);
- vfs_unlock();
-
- /* This circumvents the normal lookup() of pathnames.
- * Therefore, preliminary entries must not be used
- * (see FS_NO_DCACHE and FS_NO_PRELIM) if the fs does not
- * permit fetching *valid* inodes with plain iget().
- */
- inode = __iget(dir->i_sb, ino, 0);
- vfs_lock();
- if(!inode) {
- printk("VFS: preliminary dcache entry was invalid\n");
- *changing_entry = NULL;
- return NULL;
- }
- xcheck("d_inode iget()", inode);
- if((found = __dlookup(base, &name, NULL))) {
- d_del(entry, D_NO_CLEAR_INODE);
- *changing_entry = found;
- } else if(S_ISDIR(inode->i_mode)) {
- struct dentry * new = d_alloc(entry->d_parent, entry->d_name.len, 1);
- if(new)
- d_add(new, inode, &name, D_DIR);
- *changing_entry = new;
-
- /* Finally deallocate old entry. */
- d_del(entry, D_NO_CLEAR_INODE);
- } else {
- /* Re-insert to the parent, but now as normal dentry. */
- d_add(entry, inode, NULL, 0);
- }
- return inode;
- }
-#endif
- inode = entry->u.d_inode;
- if(inode) {
-#ifdef DEBUG
- xcheck("d_inode", inode);
-#endif
- iinc_zero(inode);
- }
- return inode;
-}
if (special == (char *)NULL && (cmds == Q_SYNC || cmds == Q_GETSTATS))
dev = 0;
else {
- int error = namei(NAM_FOLLOW_LINK, special, &ino);
+ int error = namei(special, &ino);
if(error)
goto out;
dev = ino->i_rdev;
printk ("ext2_new_block: nonexistent device");
return 0;
}
-retry:
+
lock_super (sb);
es = sb->u.ext2_sb.s_es;
if (le32_to_cpu(es->s_free_blocks_count) <= le32_to_cpu(es->s_r_blocks_count) &&
(sb->u.ext2_sb.s_resgid == 0 ||
!in_group_p (sb->u.ext2_sb.s_resgid)))) {
unlock_super (sb);
- if(sb->s_ibasket && free_ibasket(sb))
- goto retry;
return 0;
}
}
if (k >= sb->u.ext2_sb.s_groups_count) {
unlock_super (sb);
- if(sb->s_ibasket && free_ibasket(sb))
- goto retry;
return 0;
}
bitmap_nr = load_block_bitmap (sb, i);
ext2_mknod, /* mknod */
ext2_rename, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
generic_readpage, /* readpage */
NULL, /* writepage */
ext2_bmap, /* bmap */
#include <linux/string.h>
#include <linux/locks.h>
+
/*
* define how far ahead to read directories while searching them.
*/
return NULL;
}
-int ext2_lookup (struct inode * dir, const char * name, int len,
- struct inode ** result)
+int ext2_lookup(struct inode * dir, struct qstr *name, struct inode ** result)
{
unsigned long ino;
struct ext2_dir_entry * de;
*result = NULL;
if (!dir)
return -ENOENT;
- if (!S_ISDIR(dir->i_mode)) {
- iput (dir);
+
+ if (!S_ISDIR(dir->i_mode))
return -ENOTDIR;
- }
- if (len > EXT2_NAME_LEN) {
- iput (dir);
+
+ if (name->len > EXT2_NAME_LEN)
return -ENAMETOOLONG;
- }
+
ino = dir->i_version;
- if (!(bh = ext2_find_entry (dir, name, len, &de))) {
- iput (dir);
+ if (!(bh = ext2_find_entry (dir, name->name, name->len, &de)))
return -ENOENT;
- }
+
ino = le32_to_cpu(de->inode);
brelse (bh);
- if (!(*result = iget (dir->i_sb, ino))) {
- iput (dir);
+ if (!(*result = iget (dir->i_sb, ino)))
return -EACCES;
- }
- iput (dir);
+
return 0;
}
return -ENOENT;
}
-int ext2_create (struct inode * dir,const char * name, int len, int mode,
- struct inode ** result)
+/*
+ * By the time this is called, we already have created
+ * the directory cache entry for the new file, but it
+ * is so far marked "D_NEGATIVE".
+ *
+ * If the create succeeds, remove the D_NEGATIVE flag,
+ * and fill in the inode information with d_instantiate().
+ */
+int ext2_create (struct inode * dir, struct dentry * dentry, int mode)
{
struct inode * inode;
struct buffer_head * bh;
struct ext2_dir_entry * de;
int err;
- *result = NULL;
if (!dir)
return -ENOENT;
inode = ext2_new_inode (dir, mode, &err);
- if (!inode) {
- iput (dir);
+ if (!inode)
return err;
- }
+
inode->i_op = &ext2_file_inode_operations;
inode->i_mode = mode;
inode->i_dirt = 1;
- bh = ext2_add_entry (dir, name, len, &de, &err);
+ bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
if (!bh) {
inode->i_nlink--;
inode->i_dirt = 1;
iput (inode);
- iput (dir);
return err;
}
de->inode = cpu_to_le32(inode->i_ino);
wait_on_buffer (bh);
}
brelse (bh);
- iput (dir);
- *result = inode;
+ d_instantiate(dentry, inode, 0);
return 0;
}
-int ext2_mknod (struct inode * dir, const char * name, int len, int mode,
- int rdev)
+int ext2_mknod (struct inode * dir, struct dentry *dentry, int mode, int rdev)
{
struct inode * inode;
struct buffer_head * bh;
if (!dir)
return -ENOENT;
- if (len > EXT2_NAME_LEN) {
- iput (dir);
+ if (dentry->d_name.len > EXT2_NAME_LEN)
return -ENAMETOOLONG;
- }
- bh = ext2_find_entry (dir, name, len, &de);
- if (bh) {
- brelse (bh);
- iput (dir);
- return -EEXIST;
- }
+
inode = ext2_new_inode (dir, mode, &err);
- if (!inode) {
- iput (dir);
+ if (!inode)
return err;
- }
+
inode->i_uid = current->fsuid;
inode->i_mode = mode;
inode->i_op = NULL;
if (S_ISBLK(mode) || S_ISCHR(mode))
inode->i_rdev = to_kdev_t(rdev);
inode->i_dirt = 1;
- bh = ext2_add_entry (dir, name, len, &de, &err);
+ bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
if (!bh) {
inode->i_nlink--;
inode->i_dirt = 1;
- iput (inode);
- iput (dir);
+ iput(inode);
return err;
}
de->inode = cpu_to_le32(inode->i_ino);
ll_rw_block (WRITE, 1, &bh);
wait_on_buffer (bh);
}
- brelse (bh);
- iput (dir);
- iput (inode);
+ brelse(bh);
+ d_instantiate(dentry, inode, 0);
return 0;
}
-int ext2_mkdir (struct inode * dir, const char * name, int len, int mode)
+int ext2_mkdir(struct inode * dir, struct dentry * dentry, int mode)
{
struct inode * inode;
struct buffer_head * bh, * dir_block;
struct ext2_dir_entry * de;
int err;
- if (!dir)
- return -ENOENT;
- if (len > EXT2_NAME_LEN) {
- iput (dir);
+ if (dentry->d_name.len > EXT2_NAME_LEN)
return -ENAMETOOLONG;
- }
- bh = ext2_find_entry (dir, name, len, &de);
- if (bh) {
- brelse (bh);
- iput (dir);
- return -EEXIST;
- }
- if (dir->i_nlink >= EXT2_LINK_MAX) {
- iput (dir);
+
+ if (dir->i_nlink >= EXT2_LINK_MAX)
return -EMLINK;
- }
+
inode = ext2_new_inode (dir, S_IFDIR, &err);
- if (!inode) {
- iput (dir);
+ if (!inode)
return err;
- }
+
inode->i_op = &ext2_dir_inode_operations;
inode->i_size = inode->i_sb->s_blocksize;
dir_block = ext2_bread (inode, 0, 1, &err);
if (!dir_block) {
- iput (dir);
inode->i_nlink--;
inode->i_dirt = 1;
iput (inode);
if (dir->i_mode & S_ISGID)
inode->i_mode |= S_ISGID;
inode->i_dirt = 1;
- bh = ext2_add_entry (dir, name, len, &de, &err);
+ bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
if (!bh) {
- iput (dir);
inode->i_nlink = 0;
inode->i_dirt = 1;
iput (inode);
}
dir->i_nlink++;
dir->i_dirt = 1;
- iput (dir);
- iput (inode);
+ d_instantiate(dentry, inode, D_DIR);
brelse (bh);
return 0;
}
return 1;
}
-int ext2_rmdir (struct inode * dir, const char * name, int len)
+int ext2_rmdir (struct inode * dir, struct dentry *dentry)
{
int retval;
struct inode * inode;
struct buffer_head * bh;
struct ext2_dir_entry * de;
-repeat:
if (!dir)
return -ENOENT;
inode = NULL;
- if (len > EXT2_NAME_LEN) {
- iput (dir);
+ if (dentry->d_name.len > EXT2_NAME_LEN)
return -ENAMETOOLONG;
- }
- bh = ext2_find_entry (dir, name, len, &de);
+
+ bh = ext2_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de);
retval = -ENOENT;
if (!bh)
goto end_rmdir;
retval = -EPERM;
- if (!(inode = iget (dir->i_sb, le32_to_cpu(de->inode))))
- goto end_rmdir;
+ inode = dentry->d_inode;
+
if (inode->i_sb->dq_op)
inode->i_sb->dq_op->initialize (inode, -1);
- if (inode->i_dev != dir->i_dev) {
- retval = -EBUSY;
- goto end_rmdir;
- }
- if (le32_to_cpu(de->inode) != inode->i_ino) {
- iput(inode);
- brelse(bh);
- current->counter = 0;
- schedule();
- goto repeat;
- }
+
if ((dir->i_mode & S_ISVTX) && !fsuser() &&
current->fsuid != inode->i_uid &&
current->fsuid != dir->i_uid)
goto end_rmdir;
if (inode == dir) /* we may not delete ".", but "../dir" is ok */
goto end_rmdir;
- if (!S_ISDIR(inode->i_mode)) {
- retval = -ENOTDIR;
+
+ retval = -ENOTDIR;
+ if (!S_ISDIR(inode->i_mode))
goto end_rmdir;
- }
+
+ retval = -EIO;
+ if (inode->i_dev != dir->i_dev)
+ goto end_rmdir;
+ if (le32_to_cpu(de->inode) != inode->i_ino)
+ goto end_rmdir;
+
down(&inode->i_sem);
if (!empty_dir (inode))
retval = -ENOTEMPTY;
dir->i_nlink--;
inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
dir->i_dirt = 1;
+ d_delete(dentry);
+
end_rmdir:
- iput (dir);
- iput (inode);
brelse (bh);
return retval;
}
-int ext2_unlink (struct inode * dir, const char * name, int len)
+int ext2_unlink(struct inode * dir, struct dentry *dentry)
{
int retval;
struct inode * inode;
struct buffer_head * bh;
struct ext2_dir_entry * de;
-repeat:
- if (!dir)
- return -ENOENT;
retval = -ENOENT;
inode = NULL;
- if (len > EXT2_NAME_LEN) {
- iput (dir);
+ if (dentry->d_name.len > EXT2_NAME_LEN)
return -ENAMETOOLONG;
- }
- bh = ext2_find_entry (dir, name, len, &de);
+
+ bh = ext2_find_entry (dir, dentry->d_name.name, dentry->d_name.len, &de);
if (!bh)
goto end_unlink;
- if (!(inode = iget (dir->i_sb, le32_to_cpu(de->inode))))
- goto end_unlink;
+
+ inode = dentry->d_inode;
if (inode->i_sb->dq_op)
inode->i_sb->dq_op->initialize (inode, -1);
+
retval = -EPERM;
if (S_ISDIR(inode->i_mode))
goto end_unlink;
if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
goto end_unlink;
- if (le32_to_cpu(de->inode) != inode->i_ino) {
- iput(inode);
- brelse(bh);
- current->counter = 0;
- schedule();
- goto repeat;
- }
if ((dir->i_mode & S_ISVTX) && !fsuser() &&
current->fsuid != inode->i_uid &&
current->fsuid != dir->i_uid)
goto end_unlink;
+
+ retval = -EIO;
+ if (le32_to_cpu(de->inode) != inode->i_ino)
+ goto end_unlink;
+
if (!inode->i_nlink) {
ext2_warning (inode->i_sb, "ext2_unlink",
"Deleting nonexistent file (%lu), %d",
inode->i_dirt = 1;
inode->i_ctime = dir->i_ctime;
retval = 0;
+ d_delete(dentry); /* This also frees the inode */
+
end_unlink:
brelse (bh);
- iput (inode);
- iput (dir);
return retval;
}
-int ext2_symlink (struct inode * dir, const char * name, int len,
- const char * symname)
+int ext2_symlink (struct inode * dir, struct dentry *dentry, const char * symname)
{
struct ext2_dir_entry * de;
struct inode * inode = NULL;
char c;
if (!(inode = ext2_new_inode (dir, S_IFLNK, &err))) {
- iput (dir);
return err;
}
inode->i_mode = S_IFLNK | S_IRWXUGO;
name_block = ext2_bread (inode, 0, 1, &err);
if (!name_block) {
- iput (dir);
inode->i_nlink--;
inode->i_dirt = 1;
iput (inode);
inode->i_size = i;
inode->i_dirt = 1;
- bh = ext2_find_entry (dir, name, len, &de);
- if (bh) {
- inode->i_nlink--;
- inode->i_dirt = 1;
- iput (inode);
- brelse (bh);
- iput (dir);
- return -EEXIST;
- }
- bh = ext2_add_entry (dir, name, len, &de, &err);
+ bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
if (!bh) {
inode->i_nlink--;
inode->i_dirt = 1;
iput (inode);
- iput (dir);
return err;
}
de->inode = cpu_to_le32(inode->i_ino);
wait_on_buffer (bh);
}
brelse (bh);
- iput (dir);
- iput (inode);
+ d_instantiate(dentry, inode, 0);
return 0;
}
-int ext2_link (struct inode * oldinode, struct inode * dir,
- const char * name, int len)
+int ext2_link (struct inode * inode, struct inode * dir, struct dentry *dentry)
{
struct ext2_dir_entry * de;
struct buffer_head * bh;
int err;
- if (S_ISDIR(oldinode->i_mode)) {
- iput (oldinode);
- iput (dir);
+ if (S_ISDIR(inode->i_mode))
return -EPERM;
- }
- if (IS_APPEND(oldinode) || IS_IMMUTABLE(oldinode)) {
- iput (oldinode);
- iput (dir);
+
+ if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
return -EPERM;
- }
- if (oldinode->i_nlink >= EXT2_LINK_MAX) {
- iput (oldinode);
- iput (dir);
+
+ if (inode->i_nlink >= EXT2_LINK_MAX)
return -EMLINK;
- }
- bh = ext2_find_entry (dir, name, len, &de);
- if (bh) {
- brelse (bh);
- iput (dir);
- iput (oldinode);
- return -EEXIST;
- }
- bh = ext2_add_entry (dir, name, len, &de, &err);
- if (!bh) {
- iput (dir);
- iput (oldinode);
+
+ bh = ext2_add_entry (dir, dentry->d_name.name, dentry->d_name.len, &de, &err);
+ if (!bh)
return err;
- }
- de->inode = cpu_to_le32(oldinode->i_ino);
+
+ de->inode = cpu_to_le32(inode->i_ino);
dir->i_version = ++event;
mark_buffer_dirty(bh, 1);
if (IS_SYNC(dir)) {
wait_on_buffer (bh);
}
brelse (bh);
- iput (dir);
- oldinode->i_nlink++;
- oldinode->i_ctime = CURRENT_TIME;
- oldinode->i_dirt = 1;
- iput (oldinode);
+ inode->i_nlink++;
+ inode->i_ctime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ atomic_inc(&inode->i_count);
+ d_instantiate(dentry, inode, 0);
return 0;
}
if (new_inode->i_dev != old_inode->i_dev)
break;
ino = new_inode->i_ino;
- if (ext2_lookup (new_inode, "..", 2, &new_inode))
+ if (ext2_lookup (new_inode, &(struct qstr) { "..", 2, 0 }, &new_inode))
break;
if (new_inode->i_ino == ino)
break;
* Anybody can rename anything with this: the permission checks are left to the
* higher-level routines.
*/
-static int do_ext2_rename (struct inode * old_dir, const char * old_name,
- int old_len, struct inode * new_dir,
- const char * new_name, int new_len)
+static int do_ext2_rename (struct inode * old_dir, struct dentry *old_dentry,
+ struct inode * new_dir,struct dentry *new_dentry)
{
struct inode * old_inode, * new_inode;
struct buffer_head * old_bh, * new_bh, * dir_bh;
struct ext2_dir_entry * old_de, * new_de;
int retval;
- goto start_up;
-try_again:
- if (new_bh && new_de) {
- ext2_delete_entry(new_de, new_bh);
- new_dir->i_version = ++event;
- }
- brelse (old_bh);
- brelse (new_bh);
- brelse (dir_bh);
- iput (old_inode);
- iput (new_inode);
- current->counter = 0;
- schedule ();
-start_up:
old_inode = new_inode = NULL;
old_bh = new_bh = dir_bh = NULL;
new_de = NULL;
retval = -ENAMETOOLONG;
- if (old_len > EXT2_NAME_LEN)
+ if (old_dentry->d_name.len > EXT2_NAME_LEN)
goto end_rename;
- old_bh = ext2_find_entry (old_dir, old_name, old_len, &old_de);
+ old_bh = ext2_find_entry (old_dir, old_dentry->d_name.name, old_dentry->d_name.len, &old_de);
retval = -ENOENT;
if (!old_bh)
goto end_rename;
- old_inode = __iget (old_dir->i_sb, le32_to_cpu(old_de->inode), 0); /* don't cross mnt-points */
- if (!old_inode)
- goto end_rename;
+ old_inode = old_dentry->d_inode;
+
retval = -EPERM;
if ((old_dir->i_mode & S_ISVTX) &&
current->fsuid != old_inode->i_uid &&
goto end_rename;
if (IS_APPEND(old_inode) || IS_IMMUTABLE(old_inode))
goto end_rename;
- new_bh = ext2_find_entry (new_dir, new_name, new_len, &new_de);
+
+ new_inode = new_dentry->d_inode;
+ new_bh = ext2_find_entry (new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_de);
if (new_bh) {
- new_inode = __iget (new_dir->i_sb, le32_to_cpu(new_de->inode), 0); /* no mntp cross */
if (!new_inode) {
brelse (new_bh);
new_bh = NULL;
goto end_rename;
}
if (!new_bh)
- new_bh = ext2_add_entry (new_dir, new_name, new_len, &new_de,
+ new_bh = ext2_add_entry (new_dir, new_dentry->d_name.name, new_dentry->d_name.len, &new_de,
&retval);
if (!new_bh)
goto end_rename;
new_dir->i_version = ++event;
- /*
- * sanity checking before doing the rename - avoid races
- */
- if (new_inode && (le32_to_cpu(new_de->inode) != new_inode->i_ino))
- goto try_again;
- if (le32_to_cpu(new_de->inode) && !new_inode)
- goto try_again;
- if (le32_to_cpu(old_de->inode) != old_inode->i_ino)
- goto try_again;
+
/*
* ok, that's it
*/
new_de->inode = le32_to_cpu(old_inode->i_ino);
- retval = ext2_delete_entry (old_de, old_bh);
- if (retval == -ENOENT)
- goto try_again;
- if (retval)
- goto end_rename;
+ ext2_delete_entry (old_de, old_bh);
+
old_dir->i_version = ++event;
if (new_inode) {
new_inode->i_nlink--;
ll_rw_block (WRITE, 1, &new_bh);
wait_on_buffer (new_bh);
}
+
+ /* Update the dcache */
+ d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name);
+ d_delete(new_dentry);
retval = 0;
end_rename:
brelse (dir_bh);
brelse (old_bh);
brelse (new_bh);
- iput (old_inode);
- iput (new_inode);
- iput (old_dir);
- iput (new_dir);
return retval;
}
* super-block. This way, we really lock other renames only if they occur
* on the same file system
*/
-int ext2_rename (struct inode * old_dir, const char * old_name, int old_len,
- struct inode * new_dir, const char * new_name, int new_len)
+int ext2_rename (struct inode * old_dir, struct dentry *old_dentry,
+ struct inode * new_dir, struct dentry *new_dentry)
{
int result;
while (old_dir->i_sb->u.ext2_sb.s_rename_lock)
sleep_on (&old_dir->i_sb->u.ext2_sb.s_rename_wait);
old_dir->i_sb->u.ext2_sb.s_rename_lock = 1;
- result = do_ext2_rename (old_dir, old_name, old_len, new_dir,
- new_name, new_len);
+ result = do_ext2_rename (old_dir, old_dentry, new_dir, new_dentry);
old_dir->i_sb->u.ext2_sb.s_rename_lock = 0;
wake_up (&old_dir->i_sb->u.ext2_sb.s_rename_wait);
return result;
*/
sb->s_dev = dev;
sb->s_op = &ext2_sops;
- if (!(sb->s_mounted = iget (sb, EXT2_ROOT_INO))) {
+ sb->s_root = d_alloc_root(iget(sb, EXT2_ROOT_INO), NULL);
+ if (!sb->s_root) {
sb->s_dev = 0;
for (i = 0; i < db_count; i++)
if (sb->u.ext2_sb.s_group_desc[i])
#include <linux/stat.h>
static int ext2_readlink (struct inode *, char *, int);
+static struct dentry *ext2_follow_link(struct inode *, struct dentry *);
/*
* symlinks can't do much...
NULL, /* mknod */
NULL, /* rename */
ext2_readlink, /* readlink */
+ ext2_follow_link, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL /* smap */
};
+static struct dentry * ext2_follow_link(struct inode * inode, struct dentry *base)
+{
+ int error;
+ struct buffer_head * bh = NULL;
+ char * link;
+
+ link = (char *) inode->u.ext2_i.i_data;
+ if (inode->i_blocks) {
+ if (!(bh = ext2_bread (inode, 0, 0, &error))) {
+ dput(base);
+ return ERR_PTR(-EIO);
+ }
+ link = bh->b_data;
+ }
+ if (!IS_RDONLY(inode)) {
+ inode->i_atime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ }
+ base = lookup_dentry(link, base, 1);
+ if (bh)
+ brelse(bh);
+ return base;
+}
+
static int ext2_readlink (struct inode * inode, char * buffer, int buflen)
{
struct buffer_head * bh = NULL;
char * link;
- int i, err;
+ int i;
if (buflen > inode->i_sb->s_blocksize - 1)
buflen = inode->i_sb->s_blocksize - 1;
+
+ link = (char *) inode->u.ext2_i.i_data;
if (inode->i_blocks) {
+ int err;
bh = ext2_bread (inode, 0, 0, &err);
if (!bh) {
- iput (inode);
if(err < 0) /* indicate type of error */
return err;
return 0;
}
link = bh->b_data;
}
- else
- link = (char *) inode->u.ext2_i.i_data;
i = 0;
while (i < buflen && link[i])
inode->i_atime = CURRENT_TIME;
inode->i_dirt = 1;
}
- iput (inode);
if (bh)
brelse (bh);
return i;
"TXT" "ME " "HTM" "1ST" "LOG" " " /* text files */
"C " "H " "CPP" "LIS" "PAS" "FOR" /* programming languages */
"F " "MAK" "INC" "BAS" /* programming languages */
- "BAT" "SH" /* program code :) */
- "INI " /* config files */
+ "BAT" "SH " /* program code :) */
+ "INI" /* config files */
"PBM" "PGM" "DXF" /* graphics */
"TEX"; /* TeX */
{
int i;
for(i=xcnt-1; i>=0; i--)
- if(xtst[i] == p)
+ if (xtst[i] == p)
return;
printk("Bogus inode %p in %s\n", p, txt);
}
struct inode * res;
struct inode * inode = res = (struct inode*)__get_free_page(GFP_KERNEL);
int size = PAGE_SIZE;
- if(!inode)
+ if (!inode)
return NULL;
size -= sizeof(struct inode);
int res = 0;
vfs_lock();
inode->i_status &= ~flags;
- if(inode->i_status & ST_WAITING) {
+ if (inode->i_status & ST_WAITING) {
inode->i_status &= ~ST_WAITING;
vfs_unlock();
wake_up(&inode->i_wait);
unsigned short waitflags, unsigned short setflags)
{
/* Do nothing if the same op is already in progress. */
- if(op && !(inode->i_status & setflags)) {
+ if (op && !(inode->i_status & setflags)) {
set_io(inode, waitflags, setflags);
op(inode);
- if(release_io(inode, setflags)) {
+ if (release_io(inode, setflags)) {
/* Somebody grabbed my inode from under me. */
#ifdef DEBUG
printk("_io grab!\n");
}
}
-blocking int _free_ibasket(struct super_block * sb)
-{
- if(sb->s_ibasket) {
- struct inode * delinquish = sb->s_ibasket->i_basket_prev;
-#if 0
-printpath(delinquish->i_dentry);
-printk(" delinquish\n");
-#endif
- _clear_inode(delinquish, 0, 1);
- return 1;
- }
- return 0;
-}
-
-static /*inline*/ void _put_ibasket(struct inode * inode)
-{
- struct super_block * sb = inode->i_sb;
- if(!(inode->i_status & ST_IBASKET)) {
- inode->i_status |= ST_IBASKET;
- insert_ibasket(&sb->s_ibasket, inode);
- sb->s_ibasket_count++;
- if(sb->s_ibasket_count > sb->s_ibasket_max)
- (void)_free_ibasket(sb);
- }
-}
-
blocking void _clear_inode(struct inode * inode, int external, int verbose)
{
xcheck("_clear_inode",inode);
- if(inode->i_status & ST_IBASKET) {
+ if (inode->i_status & ST_IBASKET) {
struct super_block * sb = inode->i_sb;
remove_ibasket(&sb->s_ibasket, inode);
sb->s_ibasket_count--;
printk(" put_inode\n");
#endif
_io(sb->s_op->put_inode, inode, ST_TO_PUT|ST_TO_WRITE, ST_TO_PUT);
- if(inode->i_status & ST_EMPTY)
+ if (inode->i_status & ST_EMPTY)
return;
}
- if(inode->i_status & ST_HASHED)
+ if (inode->i_status & ST_HASHED)
remove_hash(&hashtable[hash(inode->i_dev, inode->i_ino)], inode);
- if(inode->i_status & ST_AGED) {
+ if (inode->i_status & ST_AGED) {
/* "cannot happen" when called from an fs because at least
* the caller must use it. Can happen when called from
* invalidate_inodes(). */
- if(verbose)
+ if (verbose)
printk("VFS: clearing aged inode\n");
- if(atomic_read(&inode->i_count))
+ if (atomic_read(&inode->i_count))
printk("VFS: aged inode is in use\n");
remove_lru(&aged_i[inode->i_level], inode);
inodes_stat.aged_count[inode->i_level]--;
}
- if(!external && inode->i_status & ST_IO) {
+ if (!external && inode->i_status & ST_IO) {
printk("VFS: clearing inode during IO operation\n");
}
- if(!(inode->i_status & ST_EMPTY)) {
+ if (!(inode->i_status & ST_EMPTY)) {
remove_all(&all_i, inode);
inode->i_status = ST_EMPTY;
- while(inode->i_dentry) {
- d_del(inode->i_dentry, D_NO_CLEAR_INODE);
- }
- if(inode->i_pages) {
+ if (inode->i_pages) {
vfs_unlock(); /* may block, can that be revised? */
truncate_inode_pages(inode, 0);
vfs_lock();
{
xcheck("insert_inode_hash",inode);
vfs_lock();
- if(!(inode->i_status & ST_HASHED)) {
+ if (!(inode->i_status & ST_HASHED)) {
insert_hash(&hashtable[hash(inode->i_dev, inode->i_ino)], inode);
inode->i_status |= ST_HASHED;
} else
retry:
inode = empty_i;
- if(inode) {
+ if (inode) {
remove_all(&empty_i, inode);
inodes_stat.nr_free_inodes--;
} else if(inodes_stat.nr_inodes < max_inodes || retry > 2) {
inode = grow_inodes();
}
- if(!inode) {
+ if (!inode) {
int level;
int usable = 0;
for(level = 0; level <= NR_LEVELS; level++)
- if(aged_i[level]) {
+ if (aged_i[level]) {
inode = aged_i[level]->i_lru_prev;
/* Here is the picking strategy, tune this */
- if(aged_reused[level] < (usable++ ?
+ if (aged_reused[level] < (usable++ ?
inodes_stat.aged_count[level] :
2))
break;
aged_reused[level] = 0;
}
- if(inode) {
- if(!(inode->i_status & ST_AGED))
+ if (inode) {
+ if (!(inode->i_status & ST_AGED))
printk("VFS: inode aging inconsistency\n");
- if(atomic_read(&inode->i_count) + inode->i_ddir_count)
+ if (atomic_read(&inode->i_count))
printk("VFS: i_count of aged inode is not zero\n");
- if(inode->i_dirt)
+ if (inode->i_dirt)
printk("VFS: Hey, somebody made my aged inode dirty\n");
_clear_inode(inode, 0, 0);
goto retry;
}
}
- if(!inode) {
+ if (!inode) {
vfs_unlock();
schedule();
- if(retry > 10)
+ if (retry > 10)
panic("VFS: cannot repair inode shortage");
- if(retry > 2)
+ if (retry > 2)
printk("VFS: no free inodes\n");
retry++;
vfs_lock();
{
struct inode ** base = &hashtable[hash(i_dev, i_ino)];
struct inode * inode = *base;
- if(inode) do {
- if(inode->i_ino == i_ino && inode->i_dev == i_dev) {
+ if (inode) do {
+ if (inode->i_ino == i_ino && inode->i_dev == i_dev) {
atomic_inc(&inode->i_count);
printk("VFS: inode %lx is already in use\n", i_ino);
return inode;
void _get_inode(struct inode * inode)
{
- if(inode->i_status & ST_IBASKET) {
+ if (inode->i_status & ST_IBASKET) {
inode->i_status &= ~ST_IBASKET;
remove_ibasket(&inode->i_sb->s_ibasket, inode);
inode->i_sb->s_ibasket_count--;
}
- if(inode->i_status & ST_AGED) {
+ if (inode->i_status & ST_AGED) {
inode->i_status &= ~ST_AGED;
remove_lru(&aged_i[inode->i_level], inode);
inodes_stat.aged_count[inode->i_level]--;
aged_reused[inode->i_level]++;
- if(S_ISDIR(inode->i_mode))
+ if (S_ISDIR(inode->i_mode))
/* make dirs less thrashable */
inode->i_level = NR_LEVELS-1;
else if(inode->i_nlink > 1)
else if(++inode->i_reuse_count >= age_table[inode->i_level]
&& inode->i_level < NR_LEVELS-1)
inode->i_level++;
- if(atomic_read(&inode->i_count) != 1)
- printk("VFS: inode count was not zero\n");
+ if (atomic_read(&inode->i_count) != 1)
+ printk("VFS: inode count was not zero (%d after ++)\n", atomic_read(&inode->i_count));
} else if(inode->i_status & ST_EMPTY)
printk("VFS: invalid reuse of empty inode\n");
}
-blocking struct inode * __iget(struct super_block * sb,
- unsigned long i_ino,
- int crossmntp)
+blocking struct inode * iget(struct super_block * sb, unsigned long i_ino)
{
struct inode ** base;
struct inode * inode;
dev_t i_dev;
- if(!sb)
+ if (!sb)
panic("VFS: iget with sb == NULL");
i_dev = sb->s_dev;
- if(!i_dev)
+ if (!i_dev)
panic("VFS: sb->s_dev is NULL\n");
base = &hashtable[hash(i_dev, i_ino)];
vfs_lock();
inode = *base;
- if(inode) do {
- if(inode->i_ino == i_ino && inode->i_dev == i_dev) {
+ if (inode) do {
+ if (inode->i_ino == i_ino && inode->i_dev == i_dev) {
atomic_inc(&inode->i_count);
_get_inode(inode);
inode = _get_empty_inode_hashed(i_dev, i_ino);
inode->i_sb = sb;
inode->i_flags = sb->s_flags;
- if(sb->s_op && sb->s_op->read_inode) {
+ if (sb->s_op && sb->s_op->read_inode) {
set_io(inode, 0, ST_TO_READ); /* do not wait at all */
sb->s_op->read_inode(inode);
- if(release_io(inode, ST_TO_READ))
+ if (release_io(inode, ST_TO_READ))
goto done;
}
vfs_unlock();
done:
- while(crossmntp && inode->i_mount) {
- struct inode * tmp = inode->i_mount;
- iinc(tmp);
- iput(inode);
- inode = tmp;
- }
-xcheck("_iget",inode);
return inode;
}
{
struct super_block * sb;
xcheck("_iput",inode);
- if(atomic_read(&inode->i_count) + inode->i_ddir_count < 0)
+
+ if (atomic_read(&inode->i_count) < 0)
printk("VFS: i_count is negative\n");
- if((atomic_read(&inode->i_count) + inode->i_ddir_count) ||
- (inode->i_status & ST_FREEING)) {
+
+ if (atomic_read(&inode->i_count) || (inode->i_status & ST_FREEING))
return;
- }
+
inode->i_status |= ST_FREEING;
-#ifdef CONFIG_OMIRR
- if(inode->i_status & ST_MODIFIED) {
- inode->i_status &= ~ST_MODIFIED;
- omirr_printall(inode, " W %ld ", CURRENT_TIME);
- }
-#endif
- if(inode->i_pipe) {
+ if (inode->i_pipe) {
free_page((unsigned long)PIPE_BASE(*inode));
PIPE_BASE(*inode)= NULL;
}
- if((sb = inode->i_sb)) {
- if(sb->s_type && (sb->s_type->fs_flags & FS_NO_DCACHE)) {
- while(inode->i_dentry)
- d_del(inode->i_dentry, D_NO_CLEAR_INODE);
- if(atomic_read(&inode->i_count) + inode->i_ddir_count)
- goto done;
- }
- if(sb->s_op) {
- if(inode->i_nlink <= 0 && inode->i_dent_count &&
- !(inode->i_status & (ST_EMPTY|ST_IBASKET)) &&
- (sb->s_type->fs_flags & FS_IBASKET)) {
- _put_ibasket(inode);
+ if ((sb = inode->i_sb)) {
+ if (sb->s_op) {
+ if (inode->i_nlink <= 0 &&
+ !(inode->i_status & (ST_EMPTY|ST_IBASKET))) {
+ _clear_inode(inode, 0, 1);
goto done;
}
- if(!inode->i_dent_count ||
- (sb->s_type->fs_flags & FS_NO_DCACHE)) {
- _io(sb->s_op->put_inode, inode,
- ST_TO_PUT|ST_TO_WRITE, ST_TO_PUT);
- if(atomic_read(&inode->i_count) + inode->i_ddir_count)
- goto done;
- if(inode->i_nlink <= 0) {
- if(!(inode->i_status & ST_EMPTY)) {
- _clear_inode(inode, 0, 1);
- }
- goto done;
- }
- }
- if(inode->i_dirt) {
+ if (inode->i_dirt) {
inode->i_dirt = 0;
_io(sb->s_op->write_inode, inode,
ST_TO_PUT|ST_TO_WRITE, ST_TO_WRITE);
- if(atomic_read(&inode->i_count) + inode->i_ddir_count)
+ if (atomic_read(&inode->i_count))
goto done;
}
}
- if(IS_WRITABLE(inode) && sb->dq_op) {
+ if (IS_WRITABLE(inode) && sb->dq_op) {
/* can operate in parallel to other ops ? */
_io(sb->dq_op->drop, inode, 0, ST_TO_DROP);
- if(atomic_read(&inode->i_count) + inode->i_ddir_count)
+ if (atomic_read(&inode->i_count))
goto done;
}
}
- if(inode->i_mmap)
+ if (inode->i_mmap)
printk("VFS: inode has mappings\n");
- if(inode->i_status & ST_AGED) {
+ if (inode->i_status & ST_AGED) {
printk("VFS: reaging inode\n");
#if defined(DEBUG)
printpath(inode->i_dentry);
#endif
goto done;
}
- if(!(inode->i_status & (ST_HASHED|ST_EMPTY))) {
+ if (!(inode->i_status & (ST_HASHED|ST_EMPTY))) {
_clear_inode(inode, 0, 1);
goto done;
}
- if(inode->i_status & ST_EMPTY) {
+ if (inode->i_status & ST_EMPTY) {
printk("VFS: aging an empty inode\n");
goto done;
}
struct inode * inode;
vfs_lock();
inode = all_i;
- if(inode) do {
+ if (inode) do {
xcheck("sync_inodes",inode);
- if(inode->i_dirt && (inode->i_dev == dev || !dev)) {
- if(inode->i_sb && inode->i_sb->s_op &&
+ if (inode->i_dirt && (inode->i_dev == dev || !dev)) {
+ if (inode->i_sb && inode->i_sb->s_op &&
!(inode->i_status & ST_FREEING)) {
inode->i_dirt = 0;
_io(inode->i_sb->s_op->write_inode, inode,
vfs_lock();
startover:
inode = all_i;
- if(inode) do {
+ if (inode) do {
struct inode * next;
xcheck("_check_inodes",inode);
next = inode->i_next;
- if(inode->i_dev == dev) {
- if(inode->i_dirt || atomic_read(&inode->i_count)) {
+ if (inode->i_dev == dev) {
+ if (inode->i_dirt || atomic_read(&inode->i_count)) {
bad++;
} else {
_clear_inode(inode, 0, 0);
/* _clear_inode() may recursively clear other
* inodes, probably also the next one.
*/
- if(next->i_status & ST_EMPTY)
+ if (next->i_status & ST_EMPTY)
goto startover;
}
}
inode = next;
} while(inode != all_i);
vfs_unlock();
- if(complain && bad)
+ if (complain && bad)
printk("VFS: %d inode(s) busy on removed device `%s'\n",
bad, kdevname(dev));
return (bad == 0);
return 1; /* not checked any more */
}
-int fs_may_umount(kdev_t dev, struct inode * mount_root)
+int fs_may_umount(kdev_t dev, struct dentry * root)
{
struct inode * inode;
vfs_lock();
inode = all_i;
- if(inode) do {
-xcheck("fs_may_umount",inode);
- if(inode->i_dev == dev && atomic_read(&inode->i_count))
- if(inode != mount_root || atomic_read(&inode->i_count) >
- (inode->i_mount == inode ? 2 : 1)) {
+ if (inode) do {
+ if (inode->i_dev == dev && atomic_read(&inode->i_count))
+ if (inode != root->d_inode) {
vfs_unlock();
return 0;
}
struct inode * inode = get_empty_inode();
PIPE_BASE(*inode) = (char*)__get_free_page(GFP_USER);
- if(!(PIPE_BASE(*inode))) {
+ if (!(PIPE_BASE(*inode))) {
iput(inode);
return NULL;
}
inode->i_op = &pipe_inode_operations;
PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 1;
- /* I hope this does not introduce security problems.
- * Please check and give me response.
- */
- {
- char dummyname[32];
- struct qstr dummy = { dummyname, 0 };
- struct dentry * new;
- sprintf(dummyname, ".anonymous-pipe-%06lud", inode->i_ino);
- dummy.len = strlen(dummyname);
- vfs_lock();
- new = d_alloc(the_root, dummy.len, 0);
- if(new)
- d_add(new, inode, &dummy, D_BASKET);
- vfs_unlock();
- }
return inode;
}
retval = -ENOENT;
if (!old_bh)
goto end_rename;
- old_inode = __iget(old_dir->i_sb, old_de->inode,0); /* don't cross mnt-points */
+ old_inode = __iget(old_dir->i_sb, old_de->inode);
if (!old_inode)
goto end_rename;
retval = -EPERM;
goto end_rename;
new_bh = minix_find_entry(new_dir,new_name,new_len,&new_de);
if (new_bh) {
- new_inode = __iget(new_dir->i_sb, new_de->inode, 0);
+ new_inode = __iget(new_dir->i_sb, new_de->inode);
if (!new_inode) {
brelse(new_bh);
new_bh = NULL;
* lookup logic.
*/
-#include <linux/config.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#undef DEBUG /* some other debugging */
-/* local flags for __namei() */
-#define NAM_SEMLOCK 8 /* set a semlock on the last dir */
-#define NAM_TRANSCREATE 16 /* last component may be created, try "=CREATE#" suffix*/
-#define NAM_NO_TRAILSLASH 32 /* disallow trailing slashes by returning EISDIR */
#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE])
/* [Feb-1997 T. Schoebel-Theuer]
* is done solely in the VFS level, such that <fs>_follow_link() is not
* used any more and could be removed in future. As a side effect,
* dir_namei(), _namei() and follow_link() are now replaced with a single
- * function __namei() that can handle all the special cases of the former
+ * function lookup_dentry() that can handle all the special cases of the former
* code.
*
* With the new dcache, the pathname is stored at each inode, at least as
char * res;
down(&quicklock);
res = quicklist;
- if(res) {
+ if (res) {
#ifdef DEBUG
char * tmp = res;
int i;
for(i=0; i<quickcount; i++)
tmp = *(char**)tmp;
- if(tmp)
+ if (tmp)
printk("bad quicklist %x\n", (int)tmp);
#endif
quicklist = *(char**)res;
return res;
}
+/*
+ * Kernel pointers have redundant information, so we can use a
+ * scheme where we can return either an error code or a dentry
+ * pointer with the same return value.
+ *
+ * This should be a per-architecture thing, to allow different
+ * error and pointer decisions.
+ */
+#define ERR_PTR(err) ((void *)((long)(err)))
+#define PTR_ERR(ptr) ((long)(ptr))
+#define IS_ERR(ptr) ((unsigned long)(ptr) > (unsigned long)(-1000))
+
inline void putname(char * name)
{
- if(name) {
+ if (name) {
down(&quicklock);
*(char**)name = quicklist;
quicklist = name;
int retval;
tmp = get_page();
- if(!tmp)
+ if (!tmp)
return -ENOMEM;
retval = do_getname(filename, tmp);
if (retval < 0)
inode->i_writecount--;
}
-static /*inline */ int concat(struct qstr * name, struct qstr * appendix, char * buf)
-{
- int totallen = name->len;
- if(name->len > MAX_TRANS_FILELEN ||
- appendix->len > MAX_TRANS_SUFFIX) {
- return -ENAMETOOLONG;
- }
- memcpy(buf, name->name, name->len);
- memcpy(buf + name->len, appendix->name, appendix->len);
- totallen += appendix->len;
- buf[totallen] = '\0';
- return totallen;
-}
-
-/* Internal lookup() using the new generic dcache.
- * buf must only be supplied if appendix!=NULL.
+/*
+ * This is called when everything else fails, and we actually have
+ * to go to the low-level filesystem to find out what we should do..
+ *
+ * We get the directory semaphore, and after getting that we also
+ * make sure that nobody added the entry to the dcache in the meantime..
*/
-static int cached_lookup(struct inode * dir, struct qstr * name,
- struct qstr * appendix, char * buf,
- struct qstr * res_name, struct dentry ** res_entry,
- struct inode ** result)
+static struct dentry * real_lookup(struct dentry * dentry, struct qstr * name)
{
- struct qstr tmp = { name->name, name->len };
- int error;
- struct dentry * cached;
+ struct inode *dir = dentry->d_inode;
+ struct dentry *result;
+ struct inode *inode;
+ int error = -ENOTDIR;
- *result = NULL;
- if(name->len >= D_MAXLEN)
- return -ENAMETOOLONG;
- vfs_lock();
- cached = d_lookup(dir, name, appendix);
- if(cached) {
- struct inode *inode = NULL;
+ down(&dir->i_sem);
- if(cached->u.d_inode && (inode = d_inode(&cached))) {
- error = 0;
- if(appendix && res_name) {
- tmp.len = error = concat(name, appendix, buf);
- tmp.name = buf;
- if(error > 0)
- error = 0;
- }
- } else {
- error = -ENOENT;
+ error = -ENOTDIR;
+ if (dir->i_op && dir->i_op->lookup)
+ error = dir->i_op->lookup(dir, name, &inode);
+ result = ERR_PTR(error);
+
+ if (!error || error == -ENOENT) {
+ struct dentry *new;
+ int isdir = 0, flags = D_NOCHECKDUP;
+
+ if (error) {
+ flags = D_NEGATIVE;
+ inode = NULL;
+ } else if (S_ISDIR(inode->i_mode)) {
+ isdir = 1;
+ flags = D_DIR|D_NOCHECKDUP;
}
- vfs_unlock();
- if(res_entry)
- *res_entry = cached;
+ new = d_alloc(dentry, name, isdir);
- /* Since we are bypassing the iget() mechanism, we have to
- * fabricate the act of crossing any mount points.
+ /*
+ * Ok, now we can't sleep any more. Double-check that
+ * nobody else added this in the meantime..
*/
- if(!error && inode && inode->i_mount) {
- do {
- struct inode *mnti = inode->i_mount;
- iinc(mnti);
- iput(inode);
- inode = mnti;
- } while(inode->i_mount);
+ result = d_lookup(dentry, name);
+ if (result) {
+ d_free(new);
+ } else {
+ d_add(new, inode, flags);
+ result = new;
}
- *result = inode;
- goto done;
- } else
- vfs_unlock();
-
- if(appendix) {
- tmp.len = error = concat(name, appendix, buf);
- tmp.name = buf;
- if(error < 0)
- goto done;
- }
- atomic_inc(&dir->i_count);
- error = dir->i_op->lookup(dir, tmp.name, tmp.len, result);
- if(dir->i_dentry && tmp.len &&
- (!error || (error == -ENOENT && (!dir->i_sb || !dir->i_sb->s_type ||
- !(dir->i_sb->s_type->fs_flags & FS_NO_DCACHE))))) {
- struct dentry * res;
- vfs_lock();
- res = d_entry(dir->i_dentry, &tmp, error ? NULL : *result);
- vfs_unlock();
- if(res_entry)
- *res_entry = res;
}
-done:
- if(res_name) {
- if(error) {
- res_name->name = name->name;
- res_name->len = name->len;
- } else {
- res_name->name = tmp.name;
- res_name->len = tmp.len;
- }
- }
- return error;
+ up(&dir->i_sem);
+ return result;
+}
+
+/* Internal lookup() using the new generic dcache. */
+static inline struct dentry * cached_lookup(struct dentry * dentry, struct qstr * name)
+{
+ return d_lookup(dentry, name);
}
-#ifdef CONFIG_TRANS_NAMES
-/* If a normal filename is seen, try to determine whether a
- * "#keyword=context#" file exists and return the new filename.
- * If the name is to be created (create_mode), check whether a
- * "#keyword=CREATE" name exists and optionally return the corresponding
- * context name even if it didn't exist before.
+/*
+ * "." and ".." are special - ".." especially so because it has to be able
+ * to know about the current root directory and parent relationships
*/
-static int check_suffixes(struct inode * dir, struct qstr * name,
- int create_mode, char * buf,
- struct qstr * res_name, struct dentry ** res_entry,
- struct inode ** result)
+static struct dentry * reserved_lookup(struct dentry * dir, struct qstr * name)
{
- struct translations * trans;
- char * env;
- struct qstr * suffixes;
- int i;
- int error = -ENOENT;
-
- if(!buf)
- panic("buf==NULL");
- env = env_transl();
-#ifdef CONFIG_TRANS_RESTRICT
- if(!env && dir->i_gid != CONFIG_TRANS_GID) {
- return error;
- }
-#endif
- trans = get_translations(env);
- suffixes = create_mode ? trans->c_name : trans->name;
- for(i = 0; i < trans->count; i++) {
- error = cached_lookup(dir, name, &suffixes[i],
- buf, res_name, res_entry, result);
- if(!error) {
- if(res_name && create_mode) {
- /* buf == res_name->name, but is writable */
- memcpy(buf + name->len,
- trans->name[i].name,
- trans->name[i].len);
- res_name->len = name->len + trans->name[i].len;
- buf[res_name->len] = '\0';
+ struct dentry *result = NULL;
+ if (name->name[0] == '.') {
+ switch (name->len) {
+ default:
+ break;
+ case 2:
+ if (name->name[1] != '.')
+ break;
+
+ if (dir != current->fs->root) {
+ dir = dir->d_covers->d_parent;
}
+ /* fallthrough */
+ case 1:
+ result = dget(dir);
break;
}
}
- if(env)
- free_page((unsigned long)trans);
- return error;
+ return result;
}
-#endif
-
-/* Any operations involving reserved names at the VFS level should go here. */
-static /*inline*/ int reserved_lookup(struct inode * dir, struct qstr * name,
- int create_mode, char * buf,
- struct inode ** result)
+static inline int is_reserved(struct dentry *dentry)
{
- int error = -ENOENT;
- if(name->name[0] == '.') {
- if(name->len == 1) {
- *result = dir;
- error = 0;
- } else if (name->len==2 && name->name[1] == '.') {
- if (dir == current->fs->root) {
- *result = dir;
- error = 0;
- }
- else if(dir->i_dentry) {
- error = 0;
- *result = dir->i_dentry->d_parent->u.d_inode;
- if(!*result) {
- printk("dcache parent directory is lost");
- error = -ESTALE; /* random error */
- }
- }
+ if (dentry->d_name.name[0] == '.') {
+ switch (dentry->d_name.len) {
+ case 2:
+ if (dentry->d_name.name[1] != '.')
+ break;
+ /* fallthrough */
+ case 1:
+ return 1;
}
- if(!error)
- atomic_inc(&(*result)->i_count);
}
- return error;
+ return 0;
}
/* In difference to the former version, lookup() no longer eats the dir. */
-static /*inline*/ int lookup(struct inode * dir, struct qstr * name, int create_mode,
- char * buf, struct qstr * res_name,
- struct dentry ** res_entry, struct inode ** result)
+static struct dentry * lookup(struct dentry * dir, struct qstr * name)
{
- int perm;
-
- *result = NULL;
- perm = -ENOENT;
- if (!dir)
- goto done;
+ int err;
+ struct dentry * result;
/* Check permissions before traversing mount-points. */
- perm = permission(dir,MAY_EXEC);
- if (perm)
+ err = permission(dir->d_inode, MAY_EXEC);
+ result = ERR_PTR(err);
+ if (err)
goto done;
- perm = reserved_lookup(dir, name, create_mode, buf, result);
- if(!perm) {
- if(res_name) {
- res_name->name = name->name;
- res_name->len = name->len;
- }
+
+ result = reserved_lookup(dir, name);
+ if (result)
goto done;
- }
- perm = -ENOTDIR;
- if (!dir->i_op || !dir->i_op->lookup)
+
+ result = cached_lookup(dir, name);
+ if (result)
goto done;
-#ifdef CONFIG_TRANS_NAMES /* try suffixes */
- perm = check_suffixes(dir, name, 0, buf, res_name, res_entry, result);
- if(perm) /* try original name */
-#endif
- perm = cached_lookup(dir, name, NULL, buf, res_name, res_entry, result);
-#ifdef CONFIG_TRANS_NAMES
- if(perm == -ENOENT && create_mode) { /* try the =CREATE# suffix */
- struct inode * dummy;
- if(!check_suffixes(dir, name, 1, buf, res_name, NULL, &dummy)) {
- iput(dummy);
- }
- }
-#endif
+
+ result = real_lookup(dir, name);
+ if (!result)
+ result = ERR_PTR(-ENOTDIR);
done:
- return perm;
+ if (!IS_ERR(result))
+ result = dget(result->d_mounts);
+
+ return result;
}
-/* [8-Feb-97 T. Schoebel-Theuer] follow_link() modified for generic operation
- * on the VFS layer: first call <fs>_readlink() and then open_namei().
- * All <fs>_follow_link() are not used any more and may be eliminated
- * (by Linus; I refrained in order to not break other patches).
- * Single exeption is procfs, where proc_follow_link() is used
- * internally (and perhaps should be rewritten).
- * Note: [partly obsolete] I removed parameters flag and mode, since now
- * __namei() is called instead of open_namei(). In the old semantics,
- * the _last_ instance of open_namei() did the real create() if O_CREAT was
- * set and the name existed already in form of a symlink. This has been
- * simplified now, and also the semantics when combined with O_EXCL has changed.
- ****************************************************************************
- * [13-Feb-97] Complete rewrite -> functionality of reading symlinks factored
- * out into _read_link(). The above notes remain valid in principle.
+/*
+ * This should check "link_count", but doesn't do that yet..
*/
-static /*inline*/ int _read_link(struct inode * inode, char ** linkname, int loopcount)
+static struct dentry * do_follow_link(struct dentry *base, struct dentry *dentry)
{
- unsigned long old_fs;
- int error;
+ struct inode * inode = dentry->d_inode;
- error = -ENOSYS;
- if (!inode->i_op || !inode->i_op->readlink)
- goto done;
- error = -ELOOP;
- if (current->link_count + loopcount > 10)
- goto done;
- error = -ENOMEM;
- if(!*linkname && !(*linkname = get_page()))
- goto done;
- if (DO_UPDATE_ATIME(inode)) {
- inode->i_atime = CURRENT_TIME;
- inode->i_dirt = 1;
- }
- atomic_inc(&inode->i_count);
- old_fs = get_fs();
- set_fs(KERNEL_DS);
- error = inode->i_op->readlink(inode, *linkname, PAGE_SIZE);
- set_fs(old_fs);
- if(!error) {
- error = -ENOENT; /* ? or other error code ? */
- } else if(error > 0) {
- (*linkname)[error] = '\0';
- error = 0;
+ if (inode->i_op && inode->i_op->follow_link) {
+ struct dentry *result;
+
+ /* This eats the base */
+ result = inode->i_op->follow_link(inode, base);
+ base = dentry;
+ dentry = result;
}
-done:
- iput(inode);
- return error;
+ dput(base);
+ return dentry;
}
-/* [13-Feb-97 T. Schoebel-Theuer] complete rewrite:
- * merged dir_name(), _namei() and follow_link() into one new routine
- * that obeys all the special cases hidden in the old routines in a
- * (hopefully) systematic way:
- * parameter retrieve_mode is bitwise or'ed of the ST_* flags.
- * if res_inode is a NULL pointer, dont try to retrieve the last component
- * at all. Parameters with prefix last_ are used only if res_inode is
- * non-NULL and refer to the last component of the path only.
+/*
+ * Name resolution.
+ *
+ * This is the basic name resolution function, turning a pathname
+ * into the final dentry.
*/
-int __namei(int retrieve_mode, const char * name, struct inode * base,
- char * buf, struct inode ** res_dir, struct inode ** res_inode,
- struct qstr * last_name, struct dentry ** last_entry,
- int * last_error)
+struct dentry * lookup_dentry(const char * name, struct dentry * base, int follow_link)
{
- char c;
- struct qstr this;
- char * linkname = NULL;
- char * oldlinkname = NULL;
- int trail_flag = 0;
- int loopcount = 0;
- int error;
-#ifdef DEBUG
- if(last_name) {
- last_name->name = "(Uninitialized)";
- last_name->len = 15;
- }
-#endif
-again:
- error = -ENOENT;
- this.name = name;
- if (this.name[0] == '/') {
- if(base)
- iput(base);
- if (__prefix_namei(retrieve_mode, this.name, base, buf,
- res_dir, res_inode,
- last_name, last_entry, last_error) == 0)
- return 0;
- base = current->fs->root;
- atomic_inc(&base->i_count);
- this.name++;
+ struct dentry * dentry;
+
+ if (*name == '/') {
+ if (base)
+ dput(base);
+ base = dget(current->fs->root);
+ do {
+ name++;
+ } while (*name == '/');
} else if (!base) {
- base = current->fs->pwd;
- atomic_inc(&base->i_count);
+ base = dget(current->fs->pwd);
}
+
+ if (*name == '\0')
+ return base;
+
+ /* At this point we know we have a real path component. */
for(;;) {
- struct inode * inode;
- const char * tmp = this.name;
int len;
+ unsigned long hash;
+ struct qstr this;
+ char c, trailing;
+
+ this.name = name;
+ hash = init_name_hash();
+ for (len = 0; (c = *name++) && (c != '/') ; len++)
+ hash = partial_name_hash(c, hash);
- for(len = 0; (c = *tmp++) && (c != '/'); len++) ;
this.len = len;
- if(!c)
- break;
- while((c = *tmp) == '/') /* remove embedded/trailing slashes */
- tmp++;
- if(!c) {
- trail_flag = 1;
- if(retrieve_mode & NAM_NO_TRAILSLASH) {
- error = -EISDIR;
- goto alldone;
- }
- break;
- }
-#if 0
- if(atomic_read(&base->i_count) == 0)
- printk("vor lookup this=%s tmp=%s\n", this.name, tmp);
-#endif
- error = lookup(base, &this, 0, buf, NULL, NULL, &inode);
-#if 0
- if(atomic_read(&base->i_count) == 0)
- printk("nach lookup this=%s tmp=%s\n", this.name, tmp);
-#endif
- if (error)
- goto alldone;
- if(S_ISLNK(inode->i_mode)) {
- error = _read_link(inode, &linkname, loopcount);
- if(error)
- goto alldone;
- current->link_count++;
- error = __namei((retrieve_mode &
- ~(NAM_SEMLOCK|NAM_TRANSCREATE|NAM_NO_TRAILSLASH))
- | NAM_FOLLOW_LINK,
- linkname, base, buf,
- &base, &inode, NULL, NULL, NULL);
- current->link_count--;
- if(error)
- goto alldone;
- }
-#if 0
- if(atomic_read(&base->i_count) == 0)
- printk("this=%s tmp=%s\n", this.name, tmp);
-#endif
- this.name = tmp;
- iput(base);
- base = inode;
- }
- if(res_inode) {
- if(retrieve_mode & NAM_SEMLOCK)
- down(&base->i_sem);
- error = lookup(base, &this, retrieve_mode & NAM_TRANSCREATE,
- buf, last_name, last_entry, res_inode);
- if(!error && S_ISLNK((*res_inode)->i_mode) &&
- ((retrieve_mode & NAM_FOLLOW_LINK) ||
- (trail_flag && (retrieve_mode & NAM_FOLLOW_TRAILSLASH)))) {
- char * tmp;
-
- error = _read_link(*res_inode, &linkname, loopcount);
- if(error)
- goto lastdone;
- if(retrieve_mode & NAM_SEMLOCK)
- up(&base->i_sem);
- /* exchange pages */
- name = tmp = linkname;
- linkname = oldlinkname; oldlinkname = tmp;
- loopcount++;
- goto again; /* Tail recursion elimination "by hand",
- * uses less dynamic memory.
- */
-
- /* Note that trail_flag is not reset, so it
- * does not matter in a symlink chain where a
- * trailing slash indicates a directory endpoint.
- */
- }
- if(!error && trail_flag && !S_ISDIR((*res_inode)->i_mode)) {
- iput(*res_inode);
- error = -ENOTDIR;
+ this.hash = end_name_hash(hash);
+
+ /* remove trailing slashes? */
+ trailing = c;
+ if (c) {
+ while ((c = *name) == '/')
+ name++;
}
- lastdone:
- if(last_error) {
- *last_error = error;
- error = 0;
+
+ dentry = lookup(base, &this);
+ if (IS_ERR(dentry))
+ break;
+ if (dentry->d_flag & D_NEGATIVE)
+ break;
+
+ /* Last component? */
+ if (!c) {
+ if (!trailing && !follow_link)
+ break;
+ return do_follow_link(base, dentry);
}
+
+ base = do_follow_link(base, dentry);
}
-alldone:
- if(!error && res_dir)
- *res_dir = base;
- else
- iput(base);
- putname(linkname);
- putname(oldlinkname);
- return error;
+ dput(base);
+ return dentry;
}
/*
* is used by most simple commands to get the inode of a specified name.
* Open, link etc use their own routines, but this is enough for things
* like 'chmod' etc.
+ *
+ * namei exists in two versions: namei/lnamei. The only difference is
+ * that namei follows links, while lnamei does not.
*/
-
-/* [Feb 1997 T.Schoebel-Theuer] lnamei() completely removed; can be
- * simulated when calling with retrieve_mode==NAM_FOLLOW_TRAILSLASH.
- */
-int namei(int retrieve_mode, const char *pathname, struct inode **res_inode)
+int __namei(const char *pathname, struct inode **res_inode, int follow_link)
{
int error;
- char * tmp;
+ char * name;
- error = getname(pathname, &tmp);
+ error = getname(pathname, &name);
if (!error) {
- char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2];
- error = __namei(retrieve_mode, tmp, NULL,
- buf, NULL, res_inode, NULL, NULL, NULL);
- putname(tmp);
+ struct dentry * dentry;
+
+ dentry = lookup_dentry(name, NULL, follow_link);
+ putname(name);
+ error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ error = -ENOENT;
+ if (dentry) {
+ if (!(dentry->d_flag & D_NEGATIVE)) {
+ struct inode *inode = dentry->d_inode;
+ atomic_inc(&inode->i_count);
+ *res_inode = inode;
+ error = 0;
+ }
+ dput(dentry);
+ }
+ }
}
return error;
}
+static inline struct inode *get_parent(struct dentry *dentry)
+{
+ struct inode *dir = dentry->d_parent->d_inode;
+
+ atomic_inc(&dir->i_count);
+ return dir;
+}
+
+static inline struct inode *lock_parent(struct dentry *dentry)
+{
+ struct inode *dir = dentry->d_parent->d_inode;
+
+ atomic_inc(&dir->i_count);
+ down(&dir->i_sem);
+ return dir;
+}
+
/*
* open_namei()
*
* for symlinks (where the permissions are checked later).
*/
int open_namei(const char * pathname, int flag, int mode,
- struct inode ** res_inode, struct inode * base)
+ struct inode ** res_inode, struct dentry * base)
{
- char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2];
- struct qstr last;
int error;
- int lasterror;
- struct inode * dir, * inode;
- int namei_mode;
+ struct inode *inode;
+ struct dentry *dentry;
mode &= S_IALLUGO & ~current->fs->umask;
mode |= S_IFREG;
- namei_mode = NAM_FOLLOW_LINK;
- if(flag & O_CREAT)
- namei_mode |= NAM_SEMLOCK|NAM_TRANSCREATE|NAM_NO_TRAILSLASH;
- error = __namei(namei_mode, pathname, base, buf,
- &dir, &inode, &last, NULL, &lasterror);
- if (error)
- goto exit;
- error = lasterror;
+ dentry = lookup_dentry(pathname, base, 1);
+ if (IS_ERR(dentry))
+ return PTR_ERR(dentry);
+
if (flag & O_CREAT) {
- if (!error) {
- if (flag & O_EXCL) {
+ struct inode *dir;
+
+ dir = lock_parent(dentry);
+ /*
+ * The existence test must be done _after_ getting the directory
+ * semaphore - the dentry might otherwise change.
+ */
+ if (!(dentry->d_flag & D_NEGATIVE)) {
+ error = 0;
+ if (flag & O_EXCL)
error = -EEXIST;
- }
} else if (IS_RDONLY(dir))
error = -EROFS;
else if (!dir->i_op || !dir->i_op->create)
error = -EACCES;
- else if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0)
- ; /* error is already set! */
- else {
- d_del(d_lookup(dir, &last, NULL), D_REMOVE);
- atomic_inc(&dir->i_count); /* create eats the dir */
+ else if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) == 0) {
if (dir->i_sb && dir->i_sb->dq_op)
dir->i_sb->dq_op->initialize(dir, -1);
- error = dir->i_op->create(dir, last.name, last.len,
- mode, res_inode);
-#ifdef CONFIG_OMIRR
- if(!error)
- omirr_print(dir->i_dentry, NULL, &last,
- " c %ld %d ", CURRENT_TIME, mode);
-#endif
- up(&dir->i_sem);
- goto exit_dir;
+ error = dir->i_op->create(dir, dentry, mode);
}
up(&dir->i_sem);
+ iput(dir);
+ if (error)
+ goto exit;
}
+
+ error = -ENOENT;
+ if (dentry->d_flag & D_NEGATIVE)
+ goto exit;
+
+ inode = dentry->d_inode;
+ error = -EISDIR;
+ if (S_ISDIR(inode->i_mode) && (flag & 2))
+ goto exit;
+
+ error = permission(inode,ACC_MODE(flag));
if (error)
- goto exit_inode;
+ goto exit;
- if (S_ISDIR(inode->i_mode) && (flag & 2)) {
- error = -EISDIR;
- goto exit_inode;
- }
- if ((error = permission(inode,ACC_MODE(flag))) != 0) {
- goto exit_inode;
- }
+ /*
+ * FIFO's, sockets and device files are special: they don't
+ * actually live on the filesystem itself, and as such you
+ * can write to them even if the filesystem is read-only.
+ */
if (S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) {
- /*
- * 2-Feb-1995 Bruce Perens <Bruce@Pixar.com>
- * Allow opens of Unix domain sockets and FIFOs for write on
- * read-only filesystems. Their data does not live on the disk.
- *
- * If there was something like IS_NODEV(inode) for
- * pipes and/or sockets I'd check it here.
- */
flag &= ~O_TRUNC;
- }
- else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
- if (IS_NODEV(inode)) {
- error = -EACCES;
- goto exit_inode;
- }
+ } else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
+ error = -EACCES;
+ if (IS_NODEV(inode))
+ goto exit;
+
flag &= ~O_TRUNC;
} else {
- if (IS_RDONLY(inode) && (flag & 2)) {
- error = -EROFS;
- goto exit_inode;
- }
+ error = -EROFS;
+ if (IS_RDONLY(inode) && (flag & 2))
+ goto exit;
}
/*
* An append-only file must be opened in append mode for writing.
*/
- if (IS_APPEND(inode) && ((flag & FMODE_WRITE) && !(flag & O_APPEND))) {
- error = -EPERM;
- goto exit_inode;
- }
+ error = -EPERM;
+ if (IS_APPEND(inode) && ((flag & FMODE_WRITE) && !(flag & O_APPEND)))
+ goto exit;
+
if (flag & O_TRUNC) {
- if ((error = get_write_access(inode)))
- goto exit_inode;
+ error = get_write_access(inode);
+ if (error)
+ goto exit;
+
/*
* Refuse to truncate files with mandatory locks held on them.
*/
error = locks_verify_locked(inode);
- if (error)
- goto exit_inode;
- if (inode->i_sb && inode->i_sb->dq_op)
- inode->i_sb->dq_op->initialize(inode, -1);
+ if (!error) {
+ if (inode->i_sb && inode->i_sb->dq_op)
+ inode->i_sb->dq_op->initialize(inode, -1);
- error = do_truncate(inode, 0);
+ error = do_truncate(inode, 0);
+ }
put_write_access(inode);
+ if (error)
+ goto exit;
} else
if (flag & FMODE_WRITE)
if (inode->i_sb && inode->i_sb->dq_op)
inode->i_sb->dq_op->initialize(inode, -1);
-exit_inode:
- if(error) {
- if(!lasterror)
- iput(inode);
- } else
- *res_inode = inode;
-exit_dir:
- iput(dir);
+
+ *res_inode = inode;
+ atomic_inc(&inode->i_count);
+ error = 0;
+
exit:
+ dput(dentry);
return error;
}
int do_mknod(const char * filename, int mode, dev_t dev)
{
- char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2];
- struct qstr last;
- int error, lasterror;
- struct inode * dir;
- struct inode * inode;
+ int error;
+ struct inode *dir;
+ struct dentry *dentry;
mode &= ~current->fs->umask;
- error = __namei(NAM_FOLLOW_LINK|NAM_TRANSCREATE|NAM_NO_TRAILSLASH,
- filename, NULL, buf,
- &dir, &inode, &last, NULL, &lasterror);
- if (error)
+ dentry = lookup_dentry(filename, NULL, 1);
+
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
goto exit;
- if(!lasterror) {
- error = -EEXIST;
- goto exit_inode;
- }
- if (!last.len) {
- error = -ENOENT;
- goto exit_inode;
- }
- if (IS_RDONLY(dir)) {
- error = -EROFS;
- goto exit_inode;
- }
- if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0)
- goto exit_inode;
- if (!dir->i_op || !dir->i_op->mknod) {
- error = -ENOSYS; /* instead of EPERM, what does Posix say? */
- goto exit_inode;
- }
- atomic_inc(&dir->i_count);
+
+ dir = lock_parent(dentry);
+
+ error = -EEXIST;
+ if (!(dentry->d_flag & D_NEGATIVE))
+ goto exit_lock;
+
+ error = -EROFS;
+ if (IS_RDONLY(dir))
+ goto exit_lock;
+
+ error = permission(dir,MAY_WRITE | MAY_EXEC);
+ if (error)
+ goto exit_lock;
+
+ error = -EPERM;
+ if (!dir->i_op || !dir->i_op->mknod)
+ goto exit_lock;
+
if (dir->i_sb && dir->i_sb->dq_op)
dir->i_sb->dq_op->initialize(dir, -1);
- down(&dir->i_sem);
- d_del(d_lookup(dir, &last, NULL), D_REMOVE);
- error = dir->i_op->mknod(dir, last.name, last.len, mode, dev);
-#ifdef CONFIG_OMIRR
- if(!error)
- omirr_print(dir->i_dentry, NULL, &last, " n %ld %d %d ",
- CURRENT_TIME, mode, dev);
-#endif
+ error = dir->i_op->mknod(dir, dentry, mode, dev);
+
+exit_lock:
up(&dir->i_sem);
-exit_inode:
- if(!lasterror)
- iput(inode);
iput(dir);
+ dput(dentry);
exit:
return error;
}
return error;
}
-/* [Feb-97 T. Schoebel-Theuer] remove_trailing_slashes() is now obsolete,
- * its functionality is handled by observing trailing slashes in __namei().
+/*
+ * Look out: this function may change a normal dentry
+ * into a directory dentry (different size)..
*/
static inline int do_mkdir(const char * pathname, int mode)
{
- char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2];
- struct qstr last;
- int error, lasterror;
- struct inode * dir;
- struct inode * inode;
+ int error;
+ struct inode *dir;
+ struct dentry *dentry;
- mode &= 0777 & ~current->fs->umask;
+ dentry = lookup_dentry(pathname, NULL, 1);
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
+ goto exit;
- error = __namei(NAM_FOLLOW_LINK|NAM_TRANSCREATE, pathname, NULL, buf,
- &dir, &inode, &last, NULL, &lasterror);
+ dir = lock_parent(dentry);
+
+ error = -EEXIST;
+ if (!(dentry->d_flag & D_NEGATIVE))
+ goto exit_lock;
+
+ error = -EROFS;
+ if (IS_RDONLY(dir))
+ goto exit_lock;
+
+ error = permission(dir,MAY_WRITE | MAY_EXEC);
if (error)
- goto exit;
- if(!lasterror) {
- error = -EEXIST;
- goto exit_inode;
- }
- if (!last.len) {
- error = -ENOENT;
- goto exit_inode;
- }
- if (IS_RDONLY(dir)) {
- error = -EROFS;
- goto exit_inode;
- }
- if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0)
- goto exit_inode;
- if (!dir->i_op || !dir->i_op->mkdir) {
- error = -ENOSYS; /* instead of EPERM, what does Posix say? */
- goto exit_inode;
- }
- atomic_inc(&dir->i_count);
+ goto exit_lock;
+
+ error = -EPERM;
+ if (!dir->i_op || !dir->i_op->mkdir)
+ goto exit_lock;
+
if (dir->i_sb && dir->i_sb->dq_op)
dir->i_sb->dq_op->initialize(dir, -1);
- down(&dir->i_sem);
- d_del(d_lookup(dir, &last, NULL), D_REMOVE);
- mode &= 01777 & ~current->fs->umask;
- error = dir->i_op->mkdir(dir, last.name, last.len, mode);
-#ifdef CONFIG_OMIRR
- if(!error)
- omirr_print(dir->i_dentry, NULL, &last, " d %ld %d ",
- CURRENT_TIME, mode);
-#endif
+ mode &= 0777 & ~current->fs->umask;
+ error = dir->i_op->mkdir(dir, dentry, mode);
+
+exit_lock:
up(&dir->i_sem);
-exit_inode:
- if(!lasterror)
- iput(inode);
iput(dir);
+ dput(dentry);
exit:
return error;
}
return error;
}
-#if 0 /* We need a "deletefs", someone please write it. -DaveM */
-/* Perhaps this could be moved out into a new file. */
-static void basket_name(struct inode * dir, struct dentry * entry)
-{
- char prefix[32];
- struct qstr prename = { prefix, 14 };
- struct qstr entname = { entry->d_name.name, entry->d_name.len };
- struct inode * inode;
- struct dentry * old = entry; /* dummy */
- int i;
- if(!entry || !(inode = d_inode(&entry)))
- return;
-#if 0
- if(atomic_read(&inode->i_count) > 2) {
- extern void printpath(struct dentry *entry);
-
- printk("Caution: in use ");
- if(inode->i_dentry)
- printpath(inode->i_dentry);
- printk(" i_nlink=%d i_count=%d i_ddir_count=%d i_dent_count=%d\n",
- inode->i_nlink, atomic_read(&inode->i_count),
- inode->i_ddir_count, inode->i_dent_count);
- }
-#endif
- vfs_lock();
- for(i = 1; old; i++) {
- sprintf(prefix, ".deleted-%04d.", i);
- old = d_lookup(dir, &prename, &entname);
- }
- d_move(entry, dir, &prename, &entname);
- vfs_unlock();
- iput(inode);
-}
-#endif
-
static inline int do_rmdir(const char * name)
{
- char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2];
- struct qstr last;
- struct dentry * lastent = NULL;
int error;
- struct inode * dir;
- struct inode * inode;
-
- /* [T.Schoebel-Theuer] I'm not sure which flags to use here.
- * Try the following on different platforms:
- * [0] rm -rf test test2
- * [1] ln -s test2 test
- * [2] mkdir test || mkdir test2
- * [3] rmdir test && mkdir test2
- * [4] rmdir test/
- * Now the rusults:
- * cmd | HP-UX | SunOS | Solaris | Old Linux | New Linux |
- * ----------------------------------------------------------------
- * [2] | (OK) | EEXIST | EEXIST | EEXIST | (OK)
- * [3] | ENOTDIR | ENOTDIR | ENOTDIR | ENOTDIR | ENOTDIR
- * [4] | (OK) | EINVAL | ENOTDIR | ENOTDIR | (OK)
- * So I implemented the HP-UX semantics. If this is not right
- * for Posix compliancy, change the flags accordingly. If Posix
- * let the question open, I'd suggest to stay at the new semantics.
- * I'd even make case [3] work by adding 2 to the flags parameter
- * if Posix tolerates that.
- */
- error = __namei(NAM_FOLLOW_TRAILSLASH, name, NULL, buf,
- &dir, &inode, &last, &lastent, NULL);
- if (error)
+ struct inode *dir;
+ struct dentry *dentry;
+
+ dentry = lookup_dentry(name, NULL, 1);
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
goto exit;
- if (IS_RDONLY(dir)) {
- error = -EROFS;
- goto exit_dir;
- }
- if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0)
- goto exit_dir;
+
+ dir = lock_parent(dentry);
+ error = -ENOENT;
+ if (dentry->d_flag & D_NEGATIVE)
+ goto exit_lock;
+
+ error = -EROFS;
+ if (IS_RDONLY(dir))
+ goto exit_lock;
+
+ error = permission(dir,MAY_WRITE | MAY_EXEC);
+ if (error)
+ goto exit_lock;
+
/*
* A subdirectory cannot be removed from an append-only directory.
*/
- if (IS_APPEND(dir)) {
- error = -EPERM;
- goto exit_dir;
- }
- if (!dir->i_op || !dir->i_op->rmdir) {
- error = -ENOSYS; /* was EPERM */
- goto exit_dir;
- }
+ error = -EPERM;
+ if (IS_APPEND(dir))
+ goto exit_lock;
+
/* Disallow removals of mountpoints. */
- if(inode->i_mount) {
- error = -EBUSY;
- goto exit_dir;
- }
+ error = -EBUSY;
+ if (dentry->d_covers != dentry)
+ goto exit_lock;
+
+ error = -EPERM;
+ if (!dir->i_op || !dir->i_op->rmdir)
+ goto exit_lock;
+
if (dir->i_sb && dir->i_sb->dq_op)
dir->i_sb->dq_op->initialize(dir, -1);
- down(&dir->i_sem);
-#if 0
- if(lastent && d_isbasket(lastent)) {
- d_del(lastent, D_REMOVE);
- error = 0;
- goto exit_lock;
- }
-#endif
- atomic_inc(&dir->i_count);
- error = dir->i_op->rmdir(dir, last.name, last.len);
-#ifdef CONFIG_OMIRR
- if(!error)
- omirr_print(lastent, NULL, NULL, " r %ld ", CURRENT_TIME);
-#endif
-#if 0
- if(!error && lastent)
- basket_name(dir, lastent);
+ error = dir->i_op->rmdir(dir, dentry);
+
exit_lock:
-#else
- if(!error && lastent)
- d_del(lastent, D_REMOVE);
-#endif
up(&dir->i_sem);
-exit_dir:
- iput(inode);
- iput(dir);
+ iput(dir);
+ dput(dentry);
exit:
return error;
}
static inline int do_unlink(const char * name)
{
- char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2];
- struct qstr last;
- struct dentry * lastent = NULL;
int error;
- struct inode * dir;
- struct inode * inode;
-
- /* HP-UX shows a strange behaviour:
- * touch y; ln -s y x; rm x/
- * this succeeds and removes the file y, not the symlink x!
- * Solaris and old Linux remove the symlink instead, and
- * old SunOS complains ENOTDIR.
- * I chose the SunOS behaviour (by not using NAM_FOLLOW_TRAILSLASH),
- * but I'm not shure whether I should.
- * The current code generally prohibits using trailing slashes with
- * non-directories if the name already exists, but not if
- * it is to be newly created.
- * Perhaps this should be further strengthened (by introducing
- * an additional flag bit indicating whether trailing slashes are
- * allowed) to get it as consistant as possible, but I don't know
- * what Posix says.
- */
- error = __namei(NAM_NO_TRAILSLASH, name, NULL, buf,
- &dir, &inode, &last, &lastent, NULL);
- if (error)
+ struct inode *dir;
+ struct dentry *dentry;
+
+ dentry = lookup_dentry(name, NULL, 0);
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
goto exit;
- if (IS_RDONLY(dir)) {
- error = -EROFS;
- goto exit_dir;
- }
- if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0)
- goto exit_dir;
+
+ dir = lock_parent(dentry);
+
+ error = -EROFS;
+ if (IS_RDONLY(dir))
+ goto exit_lock;
+
+ error = permission(dir,MAY_WRITE | MAY_EXEC);
+ if (error)
+ goto exit_lock;
+
/*
* A file cannot be removed from an append-only directory.
*/
- if (IS_APPEND(dir)) {
- error = -EPERM;
- goto exit_dir;
- }
- if (!dir->i_op || !dir->i_op->unlink) {
- error = -ENOSYS; /* was EPERM */
- goto exit_dir;
- }
+ error = -EPERM;
+ if (IS_APPEND(dir))
+ goto exit_lock;
+
+ error = -EPERM;
+ if (!dir->i_op || !dir->i_op->unlink)
+ goto exit_lock;
+
if (dir->i_sb && dir->i_sb->dq_op)
dir->i_sb->dq_op->initialize(dir, -1);
- down(&dir->i_sem);
-#if 0
- if(atomic_read(&inode->i_count) > 1) {
- extern void printpath(struct dentry *entry);
-
- printk("Fire ");
- if(lastent)
- printpath(lastent);
- printk(" i_nlink=%d i_count=%d i_ddir_count=%d i_dent_count=%d\n",
- inode->i_nlink, atomic_read(&inode->i_count),
- inode->i_ddir_count, inode->i_dent_count);
- }
-#endif
-#if 0
- if(lastent && d_isbasket(lastent)) {
- d_del(lastent, D_REMOVE);
- error = 0;
- goto exit_lock;
- }
-#endif
- atomic_inc(&dir->i_count);
- error = dir->i_op->unlink(dir, last.name, last.len);
-#ifdef CONFIG_OMIRR
- if(!error)
- omirr_print(lastent, NULL, NULL, " u %ld ", CURRENT_TIME);
-#endif
-#if 0
- if(!error && lastent)
- basket_name(dir, lastent);
+ error = dir->i_op->unlink(dir, dentry);
+
exit_lock:
-#else
- if(!error && lastent)
- d_del(lastent, D_REMOVE);
-#endif
up(&dir->i_sem);
-exit_dir:
- iput(inode);
- iput(dir);
+ iput(dir);
+ dput(dentry);
exit:
return error;
}
static inline int do_symlink(const char * oldname, const char * newname)
{
- char buf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2];
- struct qstr last;
- int error, lasterror;
- struct inode * dir;
- struct inode * inode;
-
- /* The following works on HP-UX and Solaris, by producing
- * a symlink chain:
- * rm -rf ? ; mkdir z ; ln -s z y ; ln -s y x/
- * Under old SunOS, the following occurs:
- * ln: x/: No such file or directory
- * Under old Linux, very strange things occur:
- * ln: cannot create symbolic link `x//y' to `y': No such file or directory
- * This is very probably a bug, but may be caused by the ln program
- * when checking for a directory target.
- *
- * I'm not shure whether to add NAM_NO_TRAILSLASH to inhibit trailing
- * slashes in the target generally.
- */
- error = __namei(NAM_TRANSCREATE, newname, NULL, buf,
- &dir, &inode, &last, NULL, &lasterror);
- if (error)
+ int error;
+ struct inode *dir;
+ struct dentry *dentry;
+
+ dentry = lookup_dentry(newname, NULL, 0);
+
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
goto exit;
- if(!lasterror) {
- iput(inode);
- error = -EEXIST;
- goto exit_dir;
- }
- if (!last.len) {
- error = -ENOENT;
- goto exit_dir;
- }
- if (IS_RDONLY(dir)) {
- error = -EROFS;
- goto exit_dir;
- }
- if ((error = permission(dir,MAY_WRITE | MAY_EXEC)) != 0)
- goto exit_dir;
- if (!dir->i_op || !dir->i_op->symlink) {
- error = -ENOSYS; /* was EPERM */
- goto exit_dir;
- }
- atomic_inc(&dir->i_count);
+
+ error = -EEXIST;
+ if (!(dentry->d_flag & D_NEGATIVE))
+ goto exit;
+
+ dir = lock_parent(dentry);
+
+ error = -EROFS;
+ if (IS_RDONLY(dir))
+ goto exit_lock;
+
+ error = permission(dir,MAY_WRITE | MAY_EXEC);
+ if (error)
+ goto exit_lock;
+
+ error = -EPERM;
+ if (!dir->i_op || !dir->i_op->symlink)
+ goto exit_lock;
+
if (dir->i_sb && dir->i_sb->dq_op)
dir->i_sb->dq_op->initialize(dir, -1);
- down(&dir->i_sem);
- d_del(d_lookup(dir, &last, NULL), D_REMOVE);
- error = dir->i_op->symlink(dir, last.name, last.len, oldname);
-#ifdef CONFIG_OMIRR
- if(!error)
- omirr_print(dir->i_dentry, NULL, &last,
- " s %ld %s\0", CURRENT_TIME, oldname);
-#endif
+ error = dir->i_op->symlink(dir, dentry, oldname);
+
+exit_lock:
up(&dir->i_sem);
-exit_dir:
iput(dir);
+ dput(dentry);
exit:
return error;
}
static inline int do_link(const char * oldname, const char * newname)
{
- char oldbuf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2];
- char newbuf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2];
- struct qstr oldlast;
- struct qstr newlast;
- struct dentry * oldent = NULL;
- struct inode * oldinode;
- struct inode * newinode;
- struct inode * newdir;
- int error, lasterror;
-
- error = __namei(NAM_FOLLOW_LINK|NAM_NO_TRAILSLASH,
- oldname, NULL, oldbuf,
- NULL, &oldinode, &oldlast, &oldent, NULL);
- if (error)
+ struct dentry *old_dentry, *new_dentry;
+ struct inode *dir, *inode;
+ int error;
+
+ old_dentry = lookup_dentry(oldname, NULL, 1);
+ error = PTR_ERR(old_dentry);
+ if (IS_ERR(old_dentry))
goto exit;
- error = __namei(NAM_FOLLOW_LINK|NAM_TRANSCREATE, newname, NULL, newbuf,
- &newdir, &newinode, &newlast, NULL, &lasterror);
+ new_dentry = lookup_dentry(newname, NULL, 1);
+ error = PTR_ERR(new_dentry);
+ if (IS_ERR(new_dentry))
+ goto exit_old;
+
+ dir = lock_parent(new_dentry);
+
+ error = -ENOENT;
+ if (old_dentry->d_flag & D_NEGATIVE)
+ goto exit_lock;
+
+ error = -EEXIST;
+ if (!(new_dentry->d_flag & D_NEGATIVE))
+ goto exit_lock;
+
+ error = -EROFS;
+ if (IS_RDONLY(dir))
+ goto exit_lock;
+
+ inode = old_dentry->d_inode;
+ error = -EXDEV;
+ if (dir->i_dev != inode->i_dev)
+ goto exit_lock;
+
+ error = permission(dir, MAY_WRITE | MAY_EXEC);
if (error)
- goto old_exit;
- if(!lasterror) {
- iput(newinode);
- error = -EEXIST;
- goto new_exit;
- }
- if (!newlast.len) {
- error = -EPERM;
- goto new_exit;
- }
- if (IS_RDONLY(newdir)) {
- error = -EROFS;
- goto new_exit;
- }
- if (newdir->i_dev != oldinode->i_dev) {
- error = -EXDEV;
- goto new_exit;
- }
- if ((error = permission(newdir,MAY_WRITE | MAY_EXEC)) != 0)
- goto new_exit;
+ goto exit_lock;
+
/*
* A link to an append-only or immutable file cannot be created.
*/
- if (IS_APPEND(oldinode) || IS_IMMUTABLE(oldinode)) {
- error = -EPERM;
- goto new_exit;
- }
- if (!newdir->i_op || !newdir->i_op->link) {
- error = -ENOSYS; /* was EPERM */
- goto new_exit;
- }
- atomic_inc(&oldinode->i_count);
- atomic_inc(&newdir->i_count);
- if (newdir->i_sb && newdir->i_sb->dq_op)
- newdir->i_sb->dq_op->initialize(newdir, -1);
- down(&newdir->i_sem);
- d_del(d_lookup(newdir, &newlast, NULL), D_REMOVE);
- error = newdir->i_op->link(oldinode, newdir, newlast.name, newlast.len);
-#ifdef CONFIG_OMIRR
- if(!error)
- omirr_print(oldent, newdir->i_dentry, &newlast,
- " l %ld ", CURRENT_TIME);
-#endif
- up(&newdir->i_sem);
-new_exit:
- iput(newdir);
-old_exit:
- iput(oldinode);
+ error = -EPERM;
+ if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+ goto exit_lock;
+
+ error = -EPERM;
+ if (!dir->i_op || !dir->i_op->link)
+ goto exit_lock;
+
+ if (dir->i_sb && dir->i_sb->dq_op)
+ dir->i_sb->dq_op->initialize(dir, -1);
+ error = dir->i_op->link(inode, dir, new_dentry);
+
+exit_lock:
+ up(&dir->i_sem);
+ iput(dir);
+ dput(new_dentry);
+exit_old:
+ dput(old_dentry);
exit:
return error;
}
return error;
}
+/*
+ * Whee.. Deadlock country. Happily there is only one VFS
+ * operation that does this..
+ */
+static inline void double_down(struct semaphore *s1, struct semaphore *s2)
+{
+ if ((unsigned long) s1 < (unsigned long) s2) {
+ down(s1);
+ down(s2);
+ } else if (s1 == s2) {
+ down(s1);
+ atomic_dec(&s1->count);
+ } else {
+ down(s2);
+ down(s1);
+ }
+}
+
static inline int do_rename(const char * oldname, const char * newname)
{
- char oldbuf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2];
- struct qstr oldlast;
- char newbuf[MAX_TRANS_FILELEN+MAX_TRANS_SUFFIX+2];
- struct qstr newlast;
- struct dentry * oldent = NULL;
- struct inode * olddir, * newdir;
- struct inode * oldinode, * newinode;
- int error, newlasterror;
-
- error = __namei(NAM_FOLLOW_TRAILSLASH, oldname, NULL, oldbuf,
- &olddir, &oldinode, &oldlast, &oldent, NULL);
- if (error)
+ int error;
+ struct inode * old_dir, * new_dir;
+ struct dentry * old_dentry, *new_dentry;
+
+ old_dentry = lookup_dentry(oldname, NULL, 1);
+
+ error = PTR_ERR(old_dentry);
+ if (IS_ERR(old_dentry))
goto exit;
- if ((error = permission(olddir,MAY_WRITE | MAY_EXEC)) != 0)
- goto old_exit;
- if (!oldlast.len || (oldlast.name[0] == '.' &&
- (oldlast.len == 1 || (oldlast.name[1] == '.' &&
- oldlast.len == 2)))) {
- error = -EPERM;
- goto old_exit;
- }
- /* Disallow moves of mountpoints. */
- if(oldinode->i_mount) {
- error = -EBUSY;
- goto old_exit;
- }
- error = __namei(NAM_FOLLOW_LINK|NAM_TRANSCREATE, newname, NULL, newbuf,
- &newdir, &newinode, &newlast, NULL, &newlasterror);
+ new_dentry = lookup_dentry(newname, NULL, 1);
+
+ error = PTR_ERR(new_dentry);
+ if (IS_ERR(new_dentry))
+ goto exit_old;
+
+ new_dir = get_parent(new_dentry);
+ old_dir = get_parent(old_dentry);
+
+ double_down(&new_dir->i_sem, &old_dir->i_sem);
+
+ error = -ENOENT;
+ if (old_dentry->d_flag & D_NEGATIVE)
+ goto exit_lock;
+
+ error = permission(old_dir,MAY_WRITE | MAY_EXEC);
if (error)
- goto old_exit;
- if ((error = permission(newdir,MAY_WRITE | MAY_EXEC)) != 0)
- goto new_exit;
- if (!newlast.len || (newlast.name[0] == '.' &&
- (newlast.len == 1 || (newlast.name[1] == '.' &&
- newlast.len == 2)))) {
- error = -EPERM;
- goto new_exit;
- }
- if (newdir->i_dev != olddir->i_dev) {
- error = -EXDEV;
- goto new_exit;
- }
- if (IS_RDONLY(newdir) || IS_RDONLY(olddir)) {
- error = -EROFS;
- goto new_exit;
- }
+ goto exit_lock;
+ error = permission(new_dir,MAY_WRITE | MAY_EXEC);
+ if (error)
+ goto exit_lock;
+
+ error = -EPERM;
+ if (is_reserved(new_dentry) || is_reserved(old_dentry))
+ goto exit_lock;
+
+ /* Disallow moves of mountpoints. */
+ error = -EBUSY;
+ if (old_dentry->d_covers != old_dentry)
+ goto exit_lock;
+
+ error = -EXDEV;
+ if (new_dir->i_dev != old_dir->i_dev)
+ goto exit_lock;
+
+ error = -EROFS;
+ if (IS_RDONLY(new_dir) || IS_RDONLY(old_dir))
+ goto exit_lock;
+
/*
* A file cannot be removed from an append-only directory.
*/
- if (IS_APPEND(olddir)) {
- error = -EPERM;
- goto new_exit;
- }
- if (!olddir->i_op || !olddir->i_op->rename) {
- error = -ENOSYS; /* was EPERM */
- goto new_exit;
- }
-#ifdef CONFIG_TRANS_NAMES
- /* if oldname has been translated, but newname not (and
- * has not already a suffix), take over the suffix from oldname.
- */
- if(oldlast.name == oldbuf && newlast.name != newbuf &&
- newlast.name[newlast.len-1] != '#') {
- int i = oldlast.len - 2;
- while (i > 0 && oldlast.name[i] != '#')
- i--;
- memcpy(newbuf, newlast.name, newlast.len);
- memcpy(newbuf+newlast.len, oldlast.name+i, oldlast.len - i);
- newlast.len += oldlast.len - i;
- newlast.name = newbuf;
- }
-#endif
- atomic_inc(&olddir->i_count);
- atomic_inc(&newdir->i_count);
- if (newdir->i_sb && newdir->i_sb->dq_op)
- newdir->i_sb->dq_op->initialize(newdir, -1);
- down(&newdir->i_sem);
- error = olddir->i_op->rename(olddir, oldlast.name, oldlast.len,
- newdir, newlast.name, newlast.len);
-#ifdef CONFIG_OMIRR
- if(!error)
- omirr_print(oldent, newdir->i_dentry, &newlast,
- " m %ld ", CURRENT_TIME);
-#endif
- if(!error) {
- d_del(d_lookup(newdir, &newlast, NULL), D_REMOVE);
- d_move(d_lookup(olddir, &oldlast, NULL), newdir, &newlast, NULL);
- }
- up(&newdir->i_sem);
-new_exit:
- if(!newlasterror)
- iput(newinode);
- iput(newdir);
-old_exit:
- iput(oldinode);
- iput(olddir);
+ error = -EPERM;
+ if (IS_APPEND(old_dir))
+ goto exit_lock;
+
+ error = -EPERM;
+ if (!old_dir->i_op || !old_dir->i_op->rename)
+ goto exit_lock;
+
+ if (new_dir->i_sb && new_dir->i_sb->dq_op)
+ new_dir->i_sb->dq_op->initialize(new_dir, -1);
+ error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
+
+exit_lock:
+ up(&new_dir->i_sem);
+ up(&old_dir->i_sem);
+ iput(old_dir);
+ iput(new_dir);
+ dput(new_dentry);
+exit_old:
+ dput(old_dentry);
exit:
return error;
}
static int nfs_dir_open(struct inode * inode, struct file * file);
static long nfs_dir_read(struct inode *, struct file *, char *, unsigned long);
static int nfs_readdir(struct inode *, struct file *, void *, filldir_t);
-static int nfs_lookup(struct inode *, const char *, int, struct inode **);
-static int nfs_create(struct inode *, const char *, int, int, struct inode **);
-static int nfs_mkdir(struct inode *, const char *, int, int);
-static int nfs_rmdir(struct inode *, const char *, int);
-static int nfs_unlink(struct inode *, const char *, int);
-static int nfs_symlink(struct inode *, const char *, int, const char *);
-static int nfs_link(struct inode *, struct inode *, const char *, int);
-static int nfs_mknod(struct inode *, const char *, int, int, int);
-static int nfs_rename(struct inode *, const char *, int,
- struct inode *, const char *, int);
+static int nfs_lookup(struct inode *, struct qstr *, struct inode **);
+static int nfs_create(struct inode *, struct dentry *, int);
+static int nfs_mkdir(struct inode *, struct dentry *, int);
+static int nfs_rmdir(struct inode *, struct dentry *);
+static int nfs_unlink(struct inode *, struct dentry *);
+static int nfs_symlink(struct inode *, struct dentry *, const char *);
+static int nfs_link(struct inode *, struct inode *, struct dentry *);
+static int nfs_mknod(struct inode *, struct dentry *, int, int);
+static int nfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *);
static struct file_operations nfs_dir_operations = {
NULL, /* lseek - default */
nfs_mknod, /* mknod */
nfs_rename, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
}
-/*
- * Lookup caching is a big win for performance but this is just
- * a trial to see how well it works on a small scale.
- * For example, bash does a lookup on ".." 13 times for each path
- * element when running pwd. Yes, hard to believe but true.
- * Try pwd in a filesystem mounted with noac.
- *
- * It trades a little cpu time and memory for a lot of network bandwidth.
- * Since the cache is not hashed yet, it is a good idea not to make it too
- * large because every lookup looks through the entire cache even
- * though most of them will fail.
- *
- * FIXME: The lookup cache should also cache failed lookups. This can
- * be a considerable win on diskless clients.
- */
-
-static struct nfs_lookup_cache_entry {
- kdev_t dev;
- ino_t inode;
- char filename[NFS_MAXNAMLEN + 1];
- struct nfs_fh fhandle;
- struct nfs_fattr fattr;
- unsigned long expiration_date;
-} nfs_lookup_cache[NFS_LOOKUP_CACHE_SIZE];
-
-static struct nfs_lookup_cache_entry *nfs_lookup_cache_index(struct inode *dir,
- const char *filename)
-{
- struct nfs_lookup_cache_entry *entry;
- int i;
-
- for (i = 0; i < NFS_LOOKUP_CACHE_SIZE; i++) {
- entry = nfs_lookup_cache + i;
- if (entry->dev == dir->i_dev
- && entry->inode == dir->i_ino
- && !strncmp(filename, entry->filename, NFS_MAXNAMLEN))
- return entry;
- }
- return NULL;
-}
-
-static int nfs_lookup_cache_lookup(struct inode *dir, const char *filename,
- struct nfs_fh *fhandle,
- struct nfs_fattr *fattr)
-{
- static int nfs_lookup_cache_in_use = 0;
-
- struct nfs_lookup_cache_entry *entry;
-
- dfprintk(LOOKUPCACHE, "NFS: lookup_cache_lookup(%x/%ld, %s)\n",
- dir->i_dev, dir->i_ino, filename);
- if (!nfs_lookup_cache_in_use) {
- memset(nfs_lookup_cache, 0, sizeof(nfs_lookup_cache));
- nfs_lookup_cache_in_use = 1;
- }
- if ((entry = nfs_lookup_cache_index(dir, filename))) {
- if (jiffies > entry->expiration_date) {
- entry->dev = 0;
- return 0;
- }
- *fhandle = entry->fhandle;
- *fattr = entry->fattr;
- return 1;
- }
- return 0;
-}
-
-static void nfs_lookup_cache_add(struct inode *dir, const char *filename,
- struct nfs_fh *fhandle,
- struct nfs_fattr *fattr)
-{
- static int nfs_lookup_cache_pos = 0;
- struct nfs_lookup_cache_entry *entry;
-
- dfprintk(LOOKUPCACHE, "NFS: lookup_cache_add(%x/%ld, %s\n",
- dir->i_dev, dir->i_ino, filename);
-
- /* compensate for bug in SGI NFS server */
- if (fattr->size == -1 || fattr->uid == -1 || fattr->gid == -1
- || fattr->atime.seconds == -1 || fattr->mtime.seconds == -1)
- return;
- if (!(entry = nfs_lookup_cache_index(dir, filename))) {
- entry = nfs_lookup_cache + nfs_lookup_cache_pos++;
- if (nfs_lookup_cache_pos == NFS_LOOKUP_CACHE_SIZE)
- nfs_lookup_cache_pos = 0;
- }
-
- entry->dev = dir->i_dev;
- entry->inode = dir->i_ino;
- strcpy(entry->filename, filename);
- entry->fhandle = *fhandle;
- entry->fattr = *fattr;
- entry->expiration_date = jiffies + (S_ISDIR(fattr->mode)
- ? NFS_SERVER(dir)->acdirmin : NFS_SERVER(dir)->acregmin);
-}
-
-static void nfs_lookup_cache_remove(struct inode *dir, struct inode *inode,
- const char *filename)
-{
- struct nfs_lookup_cache_entry *entry;
- kdev_t dev;
- ino_t fileid;
- int i;
-
- if (inode) {
- dev = inode->i_dev;
- fileid = inode->i_ino;
- }
- else if ((entry = nfs_lookup_cache_index(dir, filename))) {
- dev = entry->dev;
- fileid = entry->fattr.fileid;
- }
- else
- return;
-
- dfprintk(LOOKUPCACHE, "NFS: lookup_cache_remove(%x/%ld)\n",
- dev, (long)fileid);
-
- for (i = 0; i < NFS_LOOKUP_CACHE_SIZE; i++) {
- entry = nfs_lookup_cache + i;
- if (entry->dev == dev && entry->fattr.fileid == fileid)
- entry->dev = 0;
- }
-}
-
-static void nfs_lookup_cache_refresh(struct inode *file,
- struct nfs_fattr *fattr)
-{
- struct nfs_lookup_cache_entry *entry;
- kdev_t dev = file->i_dev;
- int fileid = file->i_ino;
- int i;
-
- for (i = 0; i < NFS_LOOKUP_CACHE_SIZE; i++) {
- entry = nfs_lookup_cache + i;
- if (entry->dev == dev && entry->fattr.fileid == fileid)
- entry->fattr = *fattr;
- }
-}
-
-static int nfs_lookup(struct inode *dir, const char *__name, int len,
+static int nfs_lookup(struct inode *dir, struct qstr * __name,
struct inode **result)
{
struct nfs_fh fhandle;
struct nfs_fattr fattr;
+ int len = __name->len;
char name[len > NFS_MAXNAMLEN? 1 : len+1];
int error;
dfprintk(VFS, "NFS: lookup(%x/%ld, %.*s)\n",
- dir->i_dev, dir->i_ino, len, __name);
+ dir->i_dev, dir->i_ino, len, __name->name);
*result = NULL;
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_lookup: inode is NULL or not a directory\n");
- iput(dir);
return -ENOENT;
}
- if (len > NFS_MAXNAMLEN) {
- iput(dir);
+
+ if (len > NFS_MAXNAMLEN)
return -ENAMETOOLONG;
- }
- memcpy(name,__name,len);
+
+ memcpy(name,__name->name,len);
name[len] = '\0';
- if (len == 0 || (len == 1 && name[0] == '.')) { /* cheat for "" and "." */
- *result = dir;
- return 0;
- }
- if ((NFS_SERVER(dir)->flags & NFS_MOUNT_NOAC)
- || !nfs_lookup_cache_lookup(dir, name, &fhandle, &fattr)) {
- if ((error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dir),
- name, &fhandle, &fattr))) {
- iput(dir);
- return error;
- }
- nfs_lookup_cache_add(dir, name, &fhandle, &fattr);
- }
- if (!(*result = nfs_fhget(dir->i_sb, &fhandle, &fattr))) {
- iput(dir);
+
+ error = nfs_proc_lookup(NFS_SERVER(dir), NFS_FH(dir),
+ name, &fhandle, &fattr);
+ if (error)
+ return error;
+
+ if (!(*result = nfs_fhget(dir->i_sb, &fhandle, &fattr)))
return -EACCES;
- }
- iput(dir);
+
return 0;
}
-static int nfs_create(struct inode *dir, const char *name, int len, int mode,
- struct inode **result)
+static int nfs_create(struct inode *dir, struct dentry * dentry, int mode)
{
struct nfs_sattr sattr;
struct nfs_fattr fattr;
struct nfs_fh fhandle;
+ struct inode *inode;
int error;
dfprintk(VFS, "NFS: create(%x/%ld, %s\n",
- dir->i_dev, dir->i_ino, name);
+ dir->i_dev, dir->i_ino, dentry->d_name.name);
- *result = NULL;
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_create: inode is NULL or not a directory\n");
- iput(dir);
return -ENOENT;
}
- if (len > NFS_MAXNAMLEN) {
- iput(dir);
+
+ if (dentry->d_name.len > NFS_MAXNAMLEN)
return -ENAMETOOLONG;
- }
+
sattr.mode = mode;
sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
- if ((error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir),
- name, &sattr, &fhandle, &fattr))) {
- iput(dir);
+ error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir),
+ dentry->d_name.name, &sattr, &fhandle, &fattr);
+
+ if (error)
return error;
- }
- if (!(*result = nfs_fhget(dir->i_sb, &fhandle, &fattr))) {
- iput(dir);
+
+ inode = nfs_fhget(dir->i_sb, &fhandle, &fattr);
+ if (!inode)
return -EACCES;
- }
- nfs_lookup_cache_add(dir, name, &fhandle, &fattr);
- nfs_invalidate_dircache(dir);
- iput(dir);
+
+ d_instantiate(dentry, inode, 0);
return 0;
}
-static int nfs_mknod(struct inode *dir, const char *name, int len,
- int mode, int rdev)
+static int nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
{
struct nfs_sattr sattr;
struct nfs_fattr fattr;
struct nfs_fh fhandle;
+ struct inode *inode;
int error;
dfprintk(VFS, "NFS: mknod(%x/%ld, %s\n",
- dir->i_dev, dir->i_ino, name);
+ dir->i_dev, dir->i_ino, dentry->d_name.name);
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_mknod: inode is NULL or not a directory\n");
- iput(dir);
return -ENOENT;
}
- if (len > NFS_MAXNAMLEN) {
- iput(dir);
+
+ if (dentry->d_name.len > NFS_MAXNAMLEN)
return -ENAMETOOLONG;
- }
+
sattr.mode = mode;
- sattr.uid = sattr.gid = (unsigned) -1;
+ sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
if (S_ISCHR(mode) || S_ISBLK(mode))
sattr.size = rdev; /* get out your barf bag */
- else
- sattr.size = (unsigned) -1;
+
sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
error = nfs_proc_create(NFS_SERVER(dir), NFS_FH(dir),
- name, &sattr, &fhandle, &fattr);
- if (!error)
- nfs_lookup_cache_add(dir, name, &fhandle, &fattr);
- nfs_invalidate_dircache(dir);
- iput(dir);
- return error;
+ dentry->d_name.name, &sattr, &fhandle, &fattr);
+
+ if (error)
+ return error;
+
+ inode = nfs_fhget(dir->i_sb, &fhandle, &fattr);
+ if (!inode)
+ return -EACCES;
+
+ d_instantiate(dentry, inode, 0);
+ return 0;
}
-static int nfs_mkdir(struct inode *dir, const char *name, int len, int mode)
+static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
{
struct nfs_sattr sattr;
struct nfs_fattr fattr;
struct nfs_fh fhandle;
+ struct inode * inode;
int error;
dfprintk(VFS, "NFS: mkdir(%x/%ld, %s\n",
- dir->i_dev, dir->i_ino, name);
+ dir->i_dev, dir->i_ino, dentry->d_name.name);
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_mkdir: inode is NULL or not a directory\n");
- iput(dir);
return -ENOENT;
}
- if (len > NFS_MAXNAMLEN) {
- iput(dir);
+
+ if (dentry->d_name.len > NFS_MAXNAMLEN)
return -ENAMETOOLONG;
- }
+
sattr.mode = mode;
sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
+
error = nfs_proc_mkdir(NFS_SERVER(dir), NFS_FH(dir),
- name, &sattr, &fhandle, &fattr);
- if (!error)
- nfs_lookup_cache_add(dir, name, &fhandle, &fattr);
- nfs_invalidate_dircache(dir);
- iput(dir);
- return error;
+ dentry->d_name.name, &sattr, &fhandle, &fattr);
+
+ if (error)
+ return error;
+
+ inode = nfs_fhget(dir->i_sb, &fhandle, &fattr);
+ if (!inode)
+ return -EACCES;
+
+ d_instantiate(dentry, inode, D_DIR);
+ return 0;
}
-static int nfs_rmdir(struct inode *dir, const char *name, int len)
+static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
{
int error;
dfprintk(VFS, "NFS: rmdir(%x/%ld, %s\n",
- dir->i_dev, dir->i_ino, name);
+ dir->i_dev, dir->i_ino, dentry->d_name.name);
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_rmdir: inode is NULL or not a directory\n");
- iput(dir);
return -ENOENT;
}
- if (len > NFS_MAXNAMLEN) {
- iput(dir);
- return -ENAMETOOLONG;
- }
- error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dir), name);
- if (!error)
- nfs_lookup_cache_remove(dir, NULL, name);
- nfs_invalidate_dircache(dir);
- iput(dir);
- return error;
-}
-static int nfs_sillyrename(struct inode *dir, const char *name, int len)
-{
- struct inode *inode;
- char silly[16];
- int slen, ret;
-
- atomic_inc(&dir->i_count);
- if (nfs_lookup(dir, name, len, &inode) < 0)
- return -EIO; /* arbitrary */
-
- if (atomic_read(&inode->i_count) == 1) {
- iput(inode);
- return -EIO;
- }
- if (NFS_RENAMED_DIR(inode)) {
- iput(NFS_RENAMED_DIR(inode));
- NFS_RENAMED_DIR(inode) = NULL;
- iput(inode);
- return -EIO;
- }
-
- slen = sprintf(silly, ".nfs%ld", inode->i_ino);
- if (len == slen && !strncmp(name, silly, len)) {
- iput(inode);
- return -EIO; /* DWIM */
- }
+ if (dentry->d_name.len > NFS_MAXNAMLEN)
+ return -ENAMETOOLONG;
- dfprintk(VFS, "NFS: sillyrename(%x/%ld, %s)\n",
- dir->i_dev, dir->i_ino, name);
+ error = nfs_proc_rmdir(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name);
+ if (error)
+ return error;
- ret = nfs_proc_rename(NFS_SERVER(dir), NFS_FH(dir), name,
- NFS_FH(dir), silly);
- if (ret >= 0) {
- nfs_lookup_cache_remove(dir, NULL, name);
- nfs_lookup_cache_remove(dir, NULL, silly);
- NFS_RENAMED_DIR(inode) = dir;
- atomic_inc(&dir->i_count);
- }
- nfs_invalidate_dircache(dir);
- iput(inode);
- return ret;
+ d_delete(dentry);
+ return 0;
}
/*
- * When releasing the inode, finally remove any unlinked but open files.
- * Note that we have to clear the set of pending signals temporarily;
- * otherwise the RPC call will fail.
+ * We should do silly-rename here, but I'm too lazy to fix
+ * up the directory entry implications of it..
*/
-void nfs_sillyrename_cleanup(struct inode *inode)
-{
- unsigned long oldsig;
- struct inode *dir = NFS_RENAMED_DIR(inode);
- char silly[14];
- int error, slen;
-
- dfprintk(VFS, "NFS: sillyrename cleanup(%x/%ld)\n",
- inode->i_dev, inode->i_ino);
-
- oldsig = current->signal;
- current->signal = 0;
-
- slen = sprintf(silly, ".nfs%ld", inode->i_ino);
- error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), silly);
- if (error < 0)
- printk("NFS: silly_rename cleanup failed (err %d)\n", -error);
-
- nfs_lookup_cache_remove(dir, NULL, silly);
- nfs_invalidate_dircache(dir);
- NFS_RENAMED_DIR(inode) = NULL;
- iput(dir);
-
- current->signal |= oldsig;
-}
-
-static int nfs_unlink(struct inode *dir, const char *name, int len)
+static int nfs_unlink(struct inode *dir, struct dentry *dentry)
{
int error;
dfprintk(VFS, "NFS: unlink(%x/%ld, %s)\n",
- dir->i_dev, dir->i_ino, name);
+ dir->i_dev, dir->i_ino, dentry->d_name.name);
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_unlink: inode is NULL or not a directory\n");
- iput(dir);
return -ENOENT;
}
- if (len > NFS_MAXNAMLEN) {
- iput(dir);
+
+ if (dentry->d_name.len > NFS_MAXNAMLEN)
return -ENAMETOOLONG;
- }
- if ((error = nfs_sillyrename(dir, name, len)) < 0) {
- error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), name);
- if (!error)
- nfs_lookup_cache_remove(dir, NULL, name);
- }
- nfs_invalidate_dircache(dir);
- iput(dir);
- return error;
+
+ error = nfs_proc_remove(NFS_SERVER(dir), NFS_FH(dir), dentry->d_name.name);
+ if (error)
+ return error;
+
+ d_delete(dentry);
+ return 0;
}
-static int nfs_symlink(struct inode *dir, const char *name, int len,
- const char *symname)
+static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
{
struct nfs_sattr sattr;
+ struct nfs_fattr fattr;
+ struct nfs_fh fhandle;
+ struct inode * inode;
int error;
dfprintk(VFS, "NFS: symlink(%x/%ld, %s, %s)\n",
- dir->i_dev, dir->i_ino, name, symname);
+ dir->i_dev, dir->i_ino, dentry->d_name.name, symname);
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_symlink: inode is NULL or not a directory\n");
- iput(dir);
return -ENOENT;
}
- if (len > NFS_MAXNAMLEN) {
- iput(dir);
+
+ if (dentry->d_name.len > NFS_MAXNAMLEN)
return -ENAMETOOLONG;
- }
- if (strlen(symname) > NFS_MAXPATHLEN) {
- iput(dir);
+
+ if (strlen(symname) > NFS_MAXPATHLEN)
return -ENAMETOOLONG;
- }
+
sattr.mode = S_IFLNK | S_IRWXUGO; /* SunOS 4.1.2 crashes without this! */
sattr.uid = sattr.gid = sattr.size = (unsigned) -1;
sattr.atime.seconds = sattr.mtime.seconds = (unsigned) -1;
+
error = nfs_proc_symlink(NFS_SERVER(dir), NFS_FH(dir),
- name, symname, &sattr);
- nfs_invalidate_dircache(dir);
- iput(dir);
- return error;
+ dentry->d_name.name, symname, &sattr);
+
+ if (error)
+ return error;
+
+ inode = nfs_fhget(dir->i_sb, &fhandle, &fattr);
+ if (!inode)
+ return -EACCES;
+
+ d_instantiate(dentry, inode, 0);
+ return 0;
}
-static int nfs_link(struct inode *oldinode, struct inode *dir,
- const char *name, int len)
+static int nfs_link(struct inode *inode, struct inode *dir, struct dentry *dentry)
{
int error;
dfprintk(VFS, "NFS: link(%x/%ld -> %x/%ld, %s)\n",
- oldinode->i_dev, oldinode->i_ino,
- dir->i_dev, dir->i_ino, name);
+ inode->i_dev, inode->i_ino,
+ dir->i_dev, dir->i_ino, dentry->d_name.name);
- if (!oldinode) {
- printk("nfs_link: old inode is NULL\n");
- iput(oldinode);
- iput(dir);
- return -ENOENT;
- }
if (!dir || !S_ISDIR(dir->i_mode)) {
printk("nfs_link: dir is NULL or not a directory\n");
- iput(oldinode);
- iput(dir);
return -ENOENT;
}
- if (len > NFS_MAXNAMLEN) {
- iput(oldinode);
- iput(dir);
+
+ if (dentry->d_name.len > NFS_MAXNAMLEN)
return -ENAMETOOLONG;
- }
- error = nfs_proc_link(NFS_SERVER(oldinode), NFS_FH(oldinode),
- NFS_FH(dir), name);
- if (!error) {
- nfs_lookup_cache_remove(dir, oldinode, NULL);
- NFS_READTIME(oldinode) = 0; /* force getattr */
- }
- nfs_invalidate_dircache(dir);
- iput(oldinode);
- iput(dir);
- return error;
+
+ error = nfs_proc_link(NFS_SERVER(inode), NFS_FH(inode),
+ NFS_FH(dir), dentry->d_name.name);
+
+ if (error)
+ return error;
+
+ atomic_inc(&inode->i_count);
+ d_instantiate(dentry, inode, 0);
+ return 0;
}
/*
* rename the old file using the silly_rename stuff. This way, the original
* file in old_dir will go away when the last process iput()s the inode.
*/
-static int nfs_rename(struct inode *old_dir, const char *old_name, int old_len,
- struct inode *new_dir, const char *new_name, int new_len)
+static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
{
int error;
dfprintk(VFS, "NFS: rename(%x/%ld, %s -> %x/%ld, %s)\n",
- old_dir->i_dev, old_dir->i_ino, old_name,
- new_dir->i_dev, new_dir->i_ino, new_name);
+ old_dir->i_dev, old_dir->i_ino, old_dentry->d_name.name,
+ new_dir->i_dev, new_dir->i_ino, new_dentry->d_name.name);
if (!old_dir || !S_ISDIR(old_dir->i_mode)) {
printk("nfs_rename: old inode is NULL or not a directory\n");
- iput(old_dir);
- iput(new_dir);
return -ENOENT;
}
+
if (!new_dir || !S_ISDIR(new_dir->i_mode)) {
printk("nfs_rename: new inode is NULL or not a directory\n");
- iput(old_dir);
- iput(new_dir);
return -ENOENT;
}
- if (old_len > NFS_MAXNAMLEN || new_len > NFS_MAXNAMLEN) {
- iput(old_dir);
- iput(new_dir);
+
+ if (old_dentry->d_name.len > NFS_MAXNAMLEN || new_dentry->d_name.len > NFS_MAXNAMLEN)
return -ENAMETOOLONG;
- }
error = nfs_proc_rename(NFS_SERVER(old_dir),
- NFS_FH(old_dir), old_name,
- NFS_FH(new_dir), new_name);
- if (!error) {
- nfs_lookup_cache_remove(old_dir, NULL, old_name);
- nfs_lookup_cache_remove(new_dir, NULL, new_name);
- }
- nfs_invalidate_dircache(old_dir);
- nfs_invalidate_dircache(new_dir);
- iput(old_dir);
- iput(new_dir);
- return error;
+ NFS_FH(old_dir), old_dentry->d_name.name,
+ NFS_FH(new_dir), new_dentry->d_name.name);
+
+ if (error)
+ return error;
+
+ /* Update the dcache */
+ d_move(old_dentry, new_dentry->d_parent, &new_dentry->d_name);
+ d_delete(new_dentry);
+ return 0;
}
/*
init_fifo(inode);
} else
inode->i_op = NULL;
- nfs_lookup_cache_refresh(inode, fattr);
}
-
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
nfs_readpage, /* readpage */
nfs_writepage, /* writepage */
NULL, /* bmap */
{
dprintk("NFS: put_inode(%x/%ld)\n", inode->i_dev, inode->i_ino);
- if (NFS_RENAMED_DIR(inode))
- nfs_sillyrename_cleanup(inode);
if (inode->i_pipe)
clear_inode(inode);
}
/* Unlock super block and try to get root fh attributes */
unlock_super(sb);
- if ((sb->s_mounted = nfs_fhget(sb, &data->root, NULL)) != NULL) {
+ sb->s_root = d_alloc_root(nfs_fhget(sb, &data->root, NULL), NULL);
+ if (sb->s_root != NULL) {
/* We're airborne */
if (!(server->flags & NFS_MOUNT_NONLM))
lockd_up();
#include <asm/uaccess.h>
static int nfs_readlink(struct inode *, char *, int);
+static struct dentry *nfs_follow_link(struct inode *, struct dentry *);
/*
* symlinks can't do much...
NULL, /* mknod */
NULL, /* rename */
nfs_readlink, /* readlink */
+ nfs_follow_link, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
buflen = NFS_MAXPATHLEN;
error = nfs_proc_readlink(NFS_SERVER(inode), NFS_FH(inode), &mem,
&res, &len, buflen);
- iput(inode);
if (! error) {
copy_to_user(buffer, res, len);
put_user('\0', buffer + len);
kfree(mem);
return error;
}
+
+static struct dentry * nfs_follow_link(struct inode * inode, struct dentry *base)
+{
+ int error;
+ unsigned int len;
+ char *res;
+ void *mem;
+ char *path;
+
+ dfprintk(VFS, "nfs: follow_link(%x/%ld)\n", inode->i_dev, inode->i_ino);
+
+ error = nfs_proc_readlink(NFS_SERVER(inode), NFS_FH(inode), &mem,
+ &res, &len, NFS_MAXPATHLEN);
+
+ if (error) {
+ dput(base);
+ kfree(mem);
+ return ERR_PTR(error);
+ }
+ path = kmalloc(len + 1, GFP_KERNEL);
+ if (!path) {
+ dput(base);
+ kfree(mem);
+ return ERR_PTR(-ENOMEM);
+ }
+ memcpy(path, res, len);
+ path[len] = 0;
+ kfree(mem);
+
+ base = lookup_dentry(path, base, 1);
+ kfree(path);
+ return base;
+}
error = verify_area(VERIFY_WRITE, buf, sizeof(struct statfs));
if (error)
goto out;
- error = namei(NAM_FOLLOW_LINK, path, &inode);
+ error = namei(path, &inode);
if (error)
goto out;
error = -ENOSYS;
int error;
lock_kernel();
- error = namei(NAM_FOLLOW_LINK, path, &inode);
+ error = namei(path, &inode);
if (error)
goto out;
lock_kernel();
/* Hmm, should I always follow symlinks or not ? */
- error = namei(NAM_FOLLOW_LINK, filename, &inode);
+ error = namei(filename, &inode);
if (error)
goto out;
error = -EROFS;
struct iattr newattrs;
lock_kernel();
- error = namei(NAM_FOLLOW_LINK, filename, &inode);
+ error = namei(filename, &inode);
if (error)
goto out;
error = -EROFS;
old_fsgid = current->fsgid;
current->fsuid = current->uid;
current->fsgid = current->gid;
- res = namei(NAM_FOLLOW_LINK, filename, &inode);
+ res = namei(filename, &inode);
if (!res) {
res = permission(inode, mode);
iput(inode);
asmlinkage int sys_chdir(const char * filename)
{
- struct inode * inode;
- struct inode * tmpi;
int error;
+ struct inode *inode;
+ struct dentry *dentry, *tmp;
lock_kernel();
- error = namei(NAM_FOLLOW_LINK, filename, &inode);
- if (error)
+
+ dentry = lookup_dentry(filename, NULL, 1);
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
goto out;
+
+ error = -ENOENT;
+ if (dentry->d_flag & D_NEGATIVE)
+ goto dput_and_out;
+
error = -ENOTDIR;
+ inode = dentry->d_inode;
if (!S_ISDIR(inode->i_mode))
- goto iput_and_out;
- if ((error = permission(inode,MAY_EXEC)) != 0)
- goto iput_and_out;
+ goto dput_and_out;
- /* exchange inodes */
- tmpi = current->fs->pwd; current->fs->pwd = inode; inode = tmpi;
-iput_and_out:
- iput(inode);
+ error = permission(inode,MAY_EXEC);
+ if (error)
+ goto dput_and_out;
+
+ /* exchange dentries */
+ tmp = current->fs->pwd;
+ current->fs->pwd = dentry;
+ dentry = tmp;
+
+dput_and_out:
+ dput(dentry);
out:
unlock_kernel();
return error;
asmlinkage int sys_fchdir(unsigned int fd)
{
- struct inode * inode;
- struct file * file;
- int error = -EBADF;
+ struct file *file;
+ struct inode *inode;
+ struct dentry *dentry, *tmp;
+ int error;
lock_kernel();
+
+ error = -EBADF;
if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
goto out;
+
error = -ENOENT;
if (!(inode = file->f_inode))
goto out;
+
error = -ENOTDIR;
if (!S_ISDIR(inode->i_mode))
goto out;
- if ((error = permission(inode,MAY_EXEC)) != 0)
+
+ error = permission(inode,MAY_EXEC);
+ if (error)
goto out;
- iput(current->fs->pwd);
- current->fs->pwd = inode;
- atomic_inc(&inode->i_count);
+
+ dentry = dget(inode->i_dentry);
+ tmp = current->fs->pwd;
+ current->fs->pwd = dentry;
+ dput(tmp);
out:
unlock_kernel();
return error;
asmlinkage int sys_chroot(const char * filename)
{
- struct inode * inode;
- struct inode * tmpi;
int error;
+ struct inode *inode;
+ struct dentry *dentry, *tmp;
lock_kernel();
- error = namei(NAM_FOLLOW_LINK, filename, &inode);
- if (error)
+
+ dentry = lookup_dentry(filename, NULL, 1);
+ error = PTR_ERR(dentry);
+ if (IS_ERR(dentry))
goto out;
+
+ error = -ENOENT;
+ if (dentry->d_flag & D_NEGATIVE)
+ goto dput_and_out;
+
error = -ENOTDIR;
+ inode = dentry->d_inode;
if (!S_ISDIR(inode->i_mode))
- goto iput_and_out;
+ goto dput_and_out;
+
+ error = permission(inode,MAY_EXEC);
+ if (error)
+ goto dput_and_out;
+
error = -EPERM;
if (!fsuser())
- goto iput_and_out;
- tmpi = current->fs->root; current->fs->root = inode; inode = tmpi;
- error = 0;
-iput_and_out:
- iput(inode);
+ goto dput_and_out;
+
+ /* exchange dentries */
+ tmp = current->fs->root;
+ current->fs->root = dentry;
+ dentry = tmp;
+
+dput_and_out:
+ dput(dentry);
out:
unlock_kernel();
return error;
* because permissions on symlinks now can never be changed,
* but on the other hand they are never needed.
*/
- error = namei(NAM_FOLLOW_LINK, filename, &inode);
+ error = namei(filename, &inode);
if (error)
goto out;
error = -EROFS;
struct iattr newattrs;
lock_kernel();
- error = namei(NAM_FOLLOW_TRAILSLASH, filename, &inode);
+ error = namei(filename, &inode);
if (error)
goto out;
error = -EROFS;
error = 0;
}
done:
- iput(dir);
return error;
}
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
#include <linux/stat.h>
static int proc_readfd(struct inode *, struct file *, void *, filldir_t);
-static int proc_lookupfd(struct inode *,const char *,int,struct inode **);
+static int proc_lookupfd(struct inode *,struct qstr *,struct inode **);
static struct file_operations proc_fd_operations = {
NULL, /* lseek - default */
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL /* permission */
};
-static int proc_lookupfd(struct inode * dir, const char * name, int len,
- struct inode ** result)
+static int proc_lookupfd(struct inode * dir, struct qstr *str, struct inode ** result)
{
unsigned int ino, pid, fd, c;
struct task_struct * p;
struct super_block * sb;
+ const char *name;
+ int len;
*result = NULL;
ino = dir->i_ino;
if (!dir)
return -ENOENT;
sb = dir->i_sb;
- if (!pid || ino != PROC_PID_FD || !S_ISDIR(dir->i_mode)) {
- iput(dir);
+ if (!pid || ino != PROC_PID_FD || !S_ISDIR(dir->i_mode))
return -ENOENT;
- }
- if (!len || (name[0] == '.' && (len == 1 ||
- (name[1] == '.' && len == 2)))) {
- if (len < 2) {
- *result = dir;
- return 0;
- }
- if (!(*result = proc_get_inode(sb, (pid << 16)+PROC_PID_INO, &proc_pid))) {
- iput(dir);
- return -ENOENT;
- }
- iput(dir);
- return 0;
- }
- iput(dir);
+
fd = 0;
+ len = str->len;
+ name = str->name;
while (len-- > 0) {
c = *name - '0';
name++;
static long long proc_file_lseek(struct inode * inode, struct file * file,
long long offset, int orig);
+int proc_match(int len, const char *name,struct proc_dir_entry * de)
+{
+ if (!de || !de->low_ino)
+ return 0;
+ if (de->namelen != len)
+ return 0;
+ return !memcmp(name, de->name, len);
+}
+
static struct file_operations proc_file_operations = {
proc_file_lseek, /* lseek */
proc_file_read, /* read */
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
s->s_magic = PROC_SUPER_MAGIC;
s->s_op = &proc_sops;
unlock_super(s);
- if (!(s->s_mounted = proc_get_inode(s, PROC_ROOT_INO, &proc_root))) {
+ s->s_root = d_alloc_root(proc_get_inode(s, PROC_ROOT_INO, &proc_root), NULL);
+ if (!s->s_root) {
s->s_dev = 0;
printk("get root inode failed\n");
return NULL;
}
- parse_options(data, &s->s_mounted->i_uid, &s->s_mounted->i_gid);
+ parse_options(data, &s->s_root->d_inode->i_uid, &s->s_root->d_inode->i_gid);
return s;
}
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
#include <linux/dalloc.h>
static int proc_readlink(struct inode *, char *, int);
+static struct dentry * proc_follow_link(struct inode *, struct dentry *);
/*
* PLAN9_SEMANTICS won't work any more: it used an ugly hack that broke
NULL, /* mknod */
NULL, /* rename */
proc_readlink, /* readlink */
+ proc_follow_link, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL /* permission */
};
-/* [Feb-1997 T. Schoebel-Theuer] This is no longer called from the
- * VFS, but only from proc_readlink(). All the functionality
- * should the moved there (without using temporary inodes any more)
- * and then it could be eliminated.
- */
-static int proc_follow_link(struct inode * dir, struct inode * inode,
- int flag, int mode, struct inode ** res_inode)
+static struct dentry * proc_follow_link(struct inode *inode, struct dentry *base)
{
- unsigned int pid, ino;
- struct task_struct * p;
- struct inode * new_inode;
+ struct task_struct *p;
+ struct dentry * result;
+ int ino, pid;
int error;
- *res_inode = NULL;
- if (dir)
- iput(dir);
- if (!inode)
- return -ENOENT;
- if ((error = permission(inode, MAY_EXEC)) != 0){
- iput(inode);
- return error;
- }
+ /* We don't need a base pointer in the /proc filesystem */
+ dput(base);
+
+ error = permission(inode, MAY_EXEC);
+ result = ERR_PTR(error);
+ if (error)
+ return result;
+
ino = inode->i_ino;
pid = ino >> 16;
ino &= 0x0000ffff;
p = find_task_by_pid(pid);
- if (!p) {
- iput(inode);
- return -ENOENT;
- }
- new_inode = NULL;
+ result = ERR_PTR(-ENOENT);
+ if (!p)
+ return result;
+
switch (ino) {
case PROC_PID_CWD:
- if (!p->fs)
+ if (!p->fs || !p->fs->pwd)
break;
- new_inode = p->fs->pwd;
+ result = dget(p->fs->pwd);
break;
+
case PROC_PID_ROOT:
- if (!p->fs)
+ if (!p->fs || !p->fs->root)
break;
- new_inode = p->fs->root;
+ result = dget(p->fs->root);
break;
+
case PROC_PID_EXE: {
struct vm_area_struct * vma;
if (!p->mm)
break;
vma = p->mm->mmap;
while (vma) {
- if (vma->vm_flags & VM_EXECUTABLE) {
- new_inode = vma->vm_inode;
- break;
- }
+ if (vma->vm_flags & VM_EXECUTABLE)
+ return dget(vma->vm_inode->i_dentry);
+
vma = vma->vm_next;
}
break;
if (!p->files)
break;
ino &= 0xff;
- if (ino < NR_OPEN && p->files->fd[ino]) {
- new_inode = p->files->fd[ino]->f_inode;
- }
+ if (ino >= NR_OPEN)
+ break;
+ if (!p->files->fd[ino])
+ break;
+ if (!p->files->fd[ino]->f_inode)
+ break;
+ result = dget(p->files->fd[ino]->f_inode->i_dentry);
break;
}
}
- iput(inode);
- if (!new_inode)
- return -ENOENT;
- *res_inode = new_inode;
- atomic_inc(&new_inode->i_count);
- return 0;
+ return result;
}
static int proc_readlink(struct inode * inode, char * buffer, int buflen)
{
- int error = proc_follow_link(NULL, inode, 0, 0, &inode);
+ int error;
+ struct dentry * dentry = proc_follow_link(inode, NULL);
- if (error)
- return error;
- if (!inode)
- return -EIO;
-
- /* This will return *one* of the alias names (which is not quite
- * correct). I have to rethink the problem, so this is only a
- * quick hack...
- */
- if(inode->i_dentry) {
- char * tmp = (char*)__get_free_page(GFP_KERNEL);
- int len = d_path(inode->i_dentry, current->fs->root, tmp);
- int min = buflen<PAGE_SIZE ? buflen : PAGE_SIZE;
- if(len <= min)
- min = len+1;
- copy_to_user(buffer, tmp, min);
- free_page((unsigned long)tmp);
- error = len;
- } else {
- error= -ENOENT;
+ error = PTR_ERR(dentry);
+ if (!IS_ERR(dentry)) {
+ error = -ENOENT;
+ if (dentry) {
+ char * tmp = (char*)__get_free_page(GFP_KERNEL);
+ int len = d_path(dentry, current->fs->root, tmp);
+ int min = buflen<PAGE_SIZE ? buflen : PAGE_SIZE;
+ if(len <= min)
+ min = len+1;
+ dput(dentry);
+ copy_to_user(buffer, tmp, min);
+ free_page((unsigned long)tmp);
+ error = len;
+ }
}
- iput(inode);
return error;
}
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
#define FIRST_PROCESS_ENTRY 256
static int proc_root_readdir(struct inode *, struct file *, void *, filldir_t);
-static int proc_root_lookup(struct inode *,const char *,int,struct inode **);
+static int proc_root_lookup(struct inode *,struct qstr *,struct inode **);
static unsigned char proc_alloc_map[PROC_NDYNAMIC / 8] = {0};
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
#if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE)
static int (*proc_openprom_defreaddir_ptr)(struct inode *, struct file *, void *, filldir_t);
-static int (*proc_openprom_deflookup_ptr)(struct inode *, const char *, int, struct inode **);
+static int (*proc_openprom_deflookup_ptr)(struct inode *, struct qstr *, struct inode **);
void (*proc_openprom_use)(struct inode *, int) = 0;
static struct openpromfs_dev *proc_openprom_devices = NULL;
static ino_t proc_openpromdev_ino = PROC_OPENPROMD_FIRST;
struct inode_operations *
proc_openprom_register(int (*readdir)(struct inode *, struct file *, void *, filldir_t),
- int (*lookup)(struct inode *, const char *, int, struct inode **),
+ int (*lookup)(struct inode *, struct qstr *, struct inode **),
void (*use)(struct inode *, int),
struct openpromfs_dev ***devices)
{
}
static int
-proc_openprom_deflookup(struct inode * dir,const char * name, int len,
- struct inode ** result)
+proc_openprom_deflookup(struct inode * dir, struct qstr *str, struct inode ** result)
{
request_module("openpromfs");
if (proc_openprom_inode_operations.lookup !=
proc_openprom_deflookup)
return proc_openprom_inode_operations.lookup
- (dir, name, len, result);
+ (dir, str, result);
iput(dir);
return -ENOENT;
}
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
int len;
char tmp[30];
- iput(inode);
len = sprintf(tmp, "%d", current->pid);
if (buflen < len)
len = buflen;
return len;
}
+static struct dentry * proc_self_follow_link(struct inode *inode, struct dentry *base)
+{
+ int len;
+ char tmp[30];
+
+ len = sprintf(tmp, "%d", current->pid);
+ return lookup_dentry(tmp, base, 1);
+}
+
static struct inode_operations proc_self_inode_operations = {
NULL, /* no file-ops */
NULL, /* create */
NULL, /* mknod */
NULL, /* rename */
proc_self_readlink, /* readlink */
+ proc_self_follow_link, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
}
-int proc_match(int len,const char * name,struct proc_dir_entry * de)
-{
- if (!de || !de->low_ino)
- return 0;
- /* "" means "." ---> so paths like "/usr/lib//libc.a" work */
- if (!len && (de->name[0]=='.') && (de->name[1]=='\0'))
- return 1;
- if (de->namelen != len)
- return 0;
- return !memcmp(name, de->name, len);
-}
-
-int proc_lookup(struct inode * dir,const char * name, int len,
- struct inode ** result)
+int proc_lookup(struct inode * dir, struct qstr * str, struct inode ** result)
{
struct proc_dir_entry * de;
- int ino;
*result = NULL;
- if (!dir || !S_ISDIR(dir->i_mode)) {
- iput(dir);
+ if (!dir || !S_ISDIR(dir->i_mode))
return -ENOTDIR;
- }
de = (struct proc_dir_entry *) dir->u.generic_ip;
- if (!de) {
- iput(dir);
+ if (!de)
return -EINVAL;
- }
- /* Either remove this as soon as possible due to security problems,
- * or uncomment the root-only usage.
- */
-
- /* Allow generic inode lookups everywhere.
- * No other name in /proc must begin with a '['.
- */
- if(/*!current->uid &&*/ name[0] == '[')
- return proc_arbitrary_lookup(dir,name,len,result);
-
- /* Special case "." and "..": they aren't on the directory list */
- *result = dir;
- if (!len)
- return 0;
- if (name[0] == '.') {
- if (len == 1)
- return 0;
- if (name[1] == '.' && len == 2) {
- struct inode * inode;
- inode = proc_get_inode(dir->i_sb, de->parent->low_ino, de->parent);
- iput(dir);
- if (!inode)
+ *result = NULL;
+ for (de = de->subdir; de ; de = de->next) {
+ if (!de || !de->low_ino)
+ continue;
+ if (de->namelen != str->len)
+ continue;
+ if (!memcmp(str->name, de->name, str->len)) {
+ int ino = de->low_ino | (dir->i_ino & ~(0xffff));
+ if (!(*result = proc_get_inode(dir->i_sb, ino, de)))
return -EINVAL;
- *result = inode;
return 0;
}
}
-
- *result = NULL;
- for (de = de->subdir; de ; de = de->next) {
- if (proc_match(len, name, de))
- break;
- }
- if (!de) {
- iput(dir);
- return -ENOENT;
- }
-
- ino = de->low_ino | (dir->i_ino & ~(0xffff));
-
- if (!(*result = proc_get_inode(dir->i_sb, ino, de))) {
- iput(dir);
- return -EINVAL;
- }
- iput(dir);
- return 0;
+ return -ENOENT;
}
-static int proc_root_lookup(struct inode * dir,const char * name, int len,
- struct inode ** result)
+static int proc_root_lookup(struct inode * dir,struct qstr *str, struct inode ** result)
{
unsigned int pid, c;
int ino, retval;
struct task_struct *p;
+ const char *name;
+ int len;
atomic_inc(&dir->i_count);
read_unlock(&tasklist_lock);
}
- retval = proc_lookup(dir, name, len, result);
- if (retval != -ENOENT) {
- iput(dir);
+ retval = proc_lookup(dir, str, result);
+ if (retval != -ENOENT)
return retval;
- }
pid = 0;
+ name = str->name;
+ len = str->len;
while (len-- > 0) {
c = *name - '0';
name++;
}
}
p = find_task_by_pid(pid);
- if (!pid || !p) {
- iput(dir);
+ if (!pid || !p)
return -ENOENT;
- }
+
ino = (pid << 16) + PROC_PID_INO;
- if (!(*result = proc_get_inode(dir->i_sb, ino, &proc_pid))) {
- iput(dir);
+ if (!(*result = proc_get_inode(dir->i_sb, ino, &proc_pid)))
return -EINVAL;
- }
- iput(dir);
return 0;
}
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
/*
- * fs/readdir.c
+ * linux/fs/readdir.c
*
* Copyright (C) 1995 Linus Torvalds
*/
-#include <linux/config.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/stat.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
-#ifdef CONFIG_TRANS_NAMES
-#include <linux/nametrans.h>
-#endif
-#include <linux/dalloc.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
-/* [T.Schoebel-Theuer] I am assuming that directories never get too large.
- * The problem is that getdents() delivers d_offset's that can be used
- * for lseek() by the user, so I must encode the status information for
- * name translation and dcache baskets in the offset.
- * Note that the linux man page getdents(2) does not mention that
- * the d_offset is fs-specific and can be used for lseek().
- */
-#define BASKET_BIT (1<<30) /* 31 is already used by affs */
-#define TRANS_BIT (1<<29)
-
/*
* Traditional linux readdir() handling..
*
struct readdir_callback {
struct old_linux_dirent * dirent;
- struct file * file;
- int translate;
- off_t oldoffset;
int count;
};
return -EINVAL;
buf->count++;
dirent = buf->dirent;
- copy_to_user(dirent->d_name, name, namlen);
- put_user(0, dirent->d_name + namlen);
-#ifdef CONFIG_TRANS_NAMES
- if(!buf->translate) {
- char * cut;
-#ifdef CONFIG_TRANS_RESTRICT
- struct inode * inode = buf->file->f_inode;
- cut = testname(inode && inode->i_gid != CONFIG_TRANS_GID, dirent->d_name);
-#else
- cut = testname(1, dirent->d_name);
-#endif
- if(cut) {
- put_user(0, cut);
- buf->translate = 1;
- }
- }
-#endif
put_user(ino, &dirent->d_ino);
put_user(offset, &dirent->d_offset);
put_user(namlen, &dirent->d_namlen);
+ copy_to_user(dirent->d_name, name, namlen);
+ put_user(0, dirent->d_name + namlen);
return 0;
}
int error = -EBADF;
struct file * file;
struct readdir_callback buf;
- off_t oldpos;
lock_kernel();
if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
error = verify_area(VERIFY_WRITE, dirent, sizeof(struct old_linux_dirent));
if (error)
goto out;
- oldpos = file->f_pos;
- buf.file = file;
- buf.dirent = dirent;
buf.count = 0;
- buf.translate = 0;
- if(file->f_pos & TRANS_BIT) {
- file->f_pos &= ~TRANS_BIT;
- buf.translate = 1;
- }
+ buf.dirent = dirent;
error = file->f_op->readdir(file->f_inode, file, &buf, fillonedir);
if (error < 0)
goto out;
- if(buf.translate) {
- file->f_pos = oldpos | TRANS_BIT;
- }
error = buf.count;
out:
unlock_kernel();
struct getdents_callback {
struct linux_dirent * current_dir;
struct linux_dirent * previous;
- struct file * file;
int count;
- int error;
- int restricted;
- int do_preload;
+ int error;
};
static int filldir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino)
struct getdents_callback * buf = (struct getdents_callback *) __buf;
int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1);
- /* Do not touch buf->error any more if everything is ok! */
+ buf->error = -EINVAL; /* only used if we fail.. */
if (reclen > buf->count)
- return (buf->error = -EINVAL);
-#ifdef CONFIG_DCACHE_PRELOAD
- if(buf->do_preload && (name[0] != '.' || namlen > 2)) {
- struct qstr qname = { name, namlen };
- struct inode * dir = buf->file->f_inode;
- d_entry_preliminary(dir->i_dentry, &qname, ino);
- }
-#endif
+ return -EINVAL;
+ dirent = buf->previous;
+ if (dirent)
+ put_user(offset, &dirent->d_off);
dirent = buf->current_dir;
- copy_to_user(dirent->d_name, name, namlen);
- put_user(0, dirent->d_name + namlen);
-#ifdef CONFIG_TRANS_NAMES
- {
- char * cut;
-#ifdef CONFIG_TRANS_RESTRICT
- cut = testname(buf->restricted, dirent->d_name);
-#else
- cut = testname(1, dirent->d_name);
-#endif
- if(cut) {
- int newlen = (int)cut - (int)dirent->d_name;
- int newreclen = ROUND_UP(NAME_OFFSET(dirent) + newlen + 1);
- /* Either both must fit or none. This way we need
- * no status information in f_pos */
- if (reclen+newlen > buf->count)
- return -EINVAL;
- put_user(0, cut);
- put_user(ino, &dirent->d_ino);
- put_user(newreclen, &dirent->d_reclen);
- put_user(offset, &dirent->d_off);
- ((char *) dirent) += newreclen;
- buf->count -= newreclen;
- put_user(offset, &dirent->d_off);
- copy_to_user(dirent->d_name, name, namlen);
- put_user(0, dirent->d_name + namlen);
- }
- }
-#endif
+ buf->previous = dirent;
put_user(ino, &dirent->d_ino);
put_user(reclen, &dirent->d_reclen);
- if (buf->previous)
- put_user(buf->file->f_pos, &buf->previous->d_off);
- buf->previous = dirent;
+ copy_to_user(dirent->d_name, name, namlen);
+ put_user(0, dirent->d_name + namlen);
((char *) dirent) += reclen;
buf->current_dir = dirent;
buf->count -= reclen;
asmlinkage int sys_getdents(unsigned int fd, void * dirent, unsigned int count)
{
struct file * file;
+ struct linux_dirent * lastdirent;
struct getdents_callback buf;
int error = -EBADF;
error = verify_area(VERIFY_WRITE, dirent, count);
if (error)
goto out;
- buf.file = file;
buf.current_dir = (struct linux_dirent *) dirent;
buf.previous = NULL;
buf.count = count;
buf.error = 0;
- buf.restricted = 0;
-#ifdef CONFIG_TRANS_RESTRICT
- buf.restricted = file->f_inode && file->f_inode->i_gid != CONFIG_TRANS_GID;
-#endif
- buf.do_preload = 0;
-#ifdef CONFIG_DCACHE_PRELOAD
- if(file->f_inode && file->f_inode->i_dentry &&
- !(file->f_inode->i_sb->s_type->fs_flags & (FS_NO_DCACHE|FS_NO_PRELIM)) &&
- !(file->f_inode->i_dentry->d_flag & D_PRELOADED))
- buf.do_preload = 1;
-#endif
-
- if(!(file->f_pos & BASKET_BIT)) {
- int oldcount;
- do {
- oldcount = buf.count;
- error = file->f_op->readdir(file->f_inode, file, &buf, filldir);
- if (error < 0)
- goto out;
- } while(!buf.error && buf.count != oldcount);
- }
- if(!buf.error) {
- int nr = 0;
- struct dentry * list = file->f_inode ?
- d_basket(file->f_inode->i_dentry) : NULL;
- struct dentry * ptr = list;
-#ifdef CONFIG_DCACHE_PRELOAD
- if(buf.do_preload) {
- buf.do_preload = 0;
- file->f_inode->i_dentry->d_flag |= D_PRELOADED;
- }
-#endif
- if(ptr) {
- if(!(file->f_pos & BASKET_BIT))
- file->f_pos = BASKET_BIT;
- do {
- struct dentry * next = ptr->d_basket_next;
- struct inode * inode;
- /* vfs_locks() are missing here */
- inode = d_inode(&ptr);
- if(inode) {
- nr++;
- if(nr > (file->f_pos & ~BASKET_BIT)) {
- int err = filldir(&buf, ptr->d_name.name,
- ptr->d_name.len,
- file->f_pos,
- inode->i_ino);
- if(err)
- break;
- file->f_pos++;
- }
- iput(inode);
- }
- ptr = next;
- } while(ptr != list);
- }
- }
- if (!buf.previous) {
+ error = file->f_op->readdir(file->f_inode, file, &buf, filldir);
+ if (error < 0)
+ goto out;
+ lastdirent = buf.previous;
+ if (!lastdirent) {
error = buf.error;
} else {
- put_user(file->f_pos, &buf.previous->d_off);
+ put_user(file->f_pos, &lastdirent->d_off);
error = count - buf.count;
}
out:
int error;
lock_kernel();
- error = namei(NAM_FOLLOW_LINK, filename, &inode);
+ error = namei(filename, &inode);
if (error)
goto out;
if ((error = do_revalidate(inode)) == 0)
int error;
lock_kernel();
- error = namei(NAM_FOLLOW_LINK, filename, &inode);
+ error = namei(filename, &inode);
if (error)
goto out;
if ((error = do_revalidate(inode)) == 0)
int error;
lock_kernel();
- error = namei(NAM_FOLLOW_TRAILSLASH, filename, &inode);
+ error = lnamei(filename, &inode);
if (error)
goto out;
if ((error = do_revalidate(inode)) == 0)
int error;
lock_kernel();
- error = namei(NAM_FOLLOW_TRAILSLASH, filename, &inode);
+ error = lnamei(filename, &inode);
if (error)
goto out;
if ((error = do_revalidate(inode)) == 0)
asmlinkage int sys_readlink(const char * path, char * buf, int bufsiz)
{
struct inode * inode;
- int error = -EINVAL;
+ int error;
- lock_kernel();
if (bufsiz <= 0)
- goto out;
- error = verify_area(VERIFY_WRITE,buf,bufsiz);
- if (error)
- goto out;
- error = namei(NAM_FOLLOW_TRAILSLASH, path, &inode);
+ return -EINVAL;
+
+ lock_kernel();
+ error = lnamei(path, &inode);
if (error)
goto out;
error = -EINVAL;
inode->i_dirt = 1;
}
error = inode->i_op->readlink(inode,buf,bufsiz);
+ iput(inode);
out:
unlock_kernel();
return error;
}
if (!(sb = get_super(dev)))
return;
- if (sb->s_covered) {
+ if (sb->s_root != sb->s_root->d_mounts) {
printk("VFS: Mounted device %s - tssk, tssk\n",
kdevname(dev));
return;
return NULL;
}
s->s_dev = dev;
- s->s_covered = NULL;
s->s_rd_only = 0;
s->s_dirt = 0;
s->s_type = type;
kdevname(dev));
}
+static void d_umount(struct dentry *dentry)
+{
+ struct dentry * covers = dentry->d_covers;
+
+ if (covers == dentry) {
+ printk("VFS: unmount - covers == dentry?\n");
+ return;
+ }
+ covers->d_mounts = covers;
+ dentry->d_covers = dentry;
+ dput(covers);
+ dput(dentry);
+}
+
+static void d_mount(struct dentry *covers, struct dentry *dentry)
+{
+ if (covers->d_mounts != covers) {
+ printk("VFS: mount - already mounted\n");
+ return;
+ }
+ covers->d_mounts = dentry;
+ dentry->d_covers = covers;
+}
+
static int do_umount(kdev_t dev,int unmount_root)
{
struct super_block * sb;
}
return 0;
}
- if (!(sb=get_super(dev)) || !(sb->s_covered))
+ sb=get_super(dev);
+ if (!sb)
return -ENOENT;
- if (!sb->s_covered->i_mount)
- printk("VFS: umount(%s): mounted inode has i_mount=NULL\n",
- kdevname(dev));
/*
* Before checking if the filesystem is still busy make sure the kernel
* too bad there are no quotas running anymore. Turn them on again by hand.
*/
quota_off(dev, -1);
- if (!fs_may_umount(dev, sb->s_mounted))
+ if (!fs_may_umount(dev, sb->s_root))
return -EBUSY;
/* Clear up the dcache tree. This should be cleaner.. */
- while (sb->s_ibasket)
- free_ibasket(sb);
- if (sb->s_mounted->i_dentry)
- d_del(sb->s_mounted->i_dentry, D_NO_CLEAR_INODE);
-
- sb->s_covered->i_mount = NULL;
- iput(sb->s_covered);
- sb->s_covered = NULL;
- iput(sb->s_mounted);
- sb->s_mounted = NULL;
+ if (sb->s_root) {
+ d_umount(sb->s_root);
+ d_delete(sb->s_root);
+ }
+
+ sb->s_root = NULL;
if (sb->s_op && sb->s_op->write_super && sb->s_dirt)
sb->s_op->write_super(sb);
put_super(dev);
lock_kernel();
if (!suser())
goto out;
- retval = namei(NAM_FOLLOW_LINK, name, &inode);
- if (retval) {
- retval = namei(NAM_FOLLOW_TRAILSLASH, name, &inode);
- if (retval)
- goto out;
- }
+ retval = namei(name, &inode);
if (S_ISBLK(inode->i_mode)) {
dev = inode->i_rdev;
retval = -EACCES;
}
} else {
retval = -EINVAL;
- if (!inode->i_sb || inode != inode->i_sb->s_mounted) {
+ if (!inode->i_sb || inode != inode->i_sb->s_root->d_inode) {
iput(inode);
goto out;
}
int do_mount(kdev_t dev, const char * dev_name, const char * dir_name, const char * type, int flags, void * data)
{
- struct inode * dir_i = NULL;
+ struct dentry * dir_d = NULL;
struct super_block * sb;
struct vfsmount *vfsmnt;
int error;
- int override = 0;
- if(dir_name) {
- char c;
-
- get_user(c, dir_name);
- override = (c == '!');
- }
if (!(flags & MS_RDONLY) && dev && is_read_only(dev))
return -EACCES;
/*flags |= MS_RDONLY;*/
- if(override)
- dir_name++;
- error = namei(NAM_FOLLOW_LINK, dir_name, &dir_i);
- if (error)
+
+ dir_d = lookup_dentry(dir_name, NULL, 1);
+ error = PTR_ERR(dir_d);
+ if (IS_ERR(dir_d))
return error;
- if (!override && (atomic_read(&dir_i->i_count) != 1 || dir_i->i_mount)) {
- iput(dir_i);
+
+ if (dir_d->d_flag & D_NEGATIVE) {
+ dput(dir_d);
+ return -ENOENT;
+ }
+
+ if (dir_d->d_covers != dir_d) {
+ dput(dir_d);
return -EBUSY;
}
- if (!S_ISDIR(dir_i->i_mode)) {
- iput(dir_i);
+ if (!S_ISDIR(dir_d->d_inode->i_mode)) {
+ dput(dir_d);
return -ENOTDIR;
}
- if (!fs_may_mount(dev) && !override) {
- iput(dir_i);
+ if (!fs_may_mount(dev)) {
+ dput(dir_d);
return -EBUSY;
}
sb = read_super(dev,type,flags,data,0);
if (!sb) {
- iput(dir_i);
+ dput(dir_d);
return -EINVAL;
}
- if (sb->s_covered) {
- iput(dir_i);
+ if (sb->s_root->d_covers != sb->s_root) {
+ dput(dir_d);
return -EBUSY;
}
vfsmnt = add_vfsmnt(dev, dev_name, dir_name);
vfsmnt->mnt_sb = sb;
vfsmnt->mnt_flags = flags;
}
- {
- struct dentry * old = dir_i->i_dentry;
- struct dentry * new;
- vfs_lock();
- new = d_alloc(old->d_parent, old->d_name.len, 1);
- if(new) {
- struct qstr copy = { old->d_name.name, old->d_name.len };
- d_add(new, sb->s_mounted, ©, D_DUPLICATE);
- vfs_unlock();
- } else {
- printk("VFS: cannot setup dentry for mount\n");
- iput(dir_i);
- return -ENOMEM;
- }
- vfs_unlock();
- }
- sb->s_covered = dir_i;
- dir_i->i_mount = sb->s_mounted;
- return 0; /* we don't iput(dir_i) - see umount */
+ d_mount(dir_d, sb->s_root);
+ return 0; /* we don't dput(dir) - see umount */
}
struct inode *dir_i;
int retval;
- retval = namei(NAM_FOLLOW_LINK, dir, &dir_i);
+ retval = namei(dir, &dir_i);
if (retval)
return retval;
- if (dir_i != dir_i->i_sb->s_mounted) {
+ if (dir_i != dir_i->i_sb->s_root->d_inode) {
iput(dir_i);
return -EINVAL;
}
t = fstype->name;
fops = NULL;
if ((fstype->fs_flags & FS_REQUIRES_DEV)) {
- retval = namei(NAM_FOLLOW_LINK, dev_name, &inode);
+ retval = namei(dev_name, &inode);
if (retval)
goto out;
retval = -ENOTBLK;
struct file_system_type * fs_type;
struct super_block * sb;
struct vfsmount *vfsmnt;
- struct inode * inode, * d_inode = NULL;
+ struct inode * d_inode = NULL;
struct file filp;
int retval;
sb->s_dev = get_unnamed_dev();
sb->s_flags = root_mountflags & ~MS_RDONLY;
if (nfs_root_mount(sb) >= 0) {
- inode = sb->s_mounted;
- atomic_add(3, &inode->i_count);
- sb->s_covered = inode;
sb->s_rd_only = 0;
sb->s_dirt = 0;
sb->s_type = fs_type;
- current->fs->pwd = inode;
- current->fs->root = inode;
- (void)d_alloc_root(inode);
+ current->fs->root = dget(sb->s_root);
+ current->fs->pwd = dget(sb->s_root);
ROOT_DEV = sb->s_dev;
printk (KERN_NOTICE "VFS: Mounted root (nfs filesystem).\n");
vfsmnt = add_vfsmnt(ROOT_DEV, "/dev/root", "/");
continue;
sb = read_super(ROOT_DEV,fs_type->name,root_mountflags,NULL,1);
if (sb) {
- inode = sb->s_mounted;
-
- /* NOTE! it is logically used 4 times, not 1 */
- atomic_add(3, &inode->i_count);
- sb->s_covered = inode;
sb->s_flags = root_mountflags;
- current->fs->pwd = inode;
- current->fs->root = inode;
- (void)d_alloc_root(inode);
+ current->fs->root = dget(sb->s_root);
+ current->fs->pwd = dget(sb->s_root);
printk ("VFS: Mounted root (%s filesystem)%s.\n",
fs_type->name,
(sb->s_flags & MS_RDONLY) ? " readonly" : "");
do_mount_root();
old_fs = get_fs();
set_fs(get_ds());
- error = namei(NAM_FOLLOW_LINK, put_old, &inode);
+ error = namei(put_old, &inode);
if (error) inode = NULL;
set_fs(old_fs);
if (!error && (atomic_read(&inode->i_count) != 1 || inode->i_mount))
#define SWP_OFFSET(entry) ((entry) >> 40)
#define SWP_ENTRY(type,offset) pte_val(mk_swap_pte((type),(offset)))
+#define module_map vmalloc
+#define module_unmap vfree
+
#endif /* _ALPHA_PGTABLE_H */
#define SWP_OFFSET(entry) ((entry) >> 8)
#define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << 8))
+#define module_map vmalloc
+#define module_unmap vfree
+
#endif /* _I386_PAGE_H */
#define hardirq_enter(cpu) (local_irq_count[cpu]++)
#define hardirq_exit(cpu) (local_irq_count[cpu]--)
+#define synchronize_irq() do { } while (0)
+
#endif
extern inline void flush_cache_mm(struct mm_struct *mm)
{
-#if FLUSH_VIRTUAL_CACHE_040
- if (mm == current->mm) __flush_cache_all();
-#else
- if (mm == current->mm) __flush_cache_030();
-#endif
+ if (mm == current->mm)
+ __flush_cache_030();
}
extern inline void flush_cache_range(struct mm_struct *mm,
unsigned long start,
unsigned long end)
{
- if (mm == current->mm){
-#if FLUSH_VIRTUAL_CACHE_040
- if (CPU_IS_040_OR_060)
- cache_push_v(start, end-start);
- else
-#endif
+ if (mm == current->mm)
__flush_cache_030();
- }
}
extern inline void flush_cache_page(struct vm_area_struct *vma,
unsigned long vmaddr)
{
- if (vma->vm_mm == current->mm){
-#if FLUSH_VIRTUAL_CACHE_040
- if (CPU_IS_040_OR_060)
- cache_push_v(vmaddr, PAGE_SIZE);
- else
-#endif
+ if (vma->vm_mm == current->mm)
__flush_cache_030();
- }
}
/* Push the page at kernel virtual address and clear the icache */
#endif /* __ASSEMBLY__ */
+#define module_map vmalloc
+#define module_unmap vfree
+
#endif /* _M68K_PGTABLE_H */
* for more details.
*
* Copyright (C) 1996 by Ralf Baechle
+ *
+ * $Id: atomic.h,v 1.2 1997/06/25 19:10:33 ralf Exp $
*/
#ifndef __ASM_MIPS_ATOMIC_H
#define __ASM_MIPS_ATOMIC_H
#include <asm/sgidefs.h>
-/*
- * Make sure gcc doesn't try to be clever and move things around
- * on us. We need to use _exactly_ the address the user gave us,
- * not some alias that contains the same information.
- */
-#define __atomic_fool_gcc(x) (*(struct { int a[100]; } *)x)
-
#ifdef __SMP__
typedef struct { volatile int counter; } atomic_t;
#else
typedef struct { int counter; } atomic_t;
#endif
+#ifdef __KERNEL__
#define ATOMIC_INIT(i) { (i) }
#define atomic_read(v) ((v)->counter)
* ... while for MIPS II and better we can use ll/sc instruction. This
* implementation is SMP safe ...
*/
+
+/*
+ * Make sure gcc doesn't try to be clever and move things around
+ * on us. We need to use _exactly_ the address the user gave us,
+ * not some alias that contains the same information.
+ */
+#define __atomic_fool_gcc(x) (*(struct { int a[100]; } *)x)
+
extern __inline__ void atomic_add(int i, volatile atomic_t * v)
{
unsigned long temp;
#define atomic_inc(v) atomic_add(1,(v))
#define atomic_dec(v) atomic_sub(1,(v))
+#endif /* defined(__KERNEL__) */
#endif /* __ASM_MIPS_ATOMIC_H */
* for more details.
*
* Copyright (C) 1995, 1996, 1997 by Ralf Baechle
+ *
+ * $Id: byteorder.h,v 1.5 1997/06/25 19:10:18 ralf Exp $
*/
#ifndef __ASM_MIPS_BYTEORDER_H
#define __ASM_MIPS_BYTEORDER_H
-extern unsigned long int ntohl(unsigned long int __x);
-extern unsigned short int ntohs(unsigned short int __x);
-extern unsigned short int htons(unsigned short int __x);
-extern unsigned long int htonl(unsigned long int __x);
-
#define __swap32(x) \
((unsigned long int)((((unsigned long int)(x) & 0x000000ffU) << 24) | \
(((unsigned long int)(x) & 0x0000ff00U) << 8) | \
#error "MIPS but neither __MIPSEL__ nor __MIPSEB__?"
#endif
+/* The same, but returns converted value from the location pointer by addr. */
+extern __inline__ __u16 cpu_to_le16p(__u16 *addr)
+{
+ return cpu_to_le16(*addr);
+}
+
+extern __inline__ __u32 cpu_to_le32p(__u32 *addr)
+{
+ return cpu_to_le32(*addr);
+}
+
+extern __inline__ __u16 cpu_to_be16p(__u16 *addr)
+{
+ return cpu_to_be16(*addr);
+}
+
+extern __inline__ __u32 cpu_to_be32p(__u32 *addr)
+{
+ return cpu_to_be32(*addr);
+}
+
+#define le16_to_cpup(x) cpu_to_le16p(x)
+#define le32_to_cpup(x) cpu_to_le32p(x)
+#define be16_to_cpup(x) cpu_to_be16p(x)
+#define be32_to_cpup(x) cpu_to_be32p(x)
+
+
+/* The same, but do the conversion in situ, ie. put the value back to addr. */
+extern __inline__ void cpu_to_le16s(__u16 *addr)
+{
+ *addr = cpu_to_le16(*addr);
+}
+
+extern __inline__ void cpu_to_le32s(__u32 *addr)
+{
+ *addr = cpu_to_le32(*addr);
+}
+
+extern __inline__ void cpu_to_be16s(__u16 *addr)
+{
+ *addr = cpu_to_be16(*addr);
+}
+
+extern __inline__ void cpu_to_be32s(__u32 *addr)
+{
+ *addr = cpu_to_be32(*addr);
+}
+
+#define le16_to_cpus(x) cpu_to_le16s(x)
+#define le32_to_cpus(x) cpu_to_le32s(x)
+#define be16_to_cpus(x) cpu_to_be16s(x)
+#define be32_to_cpus(x) cpu_to_be32s(x)
+
+#ifdef __KERNEL__
+extern unsigned long int ntohl(unsigned long int __x);
+extern unsigned short int ntohs(unsigned short int __x);
+extern unsigned short int htons(unsigned short int __x);
+extern unsigned long int htonl(unsigned long int __x);
+
+
extern __inline__ unsigned long int ntohl(unsigned long int __x)
{
return __constant_ntohl(__x);
{
return __constant_htons(__x);
}
+#endif /* __KERNEL__ */
#endif /* __ASM_MIPS_BYTEORDER_H */
*/
static inline unsigned short int csum_fold(unsigned int sum)
{
- unsigned int __res;
-
- __asm__("
- .set noat
- srl $1,%0,16
- andi %0,0xffff
- addu $1,%0
- srl %0,$1,16 # addup halfword carry
- andi $1,0xffff
- addu $1,%0
- nor %0,$0,$1
+ __asm__("
+ .set noat
+ sll $1,%0,16
+ addu %0,$1
+ sltu $1,%0,$1
+ srl %0,%0,16
+ addu %0,$1
+ xori %0,0xffff
.set at"
- : "=r"(__res)
+ : "=r" (sum)
: "0" (sum)
: "$1");
- return __res;
+ return sum;
}
/*
addu %0,%2
sltu $1,%0,%2
addu %0,$1
+
addu %0,%3
sltu $1,%0,%3
addu %0,$1
+
addu %0,%4
sltu $1,%0,%4
addu %0,$1
-
- srl $1,%0,16
- andi %0,0xffff
- addu %0,$1
- srl $1,%0,16 # addup halfword carry
- andi %0,0xffff
- addu %0,$1
- nor %0,$0,%0
.set at"
: "=r" (sum)
: "0" (daddr), "r"(saddr),
"r"(sum)
: "$1");
- return (unsigned short)sum;
+ return csum_fold(sum);
}
/*
*/
static inline unsigned short ip_compute_csum(unsigned char * buff, int len)
{
- unsigned short int sum;
-
- __asm__("
- .set noat
- srl $1,%0,16
- andi %0,0xffff
- addu %0,$1
- sltu $1,%0,$1
- addu %0,$1
- nor %0,$0,%0
- .set at"
- : "=r"(sum)
- : "0" (csum_partial(buff, len, 0))
- : "$1");
+ unsigned int sum;
- return sum;
+ sum = csum_partial(buff, len, 0);
+ return csum_fold(sum);
}
#define _HAVE_ARCH_IPV6_CSUM
#define MM_PGD 8
#define MM_CONTEXT 28
+/* Linux sigcontext offsets. */
+#define SC_REGMASK 0
+#define SC_STATUS 4
+#define SC_PC 8
+#define SC_REGS 16
+#define SC_FPREGS 272
+#define SC_OWNEDFP 528
+#define SC_FPC_CSR 532
+#define SC_FPC_EIR 536
+#define SC_SSFLAGS 540
+#define SC_MDHI 544
+#define SC_MDLO 552
+#define SC_CAUSE 560
+#define SC_BADVADDR 564
+#define SC_SIGSET 568
+
#endif /* !(_MIPS_OFFSET_H) */
#endif /* !defined (__LANGUAGE_ASSEMBLY__) */
+#define module_map vmalloc
+#define module_unmap vfree
+
#endif /* __ASM_MIPS_PGTABLE_H */
-/* $Id: r4kcache.h,v 1.1 1997/06/06 09:39:42 ralf Exp $
+/* $Id: r4kcache.h,v 1.2 1997/06/25 17:04:19 ralf Exp $
* r4kcache.h: Inline assembly cache operations.
*
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
unsigned long start = page;
unsigned long end = (start + PAGE_SIZE);
+ __asm__ __volatile__("nop;nop;nop;nop");
while(start < end) {
__asm__ __volatile__("
.set noreorder
.set reorder"
:
: "r" (start),
- "i" (Index_Invalidate_I));
+ "i" (Index_Writeback_Inv_D));
start += 0x800;
}
}
+/*
+ * include/asm-mips/uaccess.h
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1996, 1997 by Ralf Baechle
+ *
+ * $Id: sigcontext.h,v 1.3 1997/06/25 16:57:31 ralf Exp $
+ */
#ifndef __ASM_MIPS_SIGCONTEXT_H
#define __ASM_MIPS_SIGCONTEXT_H
-#ifdef __LANGUAGE_ASSEMBLY__
-
-#define SC_REGMASK 0
-#define SC_STATUS 4
-#define SC_PC 8
-#define SC_REGS 16
-#define SC_FPREGS 272
-#define SC_OWNEDFP 528
-#define SC_FPC_CSR 532
-#define SC_FPC_EIR 536
-#define SC_SSFLAGS 540
-#define SC_MDHI 544
-#define SC_MDLO 552
-
-#endif
-
-#if defined(__LANGUAGE_C__) || \
- defined(_LANGUAGE_C) || \
- defined(__LANGUAGE_C_PLUS_PLUS__) || \
- defined(__LANGUAGE_OBJECTIVE_C__)
-
/*
- * Whenever this structure is changed you must update the offsets in
- * arch/mips/mips<isa>/fp-context.S.
+ * Keep this struct definition in sync with the sigcontext fragment
+ * in arch/mips/tools/offset.c
*/
struct sigcontext {
unsigned int sc_regmask; /* Unused */
sigset_t sc_sigset;
unsigned long __pad0[3]; /* pad for constant size */
};
-#endif
#endif /* __ASM_MIPS_SIGCONTEXT_H */
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
- * Copyright (C) 1996 by Ralf Baechle
+ * Copyright (C) 1996, 1997 by Ralf Baechle
+ *
+ * $Id: uaccess.h,v 1.4 1997/07/01 08:23:56 ralf Exp $
*/
#ifndef __ASM_MIPS_UACCESS_H
#define __ASM_MIPS_UACCESS_H
__cu_len; \
})
+/*
+ * Yuck. We need two variants, one for 64bit operation and one
+ * for 32 bit mode and old iron.
+ */
+#ifdef __mips64
+#define __GET_USER_DW __get_user_asm("ld")
+#else
+#define __GET_USER_DW __get_user_asm_ll32
+#endif
+
#define __get_user_nocheck(x,ptr,size) ({ \
long __gu_err; \
-long __gu_val; \
+__typeof(*(ptr)) __gu_val; \
long __gu_addr; \
__asm__("":"=r" (__gu_val)); \
__gu_addr = (long) (ptr); \
-__gu_err = 0; \
+__asm__("":"=r" (__gu_err)); \
switch (size) { \
-case 1: __get_user_nocheck_asm("lb"); break; \
-case 2: __get_user_nocheck_asm("lh"); break; \
-case 4: __get_user_nocheck_asm("lw"); break; \
-case 8: __get_user_nocheck_asm("ld"); break; \
+case 1: __get_user_asm("lb"); break; \
+case 2: __get_user_asm("lh"); break; \
+case 4: __get_user_asm("lw"); break; \
+case 8: __GET_USER_DW; break; \
default: __get_user_unknown(); break; \
} x = (__typeof__(*(ptr))) __gu_val; __gu_err; })
-#define __get_user_nocheck_asm(insn) \
-({ \
-__asm__ __volatile__( \
- "1:\t" insn "\t%1,%3\n" \
- "2:\n\t" \
- ".section\t.fixup,\"ax\"\n" \
- "3:\t.set\tnoat\n\t" \
- "la\t$1,2b\n\t" \
- "li\t%0,%4\n\t" \
- "jr\t$1\n\t" \
- ".set\tat\n\t" \
- ".previous\n\t" \
- ".section\t__ex_table,\"a\"\n\t" \
- STR(PTR)"\t1b,3b\n\t" \
- ".previous" \
- :"=r" (__gu_err), "=r" (__gu_val) \
- :"0" (__gu_err), "o" (__m(__gu_addr)), "i" (-EFAULT) \
- :"$1"); })
-
#define __get_user_check(x,ptr,size,mask) ({ \
long __gu_err; \
-long __gu_val; \
+__typeof__(*(ptr)) __gu_val; \
long __gu_addr; \
__asm__("":"=r" (__gu_val)); \
__gu_addr = (long) (ptr); \
-__gu_err = -EFAULT; \
+__asm__("":"=r" (__gu_err)); \
if (__access_ok(__gu_addr,size,mask)) { \
switch (size) { \
-case 1: __get_user_check_asm("lb"); break; \
-case 2: __get_user_check_asm("lh"); break; \
-case 4: __get_user_check_asm("lw"); break; \
-case 8: __get_user_check_asm("ld"); break; \
+case 1: __get_user_asm("lb"); break; \
+case 2: __get_user_asm("lh"); break; \
+case 4: __get_user_asm("lw"); break; \
+case 8: __GET_USER_DW; break; \
default: __get_user_unknown(); break; \
} } x = (__typeof__(*(ptr))) __gu_val; __gu_err; })
-#define __get_user_check_asm(insn) \
+#define __get_user_asm(insn) \
({ \
__asm__ __volatile__( \
"1:\t" insn "\t%1,%2\n\t" \
".section\t.fixup,\"ax\"\n" \
"3:\t.set\tnoat\n\t" \
"la\t$1,2b\n\t" \
+ "li\t%0,%3\n\t" \
"jr\t$1\n\t" \
".set\tat\n\t" \
".previous\n\t" \
STR(PTR)"\t1b,3b\n\t" \
".previous" \
:"=r" (__gu_err), "=r" (__gu_val) \
- :"o" (__m(__gu_addr)) \
+ :"o" (__m(__gu_addr)), "i" (-EFAULT) \
+ :"$1"); })
+
+/*
+ * Get a long long 64 using 32 bit registers.
+ */
+#define __get_user_asm_ll32 \
+({ \
+__asm__ __volatile__( \
+ "1:\tlw\t%1,%2\n" \
+ "2:\tlw\t%D1,%3\n\t" \
+ "move\t%0,$0\n" \
+ "3:\t.section\t.fixup,\"ax\"\n" \
+ "4:\t.set\tnoat\n\t" \
+ "la\t$1,3b\n\t" \
+ "li\t%0,%4\n\t" \
+ "jr\t$1\n\t" \
+ ".set\tat\n\t" \
+ ".previous\n\t" \
+ ".section\t__ex_table,\"a\"\n\t" \
+ STR(PTR)"\t1b,4b\n\t" \
+ STR(PTR)"\t2b,4b\n\t" \
+ ".previous" \
+ :"=r" (__gu_err), "=&r" (__gu_val) \
+ :"o" (__m(__gu_addr)), "o" (__m(__gu_addr + 4)), \
+ "i" (-EFAULT) \
:"$1"); })
extern void __get_user_unknown(void);
+/*
+ * Yuck. We need two variants, one for 64bit operation and one
+ * for 32 bit mode and old iron.
+ */
+#ifdef __mips64
+#define __PUT_USER_DW __put_user_asm("sd")
+#else
+#define __PUT_USER_DW __put_user_asm_ll32
+#endif
+
#define __put_user_nocheck(x,ptr,size) ({ \
long __pu_err; \
__typeof__(*(ptr)) __pu_val; \
long __pu_addr; \
__pu_val = (x); \
__pu_addr = (long) (ptr); \
-__pu_err = 0; \
+__asm__("":"=r" (__pu_err)); \
switch (size) { \
-case 1: __put_user_nocheck_asm("sb"); break; \
-case 2: __put_user_nocheck_asm("sh"); break; \
-case 4: __put_user_nocheck_asm("sw"); break; \
-case 8: __put_user_nocheck_asm("sd"); break; \
+case 1: __put_user_asm("sb"); break; \
+case 2: __put_user_asm("sh"); break; \
+case 4: __put_user_asm("sw"); break; \
+case 8: __PUT_USER_DW; break; \
default: __put_user_unknown(); break; \
} __pu_err; })
-#define __put_user_nocheck_asm(insn) \
+#define __put_user_check(x,ptr,size,mask) ({ \
+long __pu_err; \
+__typeof__(*(ptr)) __pu_val; \
+long __pu_addr; \
+__pu_val = (x); \
+__pu_addr = (long) (ptr); \
+__asm__("":"=r" (__pu_err)); \
+if (__access_ok(__pu_addr,size,mask)) { \
+switch (size) { \
+case 1: __put_user_asm("sb"); break; \
+case 2: __put_user_asm("sh"); break; \
+case 4: __put_user_asm("sw"); break; \
+case 8: __PUT_USER_DW; break; \
+default: __put_user_unknown(); break; \
+} } __pu_err; })
+
+#define __put_user_asm(insn) \
({ \
__asm__ __volatile__( \
- "1:\t" insn "\t%1,%2\n" \
+ "1:\t" insn "\t%1,%2\n\t" \
+ "move\t%0,$0\n" \
"2:\n\t" \
".section\t.fixup,\"ax\"\n" \
"3:\t.set\tnoat\n\t" \
:"r" (__pu_val), "o" (__m(__pu_addr)), "i" (-EFAULT) \
:"$1"); })
-#define __put_user_check(x,ptr,size,mask) ({ \
-long __pu_err; \
-__typeof__(*(ptr)) __pu_val; \
-long __pu_addr; \
-__pu_val = (x); \
-__pu_addr = (long) (ptr); \
-__pu_err = -EFAULT; \
-if (__access_ok(__pu_addr,size,mask)) { \
-switch (size) { \
-case 1: __put_user_check_asm("sb"); break; \
-case 2: __put_user_check_asm("sh"); break; \
-case 4: __put_user_check_asm("sw"); break; \
-case 8: __put_user_check_asm("sd"); break; \
-default: __put_user_unknown(); break; \
-} } __pu_err; })
-
-#define __put_user_check_asm(insn) \
+#define __put_user_asm_ll32 \
({ \
__asm__ __volatile__( \
- "1:\t" insn "\t%1,%2\n\t" \
+ "1:\tsw\t%1,%2\n\t" \
+ "2:\tsw\t%D1,%3\n" \
"move\t%0,$0\n" \
- "2:\n\t" \
+ "3:\n\t" \
".section\t.fixup,\"ax\"\n" \
- "3:\t.set\tnoat\n\t" \
- "la\t$1,2b\n\t" \
+ "4:\t.set\tnoat\n\t" \
+ "la\t$1,3b\n\t" \
+ "li\t%0,%4\n\t" \
"jr\t$1\n\t" \
".set\tat\n\t" \
".previous\n\t" \
".section\t__ex_table,\"a\"\n\t" \
- STR(PTR)"\t1b,3b\n\t" \
+ STR(PTR)"\t1b,4b\n\t" \
+ STR(PTR)"\t2b,4b\n\t" \
".previous" \
:"=r" (__pu_err) \
- :"r" (__pu_val), "o" (__m(__pu_addr)) \
+ :"r" (__pu_val), "o" (__m(__pu_addr)), "o" (__m(__pu_addr + 4)), \
+ "i" (-EFAULT) \
:"$1"); })
extern void __put_user_unknown(void);
#define SWP_OFFSET(entry) ((entry) >> 8)
#define SWP_ENTRY(type,offset) (((type) << 1) | ((offset) << 8))
+#define module_map vmalloc
+#define module_unmap vfree
+
#endif /* _PPC_PAGE_H */
-/* $Id: asi.h,v 1.16 1996/04/25 06:12:43 davem Exp $ */
+/* $Id: asi.h,v 1.17 1997/06/24 15:48:10 jj Exp $ */
#ifndef _SPARC_ASI_H
#define _SPARC_ASI_H
/* Block-copy operations are available only on certain V8 cpus. */
#define ASI_M_BCOPY 0x17 /* Block copy */
-/* These affect only the ICACHE and are Ross HyperSparc specific. */
+/* These affect only the ICACHE and are Ross HyperSparc and TurboSparc specific. */
#define ASI_M_IFLUSH_PAGE 0x18 /* Flush I Cache Line (page); wo, ss */
#define ASI_M_IFLUSH_SEG 0x19 /* Flush I Cache Line (seg); wo, ss */
#define ASI_M_IFLUSH_REGION 0x1A /* Flush I Cache Line (region); wo, ss */
/* This is ROSS HyperSparc only. */
#define ASI_M_FLUSH_IWHOLE 0x31 /* Flush entire ICACHE; wo, ss */
-/* Tsunami/Viking i/d cache flash clear. */
+/* Tsunami/Viking/TurboSparc i/d cache flash clear. */
#define ASI_M_IC_FLCLEAR 0x36
#define ASI_M_DC_FLCLEAR 0x37
/* 119 is the non-posix getpgrp tty ioctl */
#define __TIOCCDTR _IO('t', 120) /* SunOS Specific */
#define __TIOCSDTR _IO('t', 121) /* SunOS Specific */
-#define __TIOCCBRK _IO('t', 122) /* SunOS Specific */
-#define __TIOCSBRK _IO('t', 123) /* SunOS Specific */
+#define TIOCCBRK _IO('t', 122)
+#define TIOCSBRK _IO('t', 123)
#define __TIOCLGET _IOW('t', 124, int) /* SunOS Specific */
#define __TIOCLSET _IOW('t', 125, int) /* SunOS Specific */
#define __TIOCLBIC _IOW('t', 126, int) /* SunOS Specific */
-/* $Id: mbus.h,v 1.8 1996/08/29 09:48:21 davem Exp $
+/* $Id: mbus.h,v 1.9 1997/06/24 15:48:12 jj Exp $
* mbus.h: Various defines for MBUS modules.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
Viking_30 = 10,
Viking_35 = 11,
Viking_new = 12,
- SRMMU_INVAL_MOD = 13,
+ TurboSparc = 13,
+ SRMMU_INVAL_MOD = 14,
};
extern enum mbus_module srmmu_modtype;
/* Fujitsu */
#define FMI_AURORA 0x4 /* MB8690x, a Swift module... */
+#define FMI_TURBO 0x5 /* MB86907, a TurboSparc module... */
/* For multiprocessor support we need to be able to obtain the CPU id and
* the MBUS Module id.
-/* $Id: oplib.h,v 1.15 1997/03/18 18:00:18 jj Exp $
+/* $Id: oplib.h,v 1.16 1997/06/27 14:55:04 jj Exp $
* oplib.h: Describes the interface and available routines in the
* Linux Prom library.
*
/* Return the first property type, as a string, for the given node.
* Returns a null string on error.
*/
-extern char *prom_firstprop(int node);
+extern char *prom_firstprop(int node, char *buffer);
/* Returns the next property after the passed property for the given
* node. Returns null string on failure.
*/
-extern char *prom_nextprop(int node, char *prev_property);
+extern char *prom_nextprop(int node, char *prev_property, char *buffer);
/* Returns 1 if the specified node has given property. */
extern int prom_node_has_property(int node, char *property);
-/* $Id: pgtable.h,v 1.61 1997/06/06 10:56:34 jj Exp $ */
+/* $Id: pgtable.h,v 1.62 1997/06/27 14:55:00 jj Exp $ */
#ifndef _SPARC_PGTABLE_H
#define _SPARC_PGTABLE_H
}
}
+#define module_map vmalloc
+#define module_unmap vfree
+
#endif /* !(_SPARC_PGTABLE_H) */
--- /dev/null
+/* $Id: turbosparc.h,v 1.3 1997/06/26 12:59:27 jj Exp $
+ * turbosparc.h: Defines specific to the TurboSparc module.
+ * This is SRMMU stuff.
+ *
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+#ifndef _SPARC_TURBOSPARC_H
+#define _SPARC_TURBOSPARC_H
+
+#include <asm/asi.h>
+#include <asm/pgtsrmmu.h>
+
+/* Bits in the SRMMU control register for TurboSparc modules.
+ *
+ * -------------------------------------------------------------------
+ * |impl-vers| RSV| PMC |PE|PC| RSV |BM| RFR |IC|DC|PSO|RSV|ICS|NF|ME|
+ * -------------------------------------------------------------------
+ * 31 24 23-21 20-19 18 17 16-15 14 13-10 9 8 7 6-3 2 1 0
+ *
+ * BM: Boot Mode -- 0 = not in boot mode, 1 = in boot mode
+ *
+ * This indicates whether the TurboSparc is in boot-mode or not.
+ *
+ * IC: Instruction Cache -- 0 = off, 1 = on
+ * DC: Data Cache -- 0 = off, 1 = 0n
+ *
+ * These bits enable the on-cpu TurboSparc split I/D caches.
+ *
+ * ICS: ICache Snooping -- 0 = disable, 1 = enable snooping of icache
+ * NF: No Fault -- 0 = faults generate traps, 1 = faults don't trap
+ * ME: MMU enable -- 0 = mmu not translating, 1 = mmu translating
+ *
+ */
+
+#define TURBOSPARC_MMUENABLE 0x00000001
+#define TURBOSPARC_NOFAULT 0x00000002
+#define TURBOSPARC_ICSNOOP 0x00000004
+#define TURBOSPARC_PSO 0x00000080
+#define TURBOSPARC_DCENABLE 0x00000100 /* Enable data cache */
+#define TURBOSPARC_ICENABLE 0x00000200 /* Enable instruction cache */
+#define TURBOSPARC_BMODE 0x00004000
+#define TURBOSPARC_PARITYODD 0x00020000 /* Parity odd, if enabled */
+#define TURBOSPARC_PCENABLE 0x00040000 /* Enable parity checking */
+
+/* Bits in the CPU configuration register for TurboSparc modules.
+ *
+ * -------------------------------------------------------
+ * |IOClk|SNP|AXClk| RAH | WS | RSV |SBC|WT|uS2|SE|SCC|
+ * -------------------------------------------------------
+ * 31 30 29-28 27-26 25-23 22-8 7-6 5 4 3 2-0
+ *
+ */
+
+#define TURBOSPARC_SCENABLE 0x00000008 /* Secondary cache enable */
+#define TURBOSPARC_uS2 0x00000010 /* Swift compatibility mode */
+#define TURBOSPARC_WTENABLE 0x00000020 /* Write thru for dcache */
+#define TURBOSPARC_SNENABLE 0x40000000 /* DVMA snoop enable */
+
+#ifndef __ASSEMBLY__
+
+/* Bits [13:5] select one of 512 instruction cache tags */
+extern __inline__ void turbosparc_inv_insn_tag(unsigned long addr)
+{
+ __asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
+ "r" (addr), "i" (ASI_M_TXTC_TAG));
+}
+
+/* Bits [13:5] select one of 512 data cache tags */
+extern __inline__ void turbosparc_inv_data_tag(unsigned long addr)
+{
+ __asm__ __volatile__("sta %%g0, [%0] %1\n\t" : :
+ "r" (addr), "i" (ASI_M_DATAC_TAG));
+}
+
+extern __inline__ void turbosparc_flush_icache(void)
+{
+ __asm__ __volatile__("sta %%g0, [%%g0] %0\n\t" : :
+ "i" (ASI_M_IC_FLCLEAR));
+}
+
+extern __inline__ void turbosparc_flush_dcache(void)
+{
+ unsigned long addr;
+
+ for(addr = 0; addr < 0x4000; addr += 0x20)
+ turbosparc_inv_data_tag(addr);
+}
+
+extern __inline__ void turbosparc_idflash_clear(void)
+{
+ turbosparc_flush_icache(); turbosparc_flush_dcache();
+}
+
+extern __inline__ void turbosparc_set_ccreg(unsigned long regval)
+{
+ __asm__ __volatile__("sta %0, [%1] %2\n\t" : :
+ "r" (regval), "r" (0x600),
+ "i" (ASI_M_MMUREGS));
+}
+
+extern __inline__ unsigned long turbosparc_get_ccreg(void)
+{
+ unsigned long regval;
+
+ __asm__ __volatile__("lda [%1] %2, %0\n\t" :
+ "=r" (regval) :
+ "r" (0x600),
+ "i" (ASI_M_MMUREGS));
+ return regval;
+}
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* !(_SPARC_TURBOSPARC_H) */
#define AOFF_task_ldt 0x00000370
#define ASIZ_task_ldt 0x00000008
#define AOFF_task_tss 0x00000380
-#define ASIZ_task_tss 0x00000600
-#define AOFF_task_fs 0x00000980
+#define ASIZ_task_tss 0x000004c0
+#define AOFF_task_fs 0x00000840
#define ASIZ_task_fs 0x00000008
-#define AOFF_task_files 0x00000988
+#define AOFF_task_files 0x00000848
#define ASIZ_task_files 0x00000008
-#define AOFF_task_mm 0x00000990
+#define AOFF_task_mm 0x00000850
#define ASIZ_task_mm 0x00000008
-#define AOFF_task_sig 0x00000998
+#define AOFF_task_sig 0x00000858
#define ASIZ_task_sig 0x00000008
-#define AOFF_task_has_cpu 0x000009a0
+#define AOFF_task_has_cpu 0x00000860
#define ASIZ_task_has_cpu 0x00000004
-#define AOFF_task_processor 0x000009a4
+#define AOFF_task_processor 0x00000864
#define ASIZ_task_processor 0x00000004
-#define AOFF_task_last_processor 0x000009a8
+#define AOFF_task_last_processor 0x00000868
#define ASIZ_task_last_processor 0x00000004
-#define AOFF_task_lock_depth 0x000009ac
+#define AOFF_task_lock_depth 0x0000086c
#define ASIZ_task_lock_depth 0x00000004
-#define AOFF_task_sigmask_lock 0x000009b0
+#define AOFF_task_sigmask_lock 0x00000870
#define ASIZ_task_sigmask_lock 0x00000000
#define AOFF_mm_mmap 0x00000000
#define ASIZ_mm_mmap 0x00000008
#define ASIZ_mm_def_flags 0x00000008
#define AOFF_mm_cpu_vm_mask 0x000000b8
#define ASIZ_mm_cpu_vm_mask 0x00000008
-#define AOFF_thread_float_regs 0x00000000
-#define ASIZ_thread_float_regs 0x00000100
-#define AOFF_thread_fsr 0x00000100
-#define ASIZ_thread_fsr 0x00000008
-#define AOFF_thread_ksp 0x00000108
+#define AOFF_thread_ksp 0x00000000
#define ASIZ_thread_ksp 0x00000008
-#define AOFF_thread_kpc 0x00000110
+#define AOFF_thread_kpc 0x00000008
#define ASIZ_thread_kpc 0x00000008
-#define AOFF_thread_wstate 0x00000118
+#define AOFF_thread_wstate 0x00000010
#define ASIZ_thread_wstate 0x00000008
-#define AOFF_thread_cwp 0x00000120
-#define ASIZ_thread_cwp 0x00000008
-#define AOFF_thread_ctx 0x00000128
-#define ASIZ_thread_ctx 0x00000008
-#define AOFF_thread_reg_window 0x00000130
+#define AOFF_thread_cwp 0x00000018
+#define ASIZ_thread_cwp 0x00000004
+#define AOFF_thread_ctx 0x0000001c
+#define ASIZ_thread_ctx 0x00000004
+#define AOFF_thread_flags 0x00000020
+#define ASIZ_thread_flags 0x00000004
+#define AOFF_thread_new_signal 0x00000024
+#define ASIZ_thread_new_signal 0x00000004
+#define AOFF_thread_current_ds 0x00000028
+#define ASIZ_thread_current_ds 0x00000008
+#define AOFF_thread_w_saved 0x00000030
+#define ASIZ_thread_w_saved 0x00000008
+#define AOFF_thread_kregs 0x00000038
+#define ASIZ_thread_kregs 0x00000008
+#define AOFF_thread_reg_window 0x00000040
#define ASIZ_thread_reg_window 0x00000400
-#define AOFF_thread_rwbuf_stkptrs 0x00000530
+#define AOFF_thread_rwbuf_stkptrs 0x00000440
#define ASIZ_thread_rwbuf_stkptrs 0x00000040
-#define AOFF_thread_w_saved 0x00000570
-#define ASIZ_thread_w_saved 0x00000008
-#define AOFF_thread_flags 0x00000578
-#define ASIZ_thread_flags 0x00000008
-#define AOFF_thread_sig_address 0x00000580
+#define AOFF_thread_sig_address 0x00000480
#define ASIZ_thread_sig_address 0x00000008
-#define AOFF_thread_sig_desc 0x00000588
+#define AOFF_thread_sig_desc 0x00000488
#define ASIZ_thread_sig_desc 0x00000008
-#define AOFF_thread_sstk_info 0x00000590
+#define AOFF_thread_sstk_info 0x00000490
#define ASIZ_thread_sstk_info 0x00000010
-#define AOFF_thread_current_ds 0x000005a0
-#define ASIZ_thread_current_ds 0x00000008
-#define AOFF_thread_new_signal 0x000005a8
-#define ASIZ_thread_new_signal 0x00000008
-#define AOFF_thread_kregs 0x000005b0
-#define ASIZ_thread_kregs 0x00000008
-#define AOFF_thread_core_exec 0x000005b8
+#define AOFF_thread_core_exec 0x000004a0
#define ASIZ_thread_core_exec 0x00000020
#endif /* __ASM_OFFSETS_H__ */
-/* $Id: atomic.h,v 1.14 1997/04/16 05:57:06 davem Exp $
+/* $Id: atomic.h,v 1.15 1997/07/03 09:18:09 davem Exp $
* atomic.h: Thankfully the V9 is at least reasonable for this
* stuff.
*
- * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
*/
#ifndef __ARCH_SPARC64_ATOMIC__
extern __inline__ void atomic_add(int i, atomic_t *v)
{
- unsigned long temp0, temp1;
__asm__ __volatile__("
- lduw [%3], %0
-1:
- add %0, %2, %1
- cas [%3], %0, %1
- cmp %0, %1
- bne,a,pn %%icc, 1b
- lduw [%3], %0
-2:
-" : "=&r" (temp0), "=&r" (temp1)
+1: lduw [%1], %%g1
+ add %%g1, %0, %%g2
+ cas [%1], %%g1, %%g2
+ sub %%g1, %%g2, %%g1
+ brnz,pn %%g1, 1b
+ nop"
+ : /* No outputs */
: "HIr" (i), "r" (__atomic_fool_gcc(v))
- : "cc");
+ : "g1", "g2");
}
extern __inline__ void atomic_sub(int i, atomic_t *v)
{
- unsigned long temp0, temp1;
__asm__ __volatile__("
- lduw [%3], %0
-1:
- sub %0, %2, %1
- cas [%3], %0, %1
- cmp %0, %1
- bne,a,pn %%icc, 1b
- lduw [%3], %0
-2:
-" : "=&r" (temp0), "=&r" (temp1)
+1: lduw [%1], %%g1
+ sub %%g1, %0, %%g2
+ cas [%1], %%g1, %%g2
+ sub %%g1, %%g2, %%g1
+ brnz,pn %%g1, 1b
+ nop"
+ : /* No outputs */
: "HIr" (i), "r" (__atomic_fool_gcc(v))
- : "cc");
+ : "g1", "g2");
}
/* Same as above, but return the result value. */
extern __inline__ int atomic_add_return(int i, atomic_t *v)
{
- unsigned long temp0, oldval;
+ unsigned long oldval;
__asm__ __volatile__("
- lduw [%3], %0
-1:
- add %0, %2, %1
- cas [%3], %0, %1
- cmp %0, %1
- bne,a,pn %%icc, 1b
- lduw [%3], %0
-2:
-" : "=&r" (temp0), "=&r" (oldval)
+1: lduw [%2], %%g1
+ add %%g1, %1, %%g2
+ cas [%2], %%g1, %%g2
+ sub %%g1, %%g2, %%g1
+ brnz,pn %%g1, 1b
+ add %%g2, %1, %0"
+ : "=&r" (oldval)
: "HIr" (i), "r" (__atomic_fool_gcc(v))
- : "cc");
- return (((int)oldval) + 1);
+ : "g1", "g2");
+ return (int)oldval;
}
extern __inline__ int atomic_sub_return(int i, atomic_t *v)
{
- unsigned long temp0, oldval;
+ unsigned long oldval;
__asm__ __volatile__("
- lduw [%3], %0
-1:
- sub %0, %2, %1
- cas [%3], %0, %1
- cmp %0, %1
- bne,a,pn %%icc, 1b
- lduw [%3], %0
-2:
-" : "=&r" (temp0), "=&r" (oldval)
+1: lduw [%2], %%g1
+ sub %%g1, %1, %%g2
+ cas [%2], %%g1, %%g2
+ sub %%g1, %%g2, %%g1
+ brnz,pn %%g1, 1b
+ sub %%g2, %1, %0"
+ : "=&r" (oldval)
: "HIr" (i), "r" (__atomic_fool_gcc(v))
- : "cc");
- return (((int)oldval) - 1);
+ : "g1", "g2");
+ return (int)oldval;
}
#define atomic_dec_return(v) atomic_sub_return(1,(v))
-/* $Id: bitops.h,v 1.17 1997/06/14 17:35:05 davem Exp $
+/* $Id: bitops.h,v 1.18 1997/06/30 12:36:18 davem Exp $
* bitops.h: Bit string operations on the V9.
*
* Copyright 1996 David S. Miller (davem@caip.rutgers.edu)
: "0" (word)
: "g1", "g2");
#else
-#ifdef EASY_CHEESE_VERSION
+#if 1 /* def EASY_CHEESE_VERSION */
result = 0;
while(word & 1) {
result++;
size -= 64;
result += 64;
}
- offset = size >> 6;
- size &= 63UL;
- while (offset) {
+ while (size & ~63UL) {
if (~(tmp = *(p++)))
goto found_middle;
result += 64;
- offset--;
+ size -= 64;
}
if (!size)
return result;
size -= 64;
result += 64;
}
- offset = size >> 6;
- size &= 63UL;
- while(offset) {
+ while(size & ~63) {
if(~(tmp = __swab64p(p++)))
goto found_middle;
result += 64;
- offset--;
+ size -= 64;
}
if(!size)
return result;
-/* $Id: checksum.h,v 1.8 1997/05/29 12:45:03 jj Exp $ */
+/* $Id: checksum.h,v 1.9 1997/06/26 04:05:17 davem Exp $ */
#ifndef __SPARC64_CHECKSUM_H
#define __SPARC64_CHECKSUM_H
* both operands.
*/
__asm__ __volatile__("
- sub %2, 4, %%g7
- lduw [%1 + 0x00], %0
- lduw [%1 + 0x04], %%g2
- lduw [%1 + 0x08], %%g3
- addcc %%g2, %0, %0
- addccc %%g3, %0, %0
- lduw [%1 + 0x0c], %%g2
- lduw [%1 + 0x10], %%g3
- addccc %%g2, %0, %0
- addc %0, %%g0, %0
-1:
- addcc %%g3, %0, %0
- add %1, 4, %1
- addccc %0, %%g0, %0
- subcc %%g7, 1, %%g7
- be,a,pt %%icc, 2f
- sll %0, 16, %%g2
- ba,pt %%xcc, 1b
- lduw [%1 + 0x10], %%g3
-2:
- addcc %0, %%g2, %%g2
- srl %%g2, 16, %0
- addc %0, %%g0, %0
- xnor %%g0, %0, %0
- srl %0, 0, %0
+ sub %2, 4, %%g7 ! IEU0
+ lduw [%1 + 0x00], %0 ! Load Group
+ lduw [%1 + 0x04], %%g2 ! Load Group
+ lduw [%1 + 0x08], %%g3 ! Load Group
+ addcc %%g2, %0, %0 ! IEU1 1 Load Bubble + Group
+ lduw [%1 + 0x0c], %%g2 ! Load
+ addccc %%g3, %0, %0 ! Sngle Group no Bubble
+ lduw [%1 + 0x10], %%g3 ! Load Group
+ addccc %%g2, %0, %0 ! Sngle Group no Bubble
+ addc %0, %%g0, %0 ! Sngle Group
+1: addcc %%g3, %0, %0 ! IEU1 Group no Bubble
+ add %1, 4, %1 ! IEU0
+ addccc %0, %%g0, %0 ! Sngle Group no Bubble
+ subcc %%g7, 1, %%g7 ! IEU1 Group
+ be,a,pt %%icc, 2f ! CTI
+ sll %0, 16, %%g2 ! IEU0
+ lduw [%1 + 0x10], %%g3 ! Load Group
+ ba,pt %%xcc, 1b ! CTI
+ nop ! IEU0
+2: addcc %0, %%g2, %%g2 ! IEU1 Group
+ srl %%g2, 16, %0 ! IEU0 Group regdep XXX Scheisse!
+ addc %0, %%g0, %0 ! Sngle Group
+ xnor %%g0, %0, %0 ! IEU0 Group
+ srl %0, 0, %0 ! IEU0 Group XXX Scheisse!
" : "=r" (sum), "=&r" (iph)
: "r" (ihl), "1" (iph)
: "g2", "g3", "g7", "cc");
-/* $Id: delay.h,v 1.4 1997/04/10 23:32:44 davem Exp $
+/* $Id: delay.h,v 1.5 1997/06/18 12:36:23 jj Exp $
* delay.h: Linux delay routines on the V9.
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu).
extern __inline__ void __delay(unsigned long loops)
{
__asm__ __volatile__("
- cmp %0, 0
+ b,pt %%xcc, 1f
+ cmp %0, 0
+ .align 32
1:
bne,pt %%xcc, 1b
subcc %0, 1, %0
__asm__ __volatile__("wr %0, 0x0, %%fprs" : : "r" (val));
}
-extern __inline__ void fpsave32(unsigned int *fpregs, unsigned long *fsr)
+extern __inline__ void fpsave(unsigned long *fpregs,
+ unsigned long *fsr,
+ unsigned long *gsr)
{
__asm__ __volatile__ ("
- wr %%g0, %2, %%asi
- stx %%fsr, [%1]
- stda %%f0, [%0] %%asi
- stda %%f16, [%0 + 64] %%asi
- " : : "r" (fpregs), "r" (fsr), "i" (ASI_BLK_P));
-}
-
-extern __inline__ void fpload32(unsigned int *fpregs, unsigned long *fsr)
-{
- __asm__ __volatile__ ("
- wr %%g0, %2, %%asi
- ldda [%0] %%asi, %%f0
- ldda [%0 + 64] %%asi, %%f16
- ldx [%1], %%fsr
- " : : "r" (fpregs), "r" (fsr), "i" (ASI_BLK_P));
-}
-
-extern __inline__ void fpsave64hi(unsigned int *fpregs, unsigned long *fsr)
-{
- __asm__ __volatile__ ("
- wr %%g0, %2, %%asi
- stx %%fsr, [%1]
- stda %%f32, [%0 + 128] %%asi
- stda %%f48, [%0 + 192] %%asi
- " : : "r" (fpregs), "r" (fsr), "i" (ASI_BLK_P));
-}
-
-extern __inline__ void fpload64hi(unsigned int *fpregs, unsigned long *fsr)
-{
- __asm__ __volatile__ ("
- wr %%g0, %2, %%asi
- ldda [%0 + 128] %%asi, %%f32
- ldda [%0 + 192] %%asi, %%f48
- ldx [%1], %%fsr
- " : : "r" (fpregs), "r" (fsr), "i" (ASI_BLK_P));
-}
-
-extern __inline__ void fpsave(unsigned int *fpregs, unsigned long *fsr)
-{
- __asm__ __volatile__ ("
- wr %%g0, %2, %%asi
+ wr %%g0, %3, %%asi
+ rd %%gsr, %%g1
+ membar #LoadStore | #StoreStore
stx %%fsr, [%1]
+ stx %%g1, [%2]
stda %%f0, [%0] %%asi
stda %%f16, [%0 + 64] %%asi
stda %%f32, [%0 + 128] %%asi
stda %%f48, [%0 + 192] %%asi
- " : : "r" (fpregs), "r" (fsr), "i" (ASI_BLK_P));
+ membar #Sync
+" : /* No outputs */
+ : "r" (fpregs), "r" (fsr), "r" (gsr), "i" (ASI_BLK_P)
+ : "g1");
}
-extern __inline__ void fpload(unsigned int *fpregs, unsigned long *fsr)
+extern __inline__ void fpload(unsigned long *fpregs,
+ unsigned long *fsr,
+ unsigned long *gsr)
{
__asm__ __volatile__ ("
- wr %%g0, %2, %%asi
+ wr %%g0, %3, %%asi
+ membar #StoreLoad | #LoadLoad
ldda [%0] %%asi, %%f0
ldda [%0 + 64] %%asi, %%f16
ldda [%0 + 128] %%asi, %%f32
ldda [%0 + 192] %%asi, %%f48
ldx [%1], %%fsr
- " : : "r" (fpregs), "r" (fsr), "i" (ASI_BLK_P));
+ ldx [%2], %%g1
+ wr %%g1, 0, %%gsr
+ membar #Sync
+" : /* No outputs */
+ : "r" (fpregs), "r" (fsr), "r" (gsr), "i" (ASI_BLK_P)
+ : "g1");
}
#endif /* !(_SPARC64_FPUMACRO_H) */
-/* $Id: head.h,v 1.23 1997/06/14 13:25:50 davem Exp $ */
+/* $Id: head.h,v 1.26 1997/07/07 03:05:23 davem Exp $ */
#ifndef _SPARC64_HEAD_H
#define _SPARC64_HEAD_H
#include <asm/pstate.h>
-#define KERNBASE 0xFFFFF80000000000
+#define KERNBASE 0x400000
#define BOOT_KERNEL b sparc64_boot; nop; nop; nop; nop; nop; nop; nop;
/* We need a "cleaned" instruction... */
nop; \
nop;
-/* Just for testing */
-#define PROM_TRAP \
- rd %pc, %g1; \
- sethi %uhi(KERNBASE), %g4; \
- sethi %hi(0xf0000000-0x8000), %g2; \
- sllx %g4, 32, %g4; \
- add %g1, %g2, %g1; \
- sub %g1, %g4, %g1; \
- jmpl %g1 + %g0, %g0; \
- nop;
-
#define TRAP_ARG(routine, arg) \
ba,pt %xcc, etrap; \
rd %pc, %g7; \
#define SUNOS_SYSCALL_TRAP SYSCALL_TRAP(linux_sparc_syscall, sunos_sys_table)
#define LINUX_32BIT_SYSCALL_TRAP SYSCALL_TRAP(linux_sparc_syscall, sys_call_table32)
#define LINUX_64BIT_SYSCALL_TRAP SYSCALL_TRAP(linux_sparc_syscall, sys_call_table64)
+#define GETCC_TRAP TRAP(getcc)
+#define SETCC_TRAP TRAP(setcc)
/* FIXME: Write these actually */
#define NETBSD_SYSCALL_TRAP TRAP(netbsd_syscall)
#define SOLARIS_SYSCALL_TRAP TRAP(solaris_syscall)
#define BREAKPOINT_TRAP TRAP(breakpoint_trap)
-#define GETCC_TRAP TRAP(getcc)
-#define SETCC_TRAP TRAP(setcc)
#define INDIRECT_SOLARIS_SYSCALL(tlvl) TRAP_ARG(indirect_syscall, tlvl)
#define TRAP_IRQ(routine, level) \
-/* $Id: ioctls.h,v 1.3 1997/06/14 17:35:08 davem Exp $ */
+/* $Id: ioctls.h,v 1.4 1997/06/23 07:26:03 davem Exp $ */
#ifndef _ASM_SPARC64_IOCTLS_H
#define _ASM_SPARC64_IOCTLS_H
/* 119 is the non-posix getpgrp tty ioctl */
#define __TIOCCDTR _IO('t', 120) /* SunOS Specific */
#define __TIOCSDTR _IO('t', 121) /* SunOS Specific */
-#define __TIOCCBRK _IO('t', 122) /* SunOS Specific */
-#define __TIOCSBRK _IO('t', 123) /* SunOS Specific */
+#define TIOCCBRK _IO('t', 122)
+#define TIOCSBRK _IO('t', 123)
#define __TIOCLGET _IOW('t', 124, int) /* SunOS Specific */
#define __TIOCLSET _IOW('t', 125, int) /* SunOS Specific */
#define __TIOCLBIC _IOW('t', 126, int) /* SunOS Specific */
-/* $Id: mmu_context.h,v 1.11 1997/06/13 14:03:04 davem Exp $ */
+/* $Id: mmu_context.h,v 1.16 1997/07/05 09:54:46 davem Exp $ */
#ifndef __SPARC64_MMU_CONTEXT_H
#define __SPARC64_MMU_CONTEXT_H
#define CTX_VERSION_MASK ((~0UL) << CTX_VERSION_SHIFT)
#define CTX_FIRST_VERSION ((1UL << CTX_VERSION_SHIFT) + 1UL)
-extern __inline__ void get_new_mmu_context(struct mm_struct *mm,
- unsigned long ctx)
-{
- if((ctx & ~(CTX_VERSION_MASK)) == 0) {
- unsigned long flags;
- int entry;
-
- save_and_cli(flags);
- __asm__ __volatile__("stxa %%g0, [%0] %1\n\t"
- "stxa %%g0, [%0] %2"
- : /* No outputs */
- : "r" (TLB_TAG_ACCESS), "i" (ASI_IMMU),
- "i" (ASI_DMMU));
- for(entry = 0; entry < 62; entry++) {
- spitfire_put_dtlb_data(entry, 0x0UL);
- spitfire_put_itlb_data(entry, 0x0UL);
- }
- membar("#Sync");
- __asm__ __volatile__("flush %g4");
- restore_flags(flags);
-
- ctx = (ctx & CTX_VERSION_MASK) + CTX_FIRST_VERSION;
- if(ctx == 1) /* _not_ zero! */
- ctx = CTX_FIRST_VERSION;
- }
- tlb_context_cache = ctx + 1;
- mm->context = ctx;
-}
+extern void get_new_mmu_context(struct mm_struct *mm, unsigned long ctx);
extern __inline__ void get_mmu_context(struct task_struct *tsk)
{
} else
tsk->tss.ctx = 0;
spitfire_set_secondary_context(tsk->tss.ctx);
- __asm__ __volatile__("flush %g4");
+ __asm__ __volatile__("flush %g6");
paddr = __pa(mm->pgd);
__asm__ __volatile__("
rdpr %%pstate, %%o4
-/* $Id: page.h,v 1.9 1997/06/14 21:28:09 davem Exp $ */
+/* $Id: page.h,v 1.14 1997/06/26 22:32:03 davem Exp $ */
#ifndef _SPARC64_PAGE_H
#define _SPARC64_PAGE_H
#ifndef __ASSEMBLY__
-#define clear_page(page) \
- __asm__ __volatile__( "mov %%o7, %%g3\n\t" \
- "call __bzero_1page\n\t" \
- " mov %0, %%g2\n\t" \
- : /* No outputs */ \
- : "r" (page) \
- : "g1", "g2", "g3")
+#define clear_page(page) memset((void *)(page), 0, PAGE_SIZE)
-#define copy_page(to,from) memcpy((void *)(to), (void *)(from), PAGE_SIZE)
+extern void copy_page(unsigned long to, unsigned long from);
-#define STRICT_MM_TYPECHECKS
+/* GROSS, defining this makes gcc pass these types as aggregates,
+ * and thus on the stack, turn this crap off... -DaveM
+ */
+
+/* #define STRICT_MM_TYPECHECKS */
#ifdef STRICT_MM_TYPECHECKS
/* These are used to make use of C type-checking.. */
#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK)
#ifndef __ASSEMBLY__
-#define PAGE_OFFSET 0xFFFFF80000000000UL
+/* Do prdele, look what happens to be in %g4... */
+register unsigned long page_offset asm("g4");
+#define PAGE_OFFSET page_offset
#else
#define PAGE_OFFSET 0xFFFFF80000000000
#endif
-/* $Id: pgtable.h,v 1.37 1997/06/13 14:03:06 davem Exp $
+/* $Id: pgtable.h,v 1.49 1997/06/30 09:24:12 jj Exp $
* pgtable.h: SpitFire page table operations.
*
* Copyright 1996,1997 David S. Miller (davem@caip.rutgers.edu)
#define PTRS_PER_PAGE (1UL << (PAGE_SHIFT-3))
/* NOTE: TLB miss handlers depend heavily upon where this is. */
-#define VMALLOC_START 0xFFFFFc0000000000UL
+#define VMALLOC_START 0x0000000800000000UL
#define VMALLOC_VMADDR(x) ((unsigned long)(x))
#endif /* !(__ASSEMBLY__) */
#define _PAGE_G 0x0000000000000001 /* Global */
/* Here are the SpitFire software bits we use in the TTE's. */
-#define _PAGE_PRESENT 0x0000000000001000 /* Present Page (ie. not swapped out) */
#define _PAGE_MODIFIED 0x0000000000000800 /* Modified Page (ie. dirty) */
#define _PAGE_ACCESSED 0x0000000000000400 /* Accessed Page (ie. referenced) */
#define _PAGE_READ 0x0000000000000200 /* Readable SW Bit */
#define _PAGE_WRITE 0x0000000000000100 /* Writable SW Bit */
+#define _PAGE_PRESENT 0x0000000000000080 /* Present Page (ie. not swapped out) */
#define _PAGE_CACHE (_PAGE_CP | _PAGE_CV)
* hit for all __pa()/__va() operations.
*/
extern unsigned long phys_base;
-
-#define ZERO_PAGE (PAGE_OFFSET + phys_base)
+#define ZERO_PAGE ((unsigned long)__va(phys_base))
/* This is for making TLB miss faster to process. */
extern unsigned long null_pmd_table;
/* Cache and TLB flush operations. */
-extern __inline__ void flush_cache_all(void)
-{
- unsigned long addr;
-
- flushw_all();
- for(addr = 0; addr < (PAGE_SIZE << 1); addr += 32)
- spitfire_put_icache_tag(addr, 0x0UL);
-}
-
-extern __inline__ void flush_cache_mm(struct mm_struct *mm)
-{
- if(mm->context != NO_CONTEXT) {
- unsigned long addr;
-
- flushw_user();
- for(addr = 0; addr < (PAGE_SIZE << 1); addr += 32)
- spitfire_put_icache_tag(addr, 0x0UL);
- }
-}
-
-extern __inline__ void flush_cache_range(struct mm_struct *mm, unsigned long start,
- unsigned long end)
-{
- if(mm->context != NO_CONTEXT) {
- unsigned long addr;
+#define flush_cache_all() \
+do { unsigned long va; \
+ flushw_all(); \
+ for(va = 0; \
+ va<(PAGE_SIZE<<1); \
+ va += 32) \
+spitfire_put_icache_tag(va,0x0);\
+} while(0)
- flushw_user();
- for(addr = 0; addr < (PAGE_SIZE << 1); addr += 32)
- spitfire_put_icache_tag(addr, 0x0UL);
- }
-}
-
-extern __inline__ void flush_cache_page(struct vm_area_struct *vma, unsigned long page)
-{
- struct mm_struct *mm = vma->vm_mm;
-
- if(mm->context != NO_CONTEXT) {
- unsigned long addr;
-
- flushw_user();
- for(addr = 0; addr < (PAGE_SIZE << 1); addr += 32)
- spitfire_put_icache_tag(addr, 0x0UL);
- }
-}
+#define flush_cache_mm(mm) do { } while(0)
+#define flush_cache_range(mm, start, end) do { } while(0)
+#define flush_cache_page(vma, page) do { } while(0)
/* This operation in unnecessary on the SpitFire since D-CACHE is write-through. */
#define flush_page_to_ram(page) do { } while (0)
-extern __inline__ void flush_tlb_all(void)
-{
- unsigned long flags;
- int entry;
-
- /* Invalidate all non-locked TTE's in both the dtlb and itlb. */
- save_and_cli(flags);
- __asm__ __volatile__("stxa %%g0, [%0] %1\n\t"
- "stxa %%g0, [%0] %2"
- : /* No outputs */
- : "r" (TLB_TAG_ACCESS), "i" (ASI_IMMU), "i" (ASI_DMMU));
- for(entry = 0; entry < 62; entry++) {
- spitfire_put_dtlb_data(entry, 0x0UL);
- spitfire_put_itlb_data(entry, 0x0UL);
- }
- membar("#Sync");
- flushi(PAGE_OFFSET);
- restore_flags(flags);
-}
+extern void flush_tlb_all(void);
+extern void __flush_tlb_mm(unsigned long context);
extern __inline__ void flush_tlb_mm(struct mm_struct *mm)
{
- if(mm->context != NO_CONTEXT) {
- __asm__ __volatile__("
- /* flush_tlb_mm() */
- rdpr %%pil, %%g1
- mov %1, %%g7
- wrpr %%g0, 15, %%pil
- ldxa [%%g7] %2, %%g2
- cmp %%g2, %0
- be,pt %%icc, 1f
- mov 0x50, %%g3
- stxa %0, [%%g7] %2
-1:
- stxa %%g0, [%%g3] %3
- stxa %%g0, [%%g3] %4
- be,pt %%icc, 1f
- nop
- stxa %%g2, [%%g7] %2
-1:
- flush %%g4
- wrpr %%g1, 0x0, %%pil
-" : /* no outputs */
- : "r" (mm->context & 0x1fff), "i" (SECONDARY_CONTEXT), "i" (ASI_DMMU),
- "i" (ASI_DMMU_DEMAP), "i" (ASI_IMMU_DEMAP)
- : "g1", "g2", "g3", "g7", "cc");
- }
+ if(mm->context != NO_CONTEXT)
+ __flush_tlb_mm(mm->context & 0x1fff);
}
+extern void __flush_tlb_range(unsigned long context, unsigned long start,
+ unsigned long end);
extern __inline__ void flush_tlb_range(struct mm_struct *mm, unsigned long start,
unsigned long end)
{
- if(mm->context != NO_CONTEXT) {
- unsigned long old_ctx = spitfire_get_secondary_context();
- unsigned long new_ctx = (mm->context & 0x1fff);
- unsigned long flags;
-
- start &= PAGE_MASK;
- save_and_cli(flags);
- if(new_ctx != old_ctx)
- spitfire_set_secondary_context(new_ctx);
- while(start < end) {
- spitfire_flush_dtlb_secondary_page(start);
- spitfire_flush_itlb_secondary_page(start);
- start += PAGE_SIZE;
- }
- if(new_ctx != old_ctx)
- spitfire_set_secondary_context(old_ctx);
- __asm__ __volatile__("flush %g4");
- restore_flags(flags);
- }
+ if(mm->context != NO_CONTEXT)
+ __flush_tlb_range(mm->context & 0x1fff, start, end);
}
+extern void __flush_tlb_page(unsigned long context, unsigned long page);
extern __inline__ void flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
{
struct mm_struct *mm = vma->vm_mm;
- if(mm->context != NO_CONTEXT) {
- __asm__ __volatile__("
- /* flush_tlb_page() */
- rdpr %%pil, %%g1
- mov %1, %%g7
- wrpr %%g0, 15, %%pil
- ldxa [%%g7] %2, %%g2
- cmp %%g2, %0
- be,pt %%icc, 1f
- or %5, 0x10, %%g3
- stxa %0, [%%g7] %2
-1:
- stxa %%g0, [%%g3] %3
- stxa %%g0, [%%g3] %4
- be,pt %%icc, 1f
- nop
- stxa %%g2, [%%g7] %2
-1:
- flush %%g4
- wrpr %%g1, 0x0, %%pil
-" : /* no outputs */
- : "r" (mm->context & 0x1fff), "i" (SECONDARY_CONTEXT), "i" (ASI_DMMU),
- "i" (ASI_DMMU_DEMAP), "i" (ASI_IMMU_DEMAP), "r" (page & PAGE_MASK)
- : "g1", "g2", "g3", "g7", "cc");
- }
+ if(mm->context != NO_CONTEXT)
+ __flush_tlb_page(mm->context & 0x1fff, page & PAGE_MASK);
}
extern inline pte_t mk_pte(unsigned long page, pgprot_t pgprot)
return __pte(pte_val(pte) | (_PAGE_ACCESSED));
}
-extern inline void SET_PAGE_DIR(struct task_struct *tsk, pgd_t *pgdir)
-{
- register unsigned long paddr asm("o5");
-
- paddr = __pa(pgdir);
-
- if(tsk == current) {
- __asm__ __volatile__ ("
- rdpr %%pstate, %%o4
- wrpr %%o4, %1, %%pstate
- mov %0, %%g7
- wrpr %%o4, 0x0, %%pstate
- " : /* No outputs */
- : "r" (paddr), "i" (PSTATE_MG|PSTATE_IE)
- : "o4");
- }
-}
-
/* to find an entry in a page-table-directory. */
extern inline pgd_t *pgd_offset(struct mm_struct *mm, unsigned long address)
{ return mm->pgd + ((address >> PGDIR_SHIFT) & (PTRS_PER_PAGE - 1)); }
extern __inline__ void __init_pmd(pmd_t *pmdp)
{
- extern void __bfill64(void *, unsigned long);
+ extern void __bfill64(void *, unsigned long *);
- __bfill64((void *)pmdp, null_pte_table);
+ __bfill64((void *)pmdp, &null_pte_table);
}
+/* Turning this off makes things much faster, but eliminates some
+ * sanity checking as well.
+ */
+/* #define PGTABLE_SANITY_CHECKS */
+
/* Allocate and free page tables. The xxx_kernel() versions are
* used to allocate a kernel page table - this turns on supervisor
* bits if any.
}
free_page((unsigned long) page);
}
+#ifdef PGTABLE_SANITY_CHECKS
if (pmd_bad(*pmd)) {
printk("Bad pmd in pte_alloc_kernel: %08lx\n", pmd_val(*pmd));
pmd_set(pmd, BAD_PTE);
return NULL;
}
+#endif
return (pte_t *) pmd_page(*pmd) + address;
}
}
free_page((unsigned long) page);
}
+#ifdef PGTABLE_SANITY_CHECKS
if (pgd_bad(*pgd)) {
printk("Bad pgd in pmd_alloc_kernel: %08lx\n", pgd_val(*pgd));
pgd_set(pgd, BAD_PMD);
return NULL;
}
+#endif
return (pmd_t *) pgd_page(*pgd) + address;
}
}
free_page((unsigned long) page);
}
+#ifdef PGTABLE_SANITY_CHECKS
if (pmd_bad(*pmd)) {
printk("Bad pmd in pte_alloc: %08lx\n", pmd_val(*pmd));
pmd_set(pmd, BAD_PTE);
return NULL;
}
+#endif
return (pte_t *) pmd_page(*pmd) + address;
}
}
free_page((unsigned long) page);
}
+#ifdef PGTABLE_SANITY_CHECKS
if (pgd_bad(*pgd)) {
printk("Bad pgd in pmd_alloc: %08lx\n", pgd_val(*pgd));
pgd_set(pgd, BAD_PMD);
return NULL;
}
+#endif
return (pmd_t *) pgd_page(*pgd) + address;
}
extern inline pgd_t * pgd_alloc(void)
{
- extern void __bfill64(void *, unsigned long);
+ extern void __bfill64(void *, unsigned long *);
pgd_t *pgd = (pgd_t *) __get_free_page(GFP_KERNEL);
if (pgd)
- __bfill64((void *)pgd, null_pmd_table);
+ __bfill64((void *)pgd, &null_pmd_table);
return pgd;
}
extern pgd_t swapper_pg_dir[1024];
+extern inline void SET_PAGE_DIR(struct task_struct *tsk, pgd_t *pgdir)
+{
+ if(pgdir != swapper_pg_dir && tsk == current) {
+ register unsigned long paddr asm("o5");
+
+ paddr = __pa(pgdir);
+ __asm__ __volatile__ ("
+ rdpr %%pstate, %%o4
+ wrpr %%o4, %1, %%pstate
+ mov %0, %%g7
+ wrpr %%o4, 0x0, %%pstate
+ " : /* No outputs */
+ : "r" (paddr), "i" (PSTATE_MG|PSTATE_IE)
+ : "o4");
+ }
+}
+
/* Routines for getting a dvma scsi buffer. */
struct mmu_sglist {
char *addr;
#define mmu_lockarea(vaddr, len) (vaddr)
#define mmu_unlockarea(vaddr, len) do { } while(0)
+extern void fixup_dcache_alias(struct vm_area_struct *vma, unsigned long address,
+ pte_t pte);
+
extern inline void update_mmu_cache(struct vm_area_struct * vma,
unsigned long address, pte_t pte)
{
/* Find and fix bad virutal cache aliases. */
- if((vma->vm_flags & (VM_WRITE|VM_SHARED)) == (VM_WRITE|VM_SHARED)) {
- struct vm_area_struct *vmaring;
- struct inode *inode;
- unsigned long vaddr, offset, start;
- pgd_t *pgdp;
- pmd_t *pmdp;
- pte_t *ptep;
- int alias_found = 0;
-
- inode = vma->vm_inode;
- if(!inode)
- return;
-
- offset = (address & PAGE_MASK) - vma->vm_start;
- vmaring = inode->i_mmap;
- do {
- vaddr = vmaring->vm_start + offset;
-
- /* This conditional is misleading... */
- if((vaddr ^ address) & PAGE_SIZE) {
- alias_found++;
- start = vmaring->vm_start;
- while(start < vmaring->vm_end) {
- pgdp = pgd_offset(vmaring->vm_mm, start);
- if(!pgdp) goto next;
- pmdp = pmd_offset(pgdp, start);
- if(!pmdp) goto next;
- ptep = pte_offset(pmdp, start);
- if(!ptep) goto next;
-
- if(pte_val(*ptep) & _PAGE_PRESENT) {
- flush_cache_page(vmaring, start);
- *ptep = __pte(pte_val(*ptep) &
- ~(_PAGE_CV));
- flush_tlb_page(vmaring, start);
- }
- next:
- start += PAGE_SIZE;
- }
- }
- } while((vmaring = vmaring->vm_next_share) != NULL);
-
- if(alias_found && (pte_val(pte) & _PAGE_CV)) {
- pgdp = pgd_offset(vma->vm_mm, address);
- pmdp = pmd_offset(pgdp, address);
- ptep = pte_offset(pmdp, address);
- flush_cache_page(vma, address);
- *ptep = __pte(pte_val(*ptep) & ~(_PAGE_CV));
- flush_tlb_page(vma, address);
- }
- }
+ if((vma->vm_flags & (VM_WRITE|VM_SHARED)) == (VM_WRITE|VM_SHARED))
+ fixup_dcache_alias(vma, address, pte);
}
/* Make a non-present pseudo-TTE. */
pgd_t *pgdp;
pmd_t *pmdp;
pte_t *ptep;
-
+
+ if (addr >= PAGE_OFFSET)
+ return addr & _PAGE_PADDR;
pgdp = pgd_offset_k (addr);
pmdp = pmd_offset (pgdp, addr);
ptep = pte_offset (pmdp, addr);
return ((sun4u_get_pte (addr) & 0xf0000000) >> 28);
}
+extern void * module_map (unsigned long size);
+extern void module_unmap (void *addr);
+
#endif /* !(__ASSEMBLY__) */
#endif /* !(_SPARC64_PGTABLE_H) */
-/* $Id: processor.h,v 1.29 1997/06/16 04:45:05 davem Exp $
+/* $Id: processor.h,v 1.32 1997/07/01 21:59:38 davem Exp $
* include/asm-sparc64/processor.h
*
* Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
/* The Sparc processor specific thread struct. */
struct thread_struct {
- /* Floating point regs */
- /* Please check asm_offsets, so that not to much precious space
- is wasted by this alignment and move the float_regs wherever
- is better in this structure. Remember every byte of alignment
- is multiplied by 512 to get the amount of wasted kernel memory. */
- unsigned int float_regs[64] __attribute__ ((aligned (64)));
- unsigned long fsr;
-
- /* Context switch saved kernel state. */
- unsigned long ksp, kpc, wstate, cwp, ctx;
+/*DC1*/ unsigned long ksp __attribute__ ((aligned(16)));
+ unsigned long kpc;
+/*DC2*/ unsigned long wstate;
+ unsigned int cwp;
+ unsigned int ctx;
+
+/*DC3*/ unsigned int flags;
+ unsigned int new_signal;
+ unsigned long current_ds;
+/*DC4*/ unsigned long w_saved;
+ struct pt_regs *kregs;
- /* Storage for windows when user stack is bogus. */
struct reg_window reg_window[NSWINS] __attribute__ ((aligned (16)));
unsigned long rwbuf_stkptrs[NSWINS] __attribute__ ((aligned (8)));
- unsigned long w_saved;
- /* Arch-specific task state flags, see below. */
- unsigned long flags;
-
- /* For signal handling */
unsigned long sig_address __attribute__ ((aligned (8)));
unsigned long sig_desc;
-
struct sigstack sstk_info;
- unsigned long current_ds;
- unsigned long new_signal;
-
- struct pt_regs *kregs;
-
struct exec core_exec; /* just what it says. */
};
PAGE_SHARED , VM_READ | VM_WRITE | VM_EXEC, NULL, &init_mm.mmap }
#define INIT_TSS { \
-/* FPU regs */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, \
-/* FPU status */ \
- 0, \
/* ksp, kpc, wstate, cwp, secctx */ \
0, 0, 0, 0, 0, \
+/* flags, new_signal, current_ds, */ \
+ SPARC_FLAG_KTHREAD, 0, USER_DS, \
+/* w_saved, kregs, */ \
+ 0, 0, \
/* reg_window */ \
-{ { { 0, }, { 0, } }, }, \
+ { { { 0, }, { 0, } }, }, \
/* rwbuf_stkptrs */ \
-{ 0, 0, 0, 0, 0, 0, 0, 0, }, \
-/* w_saved */ \
- 0, \
-/* flags */ \
- SPARC_FLAG_KTHREAD, \
-/* sig_address, sig_desc */ \
- 0, 0, \
-/* ex, sstk_info, current_ds, */ \
- { 0, 0, }, USER_DS, \
-/* new_signal, kregs */ \
- 0, 0, \
-/* core_exec */ \
-{ 0, }, \
+ { 0, 0, 0, 0, 0, 0, 0, 0, }, \
+/* sig_address, sig_desc, sstk_info, core_exec */ \
+ 0, 0, { 0, 0, }, { 0, }, \
}
#ifndef __ASSEMBLY__
/* Do necessary setup to start up a newly executed thread. */
#define start_thread(regs, pc, sp) \
do { \
- regs->tstate = (regs->tstate & (TSTATE_CWP)) | TSTATE_IE; \
+ regs->tstate = (regs->tstate & (TSTATE_CWP)) | (TSTATE_IE|TSTATE_PEF); \
regs->tpc = ((pc & (~3)) - 4); \
regs->tnpc = regs->tpc + 4; \
regs->y = 0; \
current->tss.flags &= ~SPARC_FLAG_32BIT; \
+ current->tss.wstate = (1 << 3); \
__asm__ __volatile__( \
"stx %%g0, [%0 + %2 + 0x00]\n\t" \
"stx %%g0, [%0 + %2 + 0x08]\n\t" \
pc &= 0x00000000ffffffffUL; \
sp &= 0x00000000ffffffffUL; \
\
- regs->tstate = (regs->tstate & (TSTATE_CWP)) | (TSTATE_IE | TSTATE_AM); \
+ regs->tstate = (regs->tstate & (TSTATE_CWP))|(TSTATE_IE|TSTATE_AM|TSTATE_PEF); \
regs->tpc = ((pc & (~3)) - 4); \
regs->tnpc = regs->tpc + 4; \
regs->y = 0; \
current->tss.flags |= SPARC_FLAG_32BIT; \
+ current->tss.wstate = (2 << 3); \
zero = 0; \
__asm__ __volatile__( \
"stx %%g0, [%0 + %2 + 0x00]\n\t" \
-/* $Id: psrcompat.h,v 1.3 1997/06/05 06:22:54 davem Exp $ */
+/* $Id: psrcompat.h,v 1.4 1997/06/20 11:54:39 davem Exp $ */
#ifndef _SPARC64_PSRCOMPAT_H
#define _SPARC64_PSRCOMPAT_H
extern inline unsigned int tstate_to_psr(unsigned long tstate)
{
- unsigned int psr;
unsigned long vers;
- /* These fields are in the same place. */
- psr = (tstate & (TSTATE_CWP | TSTATE_PEF));
-
- /* This is what the user would have always seen. */
- psr |= PSR_S;
-
- /* Slam in the 32-bit condition codes. */
- psr |= ((tstate & TSTATE_ICC) >> 12);
-
- /* This is completely arbitrary. */
__asm__ __volatile__("rdpr %%ver, %0" : "=r" (vers));
- psr |= ((vers << 8) >> 32) & PSR_IMPL;
- psr |= ((vers << 24) >> 36) & PSR_VERS;
-
- return psr;
+ return ((tstate & TSTATE_CWP) |
+ PSR_S |
+ ((tstate & TSTATE_ICC) >> 12) |
+ (((vers << 8) >> 32) & PSR_IMPL) |
+ (((vers << 24) >> 36) & PSR_VERS));
}
extern inline unsigned long psr_to_tstate_icc(unsigned int psr)
{
- unsigned long tstate;
-
- tstate = ((unsigned long)(psr & PSR_ICC)) << 12;
-
- return tstate;
+ return ((unsigned long)(psr & PSR_ICC)) << 12;
}
#endif /* !(_SPARC64_PSRCOMPAT_H) */
-/* $Id: pstate.h,v 1.4 1997/05/29 12:45:02 jj Exp $ */
+/* $Id: pstate.h,v 1.6 1997/06/25 07:39:45 jj Exp $ */
#ifndef _SPARC64_PSTATE_H
#define _SPARC64_PSTATE_H
#define PSTATE_CLE 0x0000000000000200 /* Current Little Endian. */
#define PSTATE_TLE 0x0000000000000100 /* Trap Little Endian. */
#define PSTATE_MM 0x00000000000000c0 /* Memory Model. */
+#define PSTATE_TSO 0x0000000000000000 /* MM: Total Store Order */
+#define PSTATE_PSO 0x0000000000000040 /* MM: Partial Store Order */
+#define PSTATE_RMO 0x0000000000000080 /* MM: Relaxed Memory Order */
#define PSTATE_RED 0x0000000000000020 /* Reset Error Debug State. */
#define PSTATE_PEF 0x0000000000000010 /* Floating Point Enable. */
#define PSTATE_AM 0x0000000000000008 /* Address Mask. */
#define TSTATE_CLE 0x0000000000020000 /* Current Little Endian. */
#define TSTATE_TLE 0x0000000000010000 /* Trap Little Endian. */
#define TSTATE_MM 0x000000000000c000 /* Memory Model. */
+#define TSTATE_TSO 0x0000000000000000 /* MM: Total Store Order */
+#define TSTATE_PSO 0x0000000000004000 /* MM: Partial Store Order */
+#define TSTATE_RMO 0x0000000000008000 /* MM: Relaxed Memory Order */
#define TSTATE_RED 0x0000000000002000 /* Reset Error Debug State. */
#define TSTATE_PEF 0x0000000000001000 /* Floating Point Enable. */
#define TSTATE_AM 0x0000000000000800 /* Address Mask. */
-/* $Id: ptrace.h,v 1.8 1997/05/27 19:30:27 jj Exp $ */
+/* $Id: ptrace.h,v 1.12 1997/06/24 16:30:35 davem Exp $ */
#ifndef _SPARC64_PTRACE_H
#define _SPARC64_PTRACE_H
unsigned long tstate;
unsigned long tpc;
unsigned long tnpc;
- unsigned long y;
+ unsigned int y;
+ unsigned int fprs;
};
struct pt_regs32 {
#define PT_V9_TPC 0x88
#define PT_V9_TNPC 0x90
#define PT_V9_Y 0x98
+#define PT_V9_FPRS 0x9c
#define PT_TSTATE PT_V9_TSTATE
#define PT_TPC PT_V9_TPC
#define PT_TNPC PT_V9_TNPC
#define PTRACE_GETFPAREGS 20
#define PTRACE_SETFPAREGS 21
+/* There are for debugging 64-bit processes, either from a 32 or 64 bit
+ * parent. Thus their compliments are for debugging 32-bit processes only.
+ */
+
+#define PTRACE_GETREGS64 22
+#define PTRACE_SETREGS64 23
+/* PTRACE_SYSCALL is 24 */
+#define PTRACE_GETFPREGS64 25
+#define PTRACE_SETFPREGS64 26
+
#define PTRACE_GETUCODE 29 /* stupid bsd-ism */
+/* These are for 32-bit processes debugging 64-bit ones.
+ * Here addr and addr2 are passed in %g2 and %g3 respectively.
+ */
+#define PTRACE_PEEKTEXT64 (30 + PTRACE_PEEKTEXT)
+#define PTRACE_POKETEXT64 (30 + PTRACE_POKETEXT)
+#define PTRACE_PEEKDATA64 (30 + PTRACE_PEEKDATA)
+#define PTRACE_POKEDATA64 (30 + PTRACE_POKEDATA)
+#define PTRACE_READDATA64 (30 + PTRACE_READDATA)
+#define PTRACE_WRITEDATA64 (30 + PTRACE_WRITEDATA)
+#define PTRACE_READTEXT64 (30 + PTRACE_READTEXT)
+#define PTRACE_WRITETEXT64 (30 + PTRACE_WRITETEXT)
+
#endif /* !(_SPARC64_PTRACE_H) */
-/* $Id: reg.h,v 1.1 1996/12/26 14:22:34 davem Exp $
+/* $Id: reg.h,v 1.2 1997/06/24 23:19:55 davem Exp $
* linux/asm-sparc64/reg.h
* Layout of the registers as expected by gdb on the Sparc
* we should replace the user.h definitions with those in
struct fp_status f_fpstatus;
};
+struct regs64 {
+ unsigned long r_g1;
+ unsigned long r_g2;
+ unsigned long r_g3;
+ unsigned long r_g4;
+ unsigned long r_g5;
+ unsigned long r_g6;
+ unsigned long r_g7;
+ unsigned long r_o0;
+ unsigned long r_o1;
+ unsigned long r_o2;
+ unsigned long r_o3;
+ unsigned long r_o4;
+ unsigned long r_o5;
+ unsigned long r_o6;
+ unsigned long r_o7;
+ unsigned long tstate;
+ unsigned long tpc;
+ unsigned long tnpc;
+ unsigned int y;
+ unsigned int fprs;
+};
+
+struct fp_status64 {
+ unsigned long regs[32];
+ unsigned long fsr;
+};
+
+
#endif /* __SPARC64_REG_H */
-/* $Id: sigcontext.h,v 1.6 1997/06/16 00:29:25 richard Exp $ */
+/* $Id: sigcontext.h,v 1.8 1997/06/20 11:54:41 davem Exp $ */
#ifndef __SPARC64_SIGCONTEXT_H
#define __SPARC64_SIGCONTEXT_H
int si_mask;
} __siginfo32_t;
-typedef struct {
- unsigned int si_float_regs [32];
- unsigned int si_fsr;
- unsigned int si_fpqdepth;
- struct {
- unsigned int *insn_addr;
- unsigned int insn;
- } si_fpqueue [16];
-} __siginfo_fpu32_t;
-
-
typedef struct {
struct pt_regs si_regs;
int si_mask;
typedef struct {
unsigned int si_float_regs [64];
unsigned long si_fsr;
+ unsigned long si_gsr;
unsigned int si_fpqdepth;
struct {
unsigned int *insn_addr;
-/* $Id: string.h,v 1.5 1997/05/18 04:16:57 davem Exp $
+/* $Id: string.h,v 1.6 1997/06/24 17:29:14 jj Exp $
* string.h: External definitions for optimized assembly string
* routines for the Linux Kernel.
*
#ifdef __KERNEL__
+#include <asm/asi.h>
+
extern void __memmove(void *,const void *,__kernel_size_t);
extern __kernel_size_t __memcpy(void *,const void *,__kernel_size_t);
+extern __kernel_size_t __memcpy_short(void *,const void *,__kernel_size_t,long,long);
+extern __kernel_size_t __memcpy_entry(void *,const void *,__kernel_size_t,long,long);
+extern __kernel_size_t __memcpy_16plus(void *,const void *,__kernel_size_t,long,long);
+extern __kernel_size_t __memcpy_384plus(void *,const void *,__kernel_size_t,long,long);
extern __kernel_size_t __memset(void *,int,__kernel_size_t);
#ifndef EXPORT_SYMTAB
extern inline void *__constant_memcpy(void *to, const void *from, __kernel_size_t n)
{
- extern void __copy_1page(void *, const void *);
-
if(n) {
if(n <= 32) {
__builtin_memcpy(to, from, n);
+#if 0
+ } else if (n < 384) {
+ __memcpy_16plus(to, from, n, ASI_BLK_P, ASI_BLK_P);
} else {
-#if 0
- switch(n) {
- case 8192:
- __copy_1page(to, from);
- break;
- default:
-#endif
- __memcpy(to, from, n);
-#if 0
- break;
- }
-#endif
+ __memcpy_384plus(to, from, n, ASI_BLK_P, ASI_BLK_P);
}
+#else
+ } else {
+ __memcpy(to, from, n);
+ }
+#endif
}
return to;
}
extern inline void *__nonconstant_memcpy(void *to, const void *from, __kernel_size_t n)
{
+#if 0
+ __memcpy_entry(to, from, n, ASI_BLK_P, ASI_BLK_P);
+#else
__memcpy(to, from, n);
+#endif
return to;
}
extern inline void *__constant_c_and_count_memset(void *s, char c, __kernel_size_t count)
{
- extern void *bzero_1page(void *);
+ extern void *__bzero_1page(void *);
extern __kernel_size_t __bzero(void *, __kernel_size_t);
if(!c) {
-#if 0
- if(count == 8192)
- bzero_1page(s);
+ if (count == 8192)
+ __bzero_1page(s);
else
-#endif
__bzero(s, count);
} else {
__memset(s, c, count);
-/* $Id: system.h,v 1.23 1997/06/16 06:17:06 davem Exp $ */
+/* $Id: system.h,v 1.26 1997/06/28 10:04:03 davem Exp $ */
#ifndef __SPARC64_SYSTEM_H
#define __SPARC64_SYSTEM_H
{
__asm__ __volatile__("
rdpr %%otherwin, %%g1
- brz,pt %%g1, 2f
+ brz,pt %%g1, 1f
+ mov %%o7, %%g3
+ call __flushw_user
clr %%g2
-1:
- save %%sp, %0, %%sp
- rdpr %%otherwin, %%g1
- brnz,pt %%g1, 1b
- add %%g2, 1, %%g2
-1:
- subcc %%g2, 1, %%g2
- bne,pt %%xcc, 1b
- restore %%g0, %%g0, %%g0
-2:
- " : : "i" (-REGWIN_SZ)
- : "g1", "g2", "cc");
+1:" : : : "g1", "g2", "g3");
}
#define flush_user_windows flushw_user
-#ifdef __SMP__
-
-#include <asm/fpumacro.h>
-
-#define SWITCH_ENTER(prev) \
- if((prev)->flags & PF_USEDFPU) { \
- fprs_write(FPRS_FEF); \
- fpsave((unsigned long *) &(prev)->tss.float_regs[0], \
- &(prev)->tss.fsr); \
- (prev)->flags &= ~PF_USEDFPU; \
- (prev)->tss.kregs->tstate &= ~TSTATE_PEF; \
- }
-
-#define SWITCH_DO_LAZY_FPU(next)
-#else
-#define SWITCH_ENTER(prev)
-#define SWITCH_DO_LAZY_FPU(next) \
- if(last_task_used_math != (next)) \
- (next)->tss.kregs->tstate &= ~TSTATE_PEF
-#endif
-
/* See what happens when you design the chip correctly?
* NOTE NOTE NOTE this is extremely non-trivial what I
* am doing here. GCC needs only one register to stuff
do { \
__label__ switch_continue; \
register unsigned long task_pc asm("o7"); \
- SWITCH_ENTER(prev) \
- SWITCH_DO_LAZY_FPU(next); \
+ (prev)->tss.kregs->fprs = 0; \
task_pc = ((unsigned long) &&switch_continue) - 0x8; \
__asm__ __volatile__( \
"rdpr %%pstate, %%g2\n\t" \
- "wrpr %%g2, 0x2, %%pstate\n\t" \
+ "wrpr %%g2, 0x3, %%pstate\n\t" \
"flushw\n\t" \
+/*XXX*/ "wr %%g0, 0, %%fprs\n\t" \
"stx %%i6, [%%sp + 2047 + 0x70]\n\t" \
"stx %%i7, [%%sp + 2047 + 0x78]\n\t" \
"rdpr %%wstate, %%o5\n\t" \
"stx %%o5, [%%g6 + %2]\n\t" \
"rdpr %%cwp, %%o5\n\t" \
"stx %%o7, [%%g6 + %4]\n\t" \
- "stx %%o5, [%%g6 + %5]\n\t" \
+ "st %%o5, [%%g6 + %5]\n\t" \
"mov %0, %%g6\n\t" \
- "ldx [%0 + %5], %%g1\n\t" \
- "wr %0, 0x0, %%pic\n\t" \
+ "ld [%0 + %5], %%g1\n\t" \
"wrpr %%g1, %%cwp\n\t" \
"ldx [%%g6 + %2], %%o5\n\t" \
"ldx [%%g6 + %3], %%o6\n\t" \
"ldx [%%g6 + %4], %%o7\n\t" \
+ "mov %%g6, %0\n\t" \
"wrpr %%o5, 0x0, %%wstate\n\t" \
"ldx [%%sp + 2047 + 0x70], %%i6\n\t" \
"ldx [%%sp + 2047 + 0x78], %%i7\n\t" \
+ "wrpr %%g0, 0x96, %%pstate\n\t" \
"jmpl %%o7 + 0x8, %%g0\n\t" \
- " wrpr %%g2, 0x0, %%pstate\n\t" \
+ " mov %0, %%g6\n\t" \
: /* No outputs */ \
: "r" (next), "r" (task_pc), \
"i" ((const unsigned long)(&((struct task_struct *)0)->tss.wstate)), \
-/* $Id: uaccess.h,v 1.14 1997/06/13 14:03:11 davem Exp $ */
+/* $Id: uaccess.h,v 1.19 1997/06/30 10:31:46 jj Exp $ */
#ifndef _ASM_UACCESS_H
#define _ASM_UACCESS_H
current->tss.ctx = (current->mm->context & 0x1fff); \
} \
spitfire_set_secondary_context(current->tss.ctx); \
- __asm__ __volatile__("flush %g4"); \
+ __asm__ __volatile__("flush %g6"); \
} while(0)
#define __user_ok(addr,size) 1
extern int __get_user_bad(void);
-extern __kernel_size_t __copy_to_user(void *to, void *from, __kernel_size_t size);
-extern __kernel_size_t __copy_from_user(void *to, void *from, __kernel_size_t size);
+extern __kernel_size_t __memcpy_short(void *to, const void *from, __kernel_size_t size, long asi_src, long asi_dst);
+extern __kernel_size_t __memcpy_entry(void *to, const void *from, __kernel_size_t size, long asi_src, long asi_dst);
+extern __kernel_size_t __memcpy_16plus(void *to, const void *from, __kernel_size_t size, long asi_src, long asi_dst);
+extern __kernel_size_t __memcpy_386plus(void *to, const void *from, __kernel_size_t size, long asi_src, long asi_dst);
+
+#if 0
+extern __inline__ __kernel_size_t __copy_to_user(void *to, void *from, __kernel_size_t size)
+{
+ if (__builtin_constant_p(size)) {
+ if (!size) return 0;
+ if (size < 16)
+ return __memcpy_short(to,(const void *)from,size,ASI_BLK_P,ASI_BLK_S);
+ else if (size < 384)
+ return __memcpy_16plus(to,(const void *)from,size,ASI_BLK_P,ASI_BLK_S);
+ else
+ return __memcpy_386plus(to,(const void *)from,size,ASI_BLK_P,ASI_BLK_S);
+ } else {
+ if (!size) return 0;
+ return __memcpy_entry(to,(const void *)from,size,ASI_BLK_P,ASI_BLK_S);
+ }
+}
+
+extern __inline__ __kernel_size_t __copy_from_user(void *to, void *from, __kernel_size_t size)
+{
+ if (__builtin_constant_p(size)) {
+ if (!size) return 0;
+ if (size < 16)
+ return __memcpy_short(to,(const void *)from,size,ASI_BLK_S,ASI_BLK_P);
+ else if (size < 384)
+ return __memcpy_16plus(to,(const void *)from,size,ASI_BLK_S,ASI_BLK_P);
+ else
+ return __memcpy_386plus(to,(const void *)from,size,ASI_BLK_S,ASI_BLK_P);
+ } else {
+ if (!size) return 0;
+ return __memcpy_entry(to,(const void *)from,size,ASI_BLK_S,ASI_BLK_P);
+ }
+}
+#else
+extern __kernel_size_t __copy_from_user(void *to, const void *from, __kernel_size_t size);
+extern __kernel_size_t __copy_to_user(void *to, const void *from, __kernel_size_t size);
+#endif
#define copy_to_user(to,from,n) \
__copy_to_user((void *)(to), \
extern __inline__ __kernel_size_t __clear_user(void *addr, __kernel_size_t size)
{
- __kernel_size_t ret;
- __asm__ __volatile__ ("
- .section __ex_table,#alloc
- .align 8
- .xword 1f,3
- .previous
-1:
- wr %%g0, %3, %%asi
- mov %2, %%o1
- call __bzero_noasi
- mov %1, %%o0
- mov %%o0, %0
- " : "=r" (ret) : "r" (addr), "r" (size), "i" (ASI_S) :
- "cc", "o0", "o1", "o2", "o3", "o4", "o5", "o7", "g1", "g2", "g3", "g5", "g7");
- return ret;
+ extern __kernel_size_t __bzero_noasi(void *addr, __kernel_size_t size);
+
+
+ __asm__ __volatile__ ("wr %%g0, %0, %%asi" : : "i" (ASI_S));
+ return __bzero_noasi(addr, size);
}
#define clear_user(addr,n) \
--- /dev/null
+/* $Id: uctx.h,v 1.1 1997/06/18 16:51:58 davem Exp $
+ * uctx.h: Sparc64 {set,get}context() register state layouts.
+ *
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ */
+
+#ifndef __SPARC64_UCTX_H
+#define __SPARC64_UCTX_H
+
+#define MC_TSTATE 0
+#define MC_PC 1
+#define MC_NPC 2
+#define MC_Y 3
+#define MC_G1 4
+#define MC_G2 5
+#define MC_G3 6
+#define MC_G4 7
+#define MC_G5 8
+#define MC_G6 9
+#define MC_G7 10
+#define MC_O0 11
+#define MC_O1 12
+#define MC_O2 13
+#define MC_O3 14
+#define MC_O4 15
+#define MC_O5 16
+#define MC_O6 17
+#define MC_O7 18
+#define MC_NGREG 19
+
+typedef unsigned long mc_greg_t;
+typedef mc_greg_t mc_gregset_t[MC_NGREG];
+
+#define MC_MAXFPQ 16
+struct mc_fq {
+ unsigned long *mcfq_addr;
+ unsigned int mcfq_insn;
+};
+
+struct mc_fpu {
+ union {
+ unsigned int sregs[32];
+ unsigned long dregs[32];
+ long double qregs[16];
+ } mcfpu_fregs;
+ unsigned long mcfpu_fsr;
+ unsigned long mcfpu_fprs;
+ unsigned long mcfpu_gsr;
+ struct mc_fq *mcfpu_fq;
+ unsigned char mcfpu_qcnt;
+ unsigned char mcfpu_qentsz;
+ unsigned char mcfpu_enab;
+};
+typedef struct mc_fpu mc_fpu_t;
+
+typedef struct {
+ mc_gregset_t mc_gregs;
+ mc_greg_t mc_fp;
+ mc_greg_t mc_i7;
+ mc_fpu_t mc_fpregs;
+} mcontext_t;
+
+struct ucontext {
+ struct ucontext *uc_link;
+ unsigned long uc_flags;
+ sigset_t uc_sigmask;
+ mcontext_t uc_mcontext;
+};
+typedef struct ucontext ucontext_t;
+
+#endif /* __SPARC64_UCTX_H */
-/* $Id: vaddrs.h,v 1.6 1997/04/04 00:50:31 davem Exp $ */
+/* $Id: vaddrs.h,v 1.8 1997/06/27 14:55:13 jj Exp $ */
#ifndef _SPARC64_VADDRS_H
#define _SPARC64_VADDRS_H
* mappings for devices and is the speedup improvements of not loading
* a pointer and then the value in the assembly code
*/
-#define IOBASE_VADDR 0xfffffd0000000000ULL /* Base for mapping pages */
-#define IOBASE_LEN 0x0000008000000000ULL /* Length of the IO area */
-#define IOBASE_END 0xfffffd8000000000ULL
-#define DVMA_VADDR 0xfffffd8000000000ULL /* Base area of the DVMA on suns */
-#define DVMA_LEN 0x0000004000000000ULL /* Size of the DVMA address space */
-#define DVMA_END 0xfffffdc000000000ULL
+#define IOBASE_VADDR 0x0000006000000000ULL /* Base for mapping pages */
+#define IOBASE_LEN 0x0000001000000000ULL /* Length of the IO area */
+#define IOBASE_END 0x0000007000000000ULL
+#define DVMA_VADDR 0x0000007000000000ULL /* Base area of the DVMA on suns */
+#define DVMA_LEN 0x0000001000000000ULL /* Size of the DVMA address space */
+#define DVMA_END 0x0000008000000000ULL
+#define MODULES_VADDR 0x0000000001000000ULL /* Where to map modules */
+#define MODULES_LEN 0x000000007f000000ULL
+#define MODULES_END 0x0000000080000000ULL
#endif /* !(_SPARC_VADDRS_H) */
extern int init_elf_binfmt(void);
extern int init_elf32_binfmt(void);
extern int init_aout_binfmt(void);
+extern int init_aout32_binfmt(void);
extern int init_script_binfmt(void);
extern int init_java_binfmt(void);
extern int init_em86_binfmt(void);
* Data structure and defines shared between console.c, vga.c and tga.c
*/
+#include <linux/config.h>
+
#define NPAR 16
struct vc_data {
unsigned char vc_halfcolor; /* Colour for half intensity mode */
unsigned long vc_origin; /* Used for EGA/VGA fast scroll */
unsigned long vc_scr_end; /* Used for EGA/VGA fast scroll */
- unsigned long vc_pos;
unsigned long vc_x,vc_y;
unsigned long vc_top,vc_bottom;
unsigned long vc_state;
unsigned long vc_npar,vc_par[NPAR];
+#ifdef CONFIG_FB_CONSOLE
+ unsigned short *vc_video_mem_start; /* Start of video RAM */
+ unsigned short *vc_pos;
+#else
+ unsigned long vc_pos;
unsigned long vc_video_mem_start; /* Start of video RAM */
+#endif
unsigned long vc_video_mem_end; /* End of video RAM (sort of) */
unsigned long vc_saved_x;
unsigned long vc_saved_y;
#define D_MAXLEN 1024
/* public flags for d_add() */
-#define D_NORMAL 0
-#define D_BASKET 1 /* put into basket (deleted/unref'd files) */
-#define D_DUPLICATE 2 /* allow duplicate entries */
-#define D_NOCHECKDUP 4 /* no not check for duplicates */
-
-/* public flags for d_flag */
-#define D_PRELOADED 8
+#define D_NORMAL 0
+#define D_BASKET 1 /* put into basket (deleted/unref'd files) */
+#define D_DUPLICATE 2 /* allow duplicate entries */
+#define D_NOCHECKDUP 4 /* no not check for duplicates */
+#define D_NEGATIVE 8 /* negative entry */
+#define D_PRELOADED 16
+#define D_DIR 32 /* directory entry - look out for allocation issues */
+#define D_HASHED 64
+#define D_ZOMBIE 128
+#define D_INC_DDIR 512
/* public flags for d_del() */
#define D_REMOVE 0
#define IS_ROOT(x) ((x) == (x)->d_parent)
/* "quick string" -- I introduced this to shorten the parameter list
- * of many routines. Think of it as a (str,stlen) pair.
+ * of many routines. Think of it as a (str,stlen,hash) pair.
* Storing the len instead of doing strlen() very often is performance
* critical.
*/
struct qstr {
- char * name;
- int len;
+ const unsigned char * name;
+ int len, hash;
};
+/* Name hashing routines. Initial hash value */
+#define init_name_hash() 0
+
+/* partial hash update function. Assume roughly 4 bits per character */
+static inline unsigned long partial_name_hash(unsigned char c, unsigned long prevhash)
+{
+ prevhash = (prevhash << 4) | (prevhash >> (8*sizeof(unsigned long)-4));
+ return prevhash ^ c;
+}
+
+/* Finally: cut down the number of bits to a int value (and try to avoid losing bits) */
+static inline unsigned long end_name_hash(unsigned long hash)
+{
+ if (sizeof(hash) > sizeof(unsigned int))
+ hash += hash >> 4*sizeof(hash);
+ return (unsigned int) hash;
+}
+
struct dentry {
- union {
- struct inode * d_inode; /* Where the name belongs to */
- unsigned long d_ino; /* for preliminary entries */
- } u;
- struct dentry * d_parent; /* parent directory */
- struct dentry * d_next; /* hardlink aliasname / empty list */
- struct dentry * d_prev; /* hardlink aliasname */
+ unsigned int d_flag;
+ unsigned int d_count;
+ struct inode * d_inode; /* Where the name belongs to */
+ struct dentry * d_parent; /* parent directory */
+ struct dentry * d_mounts; /* mount information */
+ struct dentry * d_covers;
+ struct dentry * d_next; /* hardlink aliasname / empty list */
+ struct dentry * d_prev; /* hardlink aliasname */
struct dentry * d_hash_next;
struct dentry * d_hash_prev;
struct dentry * d_basket_next;
struct dentry * d_basket_prev;
struct qstr d_name;
- unsigned int d_flag;
};
extern struct dentry * the_root;
+/*
+ * These are the low-level FS interfaces to the dcache..
+ */
+extern void d_instantiate(struct dentry *, struct inode *, int);
+extern void d_delete(struct dentry *);
+
+
/* Note that all these routines must be called with vfs_lock() held */
/* get inode, if necessary retrieve it with iget() */
extern blocking struct inode * d_inode(struct dentry ** changing_entry);
-/* allocate proper space for the len */
-extern struct dentry * d_alloc(struct dentry * parent, int len, int isdir);
+/* allocate/de-allocate */
+extern void d_free(struct dentry *);
+extern struct dentry * d_alloc(struct dentry * parent, struct qstr *name, int isdir);
-/* only used once at mount_root() */
+/* only used at mount-time */
extern blocking
-struct dentry * d_alloc_root(struct inode * root_inode);
+struct dentry * d_alloc_root(struct inode * root_inode, struct dentry * old_root);
-/* d_inode is connected with inode, and d_name is copied from ininame.
- * either of them may be NULL, but when ininame is NULL, dname must be
- * set by the caller prior to calling this. */
+/*
+ * This adds the entry to the hash queues and initializes "d_inode".
+ * The entry was actually filled in earlier during "d_alloc()"
+ */
extern blocking
-void d_add(struct dentry * entry, struct inode * inode,
- struct qstr * ininame, int flags);
+void d_add(struct dentry * entry, struct inode * inode, int flags);
/* combination of d_alloc() and d_add(), less lookup overhead */
extern blocking
/* used for rename() and baskets */
extern blocking
-void d_move(struct dentry * entry, struct inode * newdir,
- struct qstr * newname, struct qstr * newapp);
+void d_move(struct dentry * entry, struct dentry * newparent, struct qstr * newname);
/* appendix may either be NULL or be used for transname suffixes */
-extern struct dentry * d_lookup(struct inode * dir, struct qstr * name,
- struct qstr * appendix);
+extern struct dentry * d_lookup(struct dentry * dir, struct qstr * name);
/* write full pathname into buffer and return length */
-extern int d_path(struct dentry * entry, struct inode * chroot, char * buf);
+extern int d_path(struct dentry * entry, struct dentry * chroot, char * buf);
extern struct dentry * d_basket(struct dentry * dir_entry);
extern int d_isbasket(struct dentry * entry);
+
+/*
+ * Whee..
+ */
+static inline void dput(struct dentry *dentry)
+{
+ if (dentry)
+ dentry->d_count--;
+}
+
+static inline struct dentry * dget(struct dentry *dentry)
+{
+ if (dentry)
+ dentry->d_count++;
+ return dentry;
+}
+
#endif
/* namei.c */
extern void ext2_release (struct inode *, struct file *);
-extern int ext2_lookup (struct inode *,const char *, int, struct inode **);
-extern int ext2_create (struct inode *,const char *, int, int,
- struct inode **);
-extern int ext2_mkdir (struct inode *, const char *, int, int);
-extern int ext2_rmdir (struct inode *, const char *, int);
-extern int ext2_unlink (struct inode *, const char *, int);
-extern int ext2_symlink (struct inode *, const char *, int, const char *);
-extern int ext2_link (struct inode *, struct inode *, const char *, int);
-extern int ext2_mknod (struct inode *, const char *, int, int, int);
-extern int ext2_rename (struct inode *, const char *, int,
- struct inode *, const char *, int);
+extern int ext2_lookup (struct inode *,struct qstr *, struct inode **);
+extern int ext2_create (struct inode *,struct dentry *,int);
+extern int ext2_mkdir (struct inode *,struct dentry *,int);
+extern int ext2_rmdir (struct inode *,struct dentry *);
+extern int ext2_unlink (struct inode *,struct dentry *);
+extern int ext2_symlink (struct inode *,struct dentry *,const char *);
+extern int ext2_link (struct inode *, struct inode *, struct dentry *);
+extern int ext2_mknod (struct inode *, struct dentry *, int, int);
+extern int ext2_rename (struct inode *, struct dentry *,struct inode *, struct dentry *);
/* super.c */
extern void ext2_error (struct super_block *, const char *, const char *, ...)
*/
#define blocking /*routine may schedule()*/
+#include <linux/dalloc.h>
+
/*
* It's silly to have NR_OPEN bigger than NR_FILE, but I'll fix
* that later. Anyway, now the file code is no longer dependent
#define MS_MGC_VAL 0xC0ED0000 /* magic flag number to indicate "new" flags */
#define MS_MGC_MSK 0xffff0000 /* magic flag number mask */
-/*
- * Public flags for namei()
- */
-#define NAM_PLAIN 0 /* Retrieve last component of pathname as is. */
-#define NAM_FOLLOW_LINK 2 /* If last component of path is a symlink, follow it */
-#define NAM_FOLLOW_TRAILSLASH 4 /* Follow last symlink only if trailed by slash. */
-
/*
* Note that read-only etc flags are inode-specific: setting some file-system
* flags just means all the inodes inherit those flags by default. It might be
struct inode *i_basket_prev;
struct dentry *i_dentry;
- short i_ddir_count;
- short i_dent_count;
unsigned short i_status;
unsigned short i_reuse_count;
- struct inode *i_mount;
unsigned int i_flags;
unsigned char i_lock;
unsigned char i_dirt;
unsigned long s_flags;
unsigned long s_magic;
unsigned long s_time;
- struct inode *s_covered;
- struct inode *s_mounted;
+ struct dentry *s_root;
struct wait_queue *s_wait;
struct inode *s_ibasket;
struct inode_operations {
struct file_operations * default_file_ops;
- int (*create) (struct inode *,const char *,int,int,struct inode **);
- int (*lookup) (struct inode *,const char *,int,struct inode **);
- int (*link) (struct inode *,struct inode *,const char *,int);
- int (*unlink) (struct inode *,const char *,int);
- int (*symlink) (struct inode *,const char *,int,const char *);
- int (*mkdir) (struct inode *,const char *,int,int);
- int (*rmdir) (struct inode *,const char *,int);
- int (*mknod) (struct inode *,const char *,int,int,int);
- int (*rename) (struct inode *,const char *,int,struct inode *,const char *,int);
+ int (*create) (struct inode *,struct dentry *,int);
+ int (*lookup) (struct inode *,struct qstr *name,struct inode **);
+ int (*link) (struct inode *,struct inode *,struct dentry *);
+ int (*unlink) (struct inode *,struct dentry *);
+ int (*symlink) (struct inode *,struct dentry *,const char *);
+ int (*mkdir) (struct inode *,struct dentry *,int);
+ int (*rmdir) (struct inode *,struct dentry *);
+ int (*mknod) (struct inode *,struct dentry *,int,int);
+ int (*rename) (struct inode *,struct dentry *,struct inode *,struct dentry *);
int (*readlink) (struct inode *,char *,int);
+ struct dentry * (*follow_link) (struct inode *, struct dentry *);
int (*readpage) (struct inode *, struct page *);
int (*writepage) (struct inode *, struct page *);
int (*bmap) (struct inode *,int);
extern struct file_system_type *get_fs_type(const char *name);
extern int fs_may_mount(kdev_t dev);
-extern int fs_may_umount(kdev_t dev, struct inode * mount_root);
+extern int fs_may_umount(kdev_t dev, struct dentry * root);
extern int fs_may_remount_ro(kdev_t dev);
extern struct file *inuse_filps;
extern void sync_supers(kdev_t dev);
extern int bmap(struct inode * inode,int block);
extern int notify_change(struct inode *, struct iattr *);
-extern int namei(int retr_mode, const char *pathname, struct inode **res_inode);
extern int permission(struct inode * inode,int mask);
extern int get_write_access(struct inode *inode);
extern void put_write_access(struct inode *inode);
extern int open_namei(const char * pathname, int flag, int mode,
- struct inode ** res_inode, struct inode * base);
+ struct inode ** res_inode, struct dentry * base);
extern int do_mknod(const char * filename, int mode, dev_t dev);
extern int do_pipe(int *);
+/*
+ * Kernel pointers have redundant information, so we can use a
+ * scheme where we can return either an error code or a dentry
+ * pointer with the same return value.
+ *
+ * This should be a per-architecture thing, to allow different
+ * error and pointer decisions.
+ */
+#define ERR_PTR(err) ((void *)((long)(err)))
+#define PTR_ERR(ptr) ((long)(ptr))
+#define IS_ERR(ptr) ((unsigned long)(ptr) > (unsigned long)(-1000))
+
+extern struct dentry * lookup_dentry(const char *, struct dentry *, int);
+extern int __namei(const char *, struct inode **, int);
+
+#define namei(pathname, inode_p) __namei(pathname, inode_p, 1)
+#define lnamei(pathname, inode_p) __namei(pathname, inode_p, 0)
+
#include <asm/semaphore.h>
/* Intended for short locks of the global data structures in inode.c.
extern void _get_inode(struct inode * inode);
extern blocking void __iput(struct inode * inode);
-/* This must not be called if the inode is not in use (i.e. given
- * back with iput(). The atomic inc assumes that the inode is
- * already in use, and just has to be incremented higher.
- * Please do not directly manipulate i_count any more.
- * Use iget, iinc and iput.
- * You may test i_count for zero if you are aware that it
- * might change under you.
- */
-extern inline void iinc(struct inode * inode)
-{
- atomic_inc(&inode->i_count);
-}
-
-/* The same, but the inode may not be in use. This must be called
- * with vfslock() held, and be asure that the inode argument is
- * valid (i.e. not out of cache). So the vfs_lock() must span the
- * retrieval method of the inode.
- */
-extern inline void iinc_zero(struct inode * inode)
-{
- if(!atomic_read(&inode->i_count)) {
- atomic_inc(&inode->i_count);
- _get_inode(inode);
- } else
- atomic_inc(&inode->i_count);
-}
-
extern blocking void _iput(struct inode * inode);
extern inline blocking void iput(struct inode * inode)
{
- if(inode) {
+ if (inode) {
extern void wake_up_interruptible(struct wait_queue **q);
- if(inode->i_pipe)
+ if (inode->i_pipe)
wake_up_interruptible(&inode->u.pipe_i.wait);
/* It does not matter if somebody re-increments it in between,
* only the _last_ user needs to call _iput().
*/
- if(atomic_dec_and_test(&inode->i_count) && inode->i_ddir_count <= 0)
+ if (atomic_dec_and_test(&inode->i_count))
_iput(inode);
}
}
-extern blocking struct inode * __iget(struct super_block * sb, unsigned long nr, int crsmnt);
+extern blocking struct inode * iget(struct super_block * sb, unsigned long nr);
extern blocking void _clear_inode(struct inode * inode, int external, int verbose);
extern blocking inline void clear_inode(struct inode * inode)
{
*/
blocking struct inode * get_empty_inode_hashed(dev_t i_dev, unsigned long i_ino);
-extern blocking int _free_ibasket(struct super_block * sb);
-extern inline blocking int free_ibasket(struct super_block * sb)
-{
- int res;
- vfs_lock();
- res = _free_ibasket(sb);
- vfs_unlock();
- return res;
-}
-
extern void insert_inode_hash(struct inode *);
extern blocking struct inode * get_pipe_inode(void);
extern int get_unused_fd(void);
extern int inode_change_ok(struct inode *, struct iattr *);
extern void inode_setattr(struct inode *, struct iattr *);
-extern inline blocking
-struct inode * iget(struct super_block * sb, unsigned long nr)
-{
- return __iget(sb, nr, 1);
-}
-
/* kludge to get SCSI modules working */
#include <linux/minix_fs.h>
#include <linux/minix_fs_sb.h>
--- /dev/null
+/*
+ * include/linux/ghash.h -- generic hashing with fuzzy retrieval
+ *
+ * (C) 1997 Thomas Schoebel-Theuer
+ *
+ * The algorithms implemented here seem to be a completely new invention,
+ * and I'll publish the fundamentals in a paper.
+ */
+
+#ifndef _GHASH_H
+#define _GHASH_H
+/* HASHSIZE _must_ be a power of two!!! */
+
+
+#define DEF_HASH_FUZZY_STRUCTS(NAME,HASHSIZE,TYPE) \
+\
+struct NAME##_table {\
+ TYPE * hashtable[HASHSIZE];\
+ TYPE * sorted_list;\
+ int nr_entries;\
+};\
+\
+struct NAME##_ptrs {\
+ TYPE * next_hash;\
+ TYPE * prev_hash;\
+ TYPE * next_sorted;\
+ TYPE * prev_sorted;\
+};
+
+#define DEF_HASH_FUZZY(LINKAGE,NAME,HASHSIZE,TYPE,PTRS,KEYTYPE,KEY,KEYCMP,KEYEQ,HASHFN)\
+\
+LINKAGE void insert_##NAME##_hash(struct NAME##_table * tbl, TYPE * elem)\
+{\
+ int ix = HASHFN(elem->KEY);\
+ TYPE ** base = &tbl->hashtable[ix];\
+ TYPE * ptr = *base;\
+ TYPE * prev = NULL;\
+\
+ tbl->nr_entries++;\
+ while(ptr && KEYCMP(ptr->KEY, elem->KEY)) {\
+ base = &ptr->PTRS.next_hash;\
+ prev = ptr;\
+ ptr = *base;\
+ }\
+ elem->PTRS.next_hash = ptr;\
+ elem->PTRS.prev_hash = prev;\
+ if(ptr) {\
+ ptr->PTRS.prev_hash = elem;\
+ }\
+ *base = elem;\
+\
+ ptr = prev;\
+ if(!ptr) {\
+ ptr = tbl->sorted_list;\
+ prev = NULL;\
+ } else {\
+ prev = ptr->PTRS.prev_sorted;\
+ }\
+ while(ptr) {\
+ TYPE * next = ptr->PTRS.next_hash;\
+ if(next && KEYCMP(next->KEY, elem->KEY)) {\
+ prev = ptr;\
+ ptr = next;\
+ } else if(KEYCMP(ptr->KEY, elem->KEY)) {\
+ prev = ptr;\
+ ptr = ptr->PTRS.next_sorted;\
+ } else\
+ break;\
+ }\
+ elem->PTRS.next_sorted = ptr;\
+ elem->PTRS.prev_sorted = prev;\
+ if(ptr) {\
+ ptr->PTRS.prev_sorted = elem;\
+ }\
+ if(prev) {\
+ prev->PTRS.next_sorted = elem;\
+ } else {\
+ tbl->sorted_list = elem;\
+ }\
+}\
+\
+LINKAGE void remove_##NAME##_hash(struct NAME##_table * tbl, TYPE * elem)\
+{\
+ TYPE * next = elem->PTRS.next_hash;\
+ TYPE * prev = elem->PTRS.prev_hash;\
+\
+ tbl->nr_entries--;\
+ if(next)\
+ next->PTRS.prev_hash = prev;\
+ if(prev)\
+ prev->PTRS.next_hash = next;\
+ else {\
+ int ix = HASHFN(elem->KEY);\
+ tbl->hashtable[ix] = next;\
+ }\
+\
+ next = elem->PTRS.next_sorted;\
+ prev = elem->PTRS.prev_sorted;\
+ if(next)\
+ next->PTRS.prev_sorted = prev;\
+ if(prev)\
+ prev->PTRS.next_sorted = next;\
+ else\
+ tbl->sorted_list = next;\
+}\
+\
+LINKAGE TYPE * find_##NAME##_hash(struct NAME##_table * tbl, KEYTYPE pos)\
+{\
+ int ix = hashfn(pos);\
+ TYPE * ptr = tbl->hashtable[ix];\
+ while(ptr && KEYCMP(ptr->KEY, pos))\
+ ptr = ptr->PTRS.next_hash;\
+ if(ptr && !KEYEQ(ptr->KEY, pos))\
+ ptr = NULL;\
+ return ptr;\
+}\
+\
+LINKAGE TYPE * find_##NAME##_hash_fuzzy(struct NAME##_table * tbl, KEYTYPE pos)\
+{\
+ int ix;\
+ int offset;\
+ TYPE * ptr;\
+ TYPE * next;\
+\
+ ptr = tbl->sorted_list;\
+ if(!ptr || KEYCMP(pos, ptr->KEY))\
+ return NULL;\
+ ix = HASHFN(pos);\
+ offset = HASHSIZE;\
+ do {\
+ offset >>= 1;\
+ next = tbl->hashtable[(ix+offset) & ((HASHSIZE)-1)];\
+ if(next && (KEYCMP(next->KEY, pos) || KEYEQ(next->KEY, pos))\
+ && KEYCMP(ptr->KEY, next->KEY))\
+ ptr = next;\
+ } while(offset);\
+\
+ for(;;) {\
+ next = ptr->PTRS.next_hash;\
+ if(next) {\
+ if(KEYCMP(next->KEY, pos)) {\
+ ptr = next;\
+ continue;\
+ }\
+ }\
+ next = ptr->PTRS.next_sorted;\
+ if(next && KEYCMP(next->KEY, pos)) {\
+ ptr = next;\
+ continue;\
+ }\
+ return ptr;\
+ }\
+ return NULL;\
+}
+
+#define DEF_HASH_STRUCTS(NAME,HASHSIZE,TYPE) \
+\
+struct NAME##_table {\
+ TYPE * hashtable[HASHSIZE];\
+ int nr_entries;\
+};\
+\
+struct NAME##_ptrs {\
+ TYPE * next_hash;\
+ TYPE * prev_hash;\
+};
+
+#define DEF_HASH(LINKAGE,NAME,HASHSIZE,TYPE,PTRS,KEYTYPE,KEY,KEYCMP,KEYEQ,HASHFN)\
+\
+LINKAGE void insert_##NAME##_hash(struct NAME##_table * tbl, TYPE * elem)\
+{\
+ int ix = HASHFN(elem->KEY);\
+ TYPE ** base = &tbl->hashtable[ix];\
+ TYPE * ptr = *base;\
+ TYPE * prev = NULL;\
+\
+ tbl->nr_entries++;\
+ while(ptr && KEYCMP(ptr->KEY, elem->KEY)) {\
+ base = &ptr->PTRS.next_hash;\
+ prev = ptr;\
+ ptr = *base;\
+ }\
+ elem->PTRS.next_hash = ptr;\
+ elem->PTRS.prev_hash = prev;\
+ if(ptr) {\
+ ptr->PTRS.prev_hash = elem;\
+ }\
+ *base = elem;\
+}\
+\
+LINKAGE void remove_##NAME##_hash(struct NAME##_table * tbl, TYPE * elem)\
+{\
+ TYPE * next = elem->PTRS.next_hash;\
+ TYPE * prev = elem->PTRS.prev_hash;\
+\
+ tbl->nr_entries--;\
+ if(next)\
+ next->PTRS.prev_hash = prev;\
+ if(prev)\
+ prev->PTRS.next_hash = next;\
+ else {\
+ int ix = HASHFN(elem->KEY);\
+ tbl->hashtable[ix] = next;\
+ }\
+}\
+\
+LINKAGE TYPE * find_##NAME##_hash(struct NAME##_table * tbl, KEYTYPE pos)\
+{\
+ int ix = hashfn(pos);\
+ TYPE * ptr = tbl->hashtable[ix];\
+ while(ptr && KEYCMP(ptr->KEY, pos))\
+ ptr = ptr->PTRS.next_hash;\
+ if(ptr && !KEYEQ(ptr->KEY, pos))\
+ ptr = NULL;\
+ return ptr;\
+}
+
+#endif
extern void proc_statfs(struct super_block *, struct statfs *, int);
extern void proc_read_inode(struct inode *);
extern void proc_write_inode(struct inode *);
-extern int proc_match(int, const char *, struct proc_dir_entry *);
+
+extern int proc_match(int, const char *,struct proc_dir_entry *);
/*
* These are generic /proc routines that use the internal
* of the /proc/<pid> subdirectories.
*/
extern int proc_readdir(struct inode *, struct file *, void *, filldir_t);
-extern int proc_lookup(struct inode *, const char *, int, struct inode **);
+extern int proc_lookup(struct inode *, struct qstr *, struct inode **);
struct openpromfs_dev {
struct openpromfs_dev *next;
};
extern struct inode_operations *
proc_openprom_register(int (*readdir)(struct inode *, struct file *, void *, filldir_t),
- int (*lookup)(struct inode *, const char *, int, struct inode **),
+ int (*lookup)(struct inode *, struct qstr *, struct inode **),
void (*use)(struct inode *, int),
struct openpromfs_dev ***);
extern void proc_openprom_deregister(void);
#endif
extern struct inode_operations proc_omirr_inode_operations;
-/* Not sure whether this belongs here */
-int proc_arbitrary_lookup(struct inode * dir, const char * name,
- int len, struct inode ** result);
#endif
/*
#define ROSE_KERNEL_H
#define PF_ROSE AF_ROSE
-#define ROSE_MTU 128
+#define ROSE_MTU 251
+#define ROSE_DEFER 1
#define ROSE_T1 2
#define ROSE_T2 3
#define ROSE_T3 4
#define ROSE_QBITINCL 6
#define ROSE_HOLDBACK 7
+#define SIOCRSGCAUSE (SIOCPROTOPRIVATE+0)
+#define SIOCRSSCAUSE (SIOCPROTOPRIVATE+1)
#define SIOCRSL2CALL (SIOCPROTOPRIVATE+2)
+#define SIOCRSACCEPT (SIOCPROTOPRIVATE+3)
+#define SIOCRSCLRRT (SIOCPROTOPRIVATE+4)
+
+#define ROSE_DTE_ORIGINATED 0x00
+#define ROSE_NUMBER_BUSY 0x01
+#define ROSE_INVALID_FACILITY 0x03
+#define ROSE_NETWORK_CONGESTION 0x05
+#define ROSE_OUT_OF_ORDER 0x09
+#define ROSE_ACCESS_BARRED 0x0B
+#define ROSE_NOT_OBTAINABLE 0x0D
+#define ROSE_REMOTE_PROCEDURE 0x11
+#define ROSE_LOCAL_PROCEDURE 0x13
+#define ROSE_SHIP_ABSENT 0x39
typedef struct {
char rose_addr[5];
ax25_address digipeaters[AX25_MAX_DIGIS];
};
+struct rose_cause_struct {
+ unsigned char cause;
+ unsigned char diagnostic;
+};
+
#endif
struct fs_struct {
int count;
int umask;
- struct inode * root, * pwd;
+ struct dentry * root, * pwd;
};
#define INIT_FS { \
#define get_video_num_columns(dummy) video_num_columns
#define get_video_num_lines(dummy) video_num_lines
#define get_video_size_row(dummy) video_size_row
-#endif
-
extern unsigned long video_num_columns;
extern unsigned long video_num_lines;
extern unsigned long video_size_row;
+#endif
+
extern unsigned char video_type;
extern unsigned long video_mem_base;
extern unsigned long video_mem_term;
/* how to access screen memory */
+#include <linux/config.h>
+
#if defined(CONFIG_TGA_CONSOLE)
extern int tga_blitc(unsigned int, unsigned long);
--- /dev/null
+/*
+ * include/linux/simp.h -- simple allocator for cached objects
+ *
+ * This is meant as a faster and simpler (not full-featured) replacement
+ * for SLAB, thus the name "simp" :-)
+ *
+ * (C) 1997 Thomas Schoebel-Theuer
+ */
+
+#ifndef SIMP_H
+#define SIMP_H
+
+/* used for constructors / destructors */
+typedef void (*structor)(void *);
+
+/* create an object cache */
+/* positive clearable_offset means the next two pointers at that offset
+ * can be internally used for freelist pointers when the object is
+ * deallocated / not in use;
+ * if there is no space inside the element that can be reused for
+ * this purpose, supply -1. Using positive offsets is essential for
+ * saving space with very small-sized objects.
+ *
+ * Note for big-sized objects in the range of whole pages, use
+ * the fast Linux page allocator instead, directly.
+ */
+extern struct simp * simp_create(char * name, long size, long clearable_offset,
+ structor first_ctor,
+ structor again_ctor,
+ structor dtor);
+
+/* alloc / dealloc routines */
+extern void * simp_alloc(struct simp * simp);
+extern void simp_free(void * objp);
+
+/* garbage collection */
+extern long simp_garbage(void);
+
+#endif
extern void kmem_cache_free(kmem_cache_t *, void *);
extern void *kmalloc(size_t, int);
-extern void kfree(void *);
+extern void kfree(const void *);
extern void kfree_s(void *, size_t);
extern int kmem_cache_reap(int, int, int);
struct tty_flip_buffer {
struct tq_struct tqueue;
- unsigned char char_buf[2*TTY_FLIPBUF_SIZE];
- char flag_buf[2*TTY_FLIPBUF_SIZE];
+ struct semaphore pty_sem;
char *char_buf_ptr;
unsigned char *flag_buf_ptr;
int count;
int buf_num;
+ unsigned char char_buf[2*TTY_FLIPBUF_SIZE];
+ char flag_buf[2*TTY_FLIPBUF_SIZE];
+ unsigned char slop[4]; /* N.B. bug overwrites buffer by 1 */
};
+/*
+ * The pty uses char_buf and flag_buf as a contiguous buffer
+ */
+#define PTY_BUF_SIZE 4*TTY_FLIPBUF_SIZE
/*
* When a break, frame error, or parity error happens, these codes are
* most often used by a windowing system, which will set the correct
* size each time the window is created or resized anyway.
* IMPORTANT: since this structure is dynamically allocated, it must
- * be no larger than 4096 bytes. Changing TTY_BUF_SIZE will change
+ * be no larger than 4096 bytes. Changing TTY_FLIPBUF_SIZE will change
* the size of this structure, and it needs to be done with care.
* - TYT, 9/14/92
*/
#define SIOCX25SFACILITIES (SIOCPROTOPRIVATE + 3)
#define SIOCX25GCALLUSERDATA (SIOCPROTOPRIVATE + 4)
#define SIOCX25SCALLUSERDATA (SIOCPROTOPRIVATE + 5)
+#define SIOCX25GCAUSEDIAG (SIOCPROTOPRIVATE + 6)
/*
* Values for {get,set}sockopt.
#define X25_PS2048 11
#define X25_PS4096 12
-/*
- * X.25 Reset error and diagnostic codes.
- */
-#define X25_ERR_RESET 100 /* Call Reset */
-#define X25_ERR_ROUT 101 /* Out of Order */
-#define X25_ERR_RRPE 102 /* Remote Procedure Error */
-#define X25_ERR_RLPE 103 /* Local Procedure Error */
-#define X25_ERR_RNCG 104 /* Network Congestion */
-#define X25_ERR_RRDO 105 /* Remote DTE Operational */
-#define X25_ERR_RNOP 106 /* Network Operational */
-#define X25_ERR_RINV 107 /* Invalid Call */
-#define X25_ERR_RNOO 108 /* Network Out of Order */
-
-/*
- * X.25 Clear error and diagnostic codes.
- */
-#define X25_ERR_CLEAR 110 /* Call Cleared */
-#define X25_ERR_CBUSY 111 /* Number Busy */
-#define X25_ERR_COUT 112 /* Out of Order */
-#define X25_ERR_CRPE 113 /* Remote Procedure Error */
-#define X25_ERR_CRRC 114 /* Collect Call Refused */
-#define X25_ERR_CINV 115 /* Invalid Call */
-#define X25_ERR_CNFS 116 /* Invalid Fast Select */
-#define X25_ERR_CSA 117 /* Ship Absent */
-#define X25_ERR_CIFR 118 /* Invalid Facility Request */
-#define X25_ERR_CAB 119 /* Access Barred */
-#define X25_ERR_CLPE 120 /* Local Procedure Error */
-#define X25_ERR_CNCG 121 /* Network Congestion */
-#define X25_ERR_CNOB 122 /* Not Obtainable */
-#define X25_ERR_CROO 123 /* RPOA Out of Order */
-
/*
* An X.121 address, it is held as ASCII text, null terminated, up to 15
* digits and a null terminator.
*/
typedef struct {
- char x25_addr[16];
+ char x25_addr[16];
} x25_address;
/*
unsigned char cuddata[128];
};
+/*
+ * Call clearing Cause and Diagnostic structure.
+ */
+struct x25_causediag {
+ unsigned char cause;
+ unsigned char diagnostic;
+};
+
#endif
#include <linux/config.h>
#include <linux/ax25.h>
-#define AX25_SLOWHZ 10 /* Run timing at 1/10 second - gives us better resolution for 56kbit links */
-
-#define AX25_T1CLAMPLO (1 * AX25_SLOWHZ) /* If defined, clamp at 1 second **/
-#define AX25_T1CLAMPHI (30 * AX25_SLOWHZ) /* If defined, clamp at 30 seconds **/
+#define AX25_T1CLAMPLO 1
+#define AX25_T1CLAMPHI (30 * HZ)
#define AX25_BPQ_HEADER_LEN 16
#define AX25_KISS_HEADER_LEN 1
#define AX25_DEF_CONMODE 2 /* Connected mode allowed */
#define AX25_DEF_WINDOW 2 /* Window=2 */
#define AX25_DEF_EWINDOW 32 /* Module-128 Window=32 */
-#define AX25_DEF_T1 (10 * AX25_SLOWHZ) /* T1=10s */
-#define AX25_DEF_T2 (3 * AX25_SLOWHZ) /* T2=3s */
-#define AX25_DEF_T3 (300 * AX25_SLOWHZ) /* T3=300s */
+#define AX25_DEF_T1 (10 * HZ) /* T1=10s */
+#define AX25_DEF_T2 (3 * HZ) /* T2=3s */
+#define AX25_DEF_T3 (300 * HZ) /* T3=300s */
#define AX25_DEF_N2 10 /* N2=10 */
-#define AX25_DEF_IDLE (0 * 60 * AX25_SLOWHZ) /* Idle=None */
+#define AX25_DEF_IDLE (0 * 60 * HZ) /* Idle=None */
#define AX25_DEF_PACLEN 256 /* Paclen=256 */
#define AX25_DEF_PROTOCOL AX25_PROTO_STD_SIMPLEX /* Standard AX.25 */
-#define AX25_DEF_DS_TIMEOUT (3 * 60 * AX25_SLOWHZ) /* DAMA timeout 3 minutes */
+#define AX25_DEF_DS_TIMEOUT (3 * 60 * HZ) /* DAMA timeout 3 minutes */
typedef struct ax25_uid_assoc {
struct ax25_uid_assoc *next;
unsigned short vs, vr, va;
unsigned char condition, backoff;
unsigned char n2, n2count;
- unsigned short t1, t2, t3, idle, rtt;
- unsigned short t1timer, t2timer, t3timer, idletimer;
+ struct timer_list t1timer, t2timer, t3timer, idletimer;
+ unsigned long t1, t2, t3, idle, rtt;
unsigned short paclen, fragno, fraglen;
struct sk_buff_head write_queue;
struct sk_buff_head reseq_queue;
extern void ax25_ds_del_timer(ax25_dev *);
extern void ax25_ds_timer(ax25_cb *);
extern void ax25_ds_t1_timeout(ax25_cb *);
+extern void ax25_ds_heartbeat_expiry(ax25_cb *);
+extern void ax25_ds_t3timer_expiry(ax25_cb *);
+extern void ax25_ds_idletimer_expiry(ax25_cb *);
#include <net/ax25call.h>
/* ax25_iface.c */
extern int ax25_protocol_register(unsigned int, int (*)(struct sk_buff *, ax25_cb *));
extern void ax25_protocol_release(unsigned int);
-extern int ax25_linkfail_register(void (*)(ax25_address *, struct device *));
-extern void ax25_linkfail_release(void (*)(ax25_address *, struct device *));
+extern int ax25_linkfail_register(void (*)(ax25_cb *, int));
+extern void ax25_linkfail_release(void (*)(ax25_cb *, int));
extern int ax25_listen_register(ax25_address *, struct device *);
extern void ax25_listen_release(ax25_address *, struct device *);
extern int (*ax25_protocol_function(unsigned int))(struct sk_buff *, ax25_cb *);
extern int ax25_listen_mine(ax25_address *, struct device *);
-extern void ax25_link_failed(ax25_address *, struct device *);
-extern int ax25_link_up(ax25_address *, ax25_address *, ax25_digi *, struct device *);
+extern void ax25_link_failed(ax25_cb *, int);
extern int ax25_protocol_is_registered(unsigned int);
/* ax25_in.c */
extern int ax25_rebuild_header(struct sk_buff *);
/* ax25_out.c */
-extern int ax25_send_frame(struct sk_buff *, int, ax25_address *, ax25_address *, ax25_digi *, struct device *);
+extern ax25_cb *ax25_send_frame(struct sk_buff *, int, ax25_address *, ax25_address *, ax25_digi *, struct device *);
extern void ax25_output(ax25_cb *, int, struct sk_buff *);
extern void ax25_kick(ax25_cb *);
extern void ax25_transmit_buffer(ax25_cb *, struct sk_buff *, int);
extern int ax25_rt_ioctl(unsigned int, void *);
extern int ax25_rt_get_info(char *, char **, off_t, int, int);
extern int ax25_rt_autobind(ax25_cb *, ax25_address *);
-extern void ax25_rt_build_path(ax25_cb *, ax25_address *, struct device *);
-extern struct sk_buff *ax25_dg_build_path(struct sk_buff *, ax25_address *, struct device *);
-extern char ax25_ip_mode_get(ax25_address *, struct device *);
+extern ax25_route *ax25_rt_find_route(ax25_address *, struct device *);
+extern struct sk_buff *ax25_rt_build_path(struct sk_buff *, ax25_address *, ax25_address *, ax25_digi *);
extern void ax25_rt_free(void);
/* ax25_std_in.c */
extern void ax25_std_timeout_response(ax25_cb *);
/* ax25_std_timer.c */
-extern void ax25_std_timer(ax25_cb *);
+extern void ax25_std_heartbeat_expiry(ax25_cb *);
+extern void ax25_std_t1timer_expiry(ax25_cb *);
+extern void ax25_std_t2timer_expiry(ax25_cb *);
+extern void ax25_std_t3timer_expiry(ax25_cb *);
+extern void ax25_std_idletimer_expiry(ax25_cb *);
/* ax25_subr.c */
extern void ax25_clear_queues(ax25_cb *);
extern int ax25_decode(ax25_cb *, struct sk_buff *, int *, int *, int *);
extern void ax25_send_control(ax25_cb *, int, int, int);
extern void ax25_return_dm(struct device *, ax25_address *, ax25_address *, ax25_digi *);
-extern unsigned short ax25_calculate_t1(ax25_cb *);
+extern void ax25_calculate_t1(ax25_cb *);
extern void ax25_calculate_rtt(ax25_cb *);
+extern void ax25_disconnect(ax25_cb *, int);
/* ax25_timer.c */
-extern void ax25_set_timer(ax25_cb *);
+extern void ax25_start_heartbeat(ax25_cb *);
+extern void ax25_start_t1timer(ax25_cb *);
+extern void ax25_start_t2timer(ax25_cb *);
+extern void ax25_start_t3timer(ax25_cb *);
+extern void ax25_start_idletimer(ax25_cb *);
+extern void ax25_stop_heartbeat(ax25_cb *);
+extern void ax25_stop_t1timer(ax25_cb *);
+extern void ax25_stop_t2timer(ax25_cb *);
+extern void ax25_stop_t3timer(ax25_cb *);
+extern void ax25_stop_idletimer(ax25_cb *);
+extern int ax25_t1timer_running(ax25_cb *);
+extern unsigned long ax25_display_timer(struct timer_list *);
/* ax25_uid.c */
extern int ax25_uid_policy;
#define _LAPB_H
#include <linux/lapb.h>
-#define LAPB_SLOWHZ 10 /* Run timing at 1/10 second */
-
#define LAPB_HEADER_LEN 20 /* LAPB over Ethernet + a bit more */
#define LAPB_ACK_PENDING_CONDITION 0x01
};
#define LAPB_DEFAULT_MODE (LAPB_STANDARD | LAPB_SLP | LAPB_DTE)
-#define LAPB_DEFAULT_WINDOW 7 /* Window=7 */
-#define LAPB_DEFAULT_T1 (5 * LAPB_SLOWHZ) /* T1=5s */
-#define LAPB_DEFAULT_T2 (1 * LAPB_SLOWHZ) /* T2=1s */
-#define LAPB_DEFAULT_N2 20 /* N2=20 */
+#define LAPB_DEFAULT_WINDOW 7 /* Window=7 */
+#define LAPB_DEFAULT_T1 (5 * HZ) /* T1=5s */
+#define LAPB_DEFAULT_T2 (1 * HZ) /* T2=1s */
+#define LAPB_DEFAULT_N2 20 /* N2=20 */
#define LAPB_SMODULUS 8
#define LAPB_EMODULUS 128
unsigned char condition;
unsigned short n2, n2count;
unsigned short t1, t2;
- unsigned short t1timer, t2timer;
+ struct timer_list t1timer, t2timer;
/* Internal control information */
- struct sk_buff_head input_queue;
struct sk_buff_head write_queue;
struct sk_buff_head ack_queue;
unsigned char window;
- struct timer_list timer;
struct lapb_register_struct callbacks;
/* FRMR control information */
extern void lapb_transmit_frmr(lapb_cb *);
/* lapb_timer.c */
-extern void lapb_set_timer(lapb_cb *);
+extern void lapb_start_t1timer(lapb_cb *);
+extern void lapb_start_t2timer(lapb_cb *);
+extern void lapb_stop_t1timer(lapb_cb *);
+extern void lapb_stop_t2timer(lapb_cb *);
+extern int lapb_t1timer_running(lapb_cb *);
/*
* Debug levels.
#define _NETROM_H
#include <linux/netrom.h>
-#define NR_SLOWHZ 10 /* Run timing at 1/10 second */
-
#define NR_NETWORK_LEN 15
#define NR_TRANSPORT_LEN 5
#define NR_COND_PEER_RX_BUSY 0x04
#define NR_COND_OWN_RX_BUSY 0x08
-#define NR_DEFAULT_T1 (120 * NR_SLOWHZ) /* Outstanding frames - 120 seconds */
-#define NR_DEFAULT_T2 (5 * NR_SLOWHZ) /* Response delay - 5 seconds */
-#define NR_DEFAULT_N2 3 /* Number of Retries - 3 */
-#define NR_DEFAULT_T4 (180 * NR_SLOWHZ) /* Busy Delay - 180 seconds */
-#define NR_DEFAULT_IDLE (20* 60 * NR_SLOWHZ) /* No Activuty Timeout - 900 seconds*/
-#define NR_DEFAULT_WINDOW 4 /* Default Window Size - 4 */
-#define NR_DEFAULT_OBS 6 /* Default Obsolescence Count - 6 */
-#define NR_DEFAULT_QUAL 10 /* Default Neighbour Quality - 10 */
-#define NR_DEFAULT_TTL 16 /* Default Time To Live - 16 */
-#define NR_DEFAULT_ROUTING 1 /* Is routing enabled ? */
-#define NR_DEFAULT_FAILS 2 /* Link fails until route fails */
+#define NR_DEFAULT_T1 (120 * HZ) /* Outstanding frames - 120 seconds */
+#define NR_DEFAULT_T2 (5 * HZ) /* Response delay - 5 seconds */
+#define NR_DEFAULT_N2 3 /* Number of Retries - 3 */
+#define NR_DEFAULT_T4 (180 * HZ) /* Busy Delay - 180 seconds */
+#define NR_DEFAULT_IDLE (0 * 60 * HZ) /* No Activity Timeout - none */
+#define NR_DEFAULT_WINDOW 4 /* Default Window Size - 4 */
+#define NR_DEFAULT_OBS 6 /* Default Obsolescence Count - 6 */
+#define NR_DEFAULT_QUAL 10 /* Default Neighbour Quality - 10 */
+#define NR_DEFAULT_TTL 16 /* Default Time To Live - 16 */
+#define NR_DEFAULT_ROUTING 1 /* Is routing enabled ? */
+#define NR_DEFAULT_FAILS 2 /* Link fails until route fails */
#define NR_MODULUS 256
#define NR_MAX_WINDOW_SIZE 127 /* Maximum Window Allowable - 127 */
unsigned char state, condition, bpqext, window;
unsigned short vs, vr, va, vl;
unsigned char n2, n2count;
- unsigned short t1, t2, t4, idle;
- unsigned short t1timer, t2timer, t4timer, idletimer;
+ unsigned long t1, t2, t4, idle;
unsigned short fraglen;
+ struct timer_list t1timer;
+ struct timer_list t2timer;
+ struct timer_list t4timer;
+ struct timer_list idletimer;
struct sk_buff_head ack_queue;
struct sk_buff_head reseq_queue;
struct sk_buff_head frag_queue;
struct nr_neigh *next;
ax25_address callsign;
ax25_digi *digipeat;
+ ax25_cb *ax25;
struct device *dev;
unsigned char quality;
unsigned char locked;
extern struct device *nr_dev_first(void);
extern struct device *nr_dev_get(ax25_address *);
extern int nr_rt_ioctl(unsigned int, void *);
-extern void nr_link_failed(ax25_address *, struct device *);
+extern void nr_link_failed(ax25_cb *, int);
extern int nr_route_frame(struct sk_buff *, ax25_cb *);
extern int nr_nodes_get_info(char *, char **, off_t, int, int);
extern int nr_neigh_get_info(char *, char **, off_t, int, int);
extern int nr_in_rx_window(struct sock *, unsigned short);
extern void nr_write_internal(struct sock *, int);
extern void nr_transmit_dm(struct sk_buff *);
+extern void nr_disconnect(struct sock *, int);
/* nr_timer.c */
-extern void nr_set_timer(struct sock *);
+extern void nr_start_heartbeat(struct sock *);
+extern void nr_start_t1timer(struct sock *);
+extern void nr_start_t2timer(struct sock *);
+extern void nr_start_t4timer(struct sock *);
+extern void nr_start_idletimer(struct sock *);
+extern void nr_stop_heartbeat(struct sock *);
+extern void nr_stop_t1timer(struct sock *);
+extern void nr_stop_t2timer(struct sock *);
+extern void nr_stop_t4timer(struct sock *);
+extern void nr_stop_idletimer(struct sock *);
+extern int nr_t1timer_running(struct sock *);
/* sysctl_net_netrom.c */
extern void nr_register_sysctl(void);
#define _ROSE_H
#include <linux/rose.h>
-#define ROSE_SLOWHZ 10 /* Run timing at 1/10 second */
-
#define ROSE_ADDR_LEN 5
#define ROSE_MIN_LEN 3
ROSE_STATE_1, /* Awaiting Call Accepted */
ROSE_STATE_2, /* Awaiting Clear Confirmation */
ROSE_STATE_3, /* Data Transfer */
- ROSE_STATE_4 /* Awaiting Reset Confirmation */
+ ROSE_STATE_4, /* Awaiting Reset Confirmation */
+ ROSE_STATE_5 /* Deferred Call Acceptance */
};
-#define ROSE_DEFAULT_T0 (180 * ROSE_SLOWHZ) /* Default T10 T20 value */
-#define ROSE_DEFAULT_T1 (200 * ROSE_SLOWHZ) /* Default T11 T21 value */
-#define ROSE_DEFAULT_T2 (180 * ROSE_SLOWHZ) /* Default T12 T22 value */
-#define ROSE_DEFAULT_T3 (180 * ROSE_SLOWHZ) /* Default T13 T23 value */
-#define ROSE_DEFAULT_HB (5 * ROSE_SLOWHZ) /* Default Holdback value */
-#define ROSE_DEFAULT_IDLE (20 * 60 * ROSE_SLOWHZ) /* Default No Activity value */
-#define ROSE_DEFAULT_ROUTING 1 /* Default routing flag */
-#define ROSE_DEFAULT_FAIL_TIMEOUT (120 * ROSE_SLOWHZ) /* Time until link considered usable */
-#define ROSE_DEFAULT_MAXVC 50 /* Maximum number of VCs per neighbour */
-#define ROSE_DEFAULT_WINDOW_SIZE 3 /* Default window size */
+#define ROSE_DEFAULT_T0 (180 * HZ) /* Default T10 T20 value */
+#define ROSE_DEFAULT_T1 (200 * HZ) /* Default T11 T21 value */
+#define ROSE_DEFAULT_T2 (180 * HZ) /* Default T12 T22 value */
+#define ROSE_DEFAULT_T3 (180 * HZ) /* Default T13 T23 value */
+#define ROSE_DEFAULT_HB (5 * HZ) /* Default Holdback value */
+#define ROSE_DEFAULT_IDLE (0 * 60 * HZ) /* No Activity Timeout - none */
+#define ROSE_DEFAULT_ROUTING 1 /* Default routing flag */
+#define ROSE_DEFAULT_FAIL_TIMEOUT (120 * HZ) /* Time until link considered usable */
+#define ROSE_DEFAULT_MAXVC 50 /* Maximum number of VCs per neighbour */
+#define ROSE_DEFAULT_WINDOW_SIZE 3 /* Default window size */
#define ROSE_MODULUS 8
-#define ROSE_MAX_PACKET_SIZE 256 /* Maximum packet size */
+#define ROSE_MAX_PACKET_SIZE 251 /* Maximum packet size */
#define ROSE_COND_ACK_PENDING 0x01
#define ROSE_COND_PEER_RX_BUSY 0x02
struct rose_neigh *next;
ax25_address callsign;
ax25_digi *digipeat;
+ ax25_cb *ax25;
struct device *dev;
unsigned short count;
+ unsigned short use;
unsigned int number;
char restarted;
char dce_mode;
struct sk_buff_head queue;
- unsigned short t0timer, ftimer;
- struct timer_list timer;
+ struct timer_list t0timer;
+ struct timer_list ftimer;
};
struct rose_node {
struct rose_neigh *neighbour;
struct device *device;
unsigned int lci, rand;
- unsigned char state, condition, qbitincl;
+ unsigned char state, condition, qbitincl, defer;
+ unsigned char cause, diagnostic;
unsigned short vs, vr, va, vl;
- unsigned short timer;
- unsigned short t1, t2, t3, hb, idle;
+ unsigned long t1, t2, t3, hb, idle;
unsigned short fraglen;
+ struct timer_list timer;
+ struct timer_list idletimer;
struct sk_buff_head frag_queue;
struct sock *sk; /* Backlink to socket */
} rose_cb;
extern int rose_process_rx_frame(struct sock *, struct sk_buff *);
/* rose_link.c */
-extern void rose_link_set_timer(struct rose_neigh *);
+extern void rose_start_ftimer(struct rose_neigh *);
+extern void rose_start_t0timer(struct rose_neigh *);
+extern void rose_stop_ftimer(struct rose_neigh *);
+extern void rose_stop_t0timer(struct rose_neigh *);
+extern int rose_ftimer_running(struct rose_neigh *);
+extern int rose_t0timer_running(struct rose_neigh *);
extern void rose_link_rx_restart(struct sk_buff *, struct rose_neigh *, unsigned short);
extern void rose_transmit_restart_request(struct rose_neigh *);
extern void rose_transmit_restart_confirmation(struct rose_neigh *);
extern void rose_transmit_diagnostic(struct rose_neigh *, unsigned char);
-extern void rose_transmit_clear_request(struct rose_neigh *, unsigned int, unsigned char);
+extern void rose_transmit_clear_request(struct rose_neigh *, unsigned int, unsigned char, unsigned char);
extern void rose_transmit_link(struct sk_buff *, struct rose_neigh *);
/* rose_out.c */
extern struct device *rose_dev_get(rose_address *);
extern struct rose_route *rose_route_free_lci(unsigned int, struct rose_neigh *);
extern struct device *rose_ax25_dev_get(char *);
-extern struct rose_neigh *rose_get_neigh(rose_address *);
+extern struct rose_neigh *rose_get_neigh(rose_address *, unsigned char *, unsigned char *);
extern int rose_rt_ioctl(unsigned int, void *);
-extern void rose_link_failed(ax25_address *, struct device *);
+extern void rose_link_failed(ax25_cb *, int);
extern int rose_route_frame(struct sk_buff *, ax25_cb *);
extern int rose_nodes_get_info(char *, char **, off_t, int, int);
extern int rose_neigh_get_info(char *, char **, off_t, int, int);
extern int rose_decode(struct sk_buff *, int *, int *, int *, int *, int *);
extern int rose_parse_facilities(struct sk_buff *, struct rose_facilities *);
extern int rose_create_facilities(unsigned char *, rose_cb *);
+extern void rose_disconnect(struct sock *, int, int, int);
/* rose_timer.c */
-extern void rose_set_timer(struct sock *);
+extern void rose_start_heartbeat(struct sock *);
+extern void rose_start_t1timer(struct sock *);
+extern void rose_start_t2timer(struct sock *);
+extern void rose_start_t3timer(struct sock *);
+extern void rose_start_hbtimer(struct sock *);
+extern void rose_start_idletimer(struct sock *);
+extern void rose_stop_heartbeat(struct sock *);
+extern void rose_stop_timer(struct sock *);
+extern void rose_stop_idletimer(struct sock *);
/* sysctl_net_rose.c */
extern void rose_register_sysctl(void);
#define _X25_H
#include <linux/x25.h>
-#define X25_SLOWHZ 1 /* Run timing at 1 Hz */
-
#define X25_ADDR_LEN 16
#define X25_MAX_L2_LEN 18 /* 802.2 LLC */
X25_LINK_STATE_3
};
-#define X25_DEFAULT_T20 (180 * X25_SLOWHZ) /* Default T20 value */
-#define X25_DEFAULT_T21 (200 * X25_SLOWHZ) /* Default T21 value */
-#define X25_DEFAULT_T22 (180 * X25_SLOWHZ) /* Default T22 value */
-#define X25_DEFAULT_T23 (180 * X25_SLOWHZ) /* Default T23 value */
-#define X25_DEFAULT_T2 (3 * X25_SLOWHZ) /* Default ack holdback value */
+#define X25_DEFAULT_T20 (180 * HZ) /* Default T20 value */
+#define X25_DEFAULT_T21 (200 * HZ) /* Default T21 value */
+#define X25_DEFAULT_T22 (180 * HZ) /* Default T22 value */
+#define X25_DEFAULT_T23 (180 * HZ) /* Default T23 value */
+#define X25_DEFAULT_T2 (3 * HZ) /* Default ack holdback value */
#define X25_DEFAULT_WINDOW_SIZE 2 /* Default Window Size */
#define X25_DEFAULT_PACKET_SIZE X25_PS128 /* Default Packet Size */
unsigned int state;
unsigned int extended;
struct sk_buff_head queue;
- unsigned short t20, t20timer;
- struct timer_list timer;
+ unsigned long t20;
+ struct timer_list t20timer;
};
typedef struct {
unsigned int lci;
unsigned char state, condition, qbitincl, intflag;
unsigned short vs, vr, va, vl;
- unsigned short timer;
- unsigned short t2, t21, t22, t23;
+ unsigned long t2, t21, t22, t23;
unsigned short fraglen;
struct sk_buff_head fragment_queue;
struct sk_buff_head interrupt_in_queue;
struct sk_buff_head interrupt_out_queue;
struct sock *sk; /* Backlink to socket */
+ struct timer_list timer;
+ struct x25_causediag causediag;
struct x25_facilities facilities;
struct x25_calluserdata calluserdata;
} x25_cb;
extern int x25_addr_ntoa(unsigned char *, x25_address *, x25_address *);
extern int x25_addr_aton(unsigned char *, x25_address *, x25_address *);
-extern unsigned int x25_new_lci(void);
-extern struct sock *x25_find_socket(unsigned int);
+extern unsigned int x25_new_lci(struct x25_neigh *);
+extern struct sock *x25_find_socket(unsigned int, struct x25_neigh *);
extern void x25_destroy_socket(struct sock *);
extern int x25_rx_call_request(struct sk_buff *, struct x25_neigh *, unsigned int);
extern int x25_validate_nr(struct sock *, unsigned short);
extern void x25_write_internal(struct sock *, int);
extern int x25_decode(struct sock *, struct sk_buff *, int *, int *, int *, int *, int *);
+extern void x25_disconnect(struct sock *, int, unsigned char, unsigned char);
/* x25_timer.c */
-extern void x25_set_timer(struct sock *);
+extern void x25_start_heartbeat(struct sock *);
+extern void x25_start_t2timer(struct sock *);
+extern void x25_start_t21timer(struct sock *);
+extern void x25_start_t22timer(struct sock *);
+extern void x25_start_t23timer(struct sock *);
+extern void x25_stop_heartbeat(struct sock *);
+extern void x25_stop_timer(struct sock *);
+extern unsigned long x25_display_timer(struct sock *);
/* sysctl_net_x25.c */
extern void x25_register_sysctl(void);
if (fs) {
tsk->fs = NULL;
if (!--fs->count) {
- iput(fs->root);
- iput(fs->pwd);
+ dput(fs->root);
+ dput(fs->pwd);
kfree(fs);
}
}
return -1;
tsk->fs->count = 1;
tsk->fs->umask = current->fs->umask;
- if ((tsk->fs->root = current->fs->root))
- atomic_inc(&tsk->fs->root->i_count);
- if ((tsk->fs->pwd = current->fs->pwd))
- atomic_inc(&tsk->fs->pwd->i_count);
+ tsk->fs->root = dget(current->fs->root);
+ tsk->fs->pwd = dget(current->fs->pwd);
return 0;
}
EXPORT_SYMBOL(getname);
EXPORT_SYMBOL(putname);
EXPORT_SYMBOL(__fput);
-EXPORT_SYMBOL(__iget);
+EXPORT_SYMBOL(iget);
EXPORT_SYMBOL(_iput);
-EXPORT_SYMBOL(namei);
+EXPORT_SYMBOL(__namei);
+EXPORT_SYMBOL(lookup_dentry);
EXPORT_SYMBOL(open_namei);
EXPORT_SYMBOL(sys_close);
EXPORT_SYMBOL(close_fp);
EXPORT_SYMBOL(sys_tz);
EXPORT_SYMBOL(__wait_on_super);
EXPORT_SYMBOL(file_fsync);
-EXPORT_SYMBOL(_free_ibasket);
EXPORT_SYMBOL(_clear_inode);
EXPORT_SYMBOL(refile_buffer);
EXPORT_SYMBOL(nr_async_pages);
unsigned long page;
long retval;
- if ((unsigned long)user_name >= TASK_SIZE)
+ if ((unsigned long)user_name >= TASK_SIZE
+ && get_fs () != KERNEL_DS)
return -EFAULT;
page = __get_free_page(GFP_KERNEL);
error = -EEXIST;
goto err1;
}
- if ((mod = (struct module *)vmalloc(size)) == NULL) {
+ if ((mod = (struct module *)module_map(size)) == NULL) {
error = -ENOMEM;
goto err1;
}
{
struct module *mod;
int i;
+ struct kernel_sym ksym;
lock_kernel();
for (mod = module_list, i = 0; mod; mod = mod->next) {
if (table == NULL)
goto out;
+ /* So that we don't give the user our stack content */
+ memset (&ksym, 0, sizeof (ksym));
+
for (mod = module_list, i = 0; mod; mod = mod->next) {
- struct kernel_sym ksym;
struct module_symbol *msym;
unsigned int j;
/* And free the memory. */
- vfree(mod);
+ module_unmap(mod);
}
/*
NULL, /* mknod */
NULL, /* rename */
NULL, /* readlink */
+ NULL, /* follow_link */
NULL, /* readpage */
NULL, /* writepage */
NULL, /* bmap */
if (correct_wcount)
file->f_inode->i_writecount++;
merge_segments(mm, vma->vm_start, vma->vm_end);
+
+ addr = vma->vm_start;
/* merge_segments might have merged our vma, so we can't use it any more */
mm->total_vm += len >> PAGE_SHIFT;
}
void
-kfree(void *objp)
+kfree(const void *objp)
{
struct page *page;
int nr;
lock_kernel();
if (!suser())
goto out;
- err = namei(NAM_FOLLOW_LINK, specialfile, &inode);
+ err = namei(specialfile, &inode);
if (err)
goto out;
prev = -1;
} else {
p->prio = --least_priority;
}
- error = namei(NAM_FOLLOW_LINK, specialfile, &swap_inode);
+ error = namei(specialfile, &swap_inode);
if (error)
goto bad_swap_2;
p->swap_file = swap_inode;
- error = -EBUSY;
- if (atomic_read(&swap_inode->i_count) != 1)
- goto bad_swap_2;
error = -EINVAL;
if (S_ISBLK(swap_inode->i_mode)) {
}
}
-static void free_area_pages(unsigned long address, unsigned long size)
+void vmfree_area_pages(unsigned long address, unsigned long size)
{
pgd_t * dir;
unsigned long end = address + size;
return 0;
}
-static int alloc_area_pages(unsigned long address, unsigned long size)
+int vmalloc_area_pages(unsigned long address, unsigned long size)
{
pgd_t * dir;
unsigned long end = address + size;
for (p = &vmlist ; (tmp = *p) ; p = &tmp->next) {
if (tmp->addr == addr) {
*p = tmp->next;
- free_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size);
+ vmfree_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size);
kfree(tmp);
return;
}
if (!area)
return NULL;
addr = area->addr;
- if (alloc_area_pages(VMALLOC_VMADDR(addr), size)) {
+ if (vmalloc_area_pages(VMALLOC_VMADDR(addr), size)) {
vfree(addr);
return NULL;
}
endif
endif
-# We must attach netsyms.o to socket.o, as otherwise there is nothing
-# to pull the object file from the archive.
-
SOCK := socket.o
ifeq ($(CONFIG_NET),y)
ifeq ($(CONFIG_MODULES),y)
802 [other ] alan@lxorguk.ukuu.org.uk
[token ring ] pnorton@cts.com
appletalk alan@lxorguk.ukuu.org.uk and netatalk@umich.edu
-ax25 jsn@cs.nott.ac.uk
+ax25 g4klx@g4klx.demon.co.uk
core alan@lxorguk.ukuu.org.uk
decnet SteveW@ACM.org
ethernet alan@lxorguk.ukuu.org.uk
ipv4 davem@caip.rutgers.edu,Eric.Schenk@dna.lth.se
ipv6 davem@caip.rutgers.edu,Eric.Schenk@dna.lth.se
ipx alan@lxorguk.ukuu.org.uk,greg@caldera.com
-lapb jsn@cs.nott.ac.uk
-netrom jsn@cs.nott.ac.uk
-rose jsn@cs.nott.ac.uk
+lapb g4klx@g4klx.demon.co.uk
+netrom g4klx@g4klx.demon.co.uk
+rose g4klx@g4klx.demon.co.uk
wanrouter genek@compuserve.com and dm@sangoma.com
unix alan@lxorguk.ukuu.org.uk
-x25 jsn@cs.nott.ac.uk
+x25 g4klx@g4klx.demon.co.uk
If in doubt contact me <alan@lxorguk.ukuu.org.uk> first.
/*
- * AX.25 release 036
+ * AX.25 release 037
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
* AX.25 036 Jonathan(G4KLX) Major restructuring.
* Joerg(DL1BKE) Fixed DAMA Slave.
* Jonathan(G4KLX) Fix widlcard listen parameter setting.
+ * AX.25 037 Jonathan(G4KLX) New timer architecture.
*/
#include <linux/config.h>
ax25_cb *s;
unsigned long flags;
- save_flags(flags);
- cli();
+ save_flags(flags); cli();
if ((s = ax25_list) == ax25) {
ax25_list = s->next;
for (s = ax25_list; s != NULL; s = s->next) {
if (s->ax25_dev == ax25_dev) {
- s->state = AX25_STATE_0;
s->ax25_dev = NULL;
- if (s->sk != NULL) {
- s->sk->state = TCP_CLOSE;
- s->sk->err = ENETUNREACH;
- s->sk->shutdown |= SEND_SHUTDOWN;
- if (!s->sk->dead)
- s->sk->state_change(s->sk);
- s->sk->dead = 1;
- }
+ ax25_disconnect(s, ENETUNREACH);
}
}
}
* Find an AX.25 control block given both ends. It will only pick up
* floating AX.25 control blocks or non Raw socket bound control blocks.
*/
-ax25_cb *ax25_find_cb(ax25_address *my_addr, ax25_address *dest_addr, ax25_digi *digi, struct device *dev)
+ax25_cb *ax25_find_cb(ax25_address *src_addr, ax25_address *dest_addr, ax25_digi *digi, struct device *dev)
{
ax25_cb *s;
unsigned long flags;
for (s = ax25_list; s != NULL; s = s->next) {
if (s->sk != NULL && s->sk->type != SOCK_SEQPACKET)
continue;
- if (ax25cmp(&s->source_addr, my_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->ax25_dev->dev == dev) {
- if (digi != NULL) {
- if (s->digipeat == NULL && digi->ndigi != 0)
+ if (s->ax25_dev == NULL)
+ continue;
+ if (ax25cmp(&s->source_addr, src_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->ax25_dev->dev == dev) {
+ if (digi != NULL && digi->ndigi != 0) {
+ if (s->digipeat == NULL)
continue;
- if (s->digipeat != NULL && ax25digicmp(s->digipeat, digi) != 0)
+ if (ax25digicmp(s->digipeat, digi) != 0)
+ continue;
+ } else {
+ if (s->digipeat != NULL && s->digipeat->ndigi != 0)
continue;
}
restore_flags(flags);
struct sk_buff *skb;
unsigned long flags;
- save_flags(flags);
- cli();
+ save_flags(flags); cli();
- del_timer(&ax25->timer);
+ ax25_stop_heartbeat(ax25);
+ ax25_stop_t1timer(ax25);
+ ax25_stop_t2timer(ax25);
+ ax25_stop_t3timer(ax25);
+ ax25_stop_idletimer(ax25);
ax25_remove_socket(ax25);
ax25_clear_queues(ax25); /* Flush the queues */
while ((skb = skb_dequeue(&ax25->sk->receive_queue)) != NULL) {
if (skb->sk != ax25->sk) { /* A pending connection */
skb->sk->dead = 1; /* Queue the unaccepted socket for death */
- ax25_set_timer(skb->sk->protinfo.ax25);
+ ax25_start_heartbeat(skb->sk->protinfo.ax25);
skb->sk->protinfo.ax25->state = AX25_STATE_0;
}
struct ax25_ctl_struct ax25_ctl;
ax25_dev *ax25_dev;
ax25_cb *ax25;
- unsigned long flags;
- int err;
-
- if ((err = verify_area(VERIFY_READ, arg, sizeof(ax25_ctl))) != 0)
- return err;
- copy_from_user(&ax25_ctl, arg, sizeof(ax25_ctl));
+ if (copy_from_user(&ax25_ctl, arg, sizeof(ax25_ctl)))
+ return -EFAULT;
if ((ax25_dev = ax25_addr_ax25dev(&ax25_ctl.port_addr)) == NULL)
return -ENODEV;
switch (ax25_ctl.cmd) {
case AX25_KILL:
- ax25_clear_queues(ax25);
ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
- ax25->state = AX25_STATE_0;
#ifdef CONFIG_AX25_DAMA_SLAVE
if (ax25_dev->dama.slave && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE)
ax25_dama_off(ax25);
#endif
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = ENETRESET;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
- ax25_set_timer(ax25);
+ ax25_disconnect(ax25, ENETRESET);
break;
case AX25_WINDOW:
case AX25_T1:
if (ax25_ctl.arg < 1)
return -EINVAL;
- ax25->rtt = (ax25_ctl.arg * AX25_SLOWHZ) / 2;
- ax25->t1 = ax25_ctl.arg * AX25_SLOWHZ;
- save_flags(flags); cli();
- if (ax25->t1timer > ax25->t1)
- ax25->t1timer = ax25->t1;
- restore_flags(flags);
+ ax25->rtt = (ax25_ctl.arg * HZ) / 2;
+ ax25->t1 = ax25_ctl.arg * HZ;
break;
case AX25_T2:
if (ax25_ctl.arg < 1)
return -EINVAL;
- save_flags(flags); cli();
- ax25->t2 = ax25_ctl.arg * AX25_SLOWHZ;
- if (ax25->t2timer > ax25->t2)
- ax25->t2timer = ax25->t2;
- restore_flags(flags);
+ ax25->t2 = ax25_ctl.arg * HZ;
break;
case AX25_N2:
case AX25_T3:
if (ax25_ctl.arg < 0)
return -EINVAL;
- save_flags(flags); cli();
- ax25->t3 = ax25_ctl.arg * AX25_SLOWHZ;
- if (ax25->t3timer != 0)
- ax25->t3timer = ax25->t3;
- restore_flags(flags);
+ ax25->t3 = ax25_ctl.arg * HZ;
break;
case AX25_IDLE:
if (ax25_ctl.arg < 0)
return -EINVAL;
- save_flags(flags); cli();
- ax25->idle = ax25_ctl.arg * AX25_SLOWHZ * 60;
- if (ax25->idletimer != 0)
- ax25->idletimer = ax25->idle;
- restore_flags(flags);
+ ax25->idle = ax25_ctl.arg * 60 * HZ;
break;
case AX25_PACLEN:
skb_queue_head_init(&ax25->reseq_queue);
init_timer(&ax25->timer);
+ init_timer(&ax25->t1timer);
+ init_timer(&ax25->t2timer);
+ init_timer(&ax25->t3timer);
+ init_timer(&ax25->idletimer);
ax25_fillin_cb(ax25, NULL);
case AX25_T1:
if (opt < 1)
return -EINVAL;
- sk->protinfo.ax25->rtt = (opt * AX25_SLOWHZ) / 2;
+ sk->protinfo.ax25->rtt = (opt * HZ) / 2;
+ sk->protinfo.ax25->t1 = opt * HZ;
return 0;
case AX25_T2:
if (opt < 1)
return -EINVAL;
- sk->protinfo.ax25->t2 = opt * AX25_SLOWHZ;
+ sk->protinfo.ax25->t2 = opt * HZ;
return 0;
case AX25_N2:
case AX25_T3:
if (opt < 1)
return -EINVAL;
- sk->protinfo.ax25->t3 = opt * AX25_SLOWHZ;
+ sk->protinfo.ax25->t3 = opt * HZ;
return 0;
case AX25_IDLE:
if (opt < 0)
return -EINVAL;
- sk->protinfo.ax25->idle = opt * AX25_SLOWHZ * 60;
+ sk->protinfo.ax25->idle = opt * 60 * HZ;
return 0;
case AX25_BACKOFF:
break;
case AX25_T1:
- val = (sk->protinfo.ax25->t1 * 2) / AX25_SLOWHZ;
+ val = sk->protinfo.ax25->t1 / HZ;
break;
case AX25_T2:
- val = sk->protinfo.ax25->t2 / AX25_SLOWHZ;
+ val = sk->protinfo.ax25->t2 / HZ;
break;
case AX25_N2:
break;
case AX25_T3:
- val = sk->protinfo.ax25->t3 / AX25_SLOWHZ;
+ val = sk->protinfo.ax25->t3 / HZ;
break;
case AX25_IDLE:
- val = sk->protinfo.ax25->idle / (AX25_SLOWHZ * 60);
+ val = sk->protinfo.ax25->idle / (60 * HZ);
break;
case AX25_BACKOFF:
if (sk->type == SOCK_SEQPACKET) {
switch (sk->protinfo.ax25->state) {
case AX25_STATE_0:
- sk->state = TCP_CLOSE;
- sk->shutdown |= SEND_SHUTDOWN;
- sk->state_change(sk);
- sk->dead = 1;
+ ax25_disconnect(sk->protinfo.ax25, 0);
ax25_destroy_socket(sk->protinfo.ax25);
break;
case AX25_STATE_1:
case AX25_STATE_2:
- ax25_clear_queues(sk->protinfo.ax25);
ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
- sk->protinfo.ax25->state = AX25_STATE_0;
- sk->state = TCP_CLOSE;
- sk->shutdown |= SEND_SHUTDOWN;
- sk->state_change(sk);
- sk->dead = 1;
+ ax25_disconnect(sk->protinfo.ax25, 0);
ax25_destroy_socket(sk->protinfo.ax25);
break;
case AX25_PROTO_STD_SIMPLEX:
case AX25_PROTO_STD_DUPLEX:
ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
- sk->protinfo.ax25->t3timer = 0;
+ ax25_stop_t2timer(sk->protinfo.ax25);
+ ax25_stop_t3timer(sk->protinfo.ax25);
+ ax25_stop_idletimer(sk->protinfo.ax25);
break;
#ifdef AX25_CONFIG_DAMA_SLAVE
case AX25_PROTO_DAMA_SLAVE:
- sk->protinfo.ax25->t3timer = 0;
+ ax25_stop_t3timer(sk->protinfo.ax25);
break;
#endif
}
- sk->protinfo.ax25->t1timer = sk->protinfo.ax25->t1 = ax25_calculate_t1(sk->protinfo.ax25);
- sk->protinfo.ax25->state = AX25_STATE_2;
- sk->state = TCP_CLOSE;
- sk->shutdown |= SEND_SHUTDOWN;
+ ax25_calculate_t1(sk->protinfo.ax25);
+ ax25_start_t1timer(sk->protinfo.ax25);
+ sk->protinfo.ax25->state = AX25_STATE_2;
+ sk->state = TCP_CLOSE;
+ sk->shutdown |= SEND_SHUTDOWN;
sk->state_change(sk);
- sk->dead = 1;
- sk->destroy = 1;
+ sk->dead = 1;
+ sk->destroy = 1;
break;
default:
break;
}
} else {
- sk->state = TCP_CLOSE;
- sk->shutdown |= SEND_SHUTDOWN;
+ sk->state = TCP_CLOSE;
+ sk->shutdown |= SEND_SHUTDOWN;
sk->state_change(sk);
- sk->dead = 1;
+ sk->dead = 1;
ax25_destroy_socket(sk->protinfo.ax25);
}
static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
{
struct sock *sk = sock->sk;
- struct sockaddr_ax25 *addr = (struct sockaddr_ax25 *)uaddr;
- int err;
+ struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr;
+ ax25_digi *digi = NULL;
+ int ct = 0, err;
if (sk->state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) {
sock->state = SS_CONNECTED;
if (addr_len != sizeof(struct sockaddr_ax25) && addr_len != sizeof(struct full_sockaddr_ax25))
return -EINVAL;
- if (addr->sax25_family != AF_AX25)
+ if (fsa->fsa_ax25.sax25_family != AF_AX25)
return -EINVAL;
+ if (sk->protinfo.ax25->digipeat != NULL) {
+ kfree(sk->protinfo.ax25->digipeat);
+ sk->protinfo.ax25->digipeat = NULL;
+ }
+
/*
* Handle digi-peaters to be used.
*/
- if (addr_len == sizeof(struct full_sockaddr_ax25) && addr->sax25_ndigis != 0) {
- int ct = 0;
- struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)addr;
-
+ if (addr_len == sizeof(struct full_sockaddr_ax25) && fsa->fsa_ax25.sax25_ndigis != 0) {
/* Valid number of digipeaters ? */
- if (addr->sax25_ndigis < 1 || addr->sax25_ndigis > AX25_MAX_DIGIS)
+ if (fsa->fsa_ax25.sax25_ndigis < 1 || fsa->fsa_ax25.sax25_ndigis > AX25_MAX_DIGIS)
return -EINVAL;
- if (sk->protinfo.ax25->digipeat == NULL) {
- if ((sk->protinfo.ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL)
- return -ENOBUFS;
- }
+ if ((digi = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL)
+ return -ENOBUFS;
- sk->protinfo.ax25->digipeat->ndigi = addr->sax25_ndigis;
- sk->protinfo.ax25->digipeat->lastrepeat = -1;
+ digi->ndigi = fsa->fsa_ax25.sax25_ndigis;
+ digi->lastrepeat = -1;
- while (ct < addr->sax25_ndigis) {
+ while (ct < fsa->fsa_ax25.sax25_ndigis) {
if ((fsa->fsa_digipeater[ct].ax25_call[6] & AX25_HBIT) && sk->protinfo.ax25->iamdigi) {
- sk->protinfo.ax25->digipeat->repeated[ct] = 1;
- sk->protinfo.ax25->digipeat->lastrepeat = ct;
+ digi->repeated[ct] = 1;
+ digi->lastrepeat = ct;
} else {
- sk->protinfo.ax25->digipeat->repeated[ct] = 0;
+ digi->repeated[ct] = 0;
}
- sk->protinfo.ax25->digipeat->calls[ct] = fsa->fsa_digipeater[ct];
+ digi->calls[ct] = fsa->fsa_digipeater[ct];
ct++;
}
}
* been filled in, error if it hasn't.
*/
if (sk->zapped) {
- if ((err = ax25_rt_autobind(sk->protinfo.ax25, &addr->sax25_call)) < 0)
+ if ((err = ax25_rt_autobind(sk->protinfo.ax25, &fsa->fsa_ax25.sax25_call)) < 0)
return err;
ax25_fillin_cb(sk->protinfo.ax25, sk->protinfo.ax25->ax25_dev);
ax25_insert_socket(sk->protinfo.ax25);
return -EHOSTUNREACH;
}
- if (sk->type == SOCK_SEQPACKET && ax25_find_cb(&sk->protinfo.ax25->source_addr, &addr->sax25_call, NULL, sk->protinfo.ax25->ax25_dev->dev) != NULL)
+ if (sk->type == SOCK_SEQPACKET && ax25_find_cb(&sk->protinfo.ax25->source_addr, &fsa->fsa_ax25.sax25_call, digi, sk->protinfo.ax25->ax25_dev->dev) != NULL) {
+ if (digi != NULL) kfree(digi);
return -EADDRINUSE; /* Already such a connection */
+ }
- sk->protinfo.ax25->dest_addr = addr->sax25_call;
+ sk->protinfo.ax25->dest_addr = fsa->fsa_ax25.sax25_call;
+ sk->protinfo.ax25->digipeat = digi;
/* First the easy one */
if (sk->type != SOCK_SEQPACKET) {
}
sk->protinfo.ax25->state = AX25_STATE_1;
- ax25_set_timer(sk->protinfo.ax25); /* Start going SABM SABM until a UA or a give up and DM */
+
+ ax25_start_heartbeat(sk->protinfo.ax25);
/* Now the loop */
if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct sock *sk = sock->sk;
- struct ax25_info_struct ax25_info;
- int err;
- long amount = 0;
switch (cmd) {
- case TIOCOUTQ:
- if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int))) != 0)
- return err;
+ case TIOCOUTQ: {
+ long amount;
amount = sk->sndbuf - atomic_read(&sk->wmem_alloc);
if (amount < 0)
amount = 0;
- put_user(amount, (int *)arg);
+ if (put_user(amount, (int *)arg))
+ return -EFAULT;
return 0;
+ }
case TIOCINQ: {
struct sk_buff *skb;
+ long amount = 0L;
/* These two are safe on a single CPU system as only user tasks fiddle here */
if ((skb = skb_peek(&sk->receive_queue)) != NULL)
amount = skb->len;
- if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int))) != 0)
- return err;
- put_user(amount, (int *)arg);
+ if (put_user(amount, (int *)arg))
+ return -EFAULT;
return 0;
}
case SIOCGSTAMP:
if (sk != NULL) {
- if (sk->stamp.tv_sec==0)
+ if (sk->stamp.tv_sec == 0)
return -ENOENT;
- if ((err = verify_area(VERIFY_WRITE,(void *)arg,sizeof(struct timeval))) != 0)
- return err;
- copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval));
+ if (copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)))
+ return -EFAULT;
return 0;
}
return -EINVAL;
case SIOCAX25DELUID: /* Delete a uid from the uid/call map table */
case SIOCAX25GETUID: {
struct sockaddr_ax25 sax25;
- if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(struct sockaddr_ax25))) != 0)
- return err;
- copy_from_user(&sax25, (void *)arg, sizeof(sax25));
+ if (copy_from_user(&sax25, (void *)arg, sizeof(sax25)))
+ return -EFAULT;
return ax25_uid_ioctl(cmd, &sax25);
}
- case SIOCAX25NOUID: /* Set the default policy (default/bar) */
- if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(unsigned long))) != 0)
- return err;
+ case SIOCAX25NOUID: { /* Set the default policy (default/bar) */
+ long amount;
if (!suser())
return -EPERM;
- get_user(amount, (long *)arg);
+ if (get_user(amount, (long *)arg))
+ return -EFAULT;
if (amount > AX25_NOUID_BLOCK)
return -EINVAL;
ax25_uid_policy = amount;
return 0;
+ }
case SIOCADDRT:
case SIOCDELRT:
return -EPERM;
return ax25_ctl_ioctl(cmd, (void *)arg);
- case SIOCAX25GETINFO:
- if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(ax25_info))) != 0)
- return err;
- ax25_info.t1 = sk->protinfo.ax25->t1;
- ax25_info.t2 = sk->protinfo.ax25->t2;
- ax25_info.t3 = sk->protinfo.ax25->t3;
- ax25_info.idle = sk->protinfo.ax25->idle;
+ case SIOCAX25GETINFO: {
+ struct ax25_info_struct ax25_info;
+ ax25_info.t1 = sk->protinfo.ax25->t1 / HZ;
+ ax25_info.t2 = sk->protinfo.ax25->t2 / HZ;
+ ax25_info.t3 = sk->protinfo.ax25->t3 / HZ;
+ ax25_info.idle = sk->protinfo.ax25->idle / (60 * HZ);
ax25_info.n2 = sk->protinfo.ax25->n2;
- ax25_info.t1timer = sk->protinfo.ax25->t1timer;
- ax25_info.t2timer = sk->protinfo.ax25->t2timer;
- ax25_info.t3timer = sk->protinfo.ax25->t3timer;
- ax25_info.idletimer = sk->protinfo.ax25->idletimer;
+ ax25_info.t1timer = ax25_display_timer(&sk->protinfo.ax25->t1timer) / HZ;
+ ax25_info.t2timer = ax25_display_timer(&sk->protinfo.ax25->t2timer) / HZ;
+ ax25_info.t3timer = ax25_display_timer(&sk->protinfo.ax25->t3timer) / HZ;
+ ax25_info.idletimer = ax25_display_timer(&sk->protinfo.ax25->idletimer) / (60 * HZ);
ax25_info.n2count = sk->protinfo.ax25->n2count;
ax25_info.state = sk->protinfo.ax25->state;
ax25_info.rcv_q = atomic_read(&sk->rmem_alloc);
ax25_info.snd_q = atomic_read(&sk->wmem_alloc);
- copy_to_user((void *)arg, &ax25_info, sizeof(ax25_info));
+ if (copy_to_user((void *)arg, &ax25_info, sizeof(ax25_info)))
+ return -EFAULT;
return 0;
+ }
case SIOCAX25ADDFWD:
case SIOCAX25DELFWD: {
struct ax25_fwd_struct ax25_fwd;
if (!suser())
return -EPERM;
- if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(ax25_fwd))) != 0)
- return err;
- copy_from_user(&ax25_fwd, (void *)arg, sizeof(ax25_fwd));
+ if (copy_from_user(&ax25_fwd, (void *)arg, sizeof(ax25_fwd)))
+ return -EFAULT;
return ax25_fwd_ioctl(cmd, &ax25_fwd);
}
{
ax25_cb *ax25;
const char *devname;
+ char callbuf[15];
int len = 0;
off_t pos = 0;
off_t begin = 0;
cli();
- len += sprintf(buffer, "dest_addr src_addr dev st vs vr va t1 t2 t3 idle n2 rtt wnd paclen Snd-Q Rcv-Q\n");
+ len += sprintf(buffer, "dest_addr src_addr dev st vs vr va t1 t2 t3 idle n2 rtt wnd paclen Snd-Q Rcv-Q\n");
for (ax25 = ax25_list; ax25 != NULL; ax25 = ax25->next) {
if (ax25->ax25_dev == NULL)
len += sprintf(buffer + len, "%-9s ",
ax2asc(&ax25->dest_addr));
- len += sprintf(buffer + len, "%-9s %-4s %2d %3d %3d %3d %3d/%03d %2d/%02d %3d/%03d %3d/%03d %2d/%02d %3d %3d %5d",
- ax2asc(&ax25->source_addr), devname,
+
+ sprintf(callbuf, "%s%c", ax2asc(&ax25->source_addr),
+ (ax25->iamdigi) ? '*' : ' ');
+
+ len += sprintf(buffer + len, "%-10s %-4s %2d %3d %3d %3d %3lu/%03lu %2lu/%02lu %3lu/%03lu %3lu/%03lu %2d/%02d %3lu %3d %5d",
+ callbuf,
+ devname,
ax25->state,
- ax25->vs, ax25->vr, ax25->va,
- ax25->t1timer / AX25_SLOWHZ,
- ax25->t1 / AX25_SLOWHZ,
- ax25->t2timer / AX25_SLOWHZ,
- ax25->t2 / AX25_SLOWHZ,
- ax25->t3timer / AX25_SLOWHZ,
- ax25->t3 / AX25_SLOWHZ,
- ax25->idletimer / (AX25_SLOWHZ * 60),
- ax25->idle / (AX25_SLOWHZ * 60),
- ax25->n2count, ax25->n2,
- ax25->rtt / AX25_SLOWHZ,
+ ax25->vs,
+ ax25->vr,
+ ax25->va,
+ ax25_display_timer(&ax25->t1timer) / HZ,
+ ax25->t1 / HZ,
+ ax25_display_timer(&ax25->t2timer) / HZ,
+ ax25->t2 / HZ,
+ ax25_display_timer(&ax25->t3timer) / HZ,
+ ax25->t3 / HZ,
+ ax25_display_timer(&ax25->idletimer) / (60 * HZ),
+ ax25->idle / (60 * HZ),
+ ax25->n2count,
+ ax25->n2,
+ ax25->rtt / HZ,
ax25->window,
ax25->paclen);
EXPORT_SYMBOL(ax25_encapsulate);
EXPORT_SYMBOL(ax25_rebuild_header);
EXPORT_SYMBOL(ax25_findbyuid);
-EXPORT_SYMBOL(ax25_link_up);
+EXPORT_SYMBOL(ax25_find_cb);
EXPORT_SYMBOL(ax25_linkfail_register);
EXPORT_SYMBOL(ax25_linkfail_release);
EXPORT_SYMBOL(ax25_listen_register);
EXPORT_SYMBOL(ax2asc);
EXPORT_SYMBOL(asc2ax);
EXPORT_SYMBOL(null_ax25_address);
+EXPORT_SYMBOL(ax25_display_timer);
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry proc_ax25_route = {
proc_net_register(&proc_ax25_calls);
#endif
- printk(KERN_INFO "G4KLX/GW4PTS AX.25 for Linux. Version 0.36 for Linux NET3.038 (Linux 2.1)\n");
+ printk(KERN_INFO "G4KLX/GW4PTS AX.25 for Linux. Version 0.37 for Linux NET3.038 (Linux 2.1)\n");
}
#ifdef MODULE
/*
- * AX.25 release 036
+ * AX.25 release 037
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
if (len < 14) return NULL;
- if (flags != NULL) {
- *flags = 0;
+ *flags = 0;
- if (buf[6] & AX25_CBIT)
- *flags = AX25_COMMAND;
- if (buf[13] & AX25_CBIT)
- *flags = AX25_RESPONSE;
- }
+ if (buf[6] & AX25_CBIT)
+ *flags = AX25_COMMAND;
+ if (buf[13] & AX25_CBIT)
+ *flags = AX25_RESPONSE;
if (dama != NULL)
*dama = ~buf[13] & AX25_DAMA_FLAG;
/* Copy to, from */
- if (dest != NULL)
- memcpy(dest, buf + 0, AX25_ADDR_LEN);
-
- if (src != NULL)
- memcpy(src, buf + 7, AX25_ADDR_LEN);
+ memcpy(dest, buf + 0, AX25_ADDR_LEN);
+ memcpy(src, buf + 7, AX25_ADDR_LEN);
buf += 2 * AX25_ADDR_LEN;
len -= 2 * AX25_ADDR_LEN;
+
digi->lastrepeat = -1;
digi->ndigi = 0;
if (d >= AX25_MAX_DIGIS) return NULL; /* Max of 6 digis */
if (len < 7) return NULL; /* Short packet */
- if (digi != NULL) {
- memcpy(&digi->calls[d], buf, AX25_ADDR_LEN);
- digi->ndigi = d + 1;
- if (buf[6] & AX25_HBIT) {
- digi->repeated[d] = 1;
- digi->lastrepeat = d;
- } else {
- digi->repeated[d] = 0;
- }
+ memcpy(&digi->calls[d], buf, AX25_ADDR_LEN);
+ digi->ndigi = d + 1;
+
+ if (buf[6] & AX25_HBIT) {
+ digi->repeated[d] = 1;
+ digi->lastrepeat = d;
+ } else {
+ digi->repeated[d] = 0;
}
buf += AX25_ADDR_LEN;
*/
void ax25_digi_invert(ax25_digi *in, ax25_digi *out)
{
- int ct = 0;
+ int ct;
out->ndigi = in->ndigi;
out->lastrepeat = in->ndigi - in->lastrepeat - 2;
/* Invert the digipeaters */
+ for (ct = 0; ct < in->ndigi; ct++) {
+ out->calls[ct] = in->calls[in->ndigi - ct - 1];
- while (ct < in->ndigi) {
- out->calls[ct] = in->calls[in->ndigi - ct - 1];
if (ct <= out->lastrepeat) {
out->calls[ct].ax25_call[6] |= AX25_HBIT;
out->repeated[ct] = 1;
out->calls[ct].ax25_call[6] &= ~AX25_HBIT;
out->repeated[ct] = 0;
}
- ct++;
}
}
/*
- * AX.25 release 036
+ * AX.25 release 037
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
/*
- * AX.25 release 036
+ * AX.25 release 037
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
* History
* AX.25 036 Jonathan(G4KLX) Cloned from ax25_in.c
* Joerg(DL1BKE) Fixed it.
+ * AX.25 037 Jonathan(G4KLX) New timer architecture.
*/
#include <linux/config.h>
case AX25_UA:
ax25_calculate_rtt(ax25);
- ax25->t1timer = 0;
- ax25->t3timer = ax25->t3;
- ax25->idletimer = ax25->idle;
+ ax25_stop_t1timer(ax25);
+ ax25_start_t3timer(ax25);
+ ax25_start_idletimer(ax25);
ax25->vs = 0;
ax25->va = 0;
ax25->vr = 0;
break;
case AX25_DM:
- if (pf) {
- ax25_clear_queues(ax25);
- ax25->state = AX25_STATE_0;
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = ECONNREFUSED;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
- }
+ if (pf) ax25_disconnect(ax25, ECONNREFUSED);
break;
default:
- if (pf)
- ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
+ if (pf) ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
break;
}
case AX25_DISC:
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- ax25->state = AX25_STATE_0;
ax25_dama_off(ax25);
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = 0;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
+ ax25_disconnect(ax25, 0);
break;
case AX25_DM:
case AX25_UA:
if (pf) {
- ax25->state = AX25_STATE_0;
ax25_dama_off(ax25);
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = 0;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
+ ax25_disconnect(ax25, 0);
}
break;
ax25->modulus = AX25_MODULUS;
ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+ ax25_stop_t1timer(ax25);
+ ax25_start_t3timer(ax25);
+ ax25_start_idletimer(ax25);
ax25->condition = 0x00;
- ax25->t1timer = 0;
- ax25->t3timer = ax25->t3;
- ax25->idletimer = ax25->idle;
ax25->vs = 0;
ax25->va = 0;
ax25->vr = 0;
break;
case AX25_DISC:
- ax25_clear_queues(ax25);
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- ax25->t3timer = 0;
- ax25->state = AX25_STATE_0;
ax25_dama_off(ax25);
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = 0;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
+ ax25_disconnect(ax25, 0);
break;
case AX25_DM:
- ax25_clear_queues(ax25);
- ax25->t3timer = 0;
- ax25->state = AX25_STATE_0;
ax25_dama_off(ax25);
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = ECONNRESET;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
+ ax25_disconnect(ax25, ECONNRESET);
break;
case AX25_RR:
if (ax25_validate_nr(ax25, nr)) {
ax25_frames_acked(ax25, nr);
ax25_calculate_rtt(ax25);
+ ax25_stop_t1timer(ax25);
+ ax25_start_t3timer(ax25);
ax25->n2count = 0;
- ax25->t1timer = 0;
- ax25->t3timer = ax25->t3;
ax25_requeue_frames(ax25);
if (type == AX25_COMMAND && pf)
ax25_ds_enquiry_response(ax25);
if (ns == ax25->vr) {
ax25->vr = (ax25->vr + 1) % AX25_MODULUS;
queued = ax25_rx_iframe(ax25, skb);
- if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
+ if (ax25->condition & AX25_COND_OWN_RX_BUSY)
ax25->vr = ns; /* ax25->vr - 1 */
- if (pf) ax25_ds_enquiry_response(ax25);
- break;
- }
ax25->condition &= ~AX25_COND_REJECT;
if (pf) {
ax25_ds_enquiry_response(ax25);
} else {
if (!(ax25->condition & AX25_COND_ACK_PENDING)) {
- ax25->t2timer = ax25->t2;
ax25->condition |= AX25_COND_ACK_PENDING;
+ ax25_start_t2timer(ax25);
}
}
} else {
/*
- * AX.25 release 036
+ * AX.25 release 037
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
* AX.25 036 Jonathan(G4KLX) Cloned from ax25_out.c and ax25_subr.c.
* Joerg(DL1BKE) Changed ax25_ds_enquiry_response(),
* fixed ax25_dama_on() and ax25_dama_off().
+ * AX.25 037 Jonathan(G4KLX) New timer architecture.
*/
#include <linux/config.h>
else
ax25->n2count = 0;
- ax25->t3timer = ax25->t3;
+ ax25_start_t3timer(ax25);
ax25_ds_set_timer(ax25->ax25_dev);
for (ax25o = ax25_list; ax25o != NULL; ax25o = ax25o->next) {
if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2 || skb_peek(&ax25o->ack_queue) != NULL)
ax25_ds_t1_timeout(ax25o);
- ax25o->t3timer = ax25o->t3;
+ ax25_start_t3timer(ax25o);
}
}
{
ax25->condition &= AX25_COND_DAMA_MODE;
ax25->n2count = 0;
- ax25->t3timer = ax25->t3;
- ax25->t2timer = 0;
- ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
+ ax25_calculate_t1(ax25);
+ ax25_start_t1timer(ax25);
+ ax25_stop_t2timer(ax25);
+ ax25_start_t3timer(ax25);
}
/*
/*
- * AX.25 release 036
+ * AX.25 release 037
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
* History
* AX.25 036 Jonathan(G4KLX) Cloned from ax25_timer.c.
* Joerg(DL1BKE) Added DAMA Slave Timeout timer
+ * AX.25 037 Jonathan(G4KLX) New timer architecture.
*/
#include <linux/config.h>
if (ax25->ax25_dev != ax25_dev || !(ax25->condition & AX25_COND_DAMA_MODE))
continue;
- ax25_link_failed(&ax25->dest_addr, ax25_dev->dev);
- ax25_clear_queues(ax25);
ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
- ax25->state = AX25_STATE_0;
-
- if (ax25->sk != NULL) {
- SOCK_DEBUG(ax25->sk, "AX.25 DAMA Slave Timeout\n");
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = ETIMEDOUT;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
-
- ax25_set_timer(ax25); /* notify socket... */
+ ax25_disconnect(ax25, ETIMEDOUT);
}
ax25_dev_dama_off(ax25_dev);
}
-/*
- * AX.25 TIMER
- *
- * This routine is called every 100ms. Decrement timer by this
- * amount - if expired then process the event.
- */
-void ax25_ds_timer(ax25_cb *ax25)
+void ax25_ds_heartbeat_expiry(ax25_cb *ax25)
{
switch (ax25->state) {
+
case AX25_STATE_0:
/* Magic here: If we listen() and a new link dies before it
is accepted() it isn't 'dead' so doesn't get removed. */
if (ax25->sk == NULL || ax25->sk->destroy || (ax25->sk->state == TCP_LISTEN && ax25->sk->dead)) {
- del_timer(&ax25->timer);
ax25_destroy_socket(ax25);
return;
}
}
}
break;
-
- default:
- break;
- }
-
- /* dl1bke 960114: T3 works much like the IDLE timeout, but
- * gets reloaded with every frame for this
- * connection.
- */
-
- if (ax25->t3timer > 0 && --ax25->t3timer == 0) {
- ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev);
- ax25_clear_queues(ax25);
- ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
-
- ax25->state = AX25_STATE_0;
- ax25_dama_off(ax25);
-
- if (ax25->sk != NULL) {
- SOCK_DEBUG(ax25->sk, "AX.25 T3 Timeout\n");
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = ETIMEDOUT;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
-
- ax25_set_timer(ax25);
-
- return;
}
- /* dl1bke 960228: close the connection when IDLE expires.
- * unlike T3 this timer gets reloaded only on
- * I frames.
- */
-
- if (ax25->idletimer > 0 && --ax25->idletimer == 0) {
- ax25_clear_queues(ax25);
-
- ax25->n2count = 0;
- ax25->t3timer = ax25->t3;
+ ax25_start_heartbeat(ax25);
+}
+
+/* dl1bke 960114: T3 works much like the IDLE timeout, but
+ * gets reloaded with every frame for this
+ * connection.
+ */
+void ax25_ds_t3timer_expiry(ax25_cb *ax25)
+{
+ ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
+ ax25_dama_off(ax25);
+ ax25_disconnect(ax25, ETIMEDOUT);
+}
- /* state 1 or 2 should not happen, but... */
+/* dl1bke 960228: close the connection when IDLE expires.
+ * unlike T3 this timer gets reloaded only on
+ * I frames.
+ */
+void ax25_ds_idletimer_expiry(ax25_cb *ax25)
+{
+ ax25_clear_queues(ax25);
- if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2)
- ax25->state = AX25_STATE_0;
- else
- ax25->state = AX25_STATE_2;
+ ax25->n2count = 0;
- ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
+ /* state 1 or 2 should not happen, but... */
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = 0;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- ax25->sk->destroy = 1;
- }
+ if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2)
+ ax25->state = AX25_STATE_0;
+ else
+ ax25->state = AX25_STATE_2;
+
+ ax25_calculate_t1(ax25);
+ ax25_start_t1timer(ax25);
+ ax25_start_t3timer(ax25);
+
+ if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = 0;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
}
-
- ax25_set_timer(ax25);
}
/* dl1bke 960114: The DAMA protocol requires to send data and SABM/DISC
void ax25_ds_t1_timeout(ax25_cb *ax25)
{
switch (ax25->state) {
+
case AX25_STATE_1:
if (ax25->n2count == ax25->n2) {
if (ax25->modulus == AX25_MODULUS) {
- ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev);
- ax25_clear_queues(ax25);
- ax25->state = AX25_STATE_0;
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = ETIMEDOUT;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
+ ax25_disconnect(ax25, ETIMEDOUT);
+ return;
} else {
ax25->modulus = AX25_MODULUS;
ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
case AX25_STATE_2:
if (ax25->n2count == ax25->n2) {
- ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev);
- ax25_clear_queues(ax25);
- ax25->state = AX25_STATE_0;
ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
-
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = ETIMEDOUT;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
+ ax25_disconnect(ax25, ETIMEDOUT);
+ return;
} else {
ax25->n2count++;
}
case AX25_STATE_3:
if (ax25->n2count == ax25->n2) {
- ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev);
- ax25_clear_queues(ax25);
ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE);
- ax25->state = AX25_STATE_0;
- if (ax25->sk != NULL) {
- SOCK_DEBUG(ax25->sk, "AX.25 link Failure\n");
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = ETIMEDOUT;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
+ ax25_disconnect(ax25, ETIMEDOUT);
+ return;
} else {
ax25->n2count++;
}
break;
}
- ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
-
- ax25_set_timer(ax25);
+ ax25_calculate_t1(ax25);
+ ax25_start_t1timer(ax25);
}
#endif
/*
- * AX.25 release 036
+ * AX.25 release 037
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
static struct linkfail_struct {
struct linkfail_struct *next;
- void (*func)(ax25_address *, struct device *);
+ void (*func)(ax25_cb *, int);
} *linkfail_list = NULL;
static struct listen_struct {
restore_flags(flags);
}
-int ax25_linkfail_register(void (*func)(ax25_address *, struct device *))
+int ax25_linkfail_register(void (*func)(ax25_cb *, int))
{
struct linkfail_struct *linkfail;
unsigned long flags;
return 1;
}
-void ax25_linkfail_release(void (*func)(ax25_address *, struct device *))
+void ax25_linkfail_release(void (*func)(ax25_cb *, int))
{
struct linkfail_struct *s, *linkfail = linkfail_list;
unsigned long flags;
return 0;
}
-void ax25_link_failed(ax25_address *callsign, struct device *dev)
+void ax25_link_failed(ax25_cb *ax25, int reason)
{
struct linkfail_struct *linkfail;
for (linkfail = linkfail_list; linkfail != NULL; linkfail = linkfail->next)
- (linkfail->func)(callsign, dev);
-}
-
-/*
- * Return the state of an AX.25 link given source, destination, and
- * device.
- */
-int ax25_link_up(ax25_address *src, ax25_address *dest, ax25_digi *digi, struct device *dev)
-{
- return ax25_find_cb(src, dest, digi, dev) != NULL;
+ (linkfail->func)(ax25, reason);
}
int ax25_protocol_is_registered(unsigned int pid)
/*
- * AX.25 release 036
+ * AX.25 release 037
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
* AX.25 035 Hans(PE1AYX) Fixed interface to IP layer.
* AX.25 036 Jonathan(G4KLX) Move DAMA code into own file.
* Joerg(DL1BKE) Fixed DAMA Slave.
+ * AX.25 037 Jonathan(G4KLX) New timer architecture.
*/
#include <linux/config.h>
if (skb == NULL) return 0;
- ax25->idletimer = ax25->idle;
+ ax25_start_idletimer(ax25);
pid = *skb->data;
if (ax25->state == AX25_STATE_0)
return 0;
- del_timer(&ax25->timer);
-
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
case AX25_PROTO_STD_SIMPLEX:
case AX25_PROTO_STD_DUPLEX:
#endif
}
- ax25_set_timer(ax25);
-
return queued;
}
}
ax25_fillin_cb(ax25, ax25_dev);
- ax25->idletimer = ax25->idle;
}
ax25->source_addr = dest;
ax25_dama_on(ax25);
#endif
- ax25->t3timer = ax25->t3;
- ax25->state = AX25_STATE_3;
+ ax25->state = AX25_STATE_3;
ax25_insert_socket(ax25);
- ax25_set_timer(ax25);
+ ax25_start_heartbeat(ax25);
+ ax25_start_t3timer(ax25);
+ ax25_start_idletimer(ax25);
if (sk != NULL) {
if (!sk->dead)
/*
- * AX.25 release 036
+ * AX.25 release 037
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
int ax25_rebuild_header(struct sk_buff *skb)
{
struct sk_buff *ourskb;
- int mode;
unsigned char *bp = skb->data;
struct device *dev = skb->dev;
+ ax25_address *src, *dst;
+ ax25_route *route;
ax25_dev *ax25_dev;
+ dst = (ax25_address *)(bp + 1);
+ src = (ax25_address *)(bp + 8);
+
if (arp_find(bp + 1, skb))
return 1;
if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
return 1;
+ route = ax25_rt_find_route(dst, dev);
+
if (bp[16] == AX25_P_IP) {
- mode = ax25_ip_mode_get((ax25_address *)(bp + 1), dev);
- if (mode == 'V' || (mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) {
+ if (route->ip_mode == 'V' || (route->ip_mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) {
/*
* We copy the buffer and release the original thereby
* keeping it straight
skb_pull(ourskb, AX25_HEADER_LEN - 1); /* Keep PID */
- ax25_send_frame(ourskb, ax25_dev->values[AX25_VALUES_PACLEN], (ax25_address *)(bp + 8), (ax25_address *)(bp + 1), NULL, dev);
+ ax25_send_frame(ourskb, ax25_dev->values[AX25_VALUES_PACLEN], src, dst, route->digipeat, dev);
return 1;
}
bp[14] |= AX25_EBIT;
bp[14] |= AX25_SSSID_SPARE;
- if ((ourskb = ax25_dg_build_path(skb, (ax25_address *)(bp + 1), dev)) == NULL) {
- kfree_skb(skb, FREE_WRITE);
- return 1;
+ if (route->digipeat != NULL) {
+ if ((ourskb = ax25_rt_build_path(skb, src, dst, route->digipeat)) == NULL) {
+ kfree_skb(skb, FREE_WRITE);
+ return 1;
+ }
+
+ skb = ourskb;
}
- ourskb->dev = dev;
- ourskb->priority = SOPRI_NORMAL;
+ skb->dev = dev;
+ skb->priority = SOPRI_NORMAL;
- ax25_queue_xmit(ourskb);
+ ax25_queue_xmit(skb);
return 1;
}
/*
- * AX.25 release 036
+ * AX.25 release 037
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
* AX.25 I-Frames. Added PACLEN parameter.
* Joerg(DL1BKE) Fixed a problem with buffer allocation
* for fragments.
+ * AX.25 037 Jonathan(G4KLX) New timer architecture.
*/
#include <linux/config.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
-int ax25_send_frame(struct sk_buff *skb, int paclen, ax25_address *src, ax25_address *dest, ax25_digi *digi, struct device *dev)
+ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, ax25_address *src, ax25_address *dest, ax25_digi *digi, struct device *dev)
{
ax25_dev *ax25_dev;
ax25_cb *ax25;
*/
if ((ax25 = ax25_find_cb(src, dest, digi, dev)) != NULL) {
ax25_output(ax25, paclen, skb);
- ax25->idletimer = ax25->idle;
- return 1; /* It already existed */
+ return ax25; /* It already existed */
}
if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
- return 0;
+ return NULL;
if ((ax25 = ax25_create_cb()) == NULL)
- return 0;
+ return NULL;
ax25_fillin_cb(ax25, ax25_dev);
if (digi != NULL) {
if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
ax25_free_cb(ax25);
- return 0;
+ return NULL;
}
*ax25->digipeat = *digi;
- } else {
- ax25_rt_build_path(ax25, dest, dev);
}
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
#endif
}
- /* idle timeouts only for mode vc connections */
-
- ax25->idletimer = ax25->idle;
-
ax25_insert_socket(ax25);
ax25->state = AX25_STATE_1;
- ax25_set_timer(ax25);
+ ax25_start_heartbeat(ax25);
ax25_output(ax25, paclen, skb);
- return 1; /* We had to create it */
+ return ax25; /* We had to create it */
}
/*
}
if (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_STD_SIMPLEX ||
- ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_STD_DUPLEX) {
- if (ax25->state == AX25_STATE_3 || ax25->state == AX25_STATE_4)
- ax25_kick(ax25);
- }
+ ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_STD_DUPLEX)
+ ax25_kick(ax25);
}
/*
frame[1] |= (ax25->vr << 1);
}
+ ax25_start_idletimer(ax25);
+
ax25_transmit_buffer(ax25, skb, AX25_COMMAND);
}
int last = 1;
unsigned short start, end, next;
- del_timer(&ax25->timer);
+ if (ax25->state != AX25_STATE_3 && ax25->state != AX25_STATE_4)
+ return;
+
+ if (ax25->condition & AX25_COND_PEER_RX_BUSY)
+ return;
+
+ if (skb_peek(&ax25->write_queue) == NULL)
+ return;
start = (skb_peek(&ax25->ack_queue) == NULL) ? ax25->va : ax25->vs;
end = (ax25->va + ax25->window) % ax25->modulus;
- if (!(ax25->condition & AX25_COND_PEER_RX_BUSY) &&
- start != end &&
- skb_peek(&ax25->write_queue) != NULL) {
+ if (start == end)
+ return;
- ax25->vs = start;
+ ax25->vs = start;
- /*
- * Transmit data until either we're out of data to send or
- * the window is full. Send a poll on the final I frame if
- * the window is filled.
- */
+ /*
+ * Transmit data until either we're out of data to send or
+ * the window is full. Send a poll on the final I frame if
+ * the window is filled.
+ */
- /*
- * Dequeue the frame and copy it.
- */
- skb = skb_dequeue(&ax25->write_queue);
+ /*
+ * Dequeue the frame and copy it.
+ */
+ skb = skb_dequeue(&ax25->write_queue);
- do {
- if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
- skb_queue_head(&ax25->write_queue, skb);
- break;
- }
+ do {
+ if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
+ skb_queue_head(&ax25->write_queue, skb);
+ break;
+ }
- if (skb->sk != NULL)
- skb_set_owner_w(skbn, skb->sk);
+ if (skb->sk != NULL)
+ skb_set_owner_w(skbn, skb->sk);
- next = (ax25->vs + 1) % ax25->modulus;
- last = (next == end);
+ next = (ax25->vs + 1) % ax25->modulus;
+ last = (next == end);
- /*
- * Transmit the frame copy.
- * bke 960114: do not set the Poll bit on the last frame
- * in DAMA mode.
- */
- switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- ax25_send_iframe(ax25, skbn, (last) ? AX25_POLLON : AX25_POLLOFF);
- break;
+ /*
+ * Transmit the frame copy.
+ * bke 960114: do not set the Poll bit on the last frame
+ * in DAMA mode.
+ */
+ switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
+ case AX25_PROTO_STD_SIMPLEX:
+ case AX25_PROTO_STD_DUPLEX:
+ ax25_send_iframe(ax25, skbn, (last) ? AX25_POLLON : AX25_POLLOFF);
+ break;
#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- ax25_send_iframe(ax25, skbn, AX25_POLLOFF);
- break;
+ case AX25_PROTO_DAMA_SLAVE:
+ ax25_send_iframe(ax25, skbn, AX25_POLLOFF);
+ break;
#endif
- }
+ }
- ax25->vs = next;
+ ax25->vs = next;
- /*
- * Requeue the original data frame.
- */
- skb_queue_tail(&ax25->ack_queue, skb);
+ /*
+ * Requeue the original data frame.
+ */
+ skb_queue_tail(&ax25->ack_queue, skb);
- } while (!last && (skb = skb_dequeue(&ax25->write_queue)) != NULL);
+ } while (!last && (skb = skb_dequeue(&ax25->write_queue)) != NULL);
- ax25->condition &= ~AX25_COND_ACK_PENDING;
+ ax25->condition &= ~AX25_COND_ACK_PENDING;
- if (ax25->t1timer == 0) {
- ax25->t3timer = 0;
- ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
- }
+ if (!ax25_t1timer_running(ax25)) {
+ ax25_stop_t3timer(ax25);
+ ax25_calculate_t1(ax25);
+ ax25_start_t1timer(ax25);
}
-
- ax25_set_timer(ax25);
}
void ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type)
int headroom;
if (ax25->ax25_dev == NULL) {
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = ENETUNREACH;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
+ ax25_disconnect(ax25, ENETUNREACH);
return;
}
if (ax25->vs == nr) {
ax25_frames_acked(ax25, nr);
ax25_calculate_rtt(ax25);
- ax25->t1timer = 0;
- ax25->t3timer = ax25->t3;
+ ax25_stop_t1timer(ax25);
+ ax25_start_t3timer(ax25);
} else {
if (ax25->va != nr) {
ax25_frames_acked(ax25, nr);
- ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
+ ax25_calculate_t1(ax25);
+ ax25_start_t1timer(ax25);
}
}
}
/*
- * AX.25 release 036
+ * AX.25 release 037
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
struct ax25_routes_struct route;
struct ax25_route_opt_struct rt_option;
ax25_dev *ax25_dev;
- int i, err;
+ int i;
switch (cmd) {
case SIOCADDRT:
- if ((err = verify_area(VERIFY_READ, arg, sizeof(route))) != 0)
- return err;
- copy_from_user(&route, arg, sizeof(route));
+ if (copy_from_user(&route, arg, sizeof(route)))
+ return -EFAULT;
if ((ax25_dev = ax25_addr_ax25dev(&route.port_addr)) == NULL)
return -EINVAL;
if (route.digi_count > AX25_MAX_DIGIS)
break;
case SIOCDELRT:
- if ((err = verify_area(VERIFY_READ, arg, sizeof(route))) != 0)
- return err;
- copy_from_user(&route, arg, sizeof(route));
+ if (copy_from_user(&route, arg, sizeof(route)))
+ return -EFAULT;
if ((ax25_dev = ax25_addr_ax25dev(&route.port_addr)) == NULL)
return -EINVAL;
ax25_rt = ax25_route_list;
break;
case SIOCAX25OPTRT:
- if ((err = verify_area(VERIFY_READ, arg, sizeof(rt_option))) != 0)
- return err;
- copy_from_user(&rt_option, arg, sizeof(rt_option));
+ if (copy_from_user(&rt_option, arg, sizeof(rt_option)))
+ return -EFAULT;
if ((ax25_dev = ax25_addr_ax25dev(&rt_option.port_addr)) == NULL)
return -EINVAL;
for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
* dl1bke 960117: build digipeater path
* dl1bke 960301: use the default route if it exists
*/
-void ax25_rt_build_path(ax25_cb *ax25, ax25_address *addr, struct device *dev)
+ax25_route *ax25_rt_find_route(ax25_address *addr, struct device *dev)
{
+ static ax25_route route;
ax25_route *ax25_rt;
- if ((ax25_rt = ax25_find_route(addr, dev)) == NULL)
- return;
-
- if (ax25_rt->digipeat == NULL)
- return;
-
- if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL)
- return;
-
- if ((ax25->ax25_dev = ax25_dev_ax25dev(ax25_rt->dev)) == NULL)
- return;
+ if ((ax25_rt = ax25_find_route(addr, dev)) == NULL) {
+ route.next = NULL;
+ route.callsign = *addr;
+ route.dev = dev;
+ route.digipeat = NULL;
+ route.ip_mode = ' ';
+ return &route;
+ }
- *ax25->digipeat = *ax25_rt->digipeat;
- ax25_adjust_path(addr, ax25->digipeat);
+ return ax25_rt;
}
-struct sk_buff *ax25_dg_build_path(struct sk_buff *skb, ax25_address *addr, struct device *dev)
+struct sk_buff *ax25_rt_build_path(struct sk_buff *skb, ax25_address *src, ax25_address *dest, ax25_digi *digi)
{
struct sk_buff *skbn;
- ax25_route *ax25_rt;
- ax25_digi digipeat;
- ax25_address src, dest;
unsigned char *bp;
int len;
skb_pull(skb, 1); /* skip KISS command */
- if ((ax25_rt = ax25_find_route(addr, dev)) == NULL)
- return skb;
-
- if (ax25_rt->digipeat == NULL)
- return skb;
-
- digipeat = *ax25_rt->digipeat;
-
- ax25_adjust_path(addr, &digipeat);
-
- len = ax25_rt->digipeat->ndigi * AX25_ADDR_LEN;
+ len = digi->ndigi * AX25_ADDR_LEN;
if (skb_headroom(skb) < len) {
if ((skbn = skb_realloc_headroom(skb, len)) == NULL) {
skb = skbn;
}
- memcpy(&dest, skb->data + 0, AX25_ADDR_LEN);
- memcpy(&src, skb->data + 7, AX25_ADDR_LEN);
-
bp = skb_push(skb, len);
- ax25_addr_build(bp, &src, &dest, ax25_rt->digipeat, AX25_COMMAND, AX25_MODULUS);
+ ax25_addr_build(bp, src, dest, digi, AX25_COMMAND, AX25_MODULUS);
return skb;
}
-/*
- * Return the IP mode of a given callsign/device pair.
- */
-char ax25_ip_mode_get(ax25_address *callsign, struct device *dev)
-{
- ax25_route *ax25_rt;
-
- for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next)
- if (ax25cmp(&ax25_rt->callsign, callsign) == 0 && ax25_rt->dev == dev)
- return ax25_rt->ip_mode;
-
- return ' ';
-}
-
#ifdef MODULE
/*
/*
- * AX.25 release 036
+ * AX.25 release 037
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
* Modularisation changes.
* AX.25 035 Hans(PE1AYX) Fixed interface to IP layer.
* AX.25 036 Jonathan(G4KLX) Cloned from ax25_in.c.
+ * AX.25 037 Jonathan(G4KLX) New timer architecture.
*/
#include <linux/config.h>
case AX25_UA:
if (pf) {
ax25_calculate_rtt(ax25);
- ax25->t1timer = 0;
- ax25->t3timer = ax25->t3;
- ax25->idletimer = ax25->idle;
+ ax25_stop_t1timer(ax25);
+ ax25_start_t3timer(ax25);
+ ax25_start_idletimer(ax25);
ax25->vs = 0;
ax25->va = 0;
ax25->vr = 0;
case AX25_DM:
if (pf) {
if (ax25->modulus == AX25_MODULUS) {
- ax25_clear_queues(ax25);
- ax25->state = AX25_STATE_0;
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = ECONNREFUSED;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
+ ax25_disconnect(ax25, ECONNREFUSED);
} else {
ax25->modulus = AX25_MODULUS;
ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
case AX25_DISC:
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- ax25->state = AX25_STATE_0;
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = 0;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
+ ax25_disconnect(ax25, 0);
break;
case AX25_DM:
case AX25_UA:
- if (pf) {
- ax25->state = AX25_STATE_0;
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = 0;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
- }
+ if (pf) ax25_disconnect(ax25, 0);
break;
case AX25_I:
ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
}
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+ ax25_stop_t1timer(ax25);
+ ax25_stop_t2timer(ax25);
+ ax25_start_t3timer(ax25);
+ ax25_start_idletimer(ax25);
ax25->condition = 0x00;
- ax25->t1timer = 0;
- ax25->t3timer = ax25->t3;
- ax25->idletimer = ax25->idle;
ax25->vs = 0;
ax25->va = 0;
ax25->vr = 0;
break;
case AX25_DISC:
- ax25_clear_queues(ax25);
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- ax25->t3timer = 0;
- ax25->state = AX25_STATE_0;
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = 0;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
+ ax25_disconnect(ax25, 0);
break;
case AX25_DM:
- ax25_clear_queues(ax25);
- ax25->t3timer = 0;
- ax25->state = AX25_STATE_0;
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = ECONNRESET;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
+ ax25_disconnect(ax25, ECONNRESET);
break;
case AX25_RR:
if (ax25_validate_nr(ax25, nr)) {
ax25_frames_acked(ax25, nr);
ax25_calculate_rtt(ax25);
- ax25->t1timer = 0;
- ax25->t3timer = ax25->t3;
+ ax25_stop_t1timer(ax25);
+ ax25_start_t3timer(ax25);
ax25_requeue_frames(ax25);
} else {
ax25_std_nr_error_recovery(ax25);
if (ns == ax25->vr) {
ax25->vr = (ax25->vr + 1) % ax25->modulus;
queued = ax25_rx_iframe(ax25, skb);
- if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
+ if (ax25->condition & AX25_COND_OWN_RX_BUSY)
ax25->vr = ns; /* ax25->vr - 1 */
- if (pf) ax25_std_enquiry_response(ax25);
- break;
- }
ax25->condition &= ~AX25_COND_REJECT;
if (pf) {
ax25_std_enquiry_response(ax25);
} else {
if (!(ax25->condition & AX25_COND_ACK_PENDING)) {
- ax25->t2timer = ax25->t2;
ax25->condition |= AX25_COND_ACK_PENDING;
+ ax25_start_t2timer(ax25);
}
}
} else {
ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
}
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
+ ax25_stop_t1timer(ax25);
+ ax25_stop_t2timer(ax25);
+ ax25_start_t3timer(ax25);
+ ax25_start_idletimer(ax25);
ax25->condition = 0x00;
- ax25->t1timer = 0;
- ax25->t3timer = ax25->t3;
- ax25->idletimer = ax25->idle;
ax25->vs = 0;
ax25->va = 0;
ax25->vr = 0;
break;
case AX25_DISC:
- ax25_clear_queues(ax25);
ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- ax25->t3timer = 0;
- ax25->state = AX25_STATE_0;
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = 0;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
+ ax25_disconnect(ax25, 0);
break;
case AX25_DM:
- ax25_clear_queues(ax25);
- ax25->t3timer = 0;
- ax25->state = AX25_STATE_0;
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = ECONNRESET;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
+ ax25_disconnect(ax25, ECONNRESET);
break;
case AX25_RR:
else
ax25->condition |= AX25_COND_PEER_RX_BUSY;
if (type == AX25_RESPONSE && pf) {
- ax25->t1timer = 0;
+ ax25_stop_t1timer(ax25);
if (ax25_validate_nr(ax25, nr)) {
ax25_frames_acked(ax25, nr);
if (ax25->vs == ax25->va) {
- ax25->t3timer = ax25->t3;
+ ax25_start_t3timer(ax25);
ax25->n2count = 0;
ax25->state = AX25_STATE_3;
} else {
case AX25_REJ:
ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
if (pf && type == AX25_RESPONSE) {
- ax25->t1timer = 0;
+ ax25_stop_t1timer(ax25);
if (ax25_validate_nr(ax25, nr)) {
ax25_frames_acked(ax25, nr);
if (ax25->vs == ax25->va) {
- ax25->t3timer = ax25->t3;
+ ax25_start_t3timer(ax25);
ax25->n2count = 0;
ax25->state = AX25_STATE_3;
} else {
if (ns == ax25->vr) {
ax25->vr = (ax25->vr + 1) % ax25->modulus;
queued = ax25_rx_iframe(ax25, skb);
- if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
+ if (ax25->condition & AX25_COND_OWN_RX_BUSY)
ax25->vr = ns; /* ax25->vr - 1 */
- if (pf) ax25_std_enquiry_response(ax25);
- break;
- }
ax25->condition &= ~AX25_COND_REJECT;
if (pf) {
ax25_std_enquiry_response(ax25);
} else {
if (!(ax25->condition & AX25_COND_ACK_PENDING)) {
- ax25->t2timer = ax25->t2;
ax25->condition |= AX25_COND_ACK_PENDING;
+ ax25_start_t2timer(ax25);
}
}
} else {
break;
}
+ ax25_kick(ax25);
+
return queued;
}
/*
- * AX.25 release 036
+ * AX.25 release 037
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
*
* History
* AX.25 036 Jonathan(G4KLX) Split from ax25_out.c.
+ * AX.25 037 Jonathan(G4KLX) New timer architecture.
*/
#include <linux/config.h>
else
ax25_send_control(ax25, AX25_SABME, AX25_POLLON, AX25_COMMAND);
- ax25->t3timer = 0;
- ax25->t2timer = 0;
- ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
+ ax25_calculate_t1(ax25);
+ ax25_stop_idletimer(ax25);
+ ax25_stop_t3timer(ax25);
+ ax25_stop_t2timer(ax25);
+ ax25_start_t1timer(ax25);
}
void ax25_std_transmit_enquiry(ax25_cb *ax25)
ax25->condition &= ~AX25_COND_ACK_PENDING;
- ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
+ ax25_calculate_t1(ax25);
+ ax25_start_t1timer(ax25);
}
void ax25_std_enquiry_response(ax25_cb *ax25)
/*
- * AX.25 release 036
+ * AX.25 release 037
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
* AX.25 033 Jonathan(G4KLX) Modularisation functions.
* AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating.
* AX.25 036 Jonathan(G4KLX) Split from ax25_timer.c.
+ * AX.25 037 Jonathan(G4KLX) New timer architecture.
*/
#include <linux/config.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
-void ax25_std_timer(ax25_cb *ax25)
+void ax25_std_heartbeat_expiry(ax25_cb *ax25)
{
switch (ax25->state) {
+
case AX25_STATE_0:
/* Magic here: If we listen() and a new link dies before it
is accepted() it isn't 'dead' so doesn't get removed. */
if (ax25->sk == NULL || ax25->sk->destroy || (ax25->sk->state == TCP_LISTEN && ax25->sk->dead)) {
- del_timer(&ax25->timer);
ax25_destroy_socket(ax25);
return;
}
break;
}
}
- /*
- * Check for frames to transmit.
- */
- ax25_kick(ax25);
- break;
-
- default:
- break;
}
- if (ax25->t2timer > 0 && --ax25->t2timer == 0) {
- if (ax25->state == AX25_STATE_3 || ax25->state == AX25_STATE_4) {
- if (ax25->condition & AX25_COND_ACK_PENDING) {
- ax25->condition &= ~AX25_COND_ACK_PENDING;
- ax25_std_timeout_response(ax25);
- }
- }
- }
+ ax25_start_heartbeat(ax25);
+}
- if (ax25->t3timer > 0 && --ax25->t3timer == 0) {
- if (ax25->state == AX25_STATE_3) {
- ax25->n2count = 0;
- ax25_std_transmit_enquiry(ax25);
- ax25->state = AX25_STATE_4;
- }
- ax25->t3timer = ax25->t3;
+void ax25_std_t2timer_expiry(ax25_cb *ax25)
+{
+ if (ax25->condition & AX25_COND_ACK_PENDING) {
+ ax25->condition &= ~AX25_COND_ACK_PENDING;
+ ax25_std_timeout_response(ax25);
}
+}
- if (ax25->idletimer > 0 && --ax25->idletimer == 0) {
- /* dl1bke 960228: close the connection when IDLE expires */
- /* similar to DAMA T3 timeout but with */
- /* a "clean" disconnect of the connection */
-
- ax25_clear_queues(ax25);
-
- ax25->n2count = 0;
- ax25->t3timer = 0;
- ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
- ax25->state = AX25_STATE_2;
- ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
-
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = 0;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- ax25->sk->destroy = 1;
- }
- }
+void ax25_std_t3timer_expiry(ax25_cb *ax25)
+{
+ ax25->n2count = 0;
+ ax25_std_transmit_enquiry(ax25);
+ ax25->state = AX25_STATE_4;
+}
- if (ax25->t1timer == 0 || --ax25->t1timer > 0) {
- ax25_set_timer(ax25);
- return;
+void ax25_std_idletimer_expiry(ax25_cb *ax25)
+{
+ ax25_clear_queues(ax25);
+
+ ax25->n2count = 0;
+ ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
+ ax25->state = AX25_STATE_2;
+
+ ax25_calculate_t1(ax25);
+ ax25_start_t1timer(ax25);
+ ax25_stop_t2timer(ax25);
+ ax25_stop_t3timer(ax25);
+
+ if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = 0;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
}
+}
+void ax25_std_t1timer_expiry(ax25_cb *ax25)
+{
switch (ax25->state) {
case AX25_STATE_1:
if (ax25->n2count == ax25->n2) {
if (ax25->modulus == AX25_MODULUS) {
- ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev);
- ax25_clear_queues(ax25);
- ax25->state = AX25_STATE_0;
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = ETIMEDOUT;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
+ ax25_disconnect(ax25, ETIMEDOUT);
+ return;
} else {
ax25->modulus = AX25_MODULUS;
ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
case AX25_STATE_2:
if (ax25->n2count == ax25->n2) {
- ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev);
- ax25_clear_queues(ax25);
- ax25->state = AX25_STATE_0;
ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
-
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = ETIMEDOUT;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
+ ax25_disconnect(ax25, ETIMEDOUT);
+ return;
} else {
ax25->n2count++;
ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
case AX25_STATE_4:
if (ax25->n2count == ax25->n2) {
- ax25_link_failed(&ax25->dest_addr, ax25->ax25_dev->dev);
- ax25_clear_queues(ax25);
ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE);
- ax25->state = AX25_STATE_0;
- if (ax25->sk != NULL) {
- SOCK_DEBUG(ax25->sk, "AX.25 link Failure\n");
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = ETIMEDOUT;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
+ ax25_disconnect(ax25, ETIMEDOUT);
+ return;
} else {
ax25->n2count++;
ax25_std_transmit_enquiry(ax25);
break;
}
- ax25->t1timer = ax25->t1 = ax25_calculate_t1(ax25);
-
- ax25_set_timer(ax25);
+ ax25_calculate_t1(ax25);
+ ax25_start_t1timer(ax25);
}
#endif
/*
- * AX.25 release 036
+ * AX.25 release 037
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
* AX.25 032 Joerg(DL1BKE) Added ax25_queue_length to count the number of
* enqueued buffers of a socket..
* AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating.
+ * AX.25 037 Jonathan(G4KLX) New timer architecture.
*/
#include <linux/config.h>
/*
* Exponential backoff for AX.25
*/
-unsigned short ax25_calculate_t1(ax25_cb *ax25)
+void ax25_calculate_t1(ax25_cb *ax25)
{
int n, t = 2;
break;
}
- return t * ax25->rtt;
+ ax25->t1 = t * ax25->rtt;
}
/*
if (ax25->backoff == 0)
return;
- if (ax25->t1timer > 0 && ax25->n2count == 0)
- ax25->rtt = (9 * ax25->rtt + ax25->t1 - ax25->t1timer) / 10;
+ if (ax25_t1timer_running(ax25) && ax25->n2count == 0)
+ ax25->rtt = (9 * ax25->rtt + ax25->t1 - ax25_display_timer(&ax25->t1timer)) / 10;
if (ax25->rtt < AX25_T1CLAMPLO)
ax25->rtt = AX25_T1CLAMPLO;
ax25->rtt = AX25_T1CLAMPHI;
}
+void ax25_disconnect(ax25_cb *ax25, int reason)
+{
+ ax25_clear_queues(ax25);
+
+ ax25_stop_t1timer(ax25);
+ ax25_stop_t2timer(ax25);
+ ax25_stop_t3timer(ax25);
+ ax25_stop_idletimer(ax25);
+
+ ax25->state = AX25_STATE_0;
+
+ ax25_link_failed(ax25, reason);
+
+ if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_CLOSE;
+ ax25->sk->err = reason;
+ ax25->sk->shutdown |= SEND_SHUTDOWN;
+ if (!ax25->sk->dead)
+ ax25->sk->state_change(ax25->sk);
+ ax25->sk->dead = 1;
+ }
+}
+
#endif
/*
- * AX.25 release 036
+ * AX.25 release 037
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
* AX.25 036 Jonathan(G4KLX) Split Standard and DAMA code into seperate files.
* Joerg(DL1BKE) Fixed DAMA Slave. We are *required* to start with
* standard AX.25 mode.
+ * AX.25 037 Jonathan(G4KLX) New timer architecture.
*/
#include <linux/config.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
-static void ax25_timer(unsigned long);
+static void ax25_heartbeat_expiry(unsigned long);
+static void ax25_t1timer_expiry(unsigned long);
+static void ax25_t2timer_expiry(unsigned long);
+static void ax25_t3timer_expiry(unsigned long);
+static void ax25_idletimer_expiry(unsigned long);
-/*
- * Linux set timer
- */
-void ax25_set_timer(ax25_cb *ax25)
+void ax25_start_heartbeat(ax25_cb *ax25)
{
- unsigned long flags;
-
- save_flags(flags); cli();
del_timer(&ax25->timer);
- restore_flags(flags);
ax25->timer.data = (unsigned long)ax25;
- ax25->timer.function = &ax25_timer;
- ax25->timer.expires = jiffies + (HZ / 10);
+ ax25->timer.function = &ax25_heartbeat_expiry;
+ ax25->timer.expires = jiffies + 5 * HZ;
add_timer(&ax25->timer);
}
-/*
- * AX.25 TIMER
- *
- * This routine is called every 100ms. Decrement timer by this
- * amount - if expired then process the event.
- */
-static void ax25_timer(unsigned long param)
+void ax25_start_t1timer(ax25_cb *ax25)
+{
+ del_timer(&ax25->t1timer);
+
+ ax25->t1timer.data = (unsigned long)ax25;
+ ax25->t1timer.function = &ax25_t1timer_expiry;
+ ax25->t1timer.expires = jiffies + ax25->t1;
+
+ add_timer(&ax25->t1timer);
+}
+
+void ax25_start_t2timer(ax25_cb *ax25)
+{
+ del_timer(&ax25->t2timer);
+
+ ax25->t2timer.data = (unsigned long)ax25;
+ ax25->t2timer.function = &ax25_t2timer_expiry;
+ ax25->t2timer.expires = jiffies + ax25->t2;
+
+ add_timer(&ax25->t2timer);
+}
+
+void ax25_start_t3timer(ax25_cb *ax25)
+{
+ del_timer(&ax25->t3timer);
+
+ if (ax25->t3 > 0) {
+ ax25->t3timer.data = (unsigned long)ax25;
+ ax25->t3timer.function = &ax25_t3timer_expiry;
+ ax25->t3timer.expires = jiffies + ax25->t3;
+
+ add_timer(&ax25->t3timer);
+ }
+}
+
+void ax25_start_idletimer(ax25_cb *ax25)
+{
+ del_timer(&ax25->idletimer);
+
+ if (ax25->idle > 0) {
+ ax25->idletimer.data = (unsigned long)ax25;
+ ax25->idletimer.function = &ax25_idletimer_expiry;
+ ax25->idletimer.expires = jiffies + ax25->idle;
+
+ add_timer(&ax25->idletimer);
+ }
+}
+
+void ax25_stop_heartbeat(ax25_cb *ax25)
+{
+ del_timer(&ax25->timer);
+}
+
+void ax25_stop_t1timer(ax25_cb *ax25)
+{
+ del_timer(&ax25->t1timer);
+}
+
+void ax25_stop_t2timer(ax25_cb *ax25)
+{
+ del_timer(&ax25->t2timer);
+}
+
+void ax25_stop_t3timer(ax25_cb *ax25)
+{
+ del_timer(&ax25->t3timer);
+}
+
+void ax25_stop_idletimer(ax25_cb *ax25)
+{
+ del_timer(&ax25->idletimer);
+}
+
+int ax25_t1timer_running(ax25_cb *ax25)
+{
+ return (ax25->t1timer.prev != NULL || ax25->t1timer.next != NULL);
+}
+
+unsigned long ax25_display_timer(struct timer_list *timer)
+{
+ if (timer->prev == NULL && timer->next == NULL)
+ return 0;
+
+ return timer->expires - jiffies;
+}
+
+static void ax25_heartbeat_expiry(unsigned long param)
+{
+ ax25_cb *ax25 = (ax25_cb *)param;
+
+ switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
+ case AX25_PROTO_STD_SIMPLEX:
+ case AX25_PROTO_STD_DUPLEX:
+ ax25_std_heartbeat_expiry(ax25);
+ break;
+
+#ifdef CONFIG_AX25_DAMA_SLAVE
+ case AX25_PROTO_DAMA_SLAVE:
+ if (ax25->ax25_dev->dama.slave)
+ ax25_ds_heartbeat_expiry(ax25);
+ else
+ ax25_std_heartbeat_expiry(ax25);
+ break;
+#endif
+ }
+}
+
+static void ax25_t1timer_expiry(unsigned long param)
+{
+ ax25_cb *ax25 = (ax25_cb *)param;
+
+ switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
+ case AX25_PROTO_STD_SIMPLEX:
+ case AX25_PROTO_STD_DUPLEX:
+ ax25_std_t1timer_expiry(ax25);
+ break;
+
+#ifdef CONFIG_AX25_DAMA_SLAVE
+ case AX25_PROTO_DAMA_SLAVE:
+ if (!ax25->ax25_dev->dama.slave)
+ ax25_std_t1timer_expiry(ax25);
+ break;
+#endif
+ }
+}
+
+static void ax25_t2timer_expiry(unsigned long param)
+{
+ ax25_cb *ax25 = (ax25_cb *)param;
+
+ switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
+ case AX25_PROTO_STD_SIMPLEX:
+ case AX25_PROTO_STD_DUPLEX:
+ ax25_std_t2timer_expiry(ax25);
+ break;
+
+#ifdef CONFIG_AX25_DAMA_SLAVE
+ case AX25_PROTO_DAMA_SLAVE:
+ if (!ax25->ax25_dev->dama.slave)
+ ax25_std_t2timer_expiry(ax25);
+ break;
+#endif
+ }
+}
+
+static void ax25_t3timer_expiry(unsigned long param)
+{
+ ax25_cb *ax25 = (ax25_cb *)param;
+
+ switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
+ case AX25_PROTO_STD_SIMPLEX:
+ case AX25_PROTO_STD_DUPLEX:
+ ax25_std_t3timer_expiry(ax25);
+ break;
+
+#ifdef CONFIG_AX25_DAMA_SLAVE
+ case AX25_PROTO_DAMA_SLAVE:
+ if (ax25->ax25_dev->dama.slave)
+ ax25_ds_t3timer_expiry(ax25);
+ else
+ ax25_std_t3timer_expiry(ax25);
+ break;
+#endif
+ }
+}
+
+static void ax25_idletimer_expiry(unsigned long param)
{
ax25_cb *ax25 = (ax25_cb *)param;
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
case AX25_PROTO_STD_SIMPLEX:
case AX25_PROTO_STD_DUPLEX:
- ax25_std_timer(ax25);
+ ax25_std_idletimer_expiry(ax25);
break;
#ifdef CONFIG_AX25_DAMA_SLAVE
case AX25_PROTO_DAMA_SLAVE:
if (ax25->ax25_dev->dama.slave)
- ax25_ds_timer(ax25);
+ ax25_ds_idletimer_expiry(ax25);
else
- ax25_std_timer(ax25);
+ ax25_std_idletimer_expiry(ax25);
break;
#endif
}
/*
- * AX.25 release 036
+ * AX.25 release 037
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
static int min_conmode[] = {0}, max_conmode[] = {2};
static int min_window[] = {1}, max_window[] = {7};
static int min_ewindow[] = {1}, max_ewindow[] = {63};
-static int min_t1[] = {1}, max_t1[] = {30 * AX25_SLOWHZ};
-static int min_t2[] = {1}, max_t2[] = {20 * AX25_SLOWHZ};
-static int min_t3[] = {0}, max_t3[] = {3600 * AX25_SLOWHZ};
-static int min_idle[] = {0}, max_idle[] = {65535 * AX25_SLOWHZ};
+static int min_t1[] = {1}, max_t1[] = {30 * HZ};
+static int min_t2[] = {1}, max_t2[] = {20 * HZ};
+static int min_t3[] = {0}, max_t3[] = {3600 * HZ};
+static int min_idle[] = {0}, max_idle[] = {65535 * HZ};
static int min_n2[] = {1}, max_n2[] = {31};
static int min_paclen[] = {1}, max_paclen[] = {512};
static int min_proto[] = {0}, max_proto[] = {3};
-static int min_ds_timeout[] = {0}, max_ds_timeout[] = {65535 * AX25_SLOWHZ};
+static int min_ds_timeout[] = {0}, max_ds_timeout[] = {65535 * HZ};
static struct ctl_table_header *ax25_table_header;
#include <linux/mm.h>
#include <linux/sysctl.h>
+#include <linux/config.h>
+
+#ifdef CONFIG_SYSCTL
extern __u32 sysctl_wmem_max;
extern __u32 sysctl_rmem_max;
&proc_dointvec_jiffies},
{ 0 }
};
+#endif
*
* Implementation of the Transmission Control Protocol(TCP).
*
- * Version: $Id: tcp_ipv4.c,v 1.49 1997/06/09 13:27:35 freitag Exp $
+ * Version: $Id: tcp_ipv4.c,v 1.51 1997/07/04 23:35:02 freitag Exp $
*
* IPv4 specific functions
*
}
/* FIXME: What about the IP layer options size here? */
+ /* FIXME: add a timeout here, to cope with broken devices that
+ drop all DF=1 packets. Do some more sanity checking
+ here to prevent DOS attacks?
+ This code should kick the tcp_output routine to
+ retransmit a packet immediately because we know that
+ the last packet has been dropped. -AK */
if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
if (sk->ip_pmtudisc != IP_PMTUDISC_DONT) {
int new_mtu = sk->dst_cache->pmtu - sizeof(struct iphdr) - tp->tcp_header_len;
/*
- * LAPB release 001
- *
- * This is ALPHA test software. This code may break your machine, randomly fail to work with new
- * releases, misbehave and/or generally screw up. It might even work.
+ * LAPB release 002
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
*
* History
* LAPB 001 Jonathan Naylor Started Coding
+ * LAPB 002 Jonathan Naylor New timer architecture.
*/
#include <linux/config.h>
lapb_cb *s;
unsigned long flags;
- save_flags(flags);
- cli();
+ save_flags(flags); cli();
if ((s = lapb_list) == lapb) {
lapb_list = s->next;
{
unsigned long flags;
- save_flags(flags);
- cli();
+ save_flags(flags); cli();
lapb->next = lapb_list;
lapb_list = lapb;
memset(lapb, 0x00, sizeof(*lapb));
- skb_queue_head_init(&lapb->input_queue);
skb_queue_head_init(&lapb->write_queue);
skb_queue_head_init(&lapb->ack_queue);
- init_timer(&lapb->timer);
+ init_timer(&lapb->t1timer);
+ init_timer(&lapb->t2timer);
lapb->t1 = LAPB_DEFAULT_T1;
lapb->t2 = LAPB_DEFAULT_T2;
lapb_insert_cb(lapb);
- lapb->t1timer = lapb->t1;
-
- lapb_set_timer(lapb);
+ lapb_start_t1timer(lapb);
return LAPB_OK;
}
if ((lapb = lapb_tokentostruct(token)) == NULL)
return LAPB_BADTOKEN;
- del_timer(&lapb->timer);
+ lapb_stop_t1timer(lapb);
+ lapb_stop_t2timer(lapb);
lapb_clear_queues(lapb);
if ((lapb = lapb_tokentostruct(token)) == NULL)
return LAPB_BADTOKEN;
- parms->t1 = lapb->t1;
- parms->t1timer = lapb->t1timer;
- parms->t2 = lapb->t2;
- parms->t2timer = lapb->t2timer;
+ parms->t1 = lapb->t1 / HZ;
+ parms->t2 = lapb->t2 / HZ;
parms->n2 = lapb->n2;
parms->n2count = lapb->n2count;
parms->state = lapb->state;
parms->window = lapb->window;
parms->mode = lapb->mode;
+ if (lapb->t1timer.prev == NULL && lapb->t1timer.next == NULL)
+ parms->t1timer = 0;
+ else
+ parms->t1timer = (lapb->t1timer.expires - jiffies) / HZ;
+
+ if (lapb->t2timer.prev == NULL && lapb->t2timer.next == NULL)
+ parms->t2timer = 0;
+ else
+ parms->t2timer = (lapb->t2timer.expires - jiffies) / HZ;
+
return LAPB_OK;
}
lapb->window = parms->window;
}
- lapb->t1 = parms->t1;
- lapb->t2 = parms->t2;
+ lapb->t1 = parms->t1 * HZ;
+ lapb->t2 = parms->t2 * HZ;
lapb->n2 = parms->n2;
return LAPB_OK;
printk(KERN_DEBUG "lapb: (%p) S1 -> S0\n", lapb->token);
#endif
lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND);
- lapb->state = LAPB_STATE_0;
- lapb->t1timer = lapb->t1;
+ lapb->state = LAPB_STATE_0;
+ lapb_start_t1timer(lapb);
return LAPB_NOTCONNECTED;
case LAPB_STATE_2:
lapb_clear_queues(lapb);
lapb->n2count = 0;
lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND);
- lapb->t1timer = lapb->t1;
- lapb->t2timer = 0;
- lapb->state = LAPB_STATE_2;
+ lapb_start_t1timer(lapb);
+ lapb_stop_t2timer(lapb);
+ lapb->state = LAPB_STATE_2;
#if LAPB_DEBUG > 1
printk(KERN_DEBUG "lapb: (%p) S3 DISC(1)\n", lapb->token);
if ((lapb = lapb_tokentostruct(token)) == NULL)
return LAPB_BADTOKEN;
- skb_queue_tail(&lapb->input_queue, skb);
+ lapb_data_input(lapb, skb);
return LAPB_OK;
}
/*
- * LAPB release 001
- *
- * This is ALPHA test software. This code may break your machine, randomly fail to work with new
- * releases, misbehave and/or generally screw up. It might even work.
+ * LAPB release 002
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
*
* History
* LAPB 001 Jonathan Naulor Started Coding
+ * LAPB 002 Jonathan Naylor New timer architecture.
*/
#include <linux/config.h>
printk(KERN_DEBUG "lapb: (%p) S0 -> S3\n", lapb->token);
#endif
lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
+ lapb_stop_t1timer(lapb);
+ lapb_stop_t2timer(lapb);
lapb->state = LAPB_STATE_3;
lapb->condition = 0x00;
- lapb->t1timer = 0;
- lapb->t2timer = 0;
lapb->n2count = 0;
lapb->vs = 0;
lapb->vr = 0;
printk(KERN_DEBUG "lapb: (%p) S0 -> S3\n", lapb->token);
#endif
lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
+ lapb_stop_t1timer(lapb);
+ lapb_stop_t2timer(lapb);
lapb->state = LAPB_STATE_3;
lapb->condition = 0x00;
- lapb->t1timer = 0;
- lapb->t2timer = 0;
lapb->n2count = 0;
lapb->vs = 0;
lapb->vr = 0;
#if LAPB_DEBUG > 0
printk(KERN_DEBUG "lapb: (%p) S1 -> S3\n", lapb->token);
#endif
+ lapb_stop_t1timer(lapb);
+ lapb_stop_t2timer(lapb);
lapb->state = LAPB_STATE_3;
lapb->condition = 0x00;
- lapb->t1timer = 0;
- lapb->t2timer = 0;
lapb->n2count = 0;
lapb->vs = 0;
lapb->vr = 0;
printk(KERN_DEBUG "lapb: (%p) S1 -> S0\n", lapb->token);
#endif
lapb_clear_queues(lapb);
- lapb->state = LAPB_STATE_0;
- lapb->t1timer = lapb->t1;
- lapb->t2timer = 0;
+ lapb->state = LAPB_STATE_0;
+ lapb_start_t1timer(lapb);
+ lapb_stop_t2timer(lapb);
lapb_disconnect_indication(lapb, LAPB_REFUSED);
}
break;
#if LAPB_DEBUG > 0
printk(KERN_DEBUG "lapb: (%p) S2 -> S0\n", lapb->token);
#endif
- lapb->state = LAPB_STATE_0;
- lapb->t1timer = lapb->t1;
- lapb->t2timer = 0;
+ lapb->state = LAPB_STATE_0;
+ lapb_start_t1timer(lapb);
+ lapb_stop_t2timer(lapb);
lapb_disconnect_confirmation(lapb, LAPB_OK);
}
break;
#if LAPB_DEBUG > 0
printk(KERN_DEBUG "lapb: (%p) S2 -> S0\n", lapb->token);
#endif
- lapb->state = LAPB_STATE_0;
- lapb->t1timer = lapb->t1;
- lapb->t2timer = 0;
+ lapb->state = LAPB_STATE_0;
+ lapb_start_t1timer(lapb);
+ lapb_stop_t2timer(lapb);
lapb_disconnect_confirmation(lapb, LAPB_NOTCONNECTED);
}
break;
printk(KERN_DEBUG "lapb: (%p) S3 TX UA(%d)\n", lapb->token, frame->pf);
#endif
lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
+ lapb_stop_t1timer(lapb);
+ lapb_stop_t2timer(lapb);
lapb->condition = 0x00;
- lapb->t1timer = 0;
- lapb->t2timer = 0;
lapb->n2count = 0;
lapb->vs = 0;
lapb->vr = 0;
printk(KERN_DEBUG "lapb: (%p) S3 TX UA(%d)\n", lapb->token, frame->pf);
#endif
lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
+ lapb_stop_t1timer(lapb);
+ lapb_stop_t2timer(lapb);
lapb->condition = 0x00;
- lapb->t1timer = 0;
- lapb->t2timer = 0;
lapb->n2count = 0;
lapb->vs = 0;
lapb->vr = 0;
#endif
lapb_clear_queues(lapb);
lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
- lapb->state = LAPB_STATE_0;
- lapb->t1timer = lapb->t1;
- lapb->t2timer = 0;
+ lapb_start_t1timer(lapb);
+ lapb_stop_t2timer(lapb);
+ lapb->state = LAPB_STATE_0;
lapb_disconnect_indication(lapb, LAPB_OK);
break;
printk(KERN_DEBUG "lapb: (%p) S3 -> S0\n", lapb->token);
#endif
lapb_clear_queues(lapb);
- lapb->state = LAPB_STATE_0;
- lapb->t1timer = lapb->t1;
- lapb->t2timer = 0;
+ lapb->state = LAPB_STATE_0;
+ lapb_start_t1timer(lapb);
+ lapb_stop_t2timer(lapb);
lapb_disconnect_indication(lapb, LAPB_NOTCONNECTED);
break;
#if LAPB_DEBUG > 0
printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->token);
#endif
- lapb->state = LAPB_STATE_4;
- lapb->t1timer = lapb->t1;
- lapb->t2timer = 0;
- lapb->n2count = 0;
+ lapb_start_t1timer(lapb);
+ lapb_stop_t2timer(lapb);
+ lapb->state = LAPB_STATE_4;
+ lapb->n2count = 0;
}
break;
#if LAPB_DEBUG > 0
printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->token);
#endif
- lapb->state = LAPB_STATE_4;
- lapb->t1timer = lapb->t1;
- lapb->t2timer = 0;
- lapb->n2count = 0;
+ lapb_start_t1timer(lapb);
+ lapb_stop_t2timer(lapb);
+ lapb->state = LAPB_STATE_4;
+ lapb->n2count = 0;
}
break;
lapb_check_need_response(lapb, frame->cr, frame->pf);
if (lapb_validate_nr(lapb, frame->nr)) {
lapb_frames_acked(lapb, frame->nr);
- lapb->t1timer = 0;
+ lapb_stop_t1timer(lapb);
lapb->n2count = 0;
lapb_requeue_frames(lapb);
} else {
#if LAPB_DEBUG > 0
printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->token);
#endif
- lapb->state = LAPB_STATE_4;
- lapb->t1timer = lapb->t1;
- lapb->t2timer = 0;
- lapb->n2count = 0;
+ lapb_start_t1timer(lapb);
+ lapb_stop_t2timer(lapb);
+ lapb->state = LAPB_STATE_4;
+ lapb->n2count = 0;
}
break;
#if LAPB_DEBUG > 0
printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->token);
#endif
- lapb->state = LAPB_STATE_4;
- lapb->t1timer = lapb->t1;
- lapb->t2timer = 0;
- lapb->n2count = 0;
+ lapb_start_t1timer(lapb);
+ lapb_stop_t2timer(lapb);
+ lapb->state = LAPB_STATE_4;
+ lapb->n2count = 0;
break;
}
if (lapb->condition & LAPB_PEER_RX_BUSY_CONDITION) {
lapb_enquiry_response(lapb);
} else {
if (!(lapb->condition & LAPB_ACK_PENDING_CONDITION)) {
- lapb->t2timer = lapb->t2;
lapb->condition |= LAPB_ACK_PENDING_CONDITION;
+ lapb_start_t2timer(lapb);
}
}
} else {
#if LAPB_DEBUG > 0
printk(KERN_DEBUG "lapb: (%p) S3 -> S4\n", lapb->token);
#endif
- lapb->state = LAPB_STATE_4;
- lapb->t1timer = lapb->t1;
- lapb->t2timer = 0;
- lapb->n2count = 0;
+ lapb_start_t1timer(lapb);
+ lapb_stop_t2timer(lapb);
+ lapb->state = LAPB_STATE_4;
+ lapb->n2count = 0;
break;
default:
printk(KERN_DEBUG "lapb: (%p) S4 -> S3\n", lapb->token);
#endif
lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
+ lapb_stop_t1timer(lapb);
+ lapb_stop_t2timer(lapb);
lapb->state = LAPB_STATE_3;
lapb->condition = 0x00;
- lapb->t1timer = 0;
- lapb->t2timer = 0;
lapb->n2count = 0;
lapb->vs = 0;
lapb->vr = 0;
printk(KERN_DEBUG "lapb: (%p) S4 -> S3\n", lapb->token);
#endif
lapb_send_control(lapb, LAPB_UA, frame->pf, LAPB_RESPONSE);
+ lapb_stop_t1timer(lapb);
+ lapb_stop_t2timer(lapb);
lapb->state = LAPB_STATE_3;
lapb->condition = 0x00;
- lapb->t1timer = 0;
- lapb->t2timer = 0;
lapb->n2count = 0;
lapb->vs = 0;
lapb->vr = 0;
lapb_state4_machine(lapb, skb, &frame);
break;
}
+
+ lapb_kick(lapb);
}
#endif
/*
- * LAPB release 001
- *
- * This is ALPHA test software. This code may break your machine, randomly fail to work with new
- * releases, misbehave and/or generally screw up. It might even work.
+ * LAPB release 002
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
*
* History
* LAPB 001 Jonathan Naylor Started Coding
+ * LAPB 002 Jonathan Naylor New timer architecture.
*/
#include <linux/config.h>
struct sk_buff *skb, *skbn;
unsigned short modulus, start, end;
- del_timer(&lapb->timer);
-
modulus = (lapb->mode & LAPB_EXTENDED) ? LAPB_EMODULUS : LAPB_SMODULUS;
start = (skb_peek(&lapb->ack_queue) == NULL) ? lapb->va : lapb->vs;
lapb->condition &= ~LAPB_ACK_PENDING_CONDITION;
- if (lapb->t1timer == 0)
- lapb->t1timer = lapb->t1;
+ if (!lapb_t1timer_running(lapb))
+ lapb_start_t1timer(lapb);
}
-
- lapb_set_timer(lapb);
}
void lapb_transmit_buffer(lapb_cb *lapb, struct sk_buff *skb, int type)
lapb_send_control(lapb, LAPB_SABM, LAPB_POLLON, LAPB_COMMAND);
}
- lapb->t2timer = 0;
- lapb->t1timer = lapb->t1;
+ lapb_start_t1timer(lapb);
+ lapb_stop_t2timer(lapb);
}
void lapb_enquiry_response(lapb_cb *lapb)
{
if (lapb->vs == nr) {
lapb_frames_acked(lapb, nr);
- lapb->t1timer = 0;
+ lapb_stop_t1timer(lapb);
lapb->n2count = 0;
} else {
if (lapb->va != nr) {
lapb_frames_acked(lapb, nr);
- lapb->t1timer = lapb->t1;
+ lapb_start_t1timer(lapb);
}
}
}
/*
- * LAPB release 001
- *
- * This is ALPHA test software. This code may break your machine, randomly fail to work with new
- * releases, misbehave and/or generally screw up. It might even work.
+ * LAPB release 002
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
{
struct sk_buff *skb;
- while ((skb = skb_dequeue(&lapb->input_queue)) != NULL)
- kfree_skb(skb, FREE_READ);
-
while ((skb = skb_dequeue(&lapb->write_queue)) != NULL)
kfree_skb(skb, FREE_WRITE);
/*
- * LAPB release 001
- *
- * This is ALPHA test software. This code may break your machine, randomly fail to work with new
- * releases, misbehave and/or generally screw up. It might even work.
+ * LAPB release 002
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
*
* History
* LAPB 001 Jonathan Naylor Started Coding
+ * LAPB 002 Jonathan Naylor New timer architecture.
*/
#include <linux/config.h>
#include <linux/interrupt.h>
#include <net/lapb.h>
-static void lapb_timer(unsigned long);
+static void lapb_t1timer_expiry(unsigned long);
+static void lapb_t2timer_expiry(unsigned long);
-/*
- * Linux set timer
- */
-void lapb_set_timer(lapb_cb *lapb)
+void lapb_start_t1timer(lapb_cb *lapb)
{
- unsigned long flags;
+ del_timer(&lapb->t1timer);
+
+ lapb->t1timer.data = (unsigned long)lapb;
+ lapb->t1timer.function = &lapb_t1timer_expiry;
+ lapb->t1timer.expires = jiffies + lapb->t1;
- save_flags(flags); cli();
- del_timer(&lapb->timer);
- restore_flags(flags);
+ add_timer(&lapb->t1timer);
+}
+
+void lapb_start_t2timer(lapb_cb *lapb)
+{
+ del_timer(&lapb->t2timer);
- lapb->timer.data = (unsigned long)lapb;
- lapb->timer.function = &lapb_timer;
- lapb->timer.expires = jiffies + (HZ / 10);
+ lapb->t2timer.data = (unsigned long)lapb;
+ lapb->t2timer.function = &lapb_t2timer_expiry;
+ lapb->t2timer.expires = jiffies + lapb->t2;
- add_timer(&lapb->timer);
+ add_timer(&lapb->t2timer);
}
-/*
- * LAPB TIMER
- *
- * This routine is called every 100ms. Decrement timer by this
- * amount - if expired then process the event.
- */
-static void lapb_timer(unsigned long param)
+void lapb_stop_t1timer(lapb_cb *lapb)
+{
+ del_timer(&lapb->t1timer);
+}
+
+void lapb_stop_t2timer(lapb_cb *lapb)
+{
+ del_timer(&lapb->t2timer);
+}
+
+int lapb_t1timer_running(lapb_cb *lapb)
+{
+ return (lapb->t1timer.prev != NULL || lapb->t1timer.next != NULL);
+}
+
+static void lapb_t2timer_expiry(unsigned long param)
{
lapb_cb *lapb = (lapb_cb *)param;
- struct sk_buff *skb;
-
- /*
- * Process all packet received since the last clock tick.
- */
- while ((skb = skb_dequeue(&lapb->input_queue)) != NULL)
- lapb_data_input(lapb, skb);
-
- /*
- * If in a data transfer state, transmit any data.
- */
- if (lapb->state == LAPB_STATE_3)
- lapb_kick(lapb);
-
- /*
- * T2 expiry code.
- */
- if (lapb->t2timer > 0 && --lapb->t2timer == 0) {
- if (lapb->state == LAPB_STATE_3) {
- if (lapb->condition & LAPB_ACK_PENDING_CONDITION) {
- lapb->condition &= ~LAPB_ACK_PENDING_CONDITION;
- lapb_timeout_response(lapb);
- }
- }
- }
- /*
- * If T1 isn't running, or hasn't timed out yet, keep going.
- */
- if (lapb->t1timer == 0 || --lapb->t1timer > 0) {
- lapb_set_timer(lapb);
- return;
+ if (lapb->condition & LAPB_ACK_PENDING_CONDITION) {
+ lapb->condition &= ~LAPB_ACK_PENDING_CONDITION;
+ lapb_timeout_response(lapb);
}
+}
+
+static void lapb_t1timer_expiry(unsigned long param)
+{
+ lapb_cb *lapb = (lapb_cb *)param;
- /*
- * T1 has expired.
- */
switch (lapb->state) {
/*
case LAPB_STATE_1:
if (lapb->n2count == lapb->n2) {
lapb_clear_queues(lapb);
- lapb->state = LAPB_STATE_0;
- lapb->t2timer = 0;
+ lapb->state = LAPB_STATE_0;
lapb_disconnect_indication(lapb, LAPB_TIMEDOUT);
#if LAPB_DEBUG > 0
printk(KERN_DEBUG "lapb: (%p) S1 -> S0\n", lapb->token);
#endif
+ return;
} else {
lapb->n2count++;
if (lapb->mode & LAPB_EXTENDED) {
case LAPB_STATE_2:
if (lapb->n2count == lapb->n2) {
lapb_clear_queues(lapb);
- lapb->state = LAPB_STATE_0;
- lapb->t2timer = 0;
+ lapb->state = LAPB_STATE_0;
lapb_disconnect_confirmation(lapb, LAPB_TIMEDOUT);
#if LAPB_DEBUG > 0
printk(KERN_DEBUG "lapb: (%p) S2 -> S0\n", lapb->token);
#endif
+ return;
} else {
lapb->n2count++;
#if LAPB_DEBUG > 1
case LAPB_STATE_3:
if (lapb->n2count == lapb->n2) {
lapb_clear_queues(lapb);
- lapb->state = LAPB_STATE_0;
- lapb->t2timer = 0;
+ lapb->state = LAPB_STATE_0;
+ lapb_stop_t2timer(lapb);
lapb_disconnect_indication(lapb, LAPB_TIMEDOUT);
#if LAPB_DEBUG > 0
printk(KERN_DEBUG "lapb: (%p) S3 -> S0\n", lapb->token);
#endif
+ return;
} else {
lapb->n2count++;
lapb_requeue_frames(lapb);
case LAPB_STATE_4:
if (lapb->n2count == lapb->n2) {
lapb_clear_queues(lapb);
- lapb->state = LAPB_STATE_0;
- lapb->t2timer = 0;
+ lapb->state = LAPB_STATE_0;
lapb_disconnect_indication(lapb, LAPB_TIMEDOUT);
#if LAPB_DEBUG > 0
printk(KERN_DEBUG "lapb: (%p) S4 -> S0\n", lapb->token);
#endif
+ return;
} else {
lapb->n2count++;
lapb_transmit_frmr(lapb);
break;
}
- lapb->t1timer = lapb->t1;
-
- lapb_set_timer(lapb);
+ lapb_start_t1timer(lapb);
}
#endif
/*
- * NET/ROM release 006
+ * NET/ROM release 007
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
* Alan(GW4PTS) Started POSIXisms
* NET/ROM 006 Alan(GW4PTS) Brought in line with the ANK changes
* Jonathan(G4KLX) Removed hdrincl.
+ * NET/ROM 007 Jonathan(G4KLX) New timer architecture.
+ * Impmented Idle timer.
*/
#include <linux/config.h>
struct sock *s;
unsigned long flags;
- save_flags(flags);
- cli();
+ save_flags(flags); cli();
if ((s = nr_list) == sk) {
nr_list = s->next;
struct sock *s;
for (s = nr_list; s != NULL; s = s->next) {
- if (s->protinfo.nr->device == dev) {
- s->protinfo.nr->state = NR_STATE_0;
- s->protinfo.nr->device = NULL;
- s->state = TCP_CLOSE;
- s->err = ENETUNREACH;
- s->shutdown |= SEND_SHUTDOWN;
- s->state_change(s);
- s->dead = 1;
- }
+ if (s->protinfo.nr->device == dev)
+ nr_disconnect(s, ENETUNREACH);
}
}
{
unsigned long flags;
- save_flags(flags);
- cli();
+ save_flags(flags); cli();
sk->next = nr_list;
nr_list = sk;
struct sk_buff *skb;
unsigned long flags;
- save_flags(flags);
- cli();
+ save_flags(flags); cli();
- del_timer(&sk->timer);
+ nr_stop_heartbeat(sk);
+ nr_stop_t1timer(sk);
+ nr_stop_t2timer(sk);
+ nr_stop_t4timer(sk);
+ nr_stop_idletimer(sk);
nr_remove_socket(sk);
nr_clear_queues(sk); /* Flush the queues */
while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
if (skb->sk != sk) { /* A pending connection */
skb->sk->dead = 1; /* Queue the unaccepted socket for death */
- nr_set_timer(skb->sk);
+ nr_start_heartbeat(skb->sk);
skb->sk->protinfo.nr->state = NR_STATE_0;
}
case NETROM_T1:
if (opt < 1)
return -EINVAL;
- sk->protinfo.nr->t1 = opt * NR_SLOWHZ;
+ sk->protinfo.nr->t1 = opt * HZ;
return 0;
case NETROM_T2:
if (opt < 1)
return -EINVAL;
- sk->protinfo.nr->t2 = opt * NR_SLOWHZ;
+ sk->protinfo.nr->t2 = opt * HZ;
return 0;
case NETROM_N2:
case NETROM_T4:
if (opt < 1)
return -EINVAL;
- sk->protinfo.nr->t4 = opt * NR_SLOWHZ;
+ sk->protinfo.nr->t4 = opt * HZ;
return 0;
case NETROM_IDLE:
- if (opt < 1)
+ if (opt < 0)
return -EINVAL;
- sk->protinfo.nr->idle = opt * 60 * NR_SLOWHZ;
+ sk->protinfo.nr->idle = opt * 60 * HZ;
return 0;
default:
switch (optname) {
case NETROM_T1:
- val = (sk->protinfo.nr->t1 * 2) / NR_SLOWHZ;
+ val = sk->protinfo.nr->t1 / HZ;
break;
case NETROM_T2:
- val = sk->protinfo.nr->t2 / NR_SLOWHZ;
+ val = sk->protinfo.nr->t2 / HZ;
break;
case NETROM_N2:
break;
case NETROM_T4:
- val = sk->protinfo.nr->t4 / NR_SLOWHZ;
+ val = sk->protinfo.nr->t4 / HZ;
break;
case NETROM_IDLE:
- val = sk->protinfo.nr->idle / (NR_SLOWHZ * 60);
+ val = sk->protinfo.nr->idle / (60 * HZ);
break;
default:
skb_queue_head_init(&nr->reseq_queue);
skb_queue_head_init(&nr->frag_queue);
+ init_timer(&nr->t1timer);
+ init_timer(&nr->t2timer);
+ init_timer(&nr->t4timer);
+ init_timer(&nr->idletimer);
+
nr->t1 = sysctl_netrom_transport_timeout;
nr->t2 = sysctl_netrom_transport_acknowledge_delay;
nr->n2 = sysctl_netrom_transport_maximum_tries;
skb_queue_head_init(&nr->reseq_queue);
skb_queue_head_init(&nr->frag_queue);
+ init_timer(&nr->t1timer);
+ init_timer(&nr->t2timer);
+ init_timer(&nr->t4timer);
+ init_timer(&nr->idletimer);
+
nr->t1 = osk->protinfo.nr->t1;
nr->t2 = osk->protinfo.nr->t2;
nr->n2 = osk->protinfo.nr->n2;
switch (sk->protinfo.nr->state) {
case NR_STATE_0:
- sk->state = TCP_CLOSE;
- sk->shutdown |= SEND_SHUTDOWN;
- sk->state_change(sk);
- sk->dead = 1;
+ case NR_STATE_2:
+ nr_disconnect(sk, 0);
nr_destroy_socket(sk);
break;
case NR_STATE_1:
- sk->protinfo.nr->state = NR_STATE_0;
- sk->state = TCP_CLOSE;
- sk->shutdown |= SEND_SHUTDOWN;
- sk->state_change(sk);
- sk->dead = 1;
- nr_destroy_socket(sk);
- break;
-
- case NR_STATE_2:
- nr_write_internal(sk, NR_DISCACK);
- sk->protinfo.nr->state = NR_STATE_0;
- sk->state = TCP_CLOSE;
- sk->shutdown |= SEND_SHUTDOWN;
- sk->state_change(sk);
- sk->dead = 1;
- nr_destroy_socket(sk);
- break;
-
case NR_STATE_3:
nr_clear_queues(sk);
sk->protinfo.nr->n2count = 0;
nr_write_internal(sk, NR_DISCREQ);
- sk->protinfo.nr->t1timer = sk->protinfo.nr->t1;
- sk->protinfo.nr->t2timer = 0;
- sk->protinfo.nr->t4timer = 0;
+ nr_start_t1timer(sk);
+ nr_stop_t2timer(sk);
+ nr_stop_t4timer(sk);
+ nr_stop_idletimer(sk);
sk->protinfo.nr->state = NR_STATE_2;
sk->state = TCP_CLOSE;
sk->shutdown |= SEND_SHUTDOWN;
/* Move to connecting socket, start sending Connect Requests */
sock->state = SS_CONNECTING;
sk->state = TCP_SYN_SENT;
+
nr_establish_data_link(sk);
+
sk->protinfo.nr->state = NR_STATE_1;
- nr_set_timer(sk);
+
+ nr_start_heartbeat(sk);
/* Now the loop */
if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
circuit_index = skb->data[15];
circuit_id = skb->data[16];
- frametype = skb->data[19];
+ frametype = skb->data[19] & 0x0F;
#ifdef CONFIG_INET
/*
* Check for an incoming IP over NET/ROM frame.
*/
- if ((frametype & 0x0F) == NR_PROTOEXT && circuit_index == NR_PROTO_IP && circuit_id == NR_PROTO_IP) {
+ if (frametype == NR_PROTOEXT && circuit_index == NR_PROTO_IP && circuit_id == NR_PROTO_IP) {
skb_pull(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
skb->h.raw = skb->data;
* Find an existing socket connection, based on circuit ID, if it's
* a Connect Request base it on their circuit ID.
*/
- if (((frametype & 0x0F) != NR_CONNREQ && (sk = nr_find_socket(circuit_index, circuit_id)) != NULL) ||
- ((frametype & 0x0F) == NR_CONNREQ && (sk = nr_find_peer(circuit_index, circuit_id)) != NULL)) {
+ if ((frametype != NR_CONNREQ && (sk = nr_find_socket(circuit_index, circuit_id)) != NULL) ||
+ (frametype == NR_CONNREQ && (sk = nr_find_peer(circuit_index, circuit_id)) != NULL)) {
skb->h.raw = skb->data;
- if ((frametype & 0x0F) == NR_CONNACK && skb->len == 22)
+ if (frametype == NR_CONNACK && skb->len == 22)
sk->protinfo.nr->bpqext = 1;
else
sk->protinfo.nr->bpqext = 0;
return nr_process_rx_frame(sk, skb);
}
- if ((frametype & 0x0F) != NR_CONNREQ)
- return 0;
+ switch (frametype) {
+ case NR_CONNREQ:
+ break;
+ case NR_DISCREQ:
+ case NR_DISCACK:
+ return 0;
+ default:
+ nr_transmit_dm(skb);
+ return 0;
+ }
sk = nr_find_listener(dest);
/* L4 timeout negotiation */
if (skb->len == 37) {
timeout = skb->data[36] * 256 + skb->data[35];
- if (timeout * NR_SLOWHZ < make->protinfo.nr->t1)
- make->protinfo.nr->t1 = timeout * NR_SLOWHZ;
+ if (timeout * HZ < make->protinfo.nr->t1)
+ make->protinfo.nr->t1 = timeout * HZ;
make->protinfo.nr->bpqext = 1;
} else {
make->protinfo.nr->bpqext = 0;
skb_queue_head(&sk->receive_queue, skb);
- nr_set_timer(make);
+ nr_start_heartbeat(make);
+ nr_start_idletimer(make);
if (!sk->dead)
sk->data_ready(sk, skb->len);
sax.sax25_family = AF_NETROM;
sax.sax25_call = sk->protinfo.nr->dest_addr;
}
+
SOCK_DEBUG(sk, "NET/ROM: sendto: Addresses built.\n");
/* Build a packet */
static int nr_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct sock *sk = sock->sk;
- int err;
- long amount = 0;
switch (cmd) {
- case TIOCOUTQ:
- if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int))) != 0)
- return err;
+ case TIOCOUTQ: {
+ long amount;
amount = sk->sndbuf - atomic_read(&sk->wmem_alloc);
if (amount < 0)
amount = 0;
- put_user(amount, (int *)arg);
+ if (put_user(amount, (int *)arg))
+ return -EFAULT;
return 0;
+ }
case TIOCINQ: {
struct sk_buff *skb;
+ long amount = 0L;
/* These two are safe on a single CPU system as only user tasks fiddle here */
if ((skb = skb_peek(&sk->receive_queue)) != NULL)
- amount = skb->len - 20;
- if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int))) != 0)
- return err;
- put_user(amount, (int *)arg);
+ amount = skb->len;
+ if (put_user(amount, (int *)arg))
+ return -EFAULT;
return 0;
}
case SIOCGSTAMP:
if (sk != NULL) {
- if (sk->stamp.tv_sec==0)
+ if (sk->stamp.tv_sec == 0)
return -ENOENT;
- if ((err = verify_area(VERIFY_WRITE,(void *)arg,sizeof(struct timeval))) != 0)
- return err;
- copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval));
+ if (copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)))
+ return -EFAULT;
return 0;
}
return -EINVAL;
cli();
- len += sprintf(buffer, "user_addr dest_node src_node dev my your st vs vr va t1 t2 n2 wnd Snd-Q Rcv-Q\n");
+ len += sprintf(buffer, "user_addr dest_node src_node dev my your st vs vr va t1 t2 t4 idle n2 wnd Snd-Q Rcv-Q\n");
for (s = nr_list; s != NULL; s = s->next) {
if ((dev = s->protinfo.nr->device) == NULL)
ax2asc(&s->protinfo.nr->user_addr));
len += sprintf(buffer + len, "%-9s ",
ax2asc(&s->protinfo.nr->dest_addr));
- len += sprintf(buffer + len, "%-9s %-3s %02X/%02X %02X/%02X %2d %3d %3d %3d %3d/%03d %2d/%02d %2d/%02d %3d %5d %5d\n",
+ len += sprintf(buffer + len, "%-9s %-3s %02X/%02X %02X/%02X %2d %3d %3d %3d %3lu/%03lu %2lu/%02lu %3lu/%03lu %3lu/%03lu %2d/%02d %3d %5d %5d\n",
ax2asc(&s->protinfo.nr->source_addr),
- devname, s->protinfo.nr->my_index, s->protinfo.nr->my_id,
- s->protinfo.nr->your_index, s->protinfo.nr->your_id,
+ devname,
+ s->protinfo.nr->my_index,
+ s->protinfo.nr->my_id,
+ s->protinfo.nr->your_index,
+ s->protinfo.nr->your_id,
s->protinfo.nr->state,
- s->protinfo.nr->vs, s->protinfo.nr->vr, s->protinfo.nr->va,
- s->protinfo.nr->t1timer / NR_SLOWHZ,
- s->protinfo.nr->t1 / NR_SLOWHZ,
- s->protinfo.nr->t2timer / NR_SLOWHZ,
- s->protinfo.nr->t2 / NR_SLOWHZ,
+ s->protinfo.nr->vs,
+ s->protinfo.nr->vr,
+ s->protinfo.nr->va,
+ ax25_display_timer(&s->protinfo.nr->t1timer) / HZ,
+ s->protinfo.nr->t1 / HZ,
+ ax25_display_timer(&s->protinfo.nr->t2timer) / HZ,
+ s->protinfo.nr->t2 / HZ,
+ ax25_display_timer(&s->protinfo.nr->t4timer) / HZ,
+ s->protinfo.nr->t4 / HZ,
+ ax25_display_timer(&s->protinfo.nr->idletimer) / (60 * HZ),
+ s->protinfo.nr->idle / (60 * HZ),
s->protinfo.nr->n2count,
s->protinfo.nr->n2,
s->protinfo.nr->window,
- atomic_read(&s->wmem_alloc), atomic_read(&s->rmem_alloc));
+ atomic_read(&s->wmem_alloc),
+ atomic_read(&s->rmem_alloc));
pos = begin + len;
sock_register(&nr_family_ops);
register_netdevice_notifier(&nr_dev_notifier);
- printk(KERN_INFO "G4KLX NET/ROM for Linux. Version 0.6 for AX25.035 Linux 2.1\n");
+ printk(KERN_INFO "G4KLX NET/ROM for Linux. Version 0.7 for AX25.037 Linux 2.1\n");
ax25_protocol_register(AX25_P_NETROM, nr_route_frame);
ax25_linkfail_register(nr_link_failed);
/*
- * NET/ROM release 006
+ * NET/ROM release 007
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
/*
- * NET/ROM release 006
+ * NET/ROM release 007
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
* Darryl(G7LED) Added missing INFO with NAK case, optimized
* INFOACK handling, removed reconnect on error.
* NET/ROM 006 Jonathan(G4KLX) Hdrincl removal changes.
+ * NET/ROM 007 Jonathan(G4KLX) New timer architecture.
*/
#include <linux/config.h>
skb_pull(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
+ nr_start_idletimer(sk);
+
if (more) {
sk->protinfo.nr->fraglen += skb->len;
skb_queue_tail(&sk->protinfo.nr->frag_queue, skb);
switch (frametype) {
case NR_CONNACK:
+ nr_stop_t1timer(sk);
+ nr_start_idletimer(sk);
sk->protinfo.nr->your_index = skb->data[17];
sk->protinfo.nr->your_id = skb->data[18];
- sk->protinfo.nr->t1timer = 0;
- sk->protinfo.nr->t2timer = 0;
- sk->protinfo.nr->t4timer = 0;
sk->protinfo.nr->vs = 0;
sk->protinfo.nr->va = 0;
sk->protinfo.nr->vr = 0;
sk->protinfo.nr->n2count = 0;
sk->protinfo.nr->window = skb->data[20];
sk->state = TCP_ESTABLISHED;
- /* For WAIT_SABM connections we will produce an accept ready socket here */
if (!sk->dead)
sk->state_change(sk);
break;
case NR_CONNACK | NR_CHOKE_FLAG:
- nr_clear_queues(sk);
- sk->protinfo.nr->state = NR_STATE_0;
- sk->state = TCP_CLOSE;
- sk->err = ECONNREFUSED;
- sk->shutdown |= SEND_SHUTDOWN;
- if (!sk->dead)
- sk->state_change(sk);
- sk->dead = 1;
+ nr_disconnect(sk, ECONNREFUSED);
break;
default:
nr_write_internal(sk, NR_DISCACK);
case NR_DISCACK:
- sk->protinfo.nr->state = NR_STATE_0;
- sk->state = TCP_CLOSE;
- sk->err = 0;
- sk->shutdown |= SEND_SHUTDOWN;
- if (!sk->dead)
- sk->state_change(sk);
- sk->dead = 1;
+ nr_disconnect(sk, 0);
break;
default:
break;
case NR_DISCREQ:
- nr_clear_queues(sk);
nr_write_internal(sk, NR_DISCACK);
- sk->protinfo.nr->state = NR_STATE_0;
- sk->state = TCP_CLOSE;
- sk->err = 0;
- sk->shutdown |= SEND_SHUTDOWN;
- if (!sk->dead)
- sk->state_change(sk);
- sk->dead = 1;
+ nr_disconnect(sk, 0);
break;
case NR_DISCACK:
- nr_clear_queues(sk);
- sk->protinfo.nr->state = NR_STATE_0;
- sk->state = TCP_CLOSE;
- sk->err = ECONNRESET;
- sk->shutdown |= SEND_SHUTDOWN;
- if (!sk->dead)
- sk->state_change(sk);
- sk->dead = 1;
+ nr_disconnect(sk, ECONNRESET);
break;
case NR_INFOACK:
case NR_INFOACK | NR_NAK_FLAG | NR_CHOKE_FLAG:
if (frametype & NR_CHOKE_FLAG) {
sk->protinfo.nr->condition |= NR_COND_PEER_RX_BUSY;
- sk->protinfo.nr->t4timer = sk->protinfo.nr->t4;
+ nr_start_t4timer(sk);
} else {
sk->protinfo.nr->condition &= ~NR_COND_PEER_RX_BUSY;
- sk->protinfo.nr->t4timer = 0;
+ nr_stop_t4timer(sk);
}
if (!nr_validate_nr(sk, nr)) {
break;
case NR_INFO | NR_NAK_FLAG | NR_CHOKE_FLAG | NR_MORE_FLAG:
if (frametype & NR_CHOKE_FLAG) {
sk->protinfo.nr->condition |= NR_COND_PEER_RX_BUSY;
- sk->protinfo.nr->t4timer = sk->protinfo.nr->t4;
+ nr_start_t4timer(sk);
} else {
sk->protinfo.nr->condition &= ~NR_COND_PEER_RX_BUSY;
- sk->protinfo.nr->t4timer = 0;
+ nr_stop_t4timer(sk);
}
if (nr_validate_nr(sk, nr)) {
if (frametype & NR_NAK_FLAG) {
nr_enquiry_response(sk);
} else {
if (!(sk->protinfo.nr->condition & NR_COND_ACK_PENDING)) {
- sk->protinfo.nr->t2timer = sk->protinfo.nr->t2;
sk->protinfo.nr->condition |= NR_COND_ACK_PENDING;
+ nr_start_t2timer(sk);
}
}
break;
if (sk->protinfo.nr->state == NR_STATE_0)
return 0;
- del_timer(&sk->timer);
-
frametype = skb->data[19];
switch (sk->protinfo.nr->state) {
break;
}
- nr_set_timer(sk);
+ nr_kick(sk);
return queued;
}
/*
- * NET/ROM release 006
+ * NET/ROM release 007
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
* NET/ROM 001 Jonathan(G4KLX) Cloned from ax25_out.c
* NET/ROM 003 Jonathan(G4KLX) Added NET/ROM fragmentation.
* Darryl(G7LED) Fixed NAK, to give out correct reponse.
+ * NET/ROM 007 Jonathan(G4KLX) New timer architecture.
*/
#include <linux/config.h>
skb_queue_tail(&sk->write_queue, skb); /* Throw it on the queue */
}
- if (sk->protinfo.nr->state == NR_STATE_3)
- nr_kick(sk);
+ nr_kick(sk);
}
/*
if (sk->protinfo.nr->condition & NR_COND_OWN_RX_BUSY)
skb->data[4] |= NR_CHOKE_FLAG;
+ nr_start_idletimer(sk);
+
nr_transmit_buffer(sk, skb);
}
sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING;
sk->protinfo.nr->vl = sk->protinfo.nr->vr;
- sk->protinfo.nr->t1timer = 0;
+
+ nr_stop_t1timer(sk);
}
void nr_kick(struct sock *sk)
struct sk_buff *skb, *skbn;
unsigned short start, end;
- del_timer(&sk->timer);
+ if (sk->protinfo.nr->state != NR_STATE_3)
+ return;
+
+ if (sk->protinfo.nr->condition & NR_COND_PEER_RX_BUSY)
+ return;
+
+ if (skb_peek(&sk->write_queue) == NULL)
+ return;
start = (skb_peek(&sk->protinfo.nr->ack_queue) == NULL) ? sk->protinfo.nr->va : sk->protinfo.nr->vs;
end = (sk->protinfo.nr->va + sk->protinfo.nr->window) % NR_MODULUS;
- if (!(sk->protinfo.nr->condition & NR_COND_PEER_RX_BUSY) &&
- start != end &&
- skb_peek(&sk->write_queue) != NULL) {
-
- sk->protinfo.nr->vs = start;
+ if (start == end)
+ return;
- /*
- * Transmit data until either we're out of data to send or
- * the window is full.
- */
+ sk->protinfo.nr->vs = start;
- /*
- * Dequeue the frame and copy it.
- */
- skb = skb_dequeue(&sk->write_queue);
+ /*
+ * Transmit data until either we're out of data to send or
+ * the window is full.
+ */
- do {
- if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
- skb_queue_head(&sk->write_queue, skb);
- break;
- }
+ /*
+ * Dequeue the frame and copy it.
+ */
+ skb = skb_dequeue(&sk->write_queue);
- skb_set_owner_w(skbn, sk);
+ do {
+ if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
+ skb_queue_head(&sk->write_queue, skb);
+ break;
+ }
- /*
- * Transmit the frame copy.
- */
- nr_send_iframe(sk, skbn);
+ skb_set_owner_w(skbn, sk);
- sk->protinfo.nr->vs = (sk->protinfo.nr->vs + 1) % NR_MODULUS;
+ /*
+ * Transmit the frame copy.
+ */
+ nr_send_iframe(sk, skbn);
- /*
- * Requeue the original data frame.
- */
- skb_queue_tail(&sk->protinfo.nr->ack_queue, skb);
+ sk->protinfo.nr->vs = (sk->protinfo.nr->vs + 1) % NR_MODULUS;
- } while (sk->protinfo.nr->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL);
+ /*
+ * Requeue the original data frame.
+ */
+ skb_queue_tail(&sk->protinfo.nr->ack_queue, skb);
- sk->protinfo.nr->vl = sk->protinfo.nr->vr;
- sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING;
+ } while (sk->protinfo.nr->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL);
- if (sk->protinfo.nr->t1timer == 0)
- sk->protinfo.nr->t1timer = sk->protinfo.nr->t1;
- }
+ sk->protinfo.nr->vl = sk->protinfo.nr->vr;
+ sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING;
- nr_set_timer(sk);
+ if (!nr_t1timer_running(sk))
+ nr_start_t1timer(sk);
}
void nr_transmit_buffer(struct sock *sk, struct sk_buff *skb)
if (!nr_route_frame(skb, NULL)) {
kfree_skb(skb, FREE_WRITE);
-
- sk->state = TCP_CLOSE;
- sk->err = ENETUNREACH;
- sk->shutdown |= SEND_SHUTDOWN;
- if (!sk->dead)
- sk->state_change(sk);
- sk->dead = 1;
+ nr_disconnect(sk, ENETUNREACH);
}
}
nr_write_internal(sk, NR_CONNREQ);
- sk->protinfo.nr->t2timer = 0;
- sk->protinfo.nr->t1timer = sk->protinfo.nr->t1;
+ nr_stop_t2timer(sk);
+ nr_stop_t4timer(sk);
+ nr_stop_idletimer(sk);
+ nr_start_t1timer(sk);
}
/*
{
if (sk->protinfo.nr->vs == nr) {
nr_frames_acked(sk, nr);
- sk->protinfo.nr->t1timer = 0;
+ nr_stop_t1timer(sk);
sk->protinfo.nr->n2count = 0;
} else {
if (sk->protinfo.nr->va != nr) {
nr_frames_acked(sk, nr);
- sk->protinfo.nr->t1timer = sk->protinfo.nr->t1;
+ nr_start_t1timer(sk);
}
}
}
/*
- * NET/ROM release 006
+ * NET/ROM release 007
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
nr_neigh->callsign = *ax25;
nr_neigh->digipeat = NULL;
+ nr_neigh->ax25 = NULL;
nr_neigh->dev = dev;
nr_neigh->quality = sysctl_netrom_default_path_quality;
nr_neigh->locked = 0;
nr_neigh->callsign = *callsign;
nr_neigh->digipeat = NULL;
+ nr_neigh->ax25 = NULL;
nr_neigh->dev = dev;
nr_neigh->quality = quality;
nr_neigh->locked = 1;
}
ax25_digi.ndigi = ndigis;
- ax25_digi.lastrepeat = 0;
+ ax25_digi.lastrepeat = -1;
return &ax25_digi;
}
{
struct nr_route_struct nr_route;
struct device *dev;
- int err;
switch (cmd) {
case SIOCADDRT:
- if ((err = verify_area(VERIFY_READ, arg, sizeof(struct nr_route_struct))) != 0)
- return err;
- copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct));
+ if (copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct)))
+ return -EFAULT;
if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL)
return -EINVAL;
if (nr_route.ndigis < 0 || nr_route.ndigis > AX25_MAX_DIGIS)
}
case SIOCDELRT:
- if ((err = verify_area(VERIFY_READ, arg, sizeof(struct nr_route_struct))) != 0)
- return err;
- copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct));
+ if (copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct)))
+ return -EFAULT;
if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL)
return -EINVAL;
switch (nr_route.type) {
* A level 2 link has timed out, therefore it appears to be a poor link,
* then don't use that neighbour until it is reset.
*/
-void nr_link_failed(ax25_address *callsign, struct device *dev)
+void nr_link_failed(ax25_cb *ax25, int reason)
{
struct nr_neigh *nr_neigh;
struct nr_node *nr_node;
for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next)
- if (ax25cmp(&nr_neigh->callsign, callsign) == 0 && nr_neigh->dev == dev)
+ if (nr_neigh->ax25 == ax25)
break;
if (nr_neigh == NULL) return;
+ nr_neigh->ax25 = NULL;
+
if (++nr_neigh->failed < sysctl_netrom_link_fails_count) return;
for (nr_node = nr_node_list; nr_node != NULL; nr_node = nr_node->next)
dptr = skb_push(skb, 1);
*dptr = AX25_P_NETROM;
- return ax25_send_frame(skb, 256, (ax25_address *)dev->dev_addr, &nr_neigh->callsign, nr_neigh->digipeat, nr_neigh->dev);
+ nr_neigh->ax25 = ax25_send_frame(skb, 256, (ax25_address *)dev->dev_addr, &nr_neigh->callsign, nr_neigh->digipeat, nr_neigh->dev);
+
+ return (nr_neigh->ax25 != NULL);
}
int nr_nodes_get_info(char *buffer, char **start, off_t offset,
/*
- * NET/ROM release 006
+ * NET/ROM release 007
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
* History
* NET/ROM 001 Jonathan(G4KLX) Cloned from ax25_subr.c
* NET/ROM 003 Jonathan(G4KLX) Added G8BPQ NET/ROM extensions.
+ * NET/ROM 007 Jonathan(G4KLX) New timer architecture.
*/
#include <linux/config.h>
switch (frametype & 0x0F) {
case NR_CONNREQ:
- timeout = sk->protinfo.nr->t1 / NR_SLOWHZ;
+ timeout = sk->protinfo.nr->t1 / HZ;
*dptr++ = sk->protinfo.nr->my_index;
*dptr++ = sk->protinfo.nr->my_id;
*dptr++ = 0;
kfree_skb(skbn, FREE_WRITE);
}
+void nr_disconnect(struct sock *sk, int reason)
+{
+ nr_stop_t1timer(sk);
+ nr_stop_t2timer(sk);
+ nr_stop_t4timer(sk);
+ nr_stop_idletimer(sk);
+
+ nr_clear_queues(sk);
+
+ sk->protinfo.nr->state = NR_STATE_0;
+
+ sk->state = TCP_CLOSE;
+ sk->err = reason;
+ sk->shutdown |= SEND_SHUTDOWN;
+
+ if (!sk->dead)
+ sk->state_change(sk);
+
+ sk->dead = 1;
+}
+
#endif
/*
- * NET/ROM release 006
+ * NET/ROM release 007
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
*
* History
* NET/ROM 001 Jonathan(G4KLX) Cloned from ax25_timer.c
+ * NET/ROM 007 Jonathan(G4KLX) New timer architecture.
+ * Implemented idle timer.
*/
#include <linux/config.h>
#include <linux/interrupt.h>
#include <net/netrom.h>
-static void nr_timer(unsigned long);
+static void nr_heartbeat_expiry(unsigned long);
+static void nr_t1timer_expiry(unsigned long);
+static void nr_t2timer_expiry(unsigned long);
+static void nr_t4timer_expiry(unsigned long);
+static void nr_idletimer_expiry(unsigned long);
-/*
- * Linux set timer
- */
-void nr_set_timer(struct sock *sk)
+void nr_start_t1timer(struct sock *sk)
+{
+ del_timer(&sk->protinfo.nr->t1timer);
+
+ sk->protinfo.nr->t1timer.data = (unsigned long)sk;
+ sk->protinfo.nr->t1timer.function = &nr_t1timer_expiry;
+ sk->protinfo.nr->t1timer.expires = jiffies + sk->protinfo.nr->t1;
+
+ add_timer(&sk->protinfo.nr->t1timer);
+}
+
+void nr_start_t2timer(struct sock *sk)
+{
+ del_timer(&sk->protinfo.nr->t2timer);
+
+ sk->protinfo.nr->t2timer.data = (unsigned long)sk;
+ sk->protinfo.nr->t2timer.function = &nr_t2timer_expiry;
+ sk->protinfo.nr->t2timer.expires = jiffies + sk->protinfo.nr->t2;
+
+ add_timer(&sk->protinfo.nr->t2timer);
+}
+
+void nr_start_t4timer(struct sock *sk)
+{
+ del_timer(&sk->protinfo.nr->t4timer);
+
+ sk->protinfo.nr->t4timer.data = (unsigned long)sk;
+ sk->protinfo.nr->t4timer.function = &nr_t4timer_expiry;
+ sk->protinfo.nr->t4timer.expires = jiffies + sk->protinfo.nr->t4;
+
+ add_timer(&sk->protinfo.nr->t4timer);
+}
+
+void nr_start_idletimer(struct sock *sk)
{
- unsigned long flags;
+ del_timer(&sk->protinfo.nr->idletimer);
+
+ if (sk->protinfo.nr->idle > 0) {
+ sk->protinfo.nr->idletimer.data = (unsigned long)sk;
+ sk->protinfo.nr->idletimer.function = &nr_idletimer_expiry;
+ sk->protinfo.nr->idletimer.expires = jiffies + sk->protinfo.nr->idle;
+
+ add_timer(&sk->protinfo.nr->idletimer);
+ }
+}
- save_flags(flags); cli();
+void nr_start_heartbeat(struct sock *sk)
+{
del_timer(&sk->timer);
- restore_flags(flags);
sk->timer.data = (unsigned long)sk;
- sk->timer.function = &nr_timer;
- sk->timer.expires = jiffies + (HZ / 10);
+ sk->timer.function = &nr_heartbeat_expiry;
+ sk->timer.expires = jiffies + 5 * HZ;
add_timer(&sk->timer);
}
-/*
- * NET/ROM TIMER
- *
- * This routine is called every 100ms. Decrement timer by this
- * amount - if expired then process the event.
- */
-static void nr_timer(unsigned long param)
+void nr_stop_t1timer(struct sock *sk)
+{
+ del_timer(&sk->protinfo.nr->t1timer);
+}
+
+void nr_stop_t2timer(struct sock *sk)
+{
+ del_timer(&sk->protinfo.nr->t2timer);
+}
+
+void nr_stop_t4timer(struct sock *sk)
+{
+ del_timer(&sk->protinfo.nr->t4timer);
+}
+
+void nr_stop_idletimer(struct sock *sk)
+{
+ del_timer(&sk->protinfo.nr->idletimer);
+}
+
+void nr_stop_heartbeat(struct sock *sk)
+{
+ del_timer(&sk->timer);
+}
+
+int nr_t1timer_running(struct sock *sk)
+{
+ return (sk->protinfo.nr->t1timer.prev != NULL ||
+ sk->protinfo.nr->t1timer.next != NULL);
+}
+
+static void nr_heartbeat_expiry(unsigned long param)
{
struct sock *sk = (struct sock *)param;
switch (sk->protinfo.nr->state) {
+
case NR_STATE_0:
/* Magic here: If we listen() and a new link dies before it
is accepted() it isn't 'dead' so doesn't get removed. */
if (sk->destroy || (sk->state == TCP_LISTEN && sk->dead)) {
- del_timer(&sk->timer);
nr_destroy_socket(sk);
return;
}
nr_write_internal(sk, NR_INFOACK);
break;
}
- /*
- * Check for frames to transmit.
- */
- nr_kick(sk);
- break;
-
- default:
break;
}
- if (sk->protinfo.nr->t2timer > 0 && --sk->protinfo.nr->t2timer == 0) {
- if (sk->protinfo.nr->state == NR_STATE_3) {
- if (sk->protinfo.nr->condition & NR_COND_ACK_PENDING) {
- sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING;
- nr_enquiry_response(sk);
- }
- }
- }
+ nr_start_heartbeat(sk);
+}
- if (sk->protinfo.nr->t4timer > 0 && --sk->protinfo.nr->t4timer == 0) {
- sk->protinfo.nr->condition &= ~NR_COND_PEER_RX_BUSY;
- }
+static void nr_t2timer_expiry(unsigned long param)
+{
+ struct sock *sk = (struct sock *)param;
- if (sk->protinfo.nr->t1timer == 0 || --sk->protinfo.nr->t1timer > 0) {
- nr_set_timer(sk);
- return;
+ if (sk->protinfo.nr->condition & NR_COND_ACK_PENDING) {
+ sk->protinfo.nr->condition &= ~NR_COND_ACK_PENDING;
+ nr_enquiry_response(sk);
}
+}
+
+static void nr_t4timer_expiry(unsigned long param)
+{
+ struct sock *sk = (struct sock *)param;
+
+ sk->protinfo.nr->condition &= ~NR_COND_PEER_RX_BUSY;
+}
+
+static void nr_idletimer_expiry(unsigned long param)
+{
+ struct sock *sk = (struct sock *)param;
+
+ nr_clear_queues(sk);
+
+ sk->protinfo.nr->n2count = 0;
+ nr_write_internal(sk, NR_DISCREQ);
+ sk->protinfo.nr->state = NR_STATE_2;
+
+ nr_start_t1timer(sk);
+ nr_stop_t2timer(sk);
+ nr_stop_t4timer(sk);
+
+ sk->state = TCP_CLOSE;
+ sk->err = 0;
+ sk->shutdown |= SEND_SHUTDOWN;
+
+ if (!sk->dead)
+ sk->state_change(sk);
+
+ sk->dead = 1;
+}
+
+static void nr_t1timer_expiry(unsigned long param)
+{
+ struct sock *sk = (struct sock *)param;
switch (sk->protinfo.nr->state) {
+
case NR_STATE_1:
if (sk->protinfo.nr->n2count == sk->protinfo.nr->n2) {
- nr_clear_queues(sk);
- sk->protinfo.nr->state = NR_STATE_0;
- sk->state = TCP_CLOSE;
- sk->err = ETIMEDOUT;
- sk->shutdown |= SEND_SHUTDOWN;
- if (!sk->dead)
- sk->state_change(sk);
- sk->dead = 1;
+ nr_disconnect(sk, ETIMEDOUT);
+ return;
} else {
sk->protinfo.nr->n2count++;
nr_write_internal(sk, NR_CONNREQ);
case NR_STATE_2:
if (sk->protinfo.nr->n2count == sk->protinfo.nr->n2) {
- nr_clear_queues(sk);
- sk->protinfo.nr->state = NR_STATE_0;
- sk->state = TCP_CLOSE;
- sk->err = ETIMEDOUT;
- sk->shutdown |= SEND_SHUTDOWN;
- if (!sk->dead)
- sk->state_change(sk);
- sk->dead = 1;
+ nr_disconnect(sk, ETIMEDOUT);
+ return;
} else {
sk->protinfo.nr->n2count++;
nr_write_internal(sk, NR_DISCREQ);
case NR_STATE_3:
if (sk->protinfo.nr->n2count == sk->protinfo.nr->n2) {
- nr_clear_queues(sk);
- sk->protinfo.nr->state = NR_STATE_0;
- sk->state = TCP_CLOSE;
- sk->err = ETIMEDOUT;
- sk->shutdown |= SEND_SHUTDOWN;
- if (!sk->dead)
- sk->state_change(sk);
- sk->dead = 1;
+ nr_disconnect(sk, ETIMEDOUT);
+ return;
} else {
sk->protinfo.nr->n2count++;
nr_requeue_frames(sk);
break;
}
- sk->protinfo.nr->t1timer = sk->protinfo.nr->t1;
-
- nr_set_timer(sk);
+ nr_start_t1timer(sk);
}
#endif
static int min_quality[] = {0}, max_quality[] = {255};
static int min_obs[] = {0}, max_obs[] = {255};
static int min_ttl[] = {0}, max_ttl[] = {255};
-static int min_t1[] = {5 * NR_SLOWHZ};
-static int max_t1[] = {600 * NR_SLOWHZ};
+static int min_t1[] = {5 * HZ};
+static int max_t1[] = {600 * HZ};
static int min_n2[] = {2}, max_n2[] = {127};
-static int min_t2[] = {1 * NR_SLOWHZ};
-static int max_t2[] = {60 * NR_SLOWHZ};
-static int min_t4[] = {1 * NR_SLOWHZ};
-static int max_t4[] = {1000 * NR_SLOWHZ};
+static int min_t2[] = {1 * HZ};
+static int max_t2[] = {60 * HZ};
+static int min_t4[] = {1 * HZ};
+static int max_t4[] = {1000 * HZ};
static int min_window[] = {1}, max_window[] = {127};
-static int min_idle[] = {0 * NR_SLOWHZ};
-static int max_idle[] = {65535 * NR_SLOWHZ};
+static int min_idle[] = {0 * HZ};
+static int max_idle[] = {65535 * HZ};
static int min_route[] = {0}, max_route[] = {1};
static int min_fails[] = {1}, max_fails[] = {10};
/*
- * ROSE release 002
+ * ROSE release 003
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
* ROSE 002 Jonathan(G4KLX) Changed hdrincl to qbitincl.
* Added random number facilities entry.
* Variable number of ROSE devices.
+ * ROSE 003 Jonathan(G4KLX) New timer architecture.
+ * Implemented idle timer.
+ * Added use count to neighbour.
*/
#include <linux/config.h>
struct sock *s;
unsigned long flags;
- save_flags(flags);
- cli();
+ save_flags(flags); cli();
if ((s = rose_list) == sk) {
rose_list = s->next;
for (s = rose_list; s != NULL; s = s->next) {
if (s->protinfo.rose->neighbour == neigh) {
- s->protinfo.rose->state = ROSE_STATE_0;
+ rose_disconnect(s, ENETUNREACH, ROSE_OUT_OF_ORDER, 0);
+ s->protinfo.rose->neighbour->use--;
s->protinfo.rose->neighbour = NULL;
- s->state = TCP_CLOSE;
- s->err = ENETUNREACH;
- s->shutdown |= SEND_SHUTDOWN;
- s->state_change(s);
- s->dead = 1;
}
}
}
for (s = rose_list; s != NULL; s = s->next) {
if (s->protinfo.rose->device == dev) {
- s->protinfo.rose->state = ROSE_STATE_0;
+ rose_disconnect(s, ENETUNREACH, ROSE_OUT_OF_ORDER, 0);
+ s->protinfo.rose->neighbour->use--;
s->protinfo.rose->device = NULL;
- s->state = TCP_CLOSE;
- s->err = ENETUNREACH;
- s->shutdown |= SEND_SHUTDOWN;
- s->state_change(s);
- s->dead = 1;
}
}
}
{
unsigned long flags;
- save_flags(flags);
- cli();
+ save_flags(flags); cli();
sk->next = rose_list;
rose_list = sk;
unsigned long flags;
struct sock *s;
- save_flags(flags);
- cli();
+ save_flags(flags); cli();
for (s = rose_list; s != NULL; s = s->next) {
if (rosecmp(&s->protinfo.rose->source_addr, addr) == 0 && ax25cmp(&s->protinfo.rose->source_call, call) == 0 && s->protinfo.rose->source_ndigis == 0 && s->state == TCP_LISTEN) {
struct sock *s;
unsigned long flags;
- save_flags(flags);
- cli();
+ save_flags(flags); cli();
for (s = rose_list; s != NULL; s = s->next) {
if (s->protinfo.rose->lci == lci && s->protinfo.rose->neighbour == neigh) {
struct sk_buff *skb;
unsigned long flags;
- save_flags(flags);
- cli();
+ save_flags(flags); cli();
- del_timer(&sk->timer);
+ rose_stop_heartbeat(sk);
+ rose_stop_idletimer(sk);
+ rose_stop_timer(sk);
rose_remove_socket(sk);
rose_clear_queues(sk); /* Flush the queues */
while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
if (skb->sk != sk) { /* A pending connection */
skb->sk->dead = 1; /* Queue the unaccepted socket for death */
- rose_set_timer(skb->sk);
+ rose_start_heartbeat(skb->sk);
skb->sk->protinfo.rose->state = ROSE_STATE_0;
}
return -EFAULT;
switch (optname) {
+ case ROSE_DEFER:
+ sk->protinfo.rose->defer = opt ? 1 : 0;
+ return 0;
+
case ROSE_T1:
if (opt < 1)
return -EINVAL;
- sk->protinfo.rose->t1 = opt * ROSE_SLOWHZ;
+ sk->protinfo.rose->t1 = opt * HZ;
return 0;
case ROSE_T2:
if (opt < 1)
return -EINVAL;
- sk->protinfo.rose->t2 = opt * ROSE_SLOWHZ;
+ sk->protinfo.rose->t2 = opt * HZ;
return 0;
case ROSE_T3:
if (opt < 1)
return -EINVAL;
- sk->protinfo.rose->t3 = opt * ROSE_SLOWHZ;
+ sk->protinfo.rose->t3 = opt * HZ;
return 0;
case ROSE_HOLDBACK:
if (opt < 1)
return -EINVAL;
- sk->protinfo.rose->hb = opt * ROSE_SLOWHZ;
+ sk->protinfo.rose->hb = opt * HZ;
return 0;
case ROSE_IDLE:
- if (opt < 1)
+ if (opt < 0)
return -EINVAL;
- sk->protinfo.rose->idle = opt * 60 * ROSE_SLOWHZ;
+ sk->protinfo.rose->idle = opt * 60 * HZ;
return 0;
case ROSE_QBITINCL:
return -EFAULT;
switch (optname) {
+ case ROSE_DEFER:
+ val = sk->protinfo.rose->defer;
+ break;
+
case ROSE_T1:
- val = sk->protinfo.rose->t1 / ROSE_SLOWHZ;
+ val = sk->protinfo.rose->t1 / HZ;
break;
case ROSE_T2:
- val = sk->protinfo.rose->t2 / ROSE_SLOWHZ;
+ val = sk->protinfo.rose->t2 / HZ;
break;
case ROSE_T3:
- val = sk->protinfo.rose->t3 / ROSE_SLOWHZ;
+ val = sk->protinfo.rose->t3 / HZ;
break;
case ROSE_HOLDBACK:
- val = sk->protinfo.rose->hb / ROSE_SLOWHZ;
+ val = sk->protinfo.rose->hb / HZ;
break;
case ROSE_IDLE:
- val = sk->protinfo.rose->idle / (ROSE_SLOWHZ * 60);
+ val = sk->protinfo.rose->idle / (60 * HZ);
break;
case ROSE_QBITINCL:
sock->ops = &rose_proto_ops;
sk->protocol = protocol;
- sk->mtu = ROSE_MTU; /* 128 */
+ sk->mtu = ROSE_MTU; /* 253 */
+
+ init_timer(&rose->timer);
+ init_timer(&rose->idletimer);
skb_queue_head_init(&rose->frag_queue);
sk->sleep = osk->sleep;
sk->zapped = osk->zapped;
+ init_timer(&rose->timer);
+ init_timer(&rose->idletimer);
+
skb_queue_head_init(&rose->frag_queue);
rose->t1 = osk->protinfo.rose->t1;
rose->hb = osk->protinfo.rose->hb;
rose->idle = osk->protinfo.rose->idle;
+ rose->defer = osk->protinfo.rose->defer;
rose->device = osk->protinfo.rose->device;
rose->qbitincl = osk->protinfo.rose->qbitincl;
switch (sk->protinfo.rose->state) {
case ROSE_STATE_0:
- sk->state = TCP_CLOSE;
- sk->shutdown |= SEND_SHUTDOWN;
- sk->state_change(sk);
- sk->dead = 1;
+ rose_disconnect(sk, 0, -1, -1);
rose_destroy_socket(sk);
break;
case ROSE_STATE_2:
- sk->protinfo.rose->state = ROSE_STATE_0;
- sk->state = TCP_CLOSE;
- sk->shutdown |= SEND_SHUTDOWN;
- sk->state_change(sk);
- sk->dead = 1;
+ sk->protinfo.rose->neighbour->use--;
+ rose_disconnect(sk, 0, -1, -1);
rose_destroy_socket(sk);
- break;
+ break;
case ROSE_STATE_1:
case ROSE_STATE_3:
case ROSE_STATE_4:
+ case ROSE_STATE_5:
rose_clear_queues(sk);
+ rose_stop_idletimer(sk);
rose_write_internal(sk, ROSE_CLEAR_REQUEST);
- sk->protinfo.rose->timer = sk->protinfo.rose->t3;
+ rose_start_t3timer(sk);
sk->protinfo.rose->state = ROSE_STATE_2;
sk->state = TCP_CLOSE;
sk->shutdown |= SEND_SHUTDOWN;
{
struct sock *sk = sock->sk;
struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr;
+ unsigned char cause, diagnostic;
ax25_address *user;
struct device *dev;
if (addr->srose_family != AF_ROSE)
return -EINVAL;
- if ((sk->protinfo.rose->neighbour = rose_get_neigh(&addr->srose_addr)) == NULL)
+ if ((sk->protinfo.rose->neighbour = rose_get_neigh(&addr->srose_addr, &cause, &diagnostic)) == NULL)
return -ENETUNREACH;
if ((sk->protinfo.rose->lci = rose_new_lci(sk->protinfo.rose->neighbour)) == 0)
sk->state = TCP_SYN_SENT;
sk->protinfo.rose->state = ROSE_STATE_1;
- sk->protinfo.rose->timer = sk->protinfo.rose->t1;
- rose_write_internal(sk, ROSE_CALL_REQUEST);
- rose_set_timer(sk);
+ sk->protinfo.rose->neighbour->use++;
+
+ rose_write_internal(sk, ROSE_CALL_REQUEST);
+ rose_start_heartbeat(sk);
+ rose_start_t1timer(sk);
/* Now the loop */
if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
/*
* skb->data points to the rose frame start
*/
-
- /*
- * XXX This is an error.
- */
if (!rose_parse_facilities(skb, &facilities)) {
+ rose_transmit_clear_request(neigh, lci, ROSE_INVALID_FACILITY, 76);
return 0;
}
* We can't accept the Call Request.
*/
if (sk == NULL || sk->ack_backlog == sk->max_ack_backlog || (make = rose_make_new(sk)) == NULL) {
- rose_transmit_clear_request(neigh, lci, 0x01);
+ rose_transmit_clear_request(neigh, lci, ROSE_NETWORK_CONGESTION, 120);
return 0;
}
make->protinfo.rose->neighbour = neigh;
make->protinfo.rose->device = dev;
- rose_write_internal(make, ROSE_CALL_ACCEPTED);
+ make->protinfo.rose->neighbour->use++;
+
+ if (sk->protinfo.rose->defer) {
+ make->protinfo.rose->state = ROSE_STATE_5;
+ } else {
+ rose_write_internal(make, ROSE_CALL_ACCEPTED);
+ make->protinfo.rose->state = ROSE_STATE_3;
+ rose_start_idletimer(make);
+ }
make->protinfo.rose->condition = 0x00;
make->protinfo.rose->vs = 0;
make->protinfo.rose->va = 0;
make->protinfo.rose->vr = 0;
make->protinfo.rose->vl = 0;
- make->protinfo.rose->state = ROSE_STATE_3;
sk->ack_backlog++;
make->pair = sk;
skb_queue_head(&sk->receive_queue, skb);
- rose_set_timer(make);
+ rose_start_heartbeat(make);
if (!sk->dead)
sk->data_ready(sk, skb->len);
static int rose_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct sock *sk = sock->sk;
- int err;
- long amount = 0;
switch (cmd) {
- case TIOCOUTQ:
- if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned int))) != 0)
- return err;
+ case TIOCOUTQ: {
+ long amount;
amount = sk->sndbuf - atomic_read(&sk->wmem_alloc);
if (amount < 0)
amount = 0;
- put_user(amount, (unsigned int *)arg);
+ if (put_user(amount, (unsigned int *)arg))
+ return -EFAULT;
return 0;
+ }
case TIOCINQ: {
struct sk_buff *skb;
+ long amount = 0L;
/* These two are safe on a single CPU system as only user tasks fiddle here */
if ((skb = skb_peek(&sk->receive_queue)) != NULL)
- amount = skb->len - 20;
- if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned int))) != 0)
- return err;
- put_user(amount, (unsigned int *)arg);
+ amount = skb->len;
+ if (put_user(amount, (unsigned int *)arg))
+ return -EFAULT;
return 0;
}
case SIOCGSTAMP:
if (sk != NULL) {
- if (sk->stamp.tv_sec==0)
+ if (sk->stamp.tv_sec == 0)
return -ENOENT;
- if ((err = verify_area(VERIFY_WRITE,(void *)arg,sizeof(struct timeval))) != 0)
- return err;
- copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval));
+ if (copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)))
+ return -EFAULT;
return 0;
}
return -EINVAL;
case SIOCADDRT:
case SIOCDELRT:
+ case SIOCRSCLRRT:
if (!suser()) return -EPERM;
return rose_rt_ioctl(cmd, (void *)arg);
+ case SIOCRSGCAUSE: {
+ struct rose_cause_struct rose_cause;
+ rose_cause.cause = sk->protinfo.rose->cause;
+ rose_cause.diagnostic = sk->protinfo.rose->diagnostic;
+ if (copy_to_user((void *)arg, &rose_cause, sizeof(struct rose_cause_struct)))
+ return -EFAULT;
+ return 0;
+ }
+
+ case SIOCRSSCAUSE: {
+ struct rose_cause_struct rose_cause;
+ if (copy_from_user(&rose_cause, (void *)arg, sizeof(struct rose_cause_struct)))
+ return -EFAULT;
+ sk->protinfo.rose->cause = rose_cause.cause;
+ sk->protinfo.rose->diagnostic = rose_cause.diagnostic;
+ return 0;
+ }
+
case SIOCRSL2CALL:
if (!suser()) return -EPERM;
- if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(ax25_address))) != 0)
- return err;
if (ax25cmp(&rose_callsign, &null_ax25_address) != 0)
ax25_listen_release(&rose_callsign, NULL);
- copy_from_user(&rose_callsign, (void *)arg, sizeof(ax25_address));
+ if (copy_from_user(&rose_callsign, (void *)arg, sizeof(ax25_address)))
+ return -EFAULT;
if (ax25cmp(&rose_callsign, &null_ax25_address) != 0)
ax25_listen_register(&rose_callsign, NULL);
return 0;
+ case SIOCRSACCEPT:
+ if (sk->protinfo.rose->state == ROSE_STATE_5) {
+ rose_write_internal(sk, ROSE_CALL_ACCEPTED);
+ rose_start_idletimer(sk);
+ sk->protinfo.rose->condition = 0x00;
+ sk->protinfo.rose->vs = 0;
+ sk->protinfo.rose->va = 0;
+ sk->protinfo.rose->vr = 0;
+ sk->protinfo.rose->vl = 0;
+ sk->protinfo.rose->state = ROSE_STATE_3;
+ }
+ return 0;
+
default:
return dev_ioctl(cmd, (void *)arg);
}
cli();
- len += sprintf(buffer, "dest_addr dest_call src_addr src_call dev lci st vs vr va t t1 t2 t3 hb Snd-Q Rcv-Q\n");
+ len += sprintf(buffer, "dest_addr dest_call src_addr src_call dev lci st vs vr va t t1 t2 t3 hb idle Snd-Q Rcv-Q\n");
for (s = rose_list; s != NULL; s = s->next) {
if ((dev = s->protinfo.rose->device) == NULL)
else
callsign = ax2asc(&s->protinfo.rose->source_call);
- len += sprintf(buffer + len, "%-10s %-9s %-5s %3.3X %d %d %d %d %3d %3d %3d %3d %3d %5d %5d\n",
- rose2asc(&s->protinfo.rose->source_addr), callsign,
- devname, s->protinfo.rose->lci & 0x0FFF,
+ len += sprintf(buffer + len, "%-10s %-9s %-5s %3.3X %d %d %d %d %3lu %3lu %3lu %3lu %3lu %3lu/%03lu %5d %5d\n",
+ rose2asc(&s->protinfo.rose->source_addr),
+ callsign,
+ devname,
+ s->protinfo.rose->lci & 0x0FFF,
s->protinfo.rose->state,
- s->protinfo.rose->vs, s->protinfo.rose->vr, s->protinfo.rose->va,
- s->protinfo.rose->timer / ROSE_SLOWHZ,
- s->protinfo.rose->t1 / ROSE_SLOWHZ,
- s->protinfo.rose->t2 / ROSE_SLOWHZ,
- s->protinfo.rose->t3 / ROSE_SLOWHZ,
- s->protinfo.rose->hb / ROSE_SLOWHZ,
- atomic_read(&s->wmem_alloc), atomic_read(&s->rmem_alloc));
+ s->protinfo.rose->vs,
+ s->protinfo.rose->vr,
+ s->protinfo.rose->va,
+ ax25_display_timer(&s->protinfo.rose->timer) / HZ,
+ s->protinfo.rose->t1 / HZ,
+ s->protinfo.rose->t2 / HZ,
+ s->protinfo.rose->t3 / HZ,
+ s->protinfo.rose->hb / HZ,
+ ax25_display_timer(&s->protinfo.rose->idletimer) / (60 * HZ),
+ s->protinfo.rose->idle / (60 * HZ),
+ atomic_read(&s->wmem_alloc),
+ atomic_read(&s->rmem_alloc));
pos = begin + len;
sock_register(&rose_family_ops);
register_netdevice_notifier(&rose_dev_notifier);
- printk(KERN_INFO "G4KLX ROSE for Linux. Version 0.2 for AX25.035 Linux 2.1\n");
+ printk(KERN_INFO "G4KLX ROSE for Linux. Version 0.3 for AX25.037 Linux 2.1\n");
ax25_protocol_register(AX25_P_ROSE, rose_route_frame);
ax25_linkfail_register(rose_link_failed);
/*
- * ROSE release 002
+ * ROSE release 003
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
unsigned char *bp = (unsigned char *)skb->data;
struct sk_buff *skbn;
- if (!arp_find(bp + 7, skb)) {
+ if (arp_find(bp + 7, skb)) {
kfree_skb(skb, FREE_WRITE);
return 1;
}
/*
- * ROSE release 002
+ * ROSE release 003
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
*
* History
* ROSE 001 Jonathan(G4KLX) Cloned from nr_in.c
+ * ROSE 002 Jonathan(G4KLX) Return cause and diagnostic codes from Clear Requests.
+ * ROSE 003 Jonathan(G4KLX) New timer architecture.
*/
#include <linux/config.h>
{
struct sk_buff *skbo, *skbn = skb;
+ rose_start_idletimer(sk);
+
if (more) {
sk->protinfo.rose->fraglen += skb->len;
skb_queue_tail(&sk->protinfo.rose->frag_queue, skb);
switch (frametype) {
case ROSE_CALL_ACCEPTED:
+ rose_stop_timer(sk);
+ rose_start_idletimer(sk);
sk->protinfo.rose->condition = 0x00;
- sk->protinfo.rose->timer = 0;
sk->protinfo.rose->vs = 0;
sk->protinfo.rose->va = 0;
sk->protinfo.rose->vr = 0;
break;
case ROSE_CLEAR_REQUEST:
- rose_clear_queues(sk);
rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
- sk->protinfo.rose->state = ROSE_STATE_0;
- sk->state = TCP_CLOSE;
- sk->err = ECONNREFUSED;
- sk->shutdown |= SEND_SHUTDOWN;
- if (!sk->dead)
- sk->state_change(sk);
- sk->dead = 1;
+ rose_disconnect(sk, ECONNREFUSED, skb->data[3], skb->data[4]);
+ sk->protinfo.rose->neighbour->use--;
break;
default:
case ROSE_CLEAR_REQUEST:
rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
+ rose_disconnect(sk, 0, skb->data[3], skb->data[4]);
+ sk->protinfo.rose->neighbour->use--;
+ break;
+
case ROSE_CLEAR_CONFIRMATION:
- rose_clear_queues(sk);
- sk->protinfo.rose->state = ROSE_STATE_0;
- sk->state = TCP_CLOSE;
- sk->err = 0;
- sk->shutdown |= SEND_SHUTDOWN;
- if (!sk->dead)
- sk->state_change(sk);
- sk->dead = 1;
+ rose_disconnect(sk, 0, -1, -1);
+ sk->protinfo.rose->neighbour->use--;
break;
default:
switch (frametype) {
case ROSE_RESET_REQUEST:
+ rose_stop_timer(sk);
+ rose_start_idletimer(sk);
rose_write_internal(sk, ROSE_RESET_CONFIRMATION);
sk->protinfo.rose->condition = 0x00;
- sk->protinfo.rose->timer = 0;
sk->protinfo.rose->vs = 0;
sk->protinfo.rose->vr = 0;
sk->protinfo.rose->va = 0;
break;
case ROSE_CLEAR_REQUEST:
- rose_clear_queues(sk);
rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
- sk->protinfo.rose->state = ROSE_STATE_0;
- sk->state = TCP_CLOSE;
- sk->err = 0;
- sk->shutdown |= SEND_SHUTDOWN;
- if (!sk->dead)
- sk->state_change(sk);
- sk->dead = 1;
+ rose_disconnect(sk, 0, skb->data[3], skb->data[4]);
+ sk->protinfo.rose->neighbour->use--;
break;
case ROSE_RR:
else
sk->protinfo.rose->condition &= ~ROSE_COND_PEER_RX_BUSY;
if (!rose_validate_nr(sk, nr)) {
- rose_clear_queues(sk);
rose_write_internal(sk, ROSE_RESET_REQUEST);
sk->protinfo.rose->condition = 0x00;
sk->protinfo.rose->vs = 0;
sk->protinfo.rose->va = 0;
sk->protinfo.rose->vl = 0;
sk->protinfo.rose->state = ROSE_STATE_4;
- sk->protinfo.rose->timer = sk->protinfo.rose->t2;
+ rose_start_t2timer(sk);
+ rose_stop_idletimer(sk);
} else {
if (sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY) {
sk->protinfo.rose->va = nr;
case ROSE_DATA: /* XXX */
sk->protinfo.rose->condition &= ~ROSE_COND_PEER_RX_BUSY;
if (!rose_validate_nr(sk, nr)) {
- rose_clear_queues(sk);
rose_write_internal(sk, ROSE_RESET_REQUEST);
sk->protinfo.rose->condition = 0x00;
sk->protinfo.rose->vs = 0;
sk->protinfo.rose->va = 0;
sk->protinfo.rose->vl = 0;
sk->protinfo.rose->state = ROSE_STATE_4;
- sk->protinfo.rose->timer = sk->protinfo.rose->t2;
+ rose_start_t2timer(sk);
+ rose_stop_idletimer(sk);
break;
}
if (sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY) {
*/
if (((sk->protinfo.rose->vl + sysctl_rose_window_size) % ROSE_MODULUS) == sk->protinfo.rose->vr) {
sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING;
- sk->protinfo.rose->timer = 0;
+ rose_stop_timer(sk);
rose_enquiry_response(sk);
} else {
sk->protinfo.rose->condition |= ROSE_COND_ACK_PENDING;
- sk->protinfo.rose->timer = sk->protinfo.rose->hb;
+ rose_start_hbtimer(sk);
}
break;
case ROSE_RESET_REQUEST:
rose_write_internal(sk, ROSE_RESET_CONFIRMATION);
case ROSE_RESET_CONFIRMATION:
- sk->protinfo.rose->timer = 0;
+ rose_stop_timer(sk);
+ rose_start_idletimer(sk);
sk->protinfo.rose->condition = 0x00;
sk->protinfo.rose->va = 0;
sk->protinfo.rose->vr = 0;
break;
case ROSE_CLEAR_REQUEST:
- rose_clear_queues(sk);
rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
- sk->protinfo.rose->timer = 0;
- sk->protinfo.rose->state = ROSE_STATE_0;
- sk->state = TCP_CLOSE;
- sk->err = 0;
- sk->shutdown |= SEND_SHUTDOWN;
- if (!sk->dead)
- sk->state_change(sk);
- sk->dead = 1;
+ rose_disconnect(sk, 0, skb->data[3], skb->data[4]);
+ sk->protinfo.rose->neighbour->use--;
break;
default:
return 0;
}
+/*
+ * State machine for state 5, Awaiting Call Acceptance State.
+ * The handling of the timer(s) is in file rose_timer.c
+ * Handling of state 0 and connection release is in af_rose.c.
+ */
+static int rose_state5_machine(struct sock *sk, struct sk_buff *skb, int frametype)
+{
+ if (frametype == ROSE_CLEAR_REQUEST) {
+ rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
+ rose_disconnect(sk, 0, skb->data[3], skb->data[4]);
+ sk->protinfo.rose->neighbour->use--;
+ }
+
+ return 0;
+}
+
/* Higher level upcall for a LAPB frame */
int rose_process_rx_frame(struct sock *sk, struct sk_buff *skb)
{
if (sk->protinfo.rose->state == ROSE_STATE_0)
return 0;
- del_timer(&sk->timer);
-
frametype = rose_decode(skb, &ns, &nr, &q, &d, &m);
switch (sk->protinfo.rose->state) {
case ROSE_STATE_4:
queued = rose_state4_machine(sk, skb, frametype);
break;
+ case ROSE_STATE_5:
+ queued = rose_state5_machine(sk, skb, frametype);
+ break;
}
- rose_set_timer(sk);
+ rose_kick(sk);
return queued;
}
/*
- * ROSE release 002
+ * ROSE release 003
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
*
* History
* ROSE 001 Jonathan(G4KLX) Cloned from rose_timer.c
+ * ROSE 003 Jonathan(G4KLX) New timer architecture.
*/
#include <linux/config.h>
#include <linux/firewall.h>
#include <net/rose.h>
-static void rose_link_timer(unsigned long);
+static void rose_ftimer_expiry(unsigned long);
+static void rose_t0timer_expiry(unsigned long);
-/*
- * Linux set timer
- */
-void rose_link_set_timer(struct rose_neigh *neigh)
+void rose_start_ftimer(struct rose_neigh *neigh)
{
- unsigned long flags;
+ del_timer(&neigh->ftimer);
- save_flags(flags); cli();
- del_timer(&neigh->timer);
- restore_flags(flags);
+ neigh->ftimer.data = (unsigned long)neigh;
+ neigh->ftimer.function = &rose_ftimer_expiry;
+ neigh->ftimer.expires = jiffies + sysctl_rose_link_fail_timeout;
- neigh->timer.data = (unsigned long)neigh;
- neigh->timer.function = &rose_link_timer;
- neigh->timer.expires = jiffies + (HZ / 10);
+ add_timer(&neigh->ftimer);
+}
+
+void rose_start_t0timer(struct rose_neigh *neigh)
+{
+ del_timer(&neigh->t0timer);
- add_timer(&neigh->timer);
+ neigh->t0timer.data = (unsigned long)neigh;
+ neigh->t0timer.function = &rose_t0timer_expiry;
+ neigh->t0timer.expires = jiffies + sysctl_rose_restart_request_timeout;
+
+ add_timer(&neigh->t0timer);
}
-/*
- * ROSE Link Timer
- *
- * This routine is called every 100ms. Decrement timer by this
- * amount - if expired then process the event.
- */
-static void rose_link_timer(unsigned long param)
+void rose_stop_ftimer(struct rose_neigh *neigh)
{
- struct rose_neigh *neigh = (struct rose_neigh *)param;
+ del_timer(&neigh->ftimer);
+}
- if (neigh->ftimer > 0)
- neigh->ftimer--;
+void rose_stop_t0timer(struct rose_neigh *neigh)
+{
+ del_timer(&neigh->t0timer);
+}
- if (neigh->t0timer > 0) {
- neigh->t0timer--;
+int rose_ftimer_running(struct rose_neigh *neigh)
+{
+ return (neigh->ftimer.prev != NULL || neigh->ftimer.next != NULL);
+}
- if (neigh->t0timer == 0) {
- rose_transmit_restart_request(neigh);
- neigh->dce_mode = 0;
- neigh->t0timer = sysctl_rose_restart_request_timeout;
- }
- }
+int rose_t0timer_running(struct rose_neigh *neigh)
+{
+ return (neigh->t0timer.prev != NULL || neigh->t0timer.next != NULL);
+}
- if (neigh->ftimer > 0 || neigh->t0timer > 0)
- rose_link_set_timer(neigh);
- else
- del_timer(&neigh->timer);
+static void rose_ftimer_expiry(unsigned long param)
+{
+}
+
+static void rose_t0timer_expiry(unsigned long param)
+{
+ struct rose_neigh *neigh = (struct rose_neigh *)param;
+
+ rose_transmit_restart_request(neigh);
+
+ neigh->dce_mode = 0;
+
+ rose_start_t0timer(neigh);
}
/*
else
rose_call = &rose_callsign;
- return ax25_send_frame(skb, 256, rose_call, &neigh->callsign, neigh->digipeat, neigh->dev);
+ neigh->ax25 = ax25_send_frame(skb, 256, rose_call, &neigh->callsign, neigh->digipeat, neigh->dev);
+
+ return (neigh->ax25 != NULL);
}
/*
else
rose_call = &rose_callsign;
- return ax25_link_up(rose_call, &neigh->callsign, neigh->digipeat, neigh->dev);
+ neigh->ax25 = ax25_find_cb(rose_call, &neigh->callsign, neigh->digipeat, neigh->dev);
+
+ return (neigh->ax25 != NULL);
}
/*
switch (frametype) {
case ROSE_RESTART_REQUEST:
- neigh->t0timer = 0;
+ rose_stop_t0timer(neigh);
neigh->restarted = 1;
- neigh->dce_mode = (skb->data[3] == 0x00);
- del_timer(&neigh->timer);
+ neigh->dce_mode = (skb->data[3] == ROSE_DTE_ORIGINATED);
rose_transmit_restart_confirmation(neigh);
break;
case ROSE_RESTART_CONFIRMATION:
- neigh->t0timer = 0;
+ rose_stop_t0timer(neigh);
neigh->restarted = 1;
- del_timer(&neigh->timer);
break;
case ROSE_DIAGNOSTIC:
*dptr++ = ROSE_GFI;
*dptr++ = 0x00;
*dptr++ = ROSE_RESTART_REQUEST;
- *dptr++ = 0x00;
+ *dptr++ = ROSE_DTE_ORIGINATED;
*dptr++ = 0;
if (!rose_send_frame(skb, neigh))
* This routine is called when a Clear Request is needed outside of the context
* of a connected socket.
*/
-void rose_transmit_clear_request(struct rose_neigh *neigh, unsigned int lci, unsigned char cause)
+void rose_transmit_clear_request(struct rose_neigh *neigh, unsigned int lci, unsigned char cause, unsigned char diagnostic)
{
struct sk_buff *skb;
unsigned char *dptr;
*dptr++ = ((lci >> 0) & 0xFF);
*dptr++ = ROSE_CLEAR_REQUEST;
*dptr++ = cause;
- *dptr++ = 0x00;
+ *dptr++ = diagnostic;
if (!rose_send_frame(skb, neigh))
kfree_skb(skb, FREE_WRITE);
} else {
skb_queue_tail(&neigh->queue, skb);
- if (neigh->t0timer == 0) {
+ if (!rose_t0timer_running(neigh)) {
rose_transmit_restart_request(neigh);
neigh->dce_mode = 0;
- neigh->t0timer = sysctl_rose_restart_request_timeout;
- rose_link_set_timer(neigh);
+ rose_start_t0timer(neigh);
}
}
}
/*
- * ROSE release 002
+ * ROSE release 003
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
*
* History
* ROSE 001 Jonathan(G4KLX) Cloned from nr_out.c
+ * ROSE 003 Jonathan(G4KLX) New timer architecture.
*/
#include <linux/config.h>
skb_queue_tail(&sk->write_queue, skb); /* Throw it on the queue */
}
- if (sk->protinfo.rose->state == ROSE_STATE_3)
- rose_kick(sk);
+ rose_kick(sk);
}
/*
skb->data[2] |= (sk->protinfo.rose->vr << 5) & 0xE0;
skb->data[2] |= (sk->protinfo.rose->vs << 1) & 0x0E;
+ rose_start_idletimer(sk);
+
rose_transmit_link(skb, sk->protinfo.rose->neighbour);
}
struct sk_buff *skb;
unsigned short end;
- del_timer(&sk->timer);
+ if (sk->protinfo.rose->state != ROSE_STATE_3)
+ return;
+
+ if (sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY)
+ return;
+
+ if (skb_peek(&sk->write_queue) == NULL)
+ return;
end = (sk->protinfo.rose->va + sysctl_rose_window_size) % ROSE_MODULUS;
- if (!(sk->protinfo.rose->condition & ROSE_COND_PEER_RX_BUSY) &&
- sk->protinfo.rose->vs != end &&
- skb_peek(&sk->write_queue) != NULL) {
- /*
- * Transmit data until either we're out of data to send or
- * the window is full.
- */
+ if (sk->protinfo.rose->vs == end)
+ return;
- skb = skb_dequeue(&sk->write_queue);
+ /*
+ * Transmit data until either we're out of data to send or
+ * the window is full.
+ */
- do {
- /*
- * Transmit the frame.
- */
- rose_send_iframe(sk, skb);
+ skb = skb_dequeue(&sk->write_queue);
- sk->protinfo.rose->vs = (sk->protinfo.rose->vs + 1) % ROSE_MODULUS;
+ do {
+ /*
+ * Transmit the frame.
+ */
+ rose_send_iframe(sk, skb);
- } while (sk->protinfo.rose->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL);
+ sk->protinfo.rose->vs = (sk->protinfo.rose->vs + 1) % ROSE_MODULUS;
- sk->protinfo.rose->vl = sk->protinfo.rose->vr;
- sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING;
- sk->protinfo.rose->timer = 0;
- }
+ } while (sk->protinfo.rose->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL);
- rose_set_timer(sk);
+ sk->protinfo.rose->vl = sk->protinfo.rose->vr;
+ sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING;
+
+ rose_stop_timer(sk);
}
/*
sk->protinfo.rose->vl = sk->protinfo.rose->vr;
sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING;
- sk->protinfo.rose->timer = 0;
+
+ rose_stop_timer(sk);
}
void rose_check_iframes_acked(struct sock *sk, unsigned short nr)
/*
- * ROSE release 002
+ * ROSE release 003
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
* address masks.
* ROSE 002 Jonathan(G4KLX) Uprated through routing of packets.
* Routing loop detection.
+ * ROSE 003 Jonathan(G4KLX) New timer architecture.
+ * Added use count to neighbours.
*/
#include <linux/config.h>
rose_neigh->callsign = rose_route->neighbour;
rose_neigh->digipeat = NULL;
+ rose_neigh->ax25 = NULL;
rose_neigh->dev = dev;
rose_neigh->count = 0;
+ rose_neigh->use = 0;
rose_neigh->dce_mode = 0;
rose_neigh->number = rose_neigh_no++;
rose_neigh->restarted = 0;
+
skb_queue_head_init(&rose_neigh->queue);
- rose_neigh->t0timer = 0;
- rose_neigh->ftimer = 0;
- init_timer(&rose_neigh->timer);
+
+ init_timer(&rose_neigh->ftimer);
+ init_timer(&rose_neigh->t0timer);
if (rose_route->ndigis != 0) {
if ((rose_neigh->digipeat = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) {
kfree(rose_neigh);
return -ENOMEM;
}
- rose_neigh->digipeat->ndigi = rose_route->ndigis;
- for (i = 0; i < rose_route->ndigis; i++)
- rose_neigh->digipeat->calls[i] = rose_route->digipeaters[i];
+
+ rose_neigh->digipeat->ndigi = rose_route->ndigis;
+ rose_neigh->digipeat->lastrepeat = -1;
+
+ for (i = 0; i < rose_route->ndigis; i++) {
+ rose_neigh->digipeat->calls[i] = rose_route->digipeaters[i];
+ rose_neigh->digipeat->repeated[i] = 0;
+ }
}
save_flags(flags); cli();
unsigned long flags;
struct sk_buff *skb;
- del_timer(&rose_neigh->timer);
+ rose_stop_ftimer(rose_neigh);
+ rose_stop_t0timer(rose_neigh);
while ((skb = skb_dequeue(&rose_neigh->queue)) != NULL)
kfree_skb(skb, FREE_WRITE);
- save_flags(flags);
- cli();
+ save_flags(flags); cli();
if ((s = rose_neigh_list) == rose_neigh) {
rose_neigh_list = rose_neigh->next;
{
struct rose_route *s;
unsigned long flags;
-
- save_flags(flags);
- cli();
+
+ if (rose_route->neigh1 != NULL)
+ rose_route->neigh1->use--;
+
+ if (rose_route->neigh2 != NULL)
+ rose_route->neigh2->use--;
+
+ save_flags(flags); cli();
if ((s = rose_route_list) == rose_route) {
rose_route_list = rose_route->next;
if (rose_node->neighbour[i] == rose_neigh) {
rose_neigh->count--;
- if (rose_neigh->count == 0)
+ if (rose_neigh->count == 0 && rose_neigh->use == 0)
rose_remove_neigh(rose_neigh);
rose_node->count--;
}
}
+/*
+ * Clear all nodes and neighbours out, except for neighbours with
+ * active connections going through them.
+ */
+static int rose_clear_routes(void)
+{
+ struct rose_neigh *s, *rose_neigh = rose_neigh_list;
+ struct rose_node *t, *rose_node = rose_node_list;
+
+ while (rose_node != NULL) {
+ t = rose_node;
+ rose_node = rose_node->next;
+
+ rose_remove_node(t);
+ }
+
+ while (rose_neigh != NULL) {
+ s = rose_neigh;
+ rose_neigh = rose_neigh->next;
+
+ s->count = 0;
+
+ if (s->use == 0)
+ rose_remove_neigh(s);
+ }
+
+ return 0;
+}
+
/*
* Check that the device given is a valid AX.25 interface that is "up".
*/
/*
* Find a neighbour given a ROSE address.
*/
-struct rose_neigh *rose_get_neigh(rose_address *addr)
+struct rose_neigh *rose_get_neigh(rose_address *addr, unsigned char *cause, unsigned char *diagnostic)
{
struct rose_node *node;
+ int failed = 0;
int i;
for (node = rose_node_list; node != NULL; node = node->next) {
if (rosecmpm(addr, &node->address, node->mask) == 0) {
for (i = 0; i < node->count; i++) {
- if (node->neighbour[i]->ftimer == 0)
+ if (!rose_ftimer_running(node->neighbour[i]))
return node->neighbour[i];
+ else
+ failed = 1;
}
}
}
+ if (failed) {
+ *cause = ROSE_OUT_OF_ORDER;
+ *diagnostic = 0;
+ } else {
+ *cause = ROSE_NOT_OBTAINABLE;
+ *diagnostic = 0;
+ }
+
return NULL;
}
{
struct rose_route_struct rose_route;
struct device *dev;
- int err;
switch (cmd) {
case SIOCADDRT:
- if ((err = verify_area(VERIFY_READ, arg, sizeof(struct rose_route_struct))) != 0)
- return err;
- copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct));
+ if (copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct)))
+ return -EFAULT;
if ((dev = rose_ax25_dev_get(rose_route.device)) == NULL)
return -EINVAL;
if (rose_dev_get(&rose_route.address) != NULL) /* Can't add routes to ourself */
return rose_add_node(&rose_route, dev);
case SIOCDELRT:
- if ((err = verify_area(VERIFY_READ, arg, sizeof(struct rose_route_struct))) != 0)
- return err;
- copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct));
+ if (copy_from_user(&rose_route, arg, sizeof(struct rose_route_struct)))
+ return -EFAULT;
if ((dev = rose_ax25_dev_get(rose_route.device)) == NULL)
return -EINVAL;
return rose_del_node(&rose_route, dev);
+ case SIOCRSCLRRT:
+ return rose_clear_routes();
+
default:
return -EINVAL;
}
struct sk_buff *skb;
rose_neigh->restarted = 0;
- rose_neigh->t0timer = 0;
- rose_neigh->ftimer = sysctl_rose_link_fail_timeout;
- rose_link_set_timer(rose_neigh);
+ rose_stop_t0timer(rose_neigh);
+ rose_start_ftimer(rose_neigh);
while ((skb = skb_dequeue(&rose_neigh->queue)) != NULL)
kfree_skb(skb, FREE_WRITE);
}
if (rose_route->neigh1 == rose_neigh) {
+ rose_route->neigh1->use--;
rose_route->neigh1 = NULL;
- rose_transmit_clear_request(rose_route->neigh2, rose_route->lci2, 0x0D);
+ rose_transmit_clear_request(rose_route->neigh2, rose_route->lci2, ROSE_OUT_OF_ORDER, 0);
}
if (rose_route->neigh2 == rose_neigh) {
+ rose_route->neigh2->use--;
rose_route->neigh2 = NULL;
- rose_transmit_clear_request(rose_route->neigh1, rose_route->lci1, 0x0D);
+ rose_transmit_clear_request(rose_route->neigh1, rose_route->lci1, ROSE_OUT_OF_ORDER, 0);
}
rose_route = rose_route->next;
* then don't use that neighbour until it is reset. Blow away all through
* routes and connections using this route.
*/
-void rose_link_failed(ax25_address *callsign, struct device *dev)
+void rose_link_failed(ax25_cb *ax25, int reason)
{
struct rose_neigh *rose_neigh;
for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next)
- if (ax25cmp(&rose_neigh->callsign, callsign) == 0 && rose_neigh->dev == dev)
+ if (rose_neigh->ax25 == ax25)
break;
if (rose_neigh == NULL) return;
+ rose_neigh->ax25 = NULL;
+
rose_del_route_by_neigh(rose_neigh);
rose_kill_by_neigh(rose_neigh);
}
struct sock *sk;
unsigned short frametype;
unsigned int lci, new_lci;
+ unsigned char cause, diagnostic;
struct device *dev;
unsigned long flags;
/*
* Obviously the link is working, halt the ftimer.
*/
- rose_neigh->ftimer = 0;
+ rose_stop_ftimer(rose_neigh);
/*
* LCI of zero is always for us, and its always a restart
return rose_rx_call_request(skb, dev, rose_neigh, lci);
if (!sysctl_rose_routing_control) {
- rose_transmit_clear_request(rose_neigh, lci, 0x0D);
+ rose_transmit_clear_request(rose_neigh, lci, ROSE_NOT_OBTAINABLE, 0);
return 0;
}
if (frametype != ROSE_CALL_REQUEST) /* XXX */
return 0;
- rose_parse_facilities(skb, &facilities);
+ if (!rose_parse_facilities(skb, &facilities)) {
+ rose_transmit_clear_request(rose_neigh, lci, ROSE_INVALID_FACILITY, 76);
+ return 0;
+ }
/*
* Check for routing loops.
ax25cmp(&facilities.source_call, &rose_route->dest_call) == 0) {
printk(KERN_DEBUG "ROSE: routing loop from %s\n", rose2asc(src_addr));
printk(KERN_DEBUG "ROSE: to %s\n", rose2asc(dest_addr));
- rose_transmit_clear_request(rose_neigh, lci, 0x0D);
+ rose_transmit_clear_request(rose_neigh, lci, ROSE_NOT_OBTAINABLE, 120);
return 0;
}
}
- if ((new_neigh = rose_get_neigh(dest_addr)) == NULL) {
- printk(KERN_DEBUG "ROSE: no route to %s\n", rose2asc(dest_addr));
- rose_transmit_clear_request(rose_neigh, lci, 0x0D);
+ if ((new_neigh = rose_get_neigh(dest_addr, &cause, &diagnostic)) == NULL) {
+ if (cause == ROSE_NOT_OBTAINABLE)
+ printk(KERN_DEBUG "ROSE: no route to %s\n", rose2asc(dest_addr));
+ rose_transmit_clear_request(rose_neigh, lci, cause, diagnostic);
return 0;
}
if ((new_lci = rose_new_lci(new_neigh)) == 0) {
- printk(KERN_DEBUG "ROSE: no spare VCs to %s\n", rose2asc(dest_addr));
- rose_transmit_clear_request(rose_neigh, lci, 0x0D);
+ rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 71);
return 0;
}
if ((rose_route = kmalloc(sizeof(*rose_route), GFP_ATOMIC)) == NULL) {
- rose_transmit_clear_request(rose_neigh, lci, 0x0D);
+ rose_transmit_clear_request(rose_neigh, lci, ROSE_NETWORK_CONGESTION, 120);
return 0;
}
rose_route->lci2 = new_lci;
rose_route->neigh2 = new_neigh;
+ rose_route->neigh1->use++;
+ rose_route->neigh2->use++;
+
save_flags(flags); cli();
rose_route->next = rose_route_list;
rose_route_list = rose_route;
int len = 0;
off_t pos = 0;
off_t begin = 0;
+ int i;
cli();
- len += sprintf(buffer, "addr callsign dev count mode restart t0 tf\n");
+ len += sprintf(buffer, "addr callsign dev count use mode restart t0 tf digipeaters\n");
for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) {
- len += sprintf(buffer + len, "%05d %-9s %-4s %3d %3s %3s %3d %3d\n",
+ len += sprintf(buffer + len, "%05d %-9s %-4s %3d %3d %3s %3s %3lu %3lu",
rose_neigh->number,
ax2asc(&rose_neigh->callsign),
rose_neigh->dev ? rose_neigh->dev->name : "???",
rose_neigh->count,
+ rose_neigh->use,
(rose_neigh->dce_mode) ? "DCE" : "DTE",
(rose_neigh->restarted) ? "yes" : "no",
- rose_neigh->t0timer / ROSE_SLOWHZ,
- rose_neigh->ftimer / ROSE_SLOWHZ);
+ ax25_display_timer(&rose_neigh->t0timer) / HZ,
+ ax25_display_timer(&rose_neigh->ftimer) / HZ);
+
+ if (rose_neigh->digipeat != NULL) {
+ for (i = 0; i < rose_neigh->digipeat->ndigi; i++)
+ len += sprintf(buffer + len, " %s", ax2asc(&rose_neigh->digipeat->calls[i]));
+ }
+
+ len += sprintf(buffer + len, "\n");
pos = begin + len;
/*
- * ROSE release 002
+ * ROSE release 003
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
*
* History
* ROSE 001 Jonathan(G4KLX) Cloned from nr_subr.c
+ * ROSE 002 Jonathan(G4KLX) Centralised disconnect processing.
+ * ROSE 003 Jonathan(G4KLX) Added use count to neighbours.
*/
#include <linux/config.h>
case ROSE_CALL_ACCEPTED:
case ROSE_CLEAR_REQUEST:
case ROSE_RESET_REQUEST:
- case ROSE_DIAGNOSTIC:
len += 2;
break;
- case ROSE_INTERRUPT:
- len += 1;
- break;
}
if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
break;
case ROSE_CLEAR_REQUEST:
- case ROSE_RESET_REQUEST:
*dptr++ = ROSE_GFI | lci1;
*dptr++ = lci2;
*dptr++ = frametype;
- *dptr++ = 0x00; /* XXX */
- *dptr++ = 0x00; /* XXX */
+ *dptr++ = sk->protinfo.rose->cause;
+ *dptr++ = sk->protinfo.rose->diagnostic;
break;
- case ROSE_INTERRUPT:
+ case ROSE_RESET_REQUEST:
*dptr++ = ROSE_GFI | lci1;
*dptr++ = lci2;
*dptr++ = frametype;
- *dptr++ = 0x00; /* XXX */
+ *dptr++ = ROSE_DTE_ORIGINATED;
+ *dptr++ = 0;
break;
case ROSE_RR:
case ROSE_RNR:
- case ROSE_REJ:
*dptr++ = ROSE_GFI | lci1;
*dptr++ = lci2;
*dptr = frametype;
break;
case ROSE_CLEAR_CONFIRMATION:
- case ROSE_INTERRUPT_CONFIRMATION:
case ROSE_RESET_CONFIRMATION:
*dptr++ = ROSE_GFI | lci1;
*dptr++ = lci2;
case ROSE_CALL_ACCEPTED:
case ROSE_CLEAR_REQUEST:
case ROSE_CLEAR_CONFIRMATION:
- case ROSE_INTERRUPT:
- case ROSE_INTERRUPT_CONFIRMATION:
case ROSE_RESET_REQUEST:
case ROSE_RESET_CONFIRMATION:
- case ROSE_RESTART_REQUEST:
- case ROSE_RESTART_CONFIRMATION:
- case ROSE_REGISTRATION_REQUEST:
- case ROSE_REGISTRATION_CONFIRMATION:
- case ROSE_DIAGNOSTIC:
return frame[2];
default:
break;
}
if ((frame[2] & 0x1F) == ROSE_RR ||
- (frame[2] & 0x1F) == ROSE_RNR ||
- (frame[2] & 0x1F) == ROSE_REJ) {
+ (frame[2] & 0x1F) == ROSE_RNR) {
*nr = (frame[2] >> 5) & 0x07;
return frame[2] & 0x1F;
}
return len;
}
+void rose_disconnect(struct sock *sk, int reason, int cause, int diagnostic)
+{
+ rose_stop_timer(sk);
+ rose_stop_idletimer(sk);
+
+ rose_clear_queues(sk);
+
+ sk->protinfo.rose->lci = 0;
+ sk->protinfo.rose->state = ROSE_STATE_0;
+
+ if (cause != -1)
+ sk->protinfo.rose->cause = cause;
+
+ if (diagnostic != -1)
+ sk->protinfo.rose->diagnostic = diagnostic;
+
+ sk->state = TCP_CLOSE;
+ sk->err = reason;
+ sk->shutdown |= SEND_SHUTDOWN;
+
+ if (!sk->dead)
+ sk->state_change(sk);
+
+ sk->dead = 1;
+}
+
#endif
/*
- * ROSE release 002
+ * ROSE release 003
*
* This code REQUIRES 2.1.15 or higher/ NET3.038
*
*
* History
* ROSE 001 Jonathan(G4KLX) Cloned from nr_timer.c
+ * ROSE 003 Jonathan(G4KLX) New timer architecture.
+ * Implemented idle timer.
*/
#include <linux/config.h>
#include <linux/interrupt.h>
#include <net/rose.h>
-static void rose_timer(unsigned long);
+static void rose_heartbeat_expiry(unsigned long);
+static void rose_timer_expiry(unsigned long);
+static void rose_idletimer_expiry(unsigned long);
-/*
- * Linux set timer
- */
-void rose_set_timer(struct sock *sk)
+void rose_start_heartbeat(struct sock *sk)
{
- unsigned long flags;
-
- save_flags(flags); cli();
del_timer(&sk->timer);
- restore_flags(flags);
sk->timer.data = (unsigned long)sk;
- sk->timer.function = &rose_timer;
- sk->timer.expires = jiffies + (HZ / 10);
+ sk->timer.function = &rose_heartbeat_expiry;
+ sk->timer.expires = jiffies + 5 * HZ;
add_timer(&sk->timer);
}
-/*
- * ROSE Timer
- *
- * This routine is called every 100ms. Decrement timer by this
- * amount - if expired then process the event.
- */
-static void rose_timer(unsigned long param)
+void rose_start_t1timer(struct sock *sk)
+{
+ del_timer(&sk->protinfo.rose->timer);
+
+ sk->protinfo.rose->timer.data = (unsigned long)sk;
+ sk->protinfo.rose->timer.function = &rose_timer_expiry;
+ sk->protinfo.rose->timer.expires = jiffies + sk->protinfo.rose->t1;
+
+ add_timer(&sk->protinfo.rose->timer);
+}
+
+void rose_start_t2timer(struct sock *sk)
+{
+ del_timer(&sk->protinfo.rose->timer);
+
+ sk->protinfo.rose->timer.data = (unsigned long)sk;
+ sk->protinfo.rose->timer.function = &rose_timer_expiry;
+ sk->protinfo.rose->timer.expires = jiffies + sk->protinfo.rose->t2;
+
+ add_timer(&sk->protinfo.rose->timer);
+}
+
+void rose_start_t3timer(struct sock *sk)
+{
+ del_timer(&sk->protinfo.rose->timer);
+
+ sk->protinfo.rose->timer.data = (unsigned long)sk;
+ sk->protinfo.rose->timer.function = &rose_timer_expiry;
+ sk->protinfo.rose->timer.expires = jiffies + sk->protinfo.rose->t3;
+
+ add_timer(&sk->protinfo.rose->timer);
+}
+
+void rose_start_hbtimer(struct sock *sk)
+{
+ del_timer(&sk->protinfo.rose->timer);
+
+ sk->protinfo.rose->timer.data = (unsigned long)sk;
+ sk->protinfo.rose->timer.function = &rose_timer_expiry;
+ sk->protinfo.rose->timer.expires = jiffies + sk->protinfo.rose->hb;
+
+ add_timer(&sk->protinfo.rose->timer);
+}
+
+void rose_start_idletimer(struct sock *sk)
+{
+ del_timer(&sk->protinfo.rose->idletimer);
+
+ if (sk->protinfo.rose->idle > 0) {
+ sk->protinfo.rose->idletimer.data = (unsigned long)sk;
+ sk->protinfo.rose->idletimer.function = &rose_idletimer_expiry;
+ sk->protinfo.rose->idletimer.expires = jiffies + sk->protinfo.rose->idle;
+
+ add_timer(&sk->protinfo.rose->idletimer);
+ }
+}
+
+void rose_stop_heartbeat(struct sock *sk)
+{
+ del_timer(&sk->timer);
+}
+
+void rose_stop_timer(struct sock *sk)
+{
+ del_timer(&sk->protinfo.rose->timer);
+}
+
+void rose_stop_idletimer(struct sock *sk)
+{
+ del_timer(&sk->protinfo.rose->idletimer);
+}
+
+static void rose_heartbeat_expiry(unsigned long param)
{
struct sock *sk = (struct sock *)param;
switch (sk->protinfo.rose->state) {
+
case ROSE_STATE_0:
/* Magic here: If we listen() and a new link dies before it
is accepted() it isn't 'dead' so doesn't get removed. */
if (sk->destroy || (sk->state == TCP_LISTEN && sk->dead)) {
- del_timer(&sk->timer);
rose_destroy_socket(sk);
return;
}
sk->protinfo.rose->condition &= ~ROSE_COND_OWN_RX_BUSY;
sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING;
sk->protinfo.rose->vl = sk->protinfo.rose->vr;
- sk->protinfo.rose->timer = 0;
rose_write_internal(sk, ROSE_RR);
+ rose_stop_timer(sk); /* HB */
break;
}
- /*
- * Check for frames to transmit.
- */
- rose_kick(sk);
- break;
-
- default:
break;
}
- if (sk->protinfo.rose->timer == 0 || --sk->protinfo.rose->timer > 0) {
- rose_set_timer(sk);
- return;
- }
+ rose_start_heartbeat(sk);
+}
+
+static void rose_timer_expiry(unsigned long param)
+{
+ struct sock *sk = (struct sock *)param;
- /*
- * Timer has expired, it may have been T1, T2, T3 or HB. We can tell
- * by the socket state.
- */
switch (sk->protinfo.rose->state) {
- case ROSE_STATE_3: /* HB */
- if (sk->protinfo.rose->condition & ROSE_COND_ACK_PENDING) {
- sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING;
- rose_enquiry_response(sk);
- }
- break;
case ROSE_STATE_1: /* T1 */
case ROSE_STATE_4: /* T2 */
rose_write_internal(sk, ROSE_CLEAR_REQUEST);
sk->protinfo.rose->state = ROSE_STATE_2;
- sk->protinfo.rose->timer = sk->protinfo.rose->t3;
+ rose_start_t3timer(sk);
break;
case ROSE_STATE_2: /* T3 */
- rose_clear_queues(sk);
- sk->protinfo.rose->state = ROSE_STATE_0;
- sk->state = TCP_CLOSE;
- sk->err = ETIMEDOUT;
- sk->shutdown |= SEND_SHUTDOWN;
- if (!sk->dead)
- sk->state_change(sk);
- sk->dead = 1;
+ sk->protinfo.rose->neighbour->use--;
+ rose_disconnect(sk, ETIMEDOUT, -1, -1);
+ break;
+
+ case ROSE_STATE_3: /* HB */
+ if (sk->protinfo.rose->condition & ROSE_COND_ACK_PENDING) {
+ sk->protinfo.rose->condition &= ~ROSE_COND_ACK_PENDING;
+ rose_enquiry_response(sk);
+ }
break;
}
+}
+
+static void rose_idletimer_expiry(unsigned long param)
+{
+ struct sock *sk = (struct sock *)param;
+
+ rose_clear_queues(sk);
+
+ rose_write_internal(sk, ROSE_CLEAR_REQUEST);
+ sk->protinfo.rose->state = ROSE_STATE_2;
+
+ rose_start_t3timer(sk);
+
+ sk->state = TCP_CLOSE;
+ sk->err = 0;
+ sk->shutdown |= SEND_SHUTDOWN;
+
+ if (!sk->dead)
+ sk->state_change(sk);
- rose_set_timer(sk);
+ sk->dead = 1;
}
#endif
#include <net/ax25.h>
#include <net/rose.h>
-static int min_timer[] = {1 * ROSE_SLOWHZ};
-static int max_timer[] = {300 * ROSE_SLOWHZ};
-static int min_idle[] = {0 * ROSE_SLOWHZ};
-static int max_idle[] = {65535 * ROSE_SLOWHZ};
+static int min_timer[] = {1 * HZ};
+static int max_timer[] = {300 * HZ};
+static int min_idle[] = {0 * HZ};
+static int max_idle[] = {65535 * HZ};
static int min_route[] = {0}, max_route[] = {1};
-static int min_ftimer[] = {60 * ROSE_SLOWHZ};
-static int max_ftimer[] = {600 * ROSE_SLOWHZ};
+static int min_ftimer[] = {60 * HZ};
+static int max_ftimer[] = {600 * HZ};
static int min_maxvcs[] = {1}, max_maxvcs[] = {254};
static int min_window[] = {1}, max_window[] = {7};
#include <linux/socket.h>
#include <linux/fcntl.h>
#include <linux/file.h>
+#include <linux/dalloc.h>
#include <linux/net.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
inode = get_empty_inode();
if (!inode)
return NULL;
+
sock = socki_lookup(inode);
inode->i_mode = S_IFSOCK;
#include <linux/mm.h>
#include <linux/sysctl.h>
+#include <linux/config.h>
+
+#ifdef CONFIG_SYSCTL
extern int sysctl_unix_destroy_delay;
extern int sysctl_unix_delete_delay;
&proc_dointvec_jiffies},
{0}
};
+#endif
/*
- * X.25 Packet Layer release 001
+ * X.25 Packet Layer release 002
*
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
* releases, misbehave and/or generally screw up. It might even work.
*
* History
* X.25 001 Jonathan Naylor Started coding.
+ * X.25 002 Jonathan Naylor Centralised disconnect handling.
+ * New timer architecture.
*/
#include <linux/config.h>
int sysctl_x25_clear_request_timeout = X25_DEFAULT_T23;
int sysctl_x25_ack_holdback_timeout = X25_DEFAULT_T2;
-static unsigned int lci = 1;
-
static struct sock *volatile x25_list = NULL;
static struct proto_ops x25_proto_ops;
{
struct sock *s;
- for (s = x25_list; s != NULL; s = s->next) {
- if (s->protinfo.x25->neighbour->dev == dev) {
- s->protinfo.x25->state = X25_STATE_0;
- s->state = TCP_CLOSE;
- s->err = ENETUNREACH;
- s->shutdown |= SEND_SHUTDOWN;
- s->state_change(s);
- s->dead = 1;
- }
- }
+ for (s = x25_list; s != NULL; s = s->next)
+ if (s->protinfo.x25->neighbour->dev == dev)
+ x25_disconnect(s, ENETUNREACH, 0, 0);
}
/*
}
/*
- * Find a connected X.25 socket given my LCI.
+ * Find a connected X.25 socket given my LCI and neighbour.
*/
-struct sock *x25_find_socket(unsigned int lci)
+struct sock *x25_find_socket(unsigned int lci, struct x25_neigh *neigh)
{
struct sock *s;
unsigned long flags;
cli();
for (s = x25_list; s != NULL; s = s->next) {
- if (s->protinfo.x25->lci == lci) {
+ if (s->protinfo.x25->lci == lci && s->protinfo.x25->neighbour == neigh) {
restore_flags(flags);
return s;
}
/*
* Find a unique LCI for a given device.
*/
-unsigned int x25_new_lci(void)
+unsigned int x25_new_lci(struct x25_neigh *neigh)
{
- lci++;
- if (lci > 4095) lci = 1;
+ unsigned int lci = 1;
- while (x25_find_socket(lci) != NULL) {
+ while (x25_find_socket(lci, neigh) != NULL) {
lci++;
- if (lci > 4095) lci = 1;
+ if (lci == 4096) return 0;
}
return lci;
save_flags(flags);
cli();
- del_timer(&sk->timer);
+ x25_stop_heartbeat(sk);
+ x25_stop_timer(sk);
x25_remove_socket(sk);
x25_clear_queues(sk); /* Flush the queues */
while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
if (skb->sk != sk) { /* A pending connection */
skb->sk->dead = 1; /* Queue the unaccepted socket for death */
- x25_set_timer(skb->sk);
+ x25_start_heartbeat(skb->sk);
skb->sk->protinfo.x25->state = X25_STATE_0;
}
sock_init_data(sock, sk);
+ init_timer(&x25->timer);
+
sock->ops = &x25_proto_ops;
sk->protocol = protocol;
sk->mtu = X25_DEFAULT_PACKET_SIZE; /* X25_PS128 */
x25->qbitincl = osk->protinfo.x25->qbitincl;
+ init_timer(&x25->timer);
+
return sk;
}
switch (sk->protinfo.x25->state) {
case X25_STATE_0:
- sk->state = TCP_CLOSE;
- sk->shutdown |= SEND_SHUTDOWN;
- sk->state_change(sk);
- sk->dead = 1;
- x25_destroy_socket(sk);
- break;
-
case X25_STATE_2:
- sk->protinfo.x25->state = X25_STATE_0;
- sk->state = TCP_CLOSE;
- sk->shutdown |= SEND_SHUTDOWN;
- sk->state_change(sk);
- sk->dead = 1;
+ x25_disconnect(sk, 0, 0, 0);
x25_destroy_socket(sk);
- break;
+ break;
case X25_STATE_1:
case X25_STATE_3:
case X25_STATE_4:
x25_clear_queues(sk);
x25_write_internal(sk, X25_CLEAR_REQUEST);
- sk->protinfo.x25->timer = sk->protinfo.x25->t23;
+ x25_start_t23timer(sk);
sk->protinfo.x25->state = X25_STATE_2;
sk->state = TCP_CLOSE;
sk->shutdown |= SEND_SHUTDOWN;
if ((sk->protinfo.x25->neighbour = x25_get_neigh(dev)) == NULL)
return -ENETUNREACH;
+ if ((sk->protinfo.x25->lci = x25_new_lci(sk->protinfo.x25->neighbour)) == 0)
+ return -ENETUNREACH;
+
if (sk->zapped) /* Must bind first - autobinding does not work */
return -EINVAL;
memset(&sk->protinfo.x25->source_addr, '\0', X25_ADDR_LEN);
sk->protinfo.x25->dest_addr = addr->sx25_addr;
- sk->protinfo.x25->lci = x25_new_lci();
/* Move to connecting socket, start sending Connect Requests */
sock->state = SS_CONNECTING;
sk->state = TCP_SYN_SENT;
sk->protinfo.x25->state = X25_STATE_1;
- sk->protinfo.x25->timer = sk->protinfo.x25->t21;
+
x25_write_internal(sk, X25_CALL_REQUEST);
- x25_set_timer(sk);
+ x25_start_heartbeat(sk);
+ x25_start_t21timer(sk);
/* Now the loop */
if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
skb_queue_head(&sk->receive_queue, skb);
- x25_set_timer(make);
+ x25_start_heartbeat(make);
if (!sk->dead)
sk->data_ready(sk, skb->len);
x25_output(sk, skb);
}
- if (sk->protinfo.x25->state == X25_STATE_3)
- x25_kick(sk);
+ x25_kick(sk);
return len;
}
static int x25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
- struct x25_facilities facilities;
- struct x25_calluserdata calluserdata;
struct sock *sk = sock->sk;
- int err;
- long amount = 0;
switch (cmd) {
- case TIOCOUTQ:
- if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned long))) != 0)
- return err;
+ case TIOCOUTQ: {
+ long amount;
amount = sk->sndbuf - atomic_read(&sk->wmem_alloc);
if (amount < 0)
amount = 0;
- put_user(amount, (unsigned long *)arg);
+ if (put_user(amount, (unsigned long *)arg))
+ return -EFAULT;
return 0;
+ }
case TIOCINQ: {
struct sk_buff *skb;
+ long amount = 0L;
/* These two are safe on a single CPU system as only user tasks fiddle here */
if ((skb = skb_peek(&sk->receive_queue)) != NULL)
- amount = skb->len - 20;
- if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(unsigned long))) != 0)
- return err;
- put_user(amount, (unsigned long *)arg);
+ amount = skb->len;
+ if (put_user(amount, (unsigned long *)arg))
+ return -EFAULT;
return 0;
}
if (sk != NULL) {
if (sk->stamp.tv_sec == 0)
return -ENOENT;
- if ((err = verify_area(VERIFY_WRITE,(void *)arg,sizeof(struct timeval))) != 0)
- return err;
- copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval));
+ if (copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)))
+ return -EFAULT;
return 0;
}
return -EINVAL;
if (!suser()) return -EPERM;
return x25_subscr_ioctl(cmd, (void *)arg);
- case SIOCX25GFACILITIES:
- if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(facilities))) != 0)
- return err;
+ case SIOCX25GFACILITIES: {
+ struct x25_facilities facilities;
facilities = sk->protinfo.x25->facilities;
- copy_to_user((void *)arg, &facilities, sizeof(facilities));
+ if (copy_to_user((void *)arg, &facilities, sizeof(facilities)))
+ return -EFAULT;
return 0;
+ }
- case SIOCX25SFACILITIES:
- if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(facilities))) != 0)
- return err;
- copy_from_user(&facilities, (void *)arg, sizeof(facilities));
+ case SIOCX25SFACILITIES: {
+ struct x25_facilities facilities;
+ if (copy_from_user(&facilities, (void *)arg, sizeof(facilities)))
+ return -EFAULT;
if (sk->state != TCP_LISTEN)
return -EINVAL;
if (facilities.pacsize_in < X25_PS16 || facilities.pacsize_in > X25_PS4096)
return -EINVAL;
sk->protinfo.x25->facilities = facilities;
return 0;
+ }
- case SIOCX25GCALLUSERDATA:
- if ((err = verify_area(VERIFY_WRITE, (void *)arg, sizeof(calluserdata))) != 0)
- return err;
+ case SIOCX25GCALLUSERDATA: {
+ struct x25_calluserdata calluserdata;
calluserdata = sk->protinfo.x25->calluserdata;
- copy_to_user((void *)arg, &calluserdata, sizeof(calluserdata));
+ if (copy_to_user((void *)arg, &calluserdata, sizeof(calluserdata)))
+ return -EFAULT;
return 0;
+ }
- case SIOCX25SCALLUSERDATA:
- if ((err = verify_area(VERIFY_READ, (void *)arg, sizeof(calluserdata))) != 0)
- return err;
- copy_from_user(&calluserdata, (void *)arg, sizeof(calluserdata));
+ case SIOCX25SCALLUSERDATA: {
+ struct x25_calluserdata calluserdata;
+ if (copy_from_user(&calluserdata, (void *)arg, sizeof(calluserdata)))
+ return -EFAULT;
if (calluserdata.cudlength > X25_MAX_CUD_LEN)
return -EINVAL;
sk->protinfo.x25->calluserdata = calluserdata;
return 0;
+ }
+
+ case SIOCX25GCAUSEDIAG: {
+ struct x25_causediag causediag;
+ causediag = sk->protinfo.x25->causediag;
+ if (copy_to_user((void *)arg, &causediag, sizeof(causediag)))
+ return -EFAULT;
+ return 0;
+ }
default:
return dev_ioctl(cmd, (void *)arg);
else
devname = s->protinfo.x25->neighbour->dev->name;
- len += sprintf(buffer + len, "%-10s %-10s %-5s %3.3X %d %d %d %d %3d %3d %3d %3d %3d %5d %5d\n",
+ len += sprintf(buffer + len, "%-10s %-10s %-5s %3.3X %d %d %d %d %3lu %3lu %3lu %3lu %3lu %5d %5d\n",
(s->protinfo.x25->dest_addr.x25_addr[0] == '\0') ? "*" : s->protinfo.x25->dest_addr.x25_addr,
(s->protinfo.x25->source_addr.x25_addr[0] == '\0') ? "*" : s->protinfo.x25->source_addr.x25_addr,
- devname, s->protinfo.x25->lci & 0x0FFF,
+ devname,
+ s->protinfo.x25->lci & 0x0FFF,
s->protinfo.x25->state,
- s->protinfo.x25->vs, s->protinfo.x25->vr, s->protinfo.x25->va,
- s->protinfo.x25->timer / X25_SLOWHZ,
- s->protinfo.x25->t2 / X25_SLOWHZ,
- s->protinfo.x25->t21 / X25_SLOWHZ,
- s->protinfo.x25->t22 / X25_SLOWHZ,
- s->protinfo.x25->t23 / X25_SLOWHZ,
- atomic_read(&s->wmem_alloc), atomic_read(&s->rmem_alloc));
+ s->protinfo.x25->vs,
+ s->protinfo.x25->vr,
+ s->protinfo.x25->va,
+ x25_display_timer(s) / HZ,
+ s->protinfo.x25->t2 / HZ,
+ s->protinfo.x25->t21 / HZ,
+ s->protinfo.x25->t22 / HZ,
+ s->protinfo.x25->t23 / HZ,
+ atomic_read(&s->wmem_alloc),
+ atomic_read(&s->rmem_alloc));
pos = begin + len;
register_netdevice_notifier(&x25_dev_notifier);
- printk(KERN_INFO "X.25 for Linux. Version 0.1 for Linux 2.1.15\n");
+ printk(KERN_INFO "X.25 for Linux. Version 0.2 for Linux 2.1.15\n");
#ifdef CONFIG_SYSCTL
x25_register_sysctl();
#include <linux/init.h>
#include <net/x25.h>
-static int min_timer[] = {1 * X25_SLOWHZ};
-static int max_timer[] = {300 * X25_SLOWHZ};
+static int min_timer[] = {1 * HZ};
+static int max_timer[] = {300 * HZ};
static struct ctl_table_header *x25_table_header;
/*
- * X.25 Packet Layer release 001
+ * X.25 Packet Layer release 002
*
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
* releases, misbehave and/or generally screw up. It might even work.
/*
* Find an existing socket.
*/
- if ((sk = x25_find_socket(lci)) != NULL) {
+ if ((sk = x25_find_socket(lci, neigh)) != NULL) {
skb->h.raw = skb->data;
return x25_process_rx_frame(sk, skb);
}
/*
- * X.25 Packet Layer release 001
+ * X.25 Packet Layer release 002
*
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
* releases, misbehave and/or generally screw up. It might even work.
/*
- * X.25 Packet Layer release 001
+ * X.25 Packet Layer release 002
*
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
* releases, misbehave and/or generally screw up. It might even work.
*
* History
* X.25 001 Jonathan Naylor Started coding.
+ * X.25 002 Jonathan Naylor Centralised disconnection code.
+ * New timer architecture.
*/
#include <linux/config.h>
switch (frametype) {
case X25_CALL_ACCEPTED:
+ x25_stop_timer(sk);
sk->protinfo.x25->condition = 0x00;
- sk->protinfo.x25->timer = 0;
sk->protinfo.x25->vs = 0;
sk->protinfo.x25->va = 0;
sk->protinfo.x25->vr = 0;
break;
case X25_CLEAR_REQUEST:
- x25_clear_queues(sk);
x25_write_internal(sk, X25_CLEAR_CONFIRMATION);
- sk->protinfo.x25->state = X25_STATE_0;
- sk->state = TCP_CLOSE;
- sk->err = ECONNREFUSED;
- sk->shutdown |= SEND_SHUTDOWN;
- if (!sk->dead)
- sk->state_change(sk);
- sk->dead = 1;
+ x25_disconnect(sk, ECONNREFUSED, skb->data[3], skb->data[4]);
break;
default:
case X25_CLEAR_REQUEST:
x25_write_internal(sk, X25_CLEAR_CONFIRMATION);
+ x25_disconnect(sk, 0, skb->data[3], skb->data[4]);
+ break;
+
case X25_CLEAR_CONFIRMATION:
- x25_clear_queues(sk);
- sk->protinfo.x25->state = X25_STATE_0;
- sk->state = TCP_CLOSE;
- sk->err = 0;
- sk->shutdown |= SEND_SHUTDOWN;
- if (!sk->dead)
- sk->state_change(sk);
- sk->dead = 1;
+ x25_disconnect(sk, 0, 0, 0);
break;
default:
case X25_RESET_REQUEST:
x25_write_internal(sk, X25_RESET_CONFIRMATION);
+ x25_stop_timer(sk);
sk->protinfo.x25->condition = 0x00;
- sk->protinfo.x25->timer = 0;
sk->protinfo.x25->vs = 0;
sk->protinfo.x25->vr = 0;
sk->protinfo.x25->va = 0;
break;
case X25_CLEAR_REQUEST:
- x25_clear_queues(sk);
x25_write_internal(sk, X25_CLEAR_CONFIRMATION);
- sk->protinfo.x25->state = X25_STATE_0;
- sk->state = TCP_CLOSE;
- sk->err = 0;
- sk->shutdown |= SEND_SHUTDOWN;
- if (!sk->dead)
- sk->state_change(sk);
- sk->dead = 1;
+ x25_disconnect(sk, 0, skb->data[3], skb->data[4]);
break;
case X25_RR:
if (!x25_validate_nr(sk, nr)) {
x25_clear_queues(sk);
x25_write_internal(sk, X25_RESET_REQUEST);
+ x25_start_t22timer(sk);
sk->protinfo.x25->condition = 0x00;
sk->protinfo.x25->vs = 0;
sk->protinfo.x25->vr = 0;
sk->protinfo.x25->va = 0;
sk->protinfo.x25->vl = 0;
sk->protinfo.x25->state = X25_STATE_4;
- sk->protinfo.x25->timer = sk->protinfo.x25->t22;
} else {
if (sk->protinfo.x25->condition & X25_COND_PEER_RX_BUSY) {
sk->protinfo.x25->va = nr;
if (!x25_validate_nr(sk, nr)) {
x25_clear_queues(sk);
x25_write_internal(sk, X25_RESET_REQUEST);
+ x25_start_t22timer(sk);
sk->protinfo.x25->condition = 0x00;
sk->protinfo.x25->vs = 0;
sk->protinfo.x25->vr = 0;
sk->protinfo.x25->va = 0;
sk->protinfo.x25->vl = 0;
sk->protinfo.x25->state = X25_STATE_4;
- sk->protinfo.x25->timer = sk->protinfo.x25->t22;
break;
}
if (sk->protinfo.x25->condition & X25_COND_PEER_RX_BUSY) {
*/
if (((sk->protinfo.x25->vl + sk->protinfo.x25->facilities.winsize_in) % modulus) == sk->protinfo.x25->vr) {
sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING;
- sk->protinfo.x25->timer = 0;
+ x25_stop_timer(sk);
x25_enquiry_response(sk);
} else {
sk->protinfo.x25->condition |= X25_COND_ACK_PENDING;
- sk->protinfo.x25->timer = sk->protinfo.x25->t2;
+ x25_start_t2timer(sk);
}
break;
case X25_RESET_REQUEST:
x25_write_internal(sk, X25_RESET_CONFIRMATION);
case X25_RESET_CONFIRMATION:
- sk->protinfo.x25->timer = 0;
+ x25_stop_timer(sk);
sk->protinfo.x25->condition = 0x00;
sk->protinfo.x25->va = 0;
sk->protinfo.x25->vr = 0;
break;
case X25_CLEAR_REQUEST:
- x25_clear_queues(sk);
x25_write_internal(sk, X25_CLEAR_CONFIRMATION);
- sk->protinfo.x25->timer = 0;
- sk->protinfo.x25->state = X25_STATE_0;
- sk->state = TCP_CLOSE;
- sk->err = 0;
- sk->shutdown |= SEND_SHUTDOWN;
- if (!sk->dead)
- sk->state_change(sk);
- sk->dead = 1;
+ x25_disconnect(sk, 0, skb->data[3], skb->data[4]);
break;
default:
if (sk->protinfo.x25->state == X25_STATE_0)
return 0;
- del_timer(&sk->timer);
-
frametype = x25_decode(sk, skb, &ns, &nr, &q, &d, &m);
switch (sk->protinfo.x25->state) {
break;
}
- x25_set_timer(sk);
+ x25_kick(sk);
return queued;
}
/*
- * X.25 Packet Layer release 001
+ * X.25 Packet Layer release 002
*
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
* releases, misbehave and/or generally screw up. It might even work.
*
* History
* X.25 001 Jonathan Naylor Started coding.
+ * X.25 002 Jonathan Naylor New timer architecture.
*/
#include <linux/config.h>
static struct x25_neigh *x25_neigh_list = NULL;
-static void x25_link_timer(unsigned long);
+static void x25_t20timer_expiry(unsigned long);
/*
* Linux set/reset timer routines
*/
-static void x25_link_set_timer(struct x25_neigh *neigh)
+static void x25_start_t20timer(struct x25_neigh *neigh)
{
- unsigned long flags;
-
- save_flags(flags); cli();
- del_timer(&neigh->timer);
- restore_flags(flags);
+ del_timer(&neigh->t20timer);
- neigh->timer.data = (unsigned long)neigh;
- neigh->timer.function = &x25_link_timer;
- neigh->timer.expires = jiffies + (HZ / 1);
+ neigh->t20timer.data = (unsigned long)neigh;
+ neigh->t20timer.function = &x25_t20timer_expiry;
+ neigh->t20timer.expires = jiffies + neigh->t20;
- add_timer(&neigh->timer);
+ add_timer(&neigh->t20timer);
}
-/*
- * X.25 Link TIMER
- *
- * This routine is called every second. Decrement timer by this
- * amount - if expired then process the event.
- */
-static void x25_link_timer(unsigned long param)
+static void x25_t20timer_expiry(unsigned long param)
{
struct x25_neigh *neigh = (struct x25_neigh *)param;
- if (neigh->t20timer == 0 || --neigh->t20timer > 0) {
- x25_link_set_timer(neigh);
- return;
- }
-
- /*
- * T20 for a link has expired.
- */
x25_transmit_restart_request(neigh);
- neigh->t20timer = neigh->t20;
+ x25_start_t20timer(neigh);
+}
- x25_link_set_timer(neigh);
+static void x25_stop_t20timer(struct x25_neigh *neigh)
+{
+ del_timer(&neigh->t20timer);
}
/*
switch (frametype) {
case X25_RESTART_REQUEST:
- neigh->t20timer = 0;
- neigh->state = X25_LINK_STATE_3;
- del_timer(&neigh->timer);
+ x25_stop_t20timer(neigh);
+ neigh->state = X25_LINK_STATE_3;
x25_transmit_restart_confirmation(neigh);
break;
case X25_RESTART_CONFIRMATION:
- neigh->t20timer = 0;
- neigh->state = X25_LINK_STATE_3;
- del_timer(&neigh->timer);
+ x25_stop_t20timer(neigh);
+ neigh->state = X25_LINK_STATE_3;
break;
case X25_DIAGNOSTIC:
break;
case X25_LINK_STATE_1:
x25_transmit_restart_request(neigh);
- neigh->state = X25_LINK_STATE_2;
- neigh->t20timer = neigh->t20;
- x25_link_set_timer(neigh);
+ neigh->state = X25_LINK_STATE_2;
+ x25_start_t20timer(neigh);
break;
}
}
return;
skb_queue_head_init(&x25_neigh->queue);
- init_timer(&x25_neigh->timer);
+
+ init_timer(&x25_neigh->t20timer);
x25_neigh->dev = dev;
x25_neigh->state = X25_LINK_STATE_0;
x25_neigh->extended = 0;
- x25_neigh->t20timer = 0;
x25_neigh->t20 = sysctl_x25_restart_request_timeout;
save_flags(flags); cli();
while ((skb = skb_dequeue(&x25_neigh->queue)) != NULL)
kfree_skb(skb, FREE_WRITE);
- del_timer(&x25_neigh->timer);
+ x25_stop_t20timer(x25_neigh);
- save_flags(flags);
- cli();
+ save_flags(flags); cli();
if ((s = x25_neigh_list) == x25_neigh) {
x25_neigh_list = x25_neigh->next;
struct x25_subscrip_struct x25_subscr;
struct x25_neigh *x25_neigh;
struct device *dev;
- int err;
switch (cmd) {
case SIOCX25GSUBSCRIP:
- if ((err = verify_area(VERIFY_WRITE, arg, sizeof(struct x25_subscrip_struct))) != 0)
- return err;
if ((dev = x25_dev_get(x25_subscr.device)) == NULL)
return -EINVAL;
if ((x25_neigh = x25_get_neigh(dev)) == NULL)
return -EINVAL;
x25_subscr.extended = x25_neigh->extended;
- copy_to_user(arg, &x25_subscr, sizeof(struct x25_subscrip_struct));
+ if (copy_to_user(arg, &x25_subscr, sizeof(struct x25_subscrip_struct)))
+ return -EFAULT;
break;
case SIOCX25SSUBSCRIP:
- if ((err = verify_area(VERIFY_READ, arg, sizeof(struct x25_subscrip_struct))) != 0)
- return err;
- copy_from_user(&x25_subscr, arg, sizeof(struct x25_subscrip_struct));
+ if (copy_from_user(&x25_subscr, arg, sizeof(struct x25_subscrip_struct)))
+ return -EFAULT;
if ((dev = x25_dev_get(x25_subscr.device)) == NULL)
return -EINVAL;
if ((x25_neigh = x25_get_neigh(dev)) == NULL)
/*
- * X.25 Packet Layer release 001
+ * X.25 Packet Layer release 002
*
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
* releases, misbehave and/or generally screw up. It might even work.
*
* History
* X.25 001 Jonathan Naylor Started coding.
+ * X.25 002 Jonathan Naylor New timer architecture.
*/
#include <linux/config.h>
unsigned short end;
int modulus;
- del_timer(&sk->timer);
+ if (sk->protinfo.x25->state != X25_STATE_3)
+ return;
/*
* Transmit interrupt data.
x25_transmit_link(skb, sk->protinfo.x25->neighbour);
}
+ if (sk->protinfo.x25->condition & X25_COND_PEER_RX_BUSY)
+ return;
+
+ if (skb_peek(&sk->write_queue) == NULL)
+ return;
+
modulus = (sk->protinfo.x25->neighbour->extended) ? X25_EMODULUS : X25_SMODULUS;
end = (sk->protinfo.x25->va + sk->protinfo.x25->facilities.winsize_out) % modulus;
+ if (sk->protinfo.x25->vs == end)
+ return;
+
/*
- * Transmit normal stream data.
+ * Transmit data until either we're out of data to send or
+ * the window is full.
*/
- if (!(sk->protinfo.x25->condition & X25_COND_PEER_RX_BUSY) &&
- sk->protinfo.x25->vs != end &&
- skb_peek(&sk->write_queue) != NULL) {
- /*
- * Transmit data until either we're out of data to send or
- * the window is full.
- */
- skb = skb_dequeue(&sk->write_queue);
+ skb = skb_dequeue(&sk->write_queue);
- do {
- /*
- * Transmit the frame.
- */
- x25_send_iframe(sk, skb);
+ do {
+ /*
+ * Transmit the frame.
+ */
+ x25_send_iframe(sk, skb);
- sk->protinfo.x25->vs = (sk->protinfo.x25->vs + 1) % modulus;
+ sk->protinfo.x25->vs = (sk->protinfo.x25->vs + 1) % modulus;
- } while (sk->protinfo.x25->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL);
+ } while (sk->protinfo.x25->vs != end && (skb = skb_dequeue(&sk->write_queue)) != NULL);
- sk->protinfo.x25->vl = sk->protinfo.x25->vr;
- sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING;
- sk->protinfo.x25->timer = 0;
- }
+ sk->protinfo.x25->vl = sk->protinfo.x25->vr;
+ sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING;
- x25_set_timer(sk);
+ x25_stop_timer(sk);
}
/*
sk->protinfo.x25->vl = sk->protinfo.x25->vr;
sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING;
- sk->protinfo.x25->timer = 0;
+
+ x25_stop_timer(sk);
}
void x25_check_iframes_acked(struct sock *sk, unsigned short nr)
/*
- * X.25 Packet Layer release 001
+ * X.25 Packet Layer release 002
*
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
* releases, misbehave and/or generally screw up. It might even work.
{
struct x25_route_struct x25_route;
struct device *dev;
- int err;
switch (cmd) {
case SIOCADDRT:
- if ((err = verify_area(VERIFY_READ, arg, sizeof(struct x25_route_struct))) != 0)
- return err;
- copy_from_user(&x25_route, arg, sizeof(struct x25_route_struct));
+ if (copy_from_user(&x25_route, arg, sizeof(struct x25_route_struct)))
+ return -EFAULT;
if (x25_route.sigdigits < 0 || x25_route.sigdigits > 15)
return -EINVAL;
if ((dev = x25_dev_get(x25_route.device)) == NULL)
return x25_add_route(&x25_route.address, x25_route.sigdigits, dev);
case SIOCDELRT:
- if ((err = verify_area(VERIFY_READ, arg, sizeof(struct x25_route_struct))) != 0)
- return err;
- copy_from_user(&x25_route, arg, sizeof(struct x25_route_struct));
+ if (copy_from_user(&x25_route, arg, sizeof(struct x25_route_struct)))
+ return -EFAULT;
if (x25_route.sigdigits < 0 || x25_route.sigdigits > 15)
return -EINVAL;
if ((dev = x25_dev_get(x25_route.device)) == NULL)
for (x25_route = x25_route_list; x25_route != NULL; x25_route = x25_route->next) {
len += sprintf(buffer + len, "%-15s %-6d %-5s\n",
- x25_route->address.x25_addr, x25_route->sigdigits,
+ x25_route->address.x25_addr,
+ x25_route->sigdigits,
(x25_route->dev != NULL) ? x25_route->dev->name : "???");
pos = begin + len;
/*
- * X.25 Packet Layer release 001
+ * X.25 Packet Layer release 002
*
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
* releases, misbehave and/or generally screw up. It might even work.
*
* History
* X.25 001 Jonathan Naylor Started coding.
+ * X.25 002 Jonathan Naylor Centralised disconnection processing.
*/
#include <linux/config.h>
return X25_ILLEGAL;
}
+void x25_disconnect(struct sock *sk, int reason, unsigned char cause, unsigned char diagnostic)
+{
+ x25_clear_queues(sk);
+ x25_stop_timer(sk);
+
+ sk->protinfo.x25->lci = 0;
+ sk->protinfo.x25->state = X25_STATE_0;
+
+ sk->protinfo.x25->causediag.cause = cause;
+ sk->protinfo.x25->causediag.diagnostic = diagnostic;
+
+ sk->state = TCP_CLOSE;
+ sk->err = reason;
+ sk->shutdown |= SEND_SHUTDOWN;
+
+ if (!sk->dead)
+ sk->state_change(sk);
+
+ sk->dead = 1;
+}
+
#endif
/*
- * X.25 Packet Layer release 001
+ * X.25 Packet Layer release 002
*
* This is ALPHA test software. This code may break your machine, randomly fail to work with new
* releases, misbehave and/or generally screw up. It might even work.
*
* History
* X.25 001 Jonathan Naylor Started coding.
+ * X.25 002 Jonathan Naylor New timer architecture.
+ * Centralised disconnection processing.
*/
#include <linux/config.h>
#include <linux/interrupt.h>
#include <net/x25.h>
-static void x25_timer(unsigned long);
+static void x25_heartbeat_expiry(unsigned long);
+static void x25_timer_expiry(unsigned long);
-/*
- * Linux set timer
- */
-void x25_set_timer(struct sock *sk)
+void x25_start_heartbeat(struct sock *sk)
{
- unsigned long flags;
-
- save_flags(flags); cli();
del_timer(&sk->timer);
- restore_flags(flags);
sk->timer.data = (unsigned long)sk;
- sk->timer.function = &x25_timer;
- sk->timer.expires = jiffies + (HZ / 1);
+ sk->timer.function = &x25_heartbeat_expiry;
+ sk->timer.expires = jiffies + 5 * HZ;
add_timer(&sk->timer);
}
-/*
- * X.25 TIMER
- *
- * This routine is called every second. Decrement timer by this
- * amount - if expired then process the event.
- */
-static void x25_timer(unsigned long param)
+void x25_stop_heartbeat(struct sock *sk)
+{
+ del_timer(&sk->timer);
+}
+
+void x25_start_t2timer(struct sock *sk)
+{
+ del_timer(&sk->protinfo.x25->timer);
+
+ sk->protinfo.x25->timer.data = (unsigned long)sk;
+ sk->protinfo.x25->timer.function = &x25_timer_expiry;
+ sk->protinfo.x25->timer.expires = jiffies + sk->protinfo.x25->t2;
+
+ add_timer(&sk->protinfo.x25->timer);
+}
+
+void x25_start_t21timer(struct sock *sk)
+{
+ del_timer(&sk->protinfo.x25->timer);
+
+ sk->protinfo.x25->timer.data = (unsigned long)sk;
+ sk->protinfo.x25->timer.function = &x25_timer_expiry;
+ sk->protinfo.x25->timer.expires = jiffies + sk->protinfo.x25->t21;
+
+ add_timer(&sk->protinfo.x25->timer);
+}
+
+void x25_start_t22timer(struct sock *sk)
+{
+ del_timer(&sk->protinfo.x25->timer);
+
+ sk->protinfo.x25->timer.data = (unsigned long)sk;
+ sk->protinfo.x25->timer.function = &x25_timer_expiry;
+ sk->protinfo.x25->timer.expires = jiffies + sk->protinfo.x25->t22;
+
+ add_timer(&sk->protinfo.x25->timer);
+}
+
+void x25_start_t23timer(struct sock *sk)
+{
+ del_timer(&sk->protinfo.x25->timer);
+
+ sk->protinfo.x25->timer.data = (unsigned long)sk;
+ sk->protinfo.x25->timer.function = &x25_timer_expiry;
+ sk->protinfo.x25->timer.expires = jiffies + sk->protinfo.x25->t23;
+
+ add_timer(&sk->protinfo.x25->timer);
+}
+
+void x25_stop_timer(struct sock *sk)
+{
+ del_timer(&sk->protinfo.x25->timer);
+}
+
+unsigned long x25_display_timer(struct sock *sk)
+{
+ if (sk->protinfo.x25->timer.prev == NULL &&
+ sk->protinfo.x25->timer.next == NULL)
+ return 0;
+
+ return sk->protinfo.x25->timer.expires - jiffies;
+}
+
+static void x25_heartbeat_expiry(unsigned long param)
{
struct sock *sk = (struct sock *)param;
switch (sk->protinfo.x25->state) {
+
case X25_STATE_0:
/* Magic here: If we listen() and a new link dies before it
is accepted() it isn't 'dead' so doesn't get removed. */
if (sk->destroy || (sk->state == TCP_LISTEN && sk->dead)) {
- del_timer(&sk->timer);
x25_destroy_socket(sk);
return;
}
sk->protinfo.x25->condition &= ~X25_COND_OWN_RX_BUSY;
sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING;
sk->protinfo.x25->vl = sk->protinfo.x25->vr;
- sk->protinfo.x25->timer = 0;
x25_write_internal(sk, X25_RR);
+ x25_stop_timer(sk);
break;
}
- /*
- * Check for frames to transmit.
- */
- x25_kick(sk);
- break;
-
- default:
break;
}
- if (sk->protinfo.x25->timer == 0 || --sk->protinfo.x25->timer > 0) {
- x25_set_timer(sk);
- return;
- }
+ x25_start_heartbeat(sk);
+}
+
+/*
+ * Timer has expired, it may have been T2, T21, T22, or T23. We can tell
+ * by the state machine state.
+ */
+static void x25_timer_expiry(unsigned long param)
+{
+ struct sock *sk = (struct sock *)param;
- /*
- * Timer has expired, it may have been T2, T21, T22, or T23. We can tell
- * by the state machine state.
- */
switch (sk->protinfo.x25->state) {
+
case X25_STATE_3: /* T2 */
if (sk->protinfo.x25->condition & X25_COND_ACK_PENDING) {
sk->protinfo.x25->condition &= ~X25_COND_ACK_PENDING;
case X25_STATE_4: /* T22 */
x25_write_internal(sk, X25_CLEAR_REQUEST);
sk->protinfo.x25->state = X25_STATE_2;
- sk->protinfo.x25->timer = sk->protinfo.x25->t23;
+ x25_start_t23timer(sk);
break;
case X25_STATE_2: /* T23 */
- x25_clear_queues(sk);
- sk->protinfo.x25->state = X25_STATE_0;
- sk->state = TCP_CLOSE;
- sk->err = ETIMEDOUT;
- sk->shutdown |= SEND_SHUTDOWN;
- if (!sk->dead)
- sk->state_change(sk);
- sk->dead = 1;
+ x25_disconnect(sk, ETIMEDOUT, 0, 0);
break;
}
-
- x25_set_timer(sk);
}
#endif