Merge tag 'nios2-v3.20-rc1' of git://git.rocketboards.org/linux-socfpga-next
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 17 Feb 2015 22:23:42 +0000 (14:23 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 17 Feb 2015 22:23:42 +0000 (14:23 -0800)
Pull arch/nios2 update from Ley Foon Tan:
 "Here is the nios2 update for 3.20:

   - add early printk support
   - add kgdb support
   - add compressed kernel support
   - bugfixes"

* tag 'nios2-v3.20-rc1' of git://git.rocketboards.org/linux-socfpga-next:
  nios2: add kgdb support
  MAINTAINERS: update arch/nios2 git tree
  nios2: default CONFIG_NIOS2_BOOT_LINK_OFFSET to 8MB
  nios2: Add support for compressed kernel
  nios2: add early printk support
  nios2: Port OOM changes to do_page_fault()
  nios2: Remove unused prepare_to_copy()

21 files changed:
MAINTAINERS
arch/nios2/Kconfig
arch/nios2/Kconfig.debug
arch/nios2/boot/Makefile
arch/nios2/boot/compressed/Makefile [new file with mode: 0644]
arch/nios2/boot/compressed/console.c [new file with mode: 0644]
arch/nios2/boot/compressed/head.S [new file with mode: 0644]
arch/nios2/boot/compressed/misc.c [new file with mode: 0644]
arch/nios2/boot/compressed/vmlinux.lds.S [new file with mode: 0644]
arch/nios2/boot/compressed/vmlinux.scr [new file with mode: 0644]
arch/nios2/configs/3c120_defconfig
arch/nios2/include/asm/kgdb.h [new file with mode: 0644]
arch/nios2/include/asm/processor.h
arch/nios2/include/asm/prom.h [new file with mode: 0644]
arch/nios2/kernel/Makefile
arch/nios2/kernel/early_printk.c [new file with mode: 0644]
arch/nios2/kernel/entry.S
arch/nios2/kernel/kgdb.c [new file with mode: 0644]
arch/nios2/kernel/prom.c
arch/nios2/kernel/setup.c
arch/nios2/mm/fault.c

index 85024e23309ff0e8ffcd9be77a3cfd74c3bb52f9..3eee4c7739aec4675ae08daa1a1fe6d49fccb22d 100644 (file)
@@ -6876,7 +6876,7 @@ F:        drivers/scsi/nsp32*
 NIOS2 ARCHITECTURE
 M:     Ley Foon Tan <lftan@altera.com>
 L:     nios2-dev@lists.rocketboards.org (moderated for non-subscribers)
-T:     git git://git.rocketboards.org/linux-socfpga.git
+T:     git git://git.rocketboards.org/linux-socfpga-next.git
 S:     Maintained
 F:     arch/nios2/
 
index 2361acf6d2b1abb195c3f2daff5c64df88400a94..437555424bda6765c0939be4606c70c2dc7d3c84 100644 (file)
@@ -8,6 +8,7 @@ config NIOS2
        select GENERIC_IRQ_PROBE
        select GENERIC_IRQ_SHOW
        select HAVE_ARCH_TRACEHOOK
+       select HAVE_ARCH_KGDB
        select IRQ_DOMAIN
        select MODULES_USE_ELF_RELA
        select OF
@@ -134,6 +135,14 @@ config NIOS2_PASS_CMDLINE
          will override "Default kernel command string".
          Say N if you are unsure.
 
+config NIOS2_BOOT_LINK_OFFSET
+       hex "Link address offset for booting"
+       default "0x00500000"
+       help
+         This option allows you to set the link address offset of the zImage.
+         This can be useful if you are on a board which has a small amount of
+         memory.
+
 endmenu
 
 menu "Advanced setup"
index 8d4e6bacd997b3f687d1262631830a3c73c1f0f6..2fd08cbfdddb2a724db5685d0b7df86d6fe4d9bc 100644 (file)
@@ -14,4 +14,15 @@ config DEBUG_STACK_USAGE
 
          This option will slow down process creation somewhat.
 
+config EARLY_PRINTK
+       bool "Activate early kernel debugging"
+       default y
+       select SERIAL_CORE_CONSOLE
+       depends on SERIAL_ALTERA_JTAGUART_CONSOLE || SERIAL_ALTERA_UART_CONSOLE
+       help
+         Enable early printk on console
+         This is useful for kernel debugging when your machine crashes very
+         early before the console code is initialized.
+         You should normally say N here, unless you want to debug such a crash.
+
 endmenu
index 59392dc0bdcb3710908497f2ad5a1784017e5098..c899876320df331ce1b897148c9bf05f4f247a0f 100644 (file)
@@ -24,6 +24,13 @@ $(obj)/vmImage: $(obj)/vmlinux.gz
        $(call if_changed,uimage)
        @$(kecho) 'Kernel: $@ is ready'
 
+$(obj)/zImage: $(obj)/compressed/vmlinux FORCE
+       $(call if_changed,objcopy)
+       @$(kecho) 'Kernel: $@ is ready'
+
+$(obj)/compressed/vmlinux: $(obj)/vmlinux.gz FORCE
+       $(Q)$(MAKE) $(build)=$(obj)/compressed $@
+
 # Rule to build device tree blobs
 DTB_SRC := $(patsubst "%",%,$(CONFIG_NIOS2_DTB_SOURCE))
 
diff --git a/arch/nios2/boot/compressed/Makefile b/arch/nios2/boot/compressed/Makefile
new file mode 100644 (file)
index 0000000..5b0fb34
--- /dev/null
@@ -0,0 +1,19 @@
+#
+# create a compressed vmlinux image from the original vmlinux
+#
+
+targets                := vmlinux head.o misc.o piggy.o vmlinux.lds
+asflags-y      :=
+
+OBJECTS = $(obj)/head.o $(obj)/misc.o
+
+LDFLAGS_vmlinux := -T
+
+$(obj)/vmlinux: $(obj)/vmlinux.lds $(OBJECTS) $(obj)/piggy.o FORCE
+       $(call if_changed,ld)
+       @:
+
+LDFLAGS_piggy.o := -r --format binary --oformat elf32-littlenios2 -T
+
+$(obj)/piggy.o: $(obj)/vmlinux.scr $(obj)/../vmlinux.gz FORCE
+       $(call if_changed,ld)
diff --git a/arch/nios2/boot/compressed/console.c b/arch/nios2/boot/compressed/console.c
new file mode 100644 (file)
index 0000000..2675e87
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ *  Copyright (C) 2008-2010 Thomas Chou <thomas@wytron.com.tw>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/io.h>
+
+#if (defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE) && defined(JTAG_UART_BASE))\
+       || (defined(CONFIG_SERIAL_ALTERA_UART_CONSOLE) && defined(UART0_BASE))
+static void *my_ioremap(unsigned long physaddr)
+{
+       return (void *)(physaddr | CONFIG_NIOS2_IO_REGION_BASE);
+}
+#endif
+
+#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE) && defined(JTAG_UART_BASE)
+
+#define ALTERA_JTAGUART_SIZE                           8
+#define ALTERA_JTAGUART_DATA_REG                       0
+#define ALTERA_JTAGUART_CONTROL_REG                    4
+#define ALTERA_JTAGUART_CONTROL_AC_MSK                 (0x00000400)
+#define ALTERA_JTAGUART_CONTROL_WSPACE_MSK             (0xFFFF0000)
+static void *uartbase;
+
+#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS)
+static void jtag_putc(int ch)
+{
+       if (readl(uartbase + ALTERA_JTAGUART_CONTROL_REG) &
+               ALTERA_JTAGUART_CONTROL_WSPACE_MSK)
+               writeb(ch, uartbase + ALTERA_JTAGUART_DATA_REG);
+}
+#else
+static void jtag_putc(int ch)
+{
+       while ((readl(uartbase + ALTERA_JTAGUART_CONTROL_REG) &
+               ALTERA_JTAGUART_CONTROL_WSPACE_MSK) == 0)
+               ;
+       writeb(ch, uartbase + ALTERA_JTAGUART_DATA_REG);
+}
+#endif
+
+static int putchar(int ch)
+{
+       jtag_putc(ch);
+       return ch;
+}
+
+static void console_init(void)
+{
+       uartbase = my_ioremap((unsigned long) JTAG_UART_BASE);
+       writel(ALTERA_JTAGUART_CONTROL_AC_MSK,
+               uartbase + ALTERA_JTAGUART_CONTROL_REG);
+}
+
+#elif defined(CONFIG_SERIAL_ALTERA_UART_CONSOLE) && defined(UART0_BASE)
+
+#define ALTERA_UART_SIZE               32
+#define ALTERA_UART_TXDATA_REG         4
+#define ALTERA_UART_STATUS_REG         8
+#define ALTERA_UART_DIVISOR_REG                16
+#define ALTERA_UART_STATUS_TRDY_MSK    (0x40)
+static unsigned uartbase;
+
+static void uart_putc(int ch)
+{
+       int i;
+
+       for (i = 0; (i < 0x10000); i++) {
+               if (readw(uartbase + ALTERA_UART_STATUS_REG) &
+                       ALTERA_UART_STATUS_TRDY_MSK)
+                       break;
+       }
+       writeb(ch, uartbase + ALTERA_UART_TXDATA_REG);
+}
+
+static int putchar(int ch)
+{
+       uart_putc(ch);
+       if (ch == '\n')
+               uart_putc('\r');
+       return ch;
+}
+
+static void console_init(void)
+{
+       unsigned int baud, baudclk;
+
+       uartbase = (unsigned long) my_ioremap((unsigned long) UART0_BASE);
+       baud = CONFIG_SERIAL_ALTERA_UART_BAUDRATE;
+       baudclk = UART0_FREQ / baud;
+       writew(baudclk, uartbase + ALTERA_UART_DIVISOR_REG);
+}
+
+#else
+
+static int putchar(int ch)
+{
+       return ch;
+}
+
+static void console_init(void)
+{
+}
+
+#endif
+
+static int puts(const char *s)
+{
+       while (*s)
+               putchar(*s++);
+       return 0;
+}
diff --git a/arch/nios2/boot/compressed/head.S b/arch/nios2/boot/compressed/head.S
new file mode 100644 (file)
index 0000000..15c6c48
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2009 Thomas Chou <thomas@wytron.com.tw>
+ *
+ * Based on arch/nios2/kernel/head.S
+ *
+ * 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.
+ *
+ */
+
+/*
+ *  This code can be loaded anywhere, eg FLASH ROM as reset vector,
+ *  as long as output does not overlap it.
+ */
+
+#include <linux/linkage.h>
+#include <asm/cache.h>
+
+       .text
+       .set noat
+ENTRY(_start)
+       wrctl   status, r0              /* disable interrupt */
+       /* invalidate all instruction cache */
+       movia   r1, NIOS2_ICACHE_SIZE
+       movui   r2, NIOS2_ICACHE_LINE_SIZE
+1:     initi   r1
+       sub     r1, r1, r2
+       bgt     r1, r0, 1b
+       /* invalidate all data cache */
+       movia   r1, NIOS2_DCACHE_SIZE
+       movui   r2, NIOS2_DCACHE_LINE_SIZE
+1:     initd   0(r1)
+       sub     r1, r1, r2
+       bgt     r1, r0, 1b
+
+       nextpc  r1                      /* Find out where we are */
+chkadr:
+       movia   r2, chkadr
+       beq     r1, r2, finish_move     /* We are running in correct address,
+                                          done */
+       /* move code, r1: src, r2: dest, r3: last dest */
+       addi    r1, r1, (_start - chkadr)       /* Source */
+       movia   r2, _start              /* Destination */
+       movia   r3, __bss_start         /* End of copy */
+1:     ldw     r8, 0(r1)               /* load a word from [r1] */
+       stw     r8, 0(r2)               /* stort a word to dest [r2] */
+       addi    r1, r1, 4               /* inc the src addr */
+       addi    r2, r2, 4               /* inc the dest addr */
+       blt     r2, r3, 1b
+       /* flush the data cache after moving */
+       movia   r1, NIOS2_DCACHE_SIZE
+       movui   r2, NIOS2_DCACHE_LINE_SIZE
+1:     flushd  0(r1)
+       sub     r1, r1, r2
+       bgt     r1, r0, 1b
+       movia   r1, finish_move
+       jmp     r1                      /* jmp to linked address */
+
+finish_move:
+       /* zero out the .bss segment (uninitialized common data) */
+       movia   r2, __bss_start         /* presume nothing is between */
+       movia   r1, _end                /* the .bss and _end. */
+1:     stb     r0, 0(r2)
+       addi    r2, r2, 1
+       bne     r1, r2, 1b
+       /*
+        * set up the stack pointer, some where higher than _end.
+        * The stack space must be greater than 32K for decompress.
+        */
+       movia   sp, 0x10000
+       add     sp, sp, r1
+       /* save args passed from u-boot, maybe */
+       addi    sp, sp, -16
+       stw     r4, 0(sp)
+       stw     r5, 4(sp)
+       stw     r6, 8(sp)
+       stw     r7, 12(sp)
+       /* decompress the kernel */
+       call    decompress_kernel
+       /* pass saved args to kernel */
+       ldw     r4, 0(sp)
+       ldw     r5, 4(sp)
+       ldw     r6, 8(sp)
+       ldw     r7, 12(sp)
+
+       /* flush all data cache after decompressing */
+       movia   r1, NIOS2_DCACHE_SIZE
+       movui   r2, NIOS2_DCACHE_LINE_SIZE
+1:     flushd  0(r1)
+       sub     r1, r1, r2
+       bgt     r1, r0, 1b
+       /* flush all instruction cache */
+       movia   r1, NIOS2_ICACHE_SIZE
+       movui   r2, NIOS2_ICACHE_LINE_SIZE
+1:     flushi  r1
+       sub     r1, r1, r2
+       bgt     r1, r0, 1b
+       flushp
+       /* jump to start real kernel */
+       movia   r1, (CONFIG_NIOS2_MEM_BASE | CONFIG_NIOS2_KERNEL_REGION_BASE)
+       jmp     r1
+
+       .balign 512
+fake_headers_as_bzImage:
+       .short  0
+       .ascii  "HdrS"
+       .short  0x0202
+       .short  0
+       .short  0
+       .byte   0x00, 0x10
+       .short  0
+       .byte   0
+       .byte   1
+       .byte   0x00, 0x80
+       .long   0
+       .long   0
diff --git a/arch/nios2/boot/compressed/misc.c b/arch/nios2/boot/compressed/misc.c
new file mode 100644 (file)
index 0000000..8437782
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2009 Thomas Chou <thomas@wytron.com.tw>
+ *
+ * This is a collection of several routines from gzip-1.0.3
+ * adapted for Linux.
+ *
+ * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994
+ *
+ * Adapted for SH by Stuart Menefy, Aug 1999
+ *
+ * Modified to use standard LinuxSH BIOS by Greg Banks 7Jul2000
+ *
+ * Based on arch/sh/boot/compressed/misc.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/string.h>
+
+/*
+ * gzip declarations
+ */
+#define OF(args)  args
+#define STATIC static
+
+#undef memset
+#undef memcpy
+#define memzero(s, n)          memset((s), 0, (n))
+
+typedef unsigned char  uch;
+typedef unsigned short ush;
+typedef unsigned long  ulg;
+#define WSIZE 0x8000           /* Window size must be at least 32k, */
+                               /* and a power of two */
+
+static uch *inbuf;             /* input buffer */
+static uch window[WSIZE];      /* Sliding window buffer */
+
+static unsigned insize;        /* valid bytes in inbuf */
+static unsigned inptr; /* index of next byte to be processed in inbuf */
+static unsigned outcnt;        /* bytes in output buffer */
+
+/* gzip flag byte */
+#define ASCII_FLAG     0x01 /* bit 0 set: file probably ASCII text */
+#define CONTINUATION   0x02 /* bit 1 set: continuation of multi-part gzip
+                               file */
+#define EXTRA_FIELD    0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME      0x08 /* bit 3 set: original file name present */
+#define COMMENT                0x10 /* bit 4 set: file comment present */
+#define ENCRYPTED      0x20 /* bit 5 set: file is encrypted */
+#define RESERVED       0xC0 /* bit 6,7:   reserved */
+
+#define get_byte()  (inptr < insize ? inbuf[inptr++] : fill_inbuf())
+
+#ifdef DEBUG
+#  define Assert(cond, msg) {if (!(cond)) error(msg); }
+#  define Trace(x) fprintf x
+#  define Tracev(x) {if (verbose) fprintf x ; }
+#  define Tracevv(x) {if (verbose > 1) fprintf x ; }
+#  define Tracec(c, x) {if (verbose && (c)) fprintf x ; }
+#  define Tracecv(c, x) {if (verbose > 1 && (c)) fprintf x ; }
+#else
+#  define Assert(cond, msg)
+#  define Trace(x)
+#  define Tracev(x)
+#  define Tracevv(x)
+#  define Tracec(c, x)
+#  define Tracecv(c, x)
+#endif
+static int  fill_inbuf(void);
+static void flush_window(void);
+static void error(char *m);
+
+extern char input_data[];
+extern int input_len;
+
+static long bytes_out;
+static uch *output_data;
+static unsigned long output_ptr;
+
+#include "console.c"
+
+static void error(char *m);
+
+int puts(const char *);
+
+extern int _end;
+static unsigned long free_mem_ptr;
+static unsigned long free_mem_end_ptr;
+
+#define HEAP_SIZE                      0x10000
+
+#include "../../../../lib/inflate.c"
+
+void *memset(void *s, int c, size_t n)
+{
+       int i;
+       char *ss = (char *)s;
+
+       for (i = 0; i < n; i++)
+               ss[i] = c;
+       return s;
+}
+
+void *memcpy(void *__dest, __const void *__src, size_t __n)
+{
+       int i;
+       char *d = (char *)__dest, *s = (char *)__src;
+
+       for (i = 0; i < __n; i++)
+               d[i] = s[i];
+       return __dest;
+}
+
+/*
+ * Fill the input buffer. This is called only when the buffer is empty
+ * and at least one byte is really needed.
+ */
+static int fill_inbuf(void)
+{
+       if (insize != 0)
+               error("ran out of input data");
+
+       inbuf = input_data;
+       insize = input_len;
+       inptr = 1;
+       return inbuf[0];
+}
+
+/*
+ * Write the output window window[0..outcnt-1] and update crc and bytes_out.
+ * (Used for the decompressed data only.)
+ */
+static void flush_window(void)
+{
+       ulg c = crc;    /* temporary variable */
+       unsigned n;
+       uch *in, *out, ch;
+
+       in = window;
+       out = &output_data[output_ptr];
+       for (n = 0; n < outcnt; n++) {
+               ch = *out++ = *in++;
+               c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8);
+       }
+       crc = c;
+       bytes_out += (ulg)outcnt;
+       output_ptr += (ulg)outcnt;
+       outcnt = 0;
+}
+
+static void error(char *x)
+{
+       puts("\nERROR\n");
+       puts(x);
+       puts("\n\n -- System halted");
+
+       while (1)       /* Halt */
+               ;
+}
+
+void decompress_kernel(void)
+{
+       output_data = (void *) (CONFIG_NIOS2_MEM_BASE |
+                               CONFIG_NIOS2_KERNEL_REGION_BASE);
+       output_ptr = 0;
+       free_mem_ptr = (unsigned long)&_end;
+       free_mem_end_ptr = free_mem_ptr + HEAP_SIZE;
+
+       console_init();
+       makecrc();
+       puts("Uncompressing Linux... ");
+       gunzip();
+       puts("Ok, booting the kernel.\n");
+}
diff --git a/arch/nios2/boot/compressed/vmlinux.lds.S b/arch/nios2/boot/compressed/vmlinux.lds.S
new file mode 100644 (file)
index 0000000..e867b37
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2009 Thomas Chou <thomas@wytron.com.tw>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <asm-generic/vmlinux.lds.h>
+
+OUTPUT_FORMAT("elf32-littlenios2", "elf32-littlenios2", "elf32-littlenios2")
+
+OUTPUT_ARCH(nios)
+ENTRY(_start)  /* Defined in head.S */
+
+SECTIONS
+{
+       . = (CONFIG_NIOS2_MEM_BASE + CONFIG_NIOS2_BOOT_LINK_OFFSET) |   \
+               CONFIG_NIOS2_KERNEL_REGION_BASE;
+
+       _text = .;
+       .text : { *(.text) } = 0
+       .rodata : { *(.rodata) *(.rodata.*) }
+       _etext = .;
+
+       . = ALIGN(32 / 8);
+       .data : { *(.data) }
+       . = ALIGN(32 / 8);
+       _got = .;
+       .got : {
+               *(.got.plt)
+               *(.igot.plt)
+               *(.got)
+               *(.igot)
+       }
+       _egot = .;
+       _edata =  .;
+
+       . = ALIGN(32 / 8);
+       __bss_start = .;
+       .bss : { *(.bss) *(.sbss) }
+       . = ALIGN(32 / 8);
+       _ebss = .;
+       end = . ;
+       _end = . ;
+
+       got_len = (_egot - _got);
+}
diff --git a/arch/nios2/boot/compressed/vmlinux.scr b/arch/nios2/boot/compressed/vmlinux.scr
new file mode 100644 (file)
index 0000000..28c42f1
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2009 Thomas Chou <thomas@wytron.com.tw>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+SECTIONS
+{
+       .data : {
+               input_len = .;
+               LONG(input_data_end - input_data) input_data = .;
+               *(.data)
+               . = ALIGN(4);
+               input_data_end = .;
+       }
+}
index 87541f0a5d6e550f7f1cee0462cda36d1038e6f4..9451940678a03309ce9acd3b9d84bc0651013191 100644 (file)
@@ -22,6 +22,7 @@ CONFIG_NIOS2_DCACHE_SIZE=0x8000
 CONFIG_NIOS2_ICACHE_SIZE=0x8000
 # CONFIG_NIOS2_CMDLINE_IGNORE_DTB is not set
 CONFIG_NIOS2_PASS_CMDLINE=y
