aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/arm64/memory.txt2
-rw-r--r--arch/arm64/Kconfig.debug9
-rw-r--r--arch/arm64/include/asm/io.h3
-rw-r--r--arch/arm64/include/asm/memory.h1
-rw-r--r--arch/arm64/include/asm/mmu.h1
-rw-r--r--arch/arm64/kernel/Makefile1
-rw-r--r--arch/arm64/kernel/early_printk.c118
-rw-r--r--arch/arm64/kernel/head.S12
-rw-r--r--arch/arm64/mm/mmu.c42
9 files changed, 187 insertions, 2 deletions
diff --git a/Documentation/arm64/memory.txt b/Documentation/arm64/memory.txt
index d758702fc03c..5f583af0a6e1 100644
--- a/Documentation/arm64/memory.txt
+++ b/Documentation/arm64/memory.txt
@@ -35,6 +35,8 @@ ffffffbc00000000 ffffffbdffffffff 8GB vmemmap
35 35
36ffffffbe00000000 ffffffbffbbfffff ~8GB [guard, future vmmemap] 36ffffffbe00000000 ffffffbffbbfffff ~8GB [guard, future vmmemap]
37 37
38ffffffbffbc00000 ffffffbffbdfffff 2MB earlyprintk device
39
38ffffffbffbe00000 ffffffbffbe0ffff 64KB PCI I/O space 40ffffffbffbe00000 ffffffbffbe0ffff 64KB PCI I/O space
39 41
40ffffffbbffff0000 ffffffbcffffffff ~2MB [guard] 42ffffffbbffff0000 ffffffbcffffffff ~2MB [guard]
diff --git a/arch/arm64/Kconfig.debug b/arch/arm64/Kconfig.debug
index d7553f2bda66..79871cd78da8 100644
--- a/arch/arm64/Kconfig.debug
+++ b/arch/arm64/Kconfig.debug
@@ -24,4 +24,13 @@ config DEBUG_STACK_USAGE
24 Enables the display of the minimum amount of free stack which each 24 Enables the display of the minimum amount of free stack which each
25 task has ever had available in the sysrq-T output. 25 task has ever had available in the sysrq-T output.
26 26
27config EARLY_PRINTK
28 bool "Early printk support"
29 default y
30 help
31 Say Y here if you want to have an early console using the
32 earlyprintk=<name>[,<addr>][,<options>] kernel parameter. It
33 is assumed that the early console device has been initialised
34 by the boot loader prior to starting the Linux kernel.
35
27endmenu 36endmenu
diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h
index d2f05a608274..57f12c991de2 100644
--- a/arch/arm64/include/asm/io.h
+++ b/arch/arm64/include/asm/io.h
@@ -230,6 +230,9 @@ extern void __iounmap(volatile void __iomem *addr);
230#define ioremap_wc(addr, size) __ioremap((addr), (size), __pgprot(PROT_NORMAL_NC)) 230#define ioremap_wc(addr, size) __ioremap((addr), (size), __pgprot(PROT_NORMAL_NC))
231#define iounmap __iounmap 231#define iounmap __iounmap
232 232
233#define PROT_SECT_DEFAULT (PMD_TYPE_SECT | PMD_SECT_AF)
234#define PROT_SECT_DEVICE_nGnRE (PROT_SECT_DEFAULT | PTE_PXN | PTE_UXN | PMD_ATTRINDX(MT_DEVICE_nGnRE))
235
233#define ARCH_HAS_IOREMAP_WC 236#define ARCH_HAS_IOREMAP_WC
234#include <asm-generic/iomap.h> 237#include <asm-generic/iomap.h>
235 238
diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h
index 1cac16a001cb..381f556b664e 100644
--- a/arch/arm64/include/asm/memory.h
+++ b/arch/arm64/include/asm/memory.h
@@ -43,6 +43,7 @@
43#define PAGE_OFFSET UL(0xffffffc000000000) 43#define PAGE_OFFSET UL(0xffffffc000000000)
44#define MODULES_END (PAGE_OFFSET) 44#define MODULES_END (PAGE_OFFSET)
45#define MODULES_VADDR (MODULES_END - SZ_64M) 45#define MODULES_VADDR (MODULES_END - SZ_64M)
46#define EARLYCON_IOBASE (MODULES_VADDR - SZ_4M)
46#define VA_BITS (39) 47#define VA_BITS (39)
47#define TASK_SIZE_64 (UL(1) << VA_BITS) 48#define TASK_SIZE_64 (UL(1) << VA_BITS)
48 49
diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h
index d4f7fd5b9e33..2494fc01896a 100644
--- a/arch/arm64/include/asm/mmu.h
+++ b/arch/arm64/include/asm/mmu.h
@@ -26,5 +26,6 @@ typedef struct {
26 26
27extern void paging_init(void); 27extern void paging_init(void);
28extern void setup_mm_for_reboot(void); 28extern void setup_mm_for_reboot(void);
29extern void __iomem *early_io_map(phys_addr_t phys, unsigned long virt);
29 30
30#endif 31#endif
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index 74239c31e25a..a1cace45e970 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -17,6 +17,7 @@ arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o
17arm64-obj-$(CONFIG_SMP) += smp.o 17arm64-obj-$(CONFIG_SMP) += smp.o
18arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o 18arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o
19arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o 19arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o
20arm64-obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
20 21
21obj-y += $(arm64-obj-y) vdso/ 22obj-y += $(arm64-obj-y) vdso/
22obj-m += $(arm64-obj-m) 23obj-m += $(arm64-obj-m)
diff --git a/arch/arm64/kernel/early_printk.c b/arch/arm64/kernel/early_printk.c
new file mode 100644
index 000000000000..7e320a2edb9b
--- /dev/null
+++ b/arch/arm64/kernel/early_printk.c
@@ -0,0 +1,118 @@
1/*
2 * Earlyprintk support.
3 *
4 * Copyright (C) 2012 ARM Ltd.
5 * Author: Catalin Marinas <catalin.marinas@arm.com>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19#include <linux/kernel.h>
20#include <linux/console.h>
21#include <linux/init.h>
22#include <linux/string.h>
23#include <linux/mm.h>
24#include <linux/io.h>
25
26#include <linux/amba/serial.h>
27
28static void __iomem *early_base;
29static void (*printch)(char ch);
30
31/*
32 * PL011 single character TX.
33 */
34static void pl011_printch(char ch)
35{
36 while (readl_relaxed(early_base + UART01x_FR) & UART01x_FR_TXFF)
37 ;
38 writeb_relaxed(ch, early_base + UART01x_DR);
39 while (readl_relaxed(early_base + UART01x_FR) & UART01x_FR_BUSY)
40 ;
41}
42
43struct earlycon_match {
44 const char *name;
45 void (*printch)(char ch);
46};
47
48static const struct earlycon_match earlycon_match[] __initconst = {
49 { .name = "pl011", .printch = pl011_printch, },
50 {}
51};
52
53static void early_write(struct console *con, const char *s, unsigned n)
54{
55 while (n-- > 0) {
56 if (*s == '\n')
57 printch('\r');
58 printch(*s);
59 s++;
60 }
61}
62
63static struct console early_console = {
64 .name = "earlycon",
65 .write = early_write,
66 .flags = CON_PRINTBUFFER | CON_BOOT,
67 .index = -1,
68};
69
70/*
71 * Parse earlyprintk=... parameter in the format:
72 *
73 * <name>[,<addr>][,<options>]
74 *
75 * and register the early console. It is assumed that the UART has been
76 * initialised by the bootloader already.
77 */
78static int __init setup_early_printk(char *buf)
79{
80 const struct earlycon_match *match = earlycon_match;
81 phys_addr_t paddr = 0;
82
83 if (!buf) {
84 pr_warning("No earlyprintk arguments passed.\n");
85 return 0;
86 }
87
88 while (match->name) {
89 size_t len = strlen(match->name);
90 if (!strncmp(buf, match->name, len)) {
91 buf += len;
92 break;
93 }
94 match++;
95 }
96 if (!match->name) {
97 pr_warning("Unknown earlyprintk arguments: %s\n", buf);
98 return 0;
99 }
100
101 /* I/O address */
102 if (!strncmp(buf, ",0x", 3)) {
103 char *e;
104 paddr = simple_strtoul(buf + 1, &e, 16);
105 buf = e;
106 }
107 /* no options parsing yet */
108
109 if (paddr)
110 early_base = early_io_map(paddr, EARLYCON_IOBASE);
111
112 printch = match->printch;
113 register_console(&early_console);
114
115 return 0;
116}
117
118early_param("earlyprintk", setup_early_printk);
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
index 368ad1f7c36c..0a0a49756826 100644
--- a/arch/arm64/kernel/head.S
+++ b/arch/arm64/kernel/head.S
@@ -82,10 +82,8 @@
82 82
83#ifdef CONFIG_ARM64_64K_PAGES 83#ifdef CONFIG_ARM64_64K_PAGES
84#define MM_MMUFLAGS PTE_ATTRINDX(MT_NORMAL) | PTE_FLAGS 84#define MM_MMUFLAGS PTE_ATTRINDX(MT_NORMAL) | PTE_FLAGS
85#define IO_MMUFLAGS PTE_ATTRINDX(MT_DEVICE_nGnRE) | PTE_XN | PTE_FLAGS
86#else 85#else
87#define MM_MMUFLAGS PMD_ATTRINDX(MT_NORMAL) | PMD_FLAGS 86#define MM_MMUFLAGS PMD_ATTRINDX(MT_NORMAL) | PMD_FLAGS
88#define IO_MMUFLAGS PMD_ATTRINDX(MT_DEVICE_nGnRE) | PMD_SECT_XN | PMD_FLAGS
89#endif 87#endif
90 88
91/* 89/*
@@ -368,6 +366,7 @@ ENDPROC(__calc_phys_offset)
368 * - identity mapping to enable the MMU (low address, TTBR0) 366 * - identity mapping to enable the MMU (low address, TTBR0)
369 * - first few MB of the kernel linear mapping to jump to once the MMU has 367 * - first few MB of the kernel linear mapping to jump to once the MMU has
370 * been enabled, including the FDT blob (TTBR1) 368 * been enabled, including the FDT blob (TTBR1)
369 * - UART mapping if CONFIG_EARLY_PRINTK is enabled (TTBR1)
371 */ 370 */
372__create_page_tables: 371__create_page_tables:
373 pgtbl x25, x26, x24 // idmap_pg_dir and swapper_pg_dir addresses 372 pgtbl x25, x26, x24 // idmap_pg_dir and swapper_pg_dir addresses
@@ -420,6 +419,15 @@ __create_page_tables:
420 sub x6, x6, #1 // inclusive range 419 sub x6, x6, #1 // inclusive range
421 create_block_map x0, x7, x3, x5, x6 420 create_block_map x0, x7, x3, x5, x6
4221: 4211:
422#ifdef CONFIG_EARLY_PRINTK
423 /*
424 * Create the pgd entry for the UART mapping. The full mapping is done
425 * later based earlyprintk kernel parameter.
426 */
427 ldr x5, =EARLYCON_IOBASE // UART virtual address
428 add x0, x26, #2 * PAGE_SIZE // section table address
429 create_pgd_entry x26, x0, x5, x6, x7
430#endif
423 ret 431 ret
424ENDPROC(__create_page_tables) 432ENDPROC(__create_page_tables)
425 .ltorg 433 .ltorg
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index a6885d896ab6..f4dd585898c5 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -25,6 +25,7 @@
25#include <linux/nodemask.h> 25#include <linux/nodemask.h>
26#include <linux/memblock.h> 26#include <linux/memblock.h>
27#include <linux/fs.h> 27#include <linux/fs.h>
28#include <linux/io.h>
28 29
29#include <asm/cputype.h> 30#include <asm/cputype.h>
30#include <asm/sections.h> 31#include <asm/sections.h>
@@ -251,6 +252,47 @@ static void __init create_mapping(phys_addr_t phys, unsigned long virt,
251 } while (pgd++, addr = next, addr != end); 252 } while (pgd++, addr = next, addr != end);
252} 253}
253 254
255#ifdef CONFIG_EARLY_PRINTK
256/*
257 * Create an early I/O mapping using the pgd/pmd entries already populated
258 * in head.S as this function is called too early to allocated any memory. The
259 * mapping size is 2MB with 4KB pages or 64KB or 64KB pages.
260 */
261void __iomem * __init early_io_map(phys_addr_t phys, unsigned long virt)
262{
263 unsigned long size, mask;
264 bool page64k = IS_ENABLED(ARM64_64K_PAGES);
265 pgd_t *pgd;
266 pud_t *pud;
267 pmd_t *pmd;
268 pte_t *pte;
269
270 /*
271 * No early pte entries with !ARM64_64K_PAGES configuration, so using
272 * sections (pmd).
273 */
274 size = page64k ? PAGE_SIZE : SECTION_SIZE;
275 mask = ~(size - 1);
276
277 pgd = pgd_offset_k(virt);
278 pud = pud_offset(pgd, virt);
279 if (pud_none(*pud))
280 return NULL;
281 pmd = pmd_offset(pud, virt);
282
283 if (page64k) {
284 if (pmd_none(*pmd))
285 return NULL;
286 pte = pte_offset_kernel(pmd, virt);
287 set_pte(pte, __pte((phys & mask) | PROT_DEVICE_nGnRE));
288 } else {
289 set_pmd(pmd, __pmd((phys & mask) | PROT_SECT_DEVICE_nGnRE));
290 }
291
292 return (void __iomem *)((virt & mask) + (phys & ~mask));
293}
294#endif
295
254static void __init map_mem(void) 296static void __init map_mem(void)
255{ 297{
256 struct memblock_region *reg; 298 struct memblock_region *reg;