diff options
-rw-r--r-- | Documentation/arm64/memory.txt | 2 | ||||
-rw-r--r-- | arch/arm64/Kconfig.debug | 9 | ||||
-rw-r--r-- | arch/arm64/include/asm/io.h | 3 | ||||
-rw-r--r-- | arch/arm64/include/asm/memory.h | 1 | ||||
-rw-r--r-- | arch/arm64/include/asm/mmu.h | 1 | ||||
-rw-r--r-- | arch/arm64/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/arm64/kernel/early_printk.c | 118 | ||||
-rw-r--r-- | arch/arm64/kernel/head.S | 12 | ||||
-rw-r--r-- | arch/arm64/mm/mmu.c | 42 |
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 | ||
36 | ffffffbe00000000 ffffffbffbbfffff ~8GB [guard, future vmmemap] | 36 | ffffffbe00000000 ffffffbffbbfffff ~8GB [guard, future vmmemap] |
37 | 37 | ||
38 | ffffffbffbc00000 ffffffbffbdfffff 2MB earlyprintk device | ||
39 | |||
38 | ffffffbffbe00000 ffffffbffbe0ffff 64KB PCI I/O space | 40 | ffffffbffbe00000 ffffffbffbe0ffff 64KB PCI I/O space |
39 | 41 | ||
40 | ffffffbbffff0000 ffffffbcffffffff ~2MB [guard] | 42 | ffffffbbffff0000 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 | ||
27 | config 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 | |||
27 | endmenu | 36 | endmenu |
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 | ||
27 | extern void paging_init(void); | 27 | extern void paging_init(void); |
28 | extern void setup_mm_for_reboot(void); | 28 | extern void setup_mm_for_reboot(void); |
29 | extern 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 | |||
17 | arm64-obj-$(CONFIG_SMP) += smp.o | 17 | arm64-obj-$(CONFIG_SMP) += smp.o |
18 | arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o | 18 | arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o |
19 | arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o | 19 | arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o |
20 | arm64-obj-$(CONFIG_EARLY_PRINTK) += early_printk.o | ||
20 | 21 | ||
21 | obj-y += $(arm64-obj-y) vdso/ | 22 | obj-y += $(arm64-obj-y) vdso/ |
22 | obj-m += $(arm64-obj-m) | 23 | obj-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 | |||
28 | static void __iomem *early_base; | ||
29 | static void (*printch)(char ch); | ||
30 | |||
31 | /* | ||
32 | * PL011 single character TX. | ||
33 | */ | ||
34 | static 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 | |||
43 | struct earlycon_match { | ||
44 | const char *name; | ||
45 | void (*printch)(char ch); | ||
46 | }; | ||
47 | |||
48 | static const struct earlycon_match earlycon_match[] __initconst = { | ||
49 | { .name = "pl011", .printch = pl011_printch, }, | ||
50 | {} | ||
51 | }; | ||
52 | |||
53 | static 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 | |||
63 | static 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 | */ | ||
78 | static 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 | |||
118 | early_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 |
422 | 1: | 421 | 1: |
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 |
424 | ENDPROC(__create_page_tables) | 432 | ENDPROC(__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 | */ | ||
261 | void __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 | |||
254 | static void __init map_mem(void) | 296 | static void __init map_mem(void) |
255 | { | 297 | { |
256 | struct memblock_region *reg; | 298 | struct memblock_region *reg; |