+CONFIG_NIOS2_BOOT_LINK_OFFSET=0x00800000
 CONFIG_NET=y
 CONFIG_PACKET=y
 CONFIG_UNIX=y
diff --git a/arch/nios2/include/asm/kgdb.h b/arch/nios2/include/asm/kgdb.h
new file mode 100644 (file)
index 0000000..8fd5e3b
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 Altera Corporation
+ * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch>
+ *
+ * Based on the code posted by Kazuyasu on the Altera Forum at:
+ * http://www.alteraforum.com/forum/showpost.php?p=77003&postcount=20
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _ASM_NIOS2_KGDB_H
+#define _ASM_NIOS2_KGDB_H
+
+#define CACHE_FLUSH_IS_SAFE    1
+#define BUFMAX                 2048
+
+enum regnames {
+       GDB_R0 = 0,
+       GDB_AT,
+       GDB_R2,
+       GDB_R3,
+       GDB_R4,
+       GDB_R5,
+       GDB_R6,
+       GDB_R7,
+       GDB_R8,
+       GDB_R9,
+       GDB_R10,
+       GDB_R11,
+       GDB_R12,
+       GDB_R13,
+       GDB_R14,
+       GDB_R15,
+       GDB_R16,
+       GDB_R17,
+       GDB_R18,
+       GDB_R19,
+       GDB_R20,
+       GDB_R21,
+       GDB_R22,
+       GDB_R23,
+       GDB_ET,
+       GDB_BT,
+       GDB_GP,
+       GDB_SP,
+       GDB_FP,
+       GDB_EA,
+       GDB_BA,
+       GDB_RA,
+       GDB_PC,
+       GDB_STATUS,
+       GDB_ESTATUS,
+       GDB_BSTATUS,
+       GDB_IENABLE,
+       GDB_IPENDING,
+       GDB_CPUID,
+       GDB_CTL6,
+       GDB_EXCEPTION,
+       GDB_PTEADDR,
+       GDB_TLBACC,
+       GDB_TLBMISC,
+       GDB_ECCINJ,
+       GDB_BADADDR,
+       GDB_CONFIG,
+       GDB_MPUBASE,
+       GDB_MPUACC,
+       /* do not change the last entry or anything below! */
+       GDB_NUMREGBYTES         /* number of registers */
+};
+
+#define GDB_SIZEOF_REG         sizeof(u32)
+#define DBG_MAX_REG_NUM        (49)
+#define NUMREGBYTES            (DBG_MAX_REG_NUM * sizeof(GDB_SIZEOF_REG))
+
+#define BREAK_INSTR_SIZE       4
+static inline void arch_kgdb_breakpoint(void)
+{
+       __asm__ __volatile__("trap 30\n");
+}
+
+#endif /* _ASM_NIOS2_KGDB_H */
index 3bd349473b06b5f1d69ad2a2545193e7146e513d..c2ba45c159c73d144672dfb116811447ced86c26 100644 (file)
@@ -85,9 +85,6 @@ static inline void exit_thread(void)
 
 extern unsigned long get_wchan(struct task_struct *p);
 
-/* Prepare to copy thread state - unlazy all lazy status */
-#define prepare_to_copy(tsk)   do { } while (0)
-
 #define task_pt_regs(p) \
        ((struct pt_regs *)(THREAD_SIZE + task_stack_page(p)) - 1)
 
diff --git a/arch/nios2/include/asm/prom.h b/arch/nios2/include/asm/prom.h
new file mode 100644 (file)
index 0000000..75fffb4
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright Altera Corporation (C) <2015>. All rights reserved
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ASM_NIOS2_PROM_H__
+#define __ASM_NIOS2_PROM_H__
+
+extern unsigned long __init of_early_console(void);
+
+#endif
index 8ae76823ff93f2091ae28c63e1244c9ea6f99253..1aae257036575ada177ab257dcd68bb7cc3fce42 100644 (file)
@@ -20,5 +20,7 @@ obj-y += syscall_table.o
 obj-y  += time.o
 obj-y  += traps.o
 
+obj-$(CONFIG_EARLY_PRINTK)             += early_printk.o
+obj-$(CONFIG_KGDB)                     += kgdb.o
 obj-$(CONFIG_MODULES)                  += module.o
 obj-$(CONFIG_NIOS2_ALIGNMENT_TRAP)     += misaligned.o
diff --git a/arch/nios2/kernel/early_printk.c b/arch/nios2/kernel/early_printk.c
new file mode 100644 (file)
index 0000000..c08e4c1
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Early printk for Nios2.
+ *
+ * Copyright (C) 2015, Altera Corporation
+ * Copyright (C) 2010, Tobias Klauser <tklauser@distanz.ch>
+ * Copyright (C) 2009, Wind River Systems Inc
+ *   Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+
+#include <asm/prom.h>
+
+static unsigned long base_addr;
+
+#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE)
+
+#define ALTERA_JTAGUART_DATA_REG               0
+#define ALTERA_JTAGUART_CONTROL_REG            4
+#define ALTERA_JTAGUART_CONTROL_WSPACE_MSK     0xFFFF0000
+#define ALTERA_JTAGUART_CONTROL_AC_MSK         0x00000400
+
+#define JUART_GET_CR() \
+       __builtin_ldwio((void *)(base_addr + ALTERA_JTAGUART_CONTROL_REG))
+#define JUART_SET_CR(v) \
+       __builtin_stwio((void *)(base_addr + ALTERA_JTAGUART_CONTROL_REG), v)
+#define JUART_SET_TX(v) \
+       __builtin_stwio((void *)(base_addr + ALTERA_JTAGUART_DATA_REG), v)
+
+static void early_console_write(struct console *con, const char *s, unsigned n)
+{
+       unsigned long status;
+
+       while (n-- && *s) {
+               while (((status = JUART_GET_CR())
+                               & ALTERA_JTAGUART_CONTROL_WSPACE_MSK) == 0) {
+#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS)
+                       if ((status & ALTERA_JTAGUART_CONTROL_AC_MSK) == 0)
+                               return; /* no connection activity */
+#endif
+               }
+               JUART_SET_TX(*s);
+               s++;
+       }
+}
+
+#elif defined(CONFIG_SERIAL_ALTERA_UART_CONSOLE)
+
+#define ALTERA_UART_TXDATA_REG         4
+#define ALTERA_UART_STATUS_REG         8
+#define ALTERA_UART_STATUS_TRDY                0x0040
+
+#define UART_GET_SR() \
+       __builtin_ldwio((void *)(base_addr + ALTERA_UART_STATUS_REG))
+#define UART_SET_TX(v) \
+       __builtin_stwio((void *)(base_addr + ALTERA_UART_TXDATA_REG), v)
+
+static void early_console_putc(char c)
+{
+       while (!(UART_GET_SR() & ALTERA_UART_STATUS_TRDY))
+               ;
+
+       UART_SET_TX(c);
+}
+
+static void early_console_write(struct console *con, const char *s, unsigned n)
+{
+       while (n-- && *s) {
+               early_console_putc(*s);
+               if (*s == '\n')
+                       early_console_putc('\r');
+               s++;
+       }
+}
+
+#else
+# error Neither SERIAL_ALTERA_JTAGUART_CONSOLE nor SERIAL_ALTERA_UART_CONSOLE \
+selected
+#endif
+
+static struct console early_console_prom = {
+       .name   = "early",
+       .write  = early_console_write,
+       .flags  = CON_PRINTBUFFER | CON_BOOT,
+       .index  = -1
+};
+
+void __init setup_early_printk(void)
+{
+#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE) ||  \
+       defined(CONFIG_SERIAL_ALTERA_UART_CONSOLE)
+       base_addr = of_early_console();
+#else
+       base_addr = 0;
+#endif
+
+       if (!base_addr)
+               return;
+
+#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS)
+       /* Clear activity bit so BYPASS doesn't stall if we've used JTAG for
+        * downloading the kernel. This might cause early data to be lost even
+        * if the JTAG terminal is running.
+        */
+       JUART_SET_CR(JUART_GET_CR() | ALTERA_JTAGUART_CONTROL_AC_MSK);
+#endif
+
+       early_console = &early_console_prom;
+       register_console(early_console);
+       pr_info("early_console initialized at 0x%08lx\n", base_addr);
+}
index 0bdfd13ff98bbbbd5af7fc4251abf1c618d63a94..7729bd3f2e79d48937e7dbead303640610ca24ae 100644 (file)
@@ -121,7 +121,11 @@ trap_table:
        .word   instruction_trap        /* 27 */
        .word   instruction_trap        /* 28 */
        .word   instruction_trap        /* 29 */
-       .word   instruction_trap        /* 30 */
+#ifdef CONFIG_KGDB
+       .word   handle_kgdb_breakpoint  /* 30 KGDB breakpoint */
+#else
+       .word   instruction_trap                /* 30 */
+#endif
        .word   handle_breakpoint       /* 31 */
 
 .text
@@ -445,6 +449,12 @@ handle_diverror:
        call    handle_diverror_c
        br      ret_from_exception
 
+#ifdef CONFIG_KGDB
+handle_kgdb_breakpoint:
+       call    kgdb_breakpoint_c
+       br      ret_from_exception
+#endif
+
 /*
  * Beware - when entering resume, prev (the current task) is
  * in r4, next (the new task) is in r5, don't change these
diff --git a/arch/nios2/kernel/kgdb.c b/arch/nios2/kernel/kgdb.c
new file mode 100644 (file)
index 0000000..1178591
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * Nios2 KGDB support
+ *
+ * Copyright (C) 2015 Altera Corporation
+ * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch>
+ *
+ * Based on the code posted by Kazuyasu on the Altera Forum at:
+ * http://www.alteraforum.com/forum/showpost.php?p=77003&postcount=20
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <linux/ptrace.h>
+#include <linux/kgdb.h>
+#include <linux/kdebug.h>
+#include <linux/io.h>
+
+static int wait_for_remote_debugger;
+
+struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] =
+{
+       { "zero", GDB_SIZEOF_REG, -1 },
+       { "at", GDB_SIZEOF_REG, offsetof(struct pt_regs, r1) },
+       { "r2", GDB_SIZEOF_REG, offsetof(struct pt_regs, r2) },
+       { "r3", GDB_SIZEOF_REG, offsetof(struct pt_regs, r3) },
+       { "r4", GDB_SIZEOF_REG, offsetof(struct pt_regs, r4) },
+       { "r5", GDB_SIZEOF_REG, offsetof(struct pt_regs, r5) },
+       { "r6", GDB_SIZEOF_REG, offsetof(struct pt_regs, r6) },
+       { "r7", GDB_SIZEOF_REG, offsetof(struct pt_regs, r7) },
+       { "r8", GDB_SIZEOF_REG, offsetof(struct pt_regs, r8) },
+       { "r9", GDB_SIZEOF_REG, offsetof(struct pt_regs, r9) },
+       { "r10", GDB_SIZEOF_REG, offsetof(struct pt_regs, r10) },
+       { "r11", GDB_SIZEOF_REG, offsetof(struct pt_regs, r11) },
+       { "r12", GDB_SIZEOF_REG, offsetof(struct pt_regs, r12) },
+       { "r13", GDB_SIZEOF_REG, offsetof(struct pt_regs, r13) },
+       { "r14", GDB_SIZEOF_REG, offsetof(struct pt_regs, r14) },
+       { "r15", GDB_SIZEOF_REG, offsetof(struct pt_regs, r15) },
+       { "r16", GDB_SIZEOF_REG, -1 },
+       { "r17", GDB_SIZEOF_REG, -1 },
+       { "r18", GDB_SIZEOF_REG, -1 },
+       { "r19", GDB_SIZEOF_REG, -1 },
+       { "r20", GDB_SIZEOF_REG, -1 },
+       { "r21", GDB_SIZEOF_REG, -1 },
+       { "r22", GDB_SIZEOF_REG, -1 },
+       { "r23", GDB_SIZEOF_REG, -1 },
+       { "et", GDB_SIZEOF_REG, -1 },
+       { "bt", GDB_SIZEOF_REG, -1 },
+       { "gp", GDB_SIZEOF_REG, offsetof(struct pt_regs, gp) },
+       { "sp", GDB_SIZEOF_REG, offsetof(struct pt_regs, sp) },
+       { "fp", GDB_SIZEOF_REG, offsetof(struct pt_regs, fp) },
+       { "ea", GDB_SIZEOF_REG, -1 },
+       { "ba", GDB_SIZEOF_REG, -1 },
+       { "ra", GDB_SIZEOF_REG, offsetof(struct pt_regs, ra) },
+       { "pc", GDB_SIZEOF_REG, offsetof(struct pt_regs, ea) },
+       { "status", GDB_SIZEOF_REG, -1 },
+       { "estatus", GDB_SIZEOF_REG, offsetof(struct pt_regs, estatus) },
+       { "bstatus", GDB_SIZEOF_REG, -1 },
+       { "ienable", GDB_SIZEOF_REG, -1 },
+       { "ipending", GDB_SIZEOF_REG, -1},
+       { "cpuid", GDB_SIZEOF_REG, -1 },
+       { "ctl6", GDB_SIZEOF_REG, -1 },
+       { "exception", GDB_SIZEOF_REG, -1 },
+       { "pteaddr", GDB_SIZEOF_REG, -1 },
+       { "tlbacc", GDB_SIZEOF_REG, -1 },
+       { "tlbmisc", GDB_SIZEOF_REG, -1 },
+       { "eccinj", GDB_SIZEOF_REG, -1 },
+       { "badaddr", GDB_SIZEOF_REG, -1 },
+       { "config", GDB_SIZEOF_REG, -1 },
+       { "mpubase", GDB_SIZEOF_REG, -1 },
+       { "mpuacc", GDB_SIZEOF_REG, -1 },
+};
+
+char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs)
+{
+       if (regno >= DBG_MAX_REG_NUM || regno < 0)
+               return NULL;
+
+       if (dbg_reg_def[regno].offset != -1)
+               memcpy(mem, (void *)regs + dbg_reg_def[regno].offset,
+                      dbg_reg_def[regno].size);
+       else
+               memset(mem, 0, dbg_reg_def[regno].size);
+
+       return dbg_reg_def[regno].name;
+}
+
+int dbg_set_reg(int regno, void *mem, struct pt_regs *regs)
+{
+       if (regno >= DBG_MAX_REG_NUM || regno < 0)
+               return -EINVAL;
+
+       if (dbg_reg_def[regno].offset != -1)
+               memcpy((void *)regs + dbg_reg_def[regno].offset, mem,
+                      dbg_reg_def[regno].size);
+
+       return 0;
+}
+
+void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)
+{
+       memset((char *)gdb_regs, 0, NUMREGBYTES);
+       gdb_regs[GDB_SP] = p->thread.kregs->sp;
+       gdb_regs[GDB_PC] = p->thread.kregs->ea;
+}
+
+void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc)
+{
+       regs->ea = pc;
+}
+
+int kgdb_arch_handle_exception(int vector, int signo, int err_code,
+                               char *remcom_in_buffer, char *remcom_out_buffer,
+                               struct pt_regs *regs)
+{
+       char *ptr;
+       unsigned long addr;
+
+       switch (remcom_in_buffer[0]) {
+       case 's':
+       case 'c':
+               /* handle the optional parameters */
+               ptr = &remcom_in_buffer[1];
+               if (kgdb_hex2long(&ptr, &addr))
+                       regs->ea = addr;
+
+               return 0;
+       }
+
+       return -1; /* this means that we do not want to exit from the handler */
+}
+
+asmlinkage void kgdb_breakpoint_c(struct pt_regs *regs)
+{
+       /*
+        * The breakpoint entry code has moved the PC on by 4 bytes, so we must
+        * move it back.  This could be done on the host but we do it here
+        */
+       if (!wait_for_remote_debugger)
+               regs->ea -= 4;
+       else    /* pass the first trap 30 code */
+               wait_for_remote_debugger = 0;
+
+       kgdb_handle_exception(30, SIGTRAP, 0, regs);
+}
+
+int kgdb_arch_init(void)
+{
+       wait_for_remote_debugger = 1;
+       return 0;
+}
+
+void kgdb_arch_exit(void)
+{
+       /* Nothing to do */
+}
+
+struct kgdb_arch arch_kgdb_ops = {
+       /* Breakpoint instruction: trap 30 */
+       .gdb_bpt_instr = { 0xba, 0x6f, 0x3b, 0x00 },
+};
index 0522d3378e3f7e1b307bd9f13500a87535dfe94d..718dd197909faf863069bbb085a79b7987ee26af 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Device tree support
  *
- * Copyright (C) 2013 Altera Corporation
+ * Copyright (C) 2013, 2015 Altera Corporation
  * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
  *
  * Based on MIPS support for CONFIG_OF device tree support
@@ -30,6 +30,7 @@
 #include <linux/of_fdt.h>
 #include <linux/io.h>
 
+#include <asm/prom.h>
 #include <asm/sections.h>
 
 void __init early_init_dt_add_memory_arch(u64 base, u64 size)
@@ -63,3 +64,52 @@ void __init early_init_devtree(void *params)
 
        early_init_dt_scan(params);
 }
+
+#ifdef CONFIG_EARLY_PRINTK
+static int __init early_init_dt_scan_serial(unsigned long node,
+                       const char *uname, int depth, void *data)
+{
+       u64 *addr64 = (u64 *) data;
+       const char *p;
+
+       /* only consider serial nodes */
+       if (strncmp(uname, "serial", 6) != 0)
+               return 0;
+
+       p = of_get_flat_dt_prop(node, "compatible", NULL);
+       if (!p)
+               return 0;
+
+       /*
+        * We found an altera_jtaguart but it wasn't configured for console, so
+        * skip it.
+        */
+#ifndef CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE
+       if (strncmp(p, "altr,juart", 10) == 0)
+               return 0;
+#endif
+
+       /*
+        * Same for altera_uart.
+        */
+#ifndef CONFIG_SERIAL_ALTERA_UART_CONSOLE
+       if (strncmp(p, "altr,uart", 9) == 0)
+               return 0;
+#endif
+
+       *addr64 = fdt_translate_address((const void *)initial_boot_params,
+               node);
+
+       return *addr64 == OF_BAD_ADDR ? 0 : 1;
+}
+
+unsigned long __init of_early_console(void)
+{
+       u64 base = 0;
+
+       if (of_scan_flat_dt(early_init_dt_scan_serial, &base))
+               return (u32)ioremap(base, 32);
+       else
+               return 0;
+}
+#endif /* CONFIG_EARLY_PRINTK */
index cb3121f975d49592ccf15d3795721cc859274f4c..b101a43d3c5a05ed70fa6a1bcbdb1e7aab537495 100644 (file)
@@ -139,6 +139,10 @@ void __init setup_arch(char **cmdline_p)
 
        console_verbose();
 
+#ifdef CONFIG_EARLY_PRINTK
+       setup_early_printk();
+#endif
+
        memory_start = PAGE_ALIGN((unsigned long)__pa(_end));
        memory_end = (unsigned long) CONFIG_NIOS2_MEM_BASE + memory_size;
 
index d194c0427b26ee8de18ca5bf1f301df681a6ab70..0d231adfe576b897073c924e02211dea7b110285 100644 (file)
@@ -47,7 +47,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long cause,
        struct mm_struct *mm = tsk->mm;
        int code = SEGV_MAPERR;
        int fault;
-       unsigned int flags = 0;
+       unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
 
        cause >>= 2;
 
@@ -86,6 +86,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long cause,
        if (!down_read_trylock(&mm->mmap_sem)) {
                if (!user_mode(regs) && !search_exception_tables(regs->ea))
                        goto bad_area_nosemaphore;
+retry:
                down_read(&mm->mmap_sem);
        }
 
@@ -132,6 +133,10 @@ survive:
         * the fault.
         */
        fault = handle_mm_fault(mm, vma, address, flags);
+
+       if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current))
+               return;
+
        if (unlikely(fault & VM_FAULT_ERROR)) {
                if (fault & VM_FAULT_OOM)
                        goto out_of_memory;
@@ -141,10 +146,32 @@ survive:
                        goto do_sigbus;
                BUG();
        }
-       if (fault & VM_FAULT_MAJOR)
-               tsk->maj_flt++;
-       else
-               tsk->min_flt++;
+
+       /*
+        * Major/minor page fault accounting is only done on the
+        * initial attempt. If we go through a retry, it is extremely
+        * likely that the page will be found in page cache at that point.
+        */
+       if (flags & FAULT_FLAG_ALLOW_RETRY) {
+               if (fault & VM_FAULT_MAJOR)
+                       current->maj_flt++;
+               else
+                       current->min_flt++;
+               if (fault & VM_FAULT_RETRY) {
+                       /* Clear FAULT_FLAG_ALLOW_RETRY to avoid any risk
+                        * of starvation. */
+                       flags &= ~FAULT_FLAG_ALLOW_RETRY;
+                       flags |= FAULT_FLAG_TRIED;
+
+                       /*
+                        * No need to up_read(&mm->mmap_sem) as we would
+                        * have already released it in __lock_page_or_retry
+                        * in mm/filemap.c.
+                        */
+
+                       goto retry;
+               }
+       }
 
        up_read(&mm->mmap_sem);
        return;