Commit f657af0f authored by Russell King's avatar Russell King

[ARM] Provide more early command line parsing.

We need to parse the command line arguments not only for the memory
parameters, but also CPU cache policies.  Rather than extending the
early parsing in arch/arm/kernel/setup.c, we make this a generic
feature.  The parameters and their parsing function can now be
placed along side the code which makes use of the parsed information.
parent 308e8f7a
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/utsname.h> #include <linux/utsname.h>
#include <linux/initrd.h> #include <linux/blk.h>
#include <linux/console.h> #include <linux/console.h>
#include <linux/bootmem.h> #include <linux/bootmem.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
...@@ -323,24 +323,29 @@ static struct machine_desc * __init setup_machine(unsigned int nr) ...@@ -323,24 +323,29 @@ static struct machine_desc * __init setup_machine(unsigned int nr)
return list; return list;
} }
static void __init early_initrd(char **p)
{
unsigned long start, size;
start = memparse(*p, p);
if (**p == ',') {
size = memparse((*p) + 1, p);
phys_initrd_start = start;
phys_initrd_size = size;
}
}
__early_param("initrd=", early_initrd);
/* /*
* Initial parsing of the command line. We need to pick out the * Pick out the memory size. We look for mem=size@start,
* memory size. We look for mem=size@start, where start and size * where start and size are "size[KkMm]"
* are "size[KkMm]"
*/ */
static void __init static void __init early_mem(char **p)
parse_cmdline(struct meminfo *mi, char **cmdline_p, char *from)
{ {
char c = ' ', *to = command_line; static int usermem __initdata = 0;
int usermem = 0, len = 0;
for (;;) {
if (c == ' ' && !memcmp(from, "mem=", 4)) {
unsigned long size, start; unsigned long size, start;
if (to != command_line)
to -= 1;
/* /*
* If the user specifies memory size, we * If the user specifies memory size, we
* blow away any automatically generated * blow away any automatically generated
...@@ -348,33 +353,47 @@ parse_cmdline(struct meminfo *mi, char **cmdline_p, char *from) ...@@ -348,33 +353,47 @@ parse_cmdline(struct meminfo *mi, char **cmdline_p, char *from)
*/ */
if (usermem == 0) { if (usermem == 0) {
usermem = 1; usermem = 1;
mi->nr_banks = 0; meminfo.nr_banks = 0;
} }
start = PHYS_OFFSET; start = PHYS_OFFSET;
size = memparse(from + 4, &from); size = memparse(*p, p);
if (*from == '@') if (**p == '@')
start = memparse(from + 1, &from); start = memparse(*p + 1, p);
mi->bank[mi->nr_banks].start = start;
mi->bank[mi->nr_banks].size = size;
mi->bank[mi->nr_banks].node = PHYS_TO_NID(start);
mi->nr_banks += 1;
} else if (c == ' ' && !memcmp(from, "initrd=", 7)) {
unsigned long start, size;
/* meminfo.bank[meminfo.nr_banks].start = start;
* Remove space character meminfo.bank[meminfo.nr_banks].size = size;
meminfo.bank[meminfo.nr_banks].node = PHYS_TO_NID(start);
meminfo.nr_banks += 1;
}
__early_param("mem=", early_mem);
/*
* Initial parsing of the command line.
*/ */
static void __init parse_cmdline(char **cmdline_p, char *from)
{
char c = ' ', *to = command_line;
int len = 0;
for (;;) {
if (c == ' ') {
extern struct early_params __early_begin, __early_end;
struct early_params *p;
for (p = &__early_begin; p < &__early_end; p++) {
int len = strlen(p->arg);
if (memcmp(from, p->arg, len) == 0) {
if (to != command_line) if (to != command_line)
to -= 1; to -= 1;
from += len;
p->fn(&from);
start = memparse(from + 7, &from); while (*from != ' ' && *from != '\0')
if (*from == ',') { from++;
size = memparse(from + 1, &from); break;
}
phys_initrd_start = start;
phys_initrd_size = size;
} }
} }
c = *from++; c = *from++;
...@@ -536,6 +555,8 @@ __tagtable(ATAG_RAMDISK, parse_tag_ramdisk); ...@@ -536,6 +555,8 @@ __tagtable(ATAG_RAMDISK, parse_tag_ramdisk);
static int __init parse_tag_initrd(const struct tag *tag) static int __init parse_tag_initrd(const struct tag *tag)
{ {
printk(KERN_WARNING "ATAG_INITRD is deprecated; "
"please update your bootloader.\n");
phys_initrd_start = __virt_to_phys(tag->u.initrd.start); phys_initrd_start = __virt_to_phys(tag->u.initrd.start);
phys_initrd_size = tag->u.initrd.size; phys_initrd_size = tag->u.initrd.size;
return 0; return 0;
...@@ -668,7 +689,7 @@ void __init setup_arch(char **cmdline_p) ...@@ -668,7 +689,7 @@ void __init setup_arch(char **cmdline_p)
memcpy(saved_command_line, from, COMMAND_LINE_SIZE); memcpy(saved_command_line, from, COMMAND_LINE_SIZE);
saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
parse_cmdline(&meminfo, cmdline_p, from); parse_cmdline(cmdline_p, from);
bootmem_init(&meminfo); bootmem_init(&meminfo);
paging_init(&meminfo, mdesc); paging_init(&meminfo, mdesc);
request_standard_resources(&meminfo, mdesc); request_standard_resources(&meminfo, mdesc);
......
...@@ -24,30 +24,82 @@ ...@@ -24,30 +24,82 @@
#include <asm/mach/map.h> #include <asm/mach/map.h>
static unsigned int cachepolicy __initdata = PMD_SECT_WB;
static unsigned int ecc_mask __initdata = 0;
struct cachepolicy {
char *policy;
unsigned int cr_mask;
unsigned int pmd;
};
static struct cachepolicy cache_policies[] __initdata = {
{ "uncached", CR1_W|CR1_C, PMD_SECT_UNCACHED },
{ "buffered", CR1_C, PMD_SECT_BUFFERED },
{ "writethrough", 0, PMD_SECT_WT },
#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
{ "writeback", 0, PMD_SECT_WB },
{ "writealloc", 0, PMD_SECT_WBWA }
#endif
};
/* /*
* These are useful for identifing cache coherency * These are useful for identifing cache coherency
* problems by allowing the cache or the cache and * problems by allowing the cache or the cache and
* writebuffer to be turned off. (Note: the write * writebuffer to be turned off. (Note: the write
* buffer should not be on and the cache off). * buffer should not be on and the cache off).
*/ */
static int __init nocache_setup(char *__unused) static void __init early_cachepolicy(char **p)
{ {
cr_alignment &= ~CR1_C; int i;
cr_no_alignment &= ~CR1_C;
for (i = 0; i < ARRAY_SIZE(cache_policies); i++) {
int len = strlen(cache_policies[i].policy);
if (memcmp(*p, cache_policies[i].policy, len) == 0) {
cachepolicy = cache_policies[i].pmd;
cr_alignment &= ~cache_policies[i].cr_mask;
cr_no_alignment &= ~cache_policies[i].cr_mask;
*p += len;
break;
}
}
if (i == ARRAY_SIZE(cache_policies))
printk(KERN_ERR "ERROR: unknown or unsupported cache policy\n");
flush_cache_all(); flush_cache_all();
set_cr(cr_alignment); set_cr(cr_alignment);
return 1;
} }
static int __init nowrite_setup(char *__unused) static void __init early_nocache(char **__unused)
{ {
cr_alignment &= ~(CR1_W|CR1_C); char *p = "buffered";
cr_no_alignment &= ~(CR1_W|CR1_C); printk(KERN_WARNING "nocache is deprecated; use cachepolicy=%s\n", p);
flush_cache_all(); early_cachepolicy(&p);
set_cr(cr_alignment); }
return 1;
static void __init early_nowrite(char **__unused)
{
char *p = "uncached";
printk(KERN_WARNING "nowb is deprecated; use cachepolicy=%s\n", p);
early_cachepolicy(&p);
}
static void __init early_ecc(char **p)
{
if (memcmp(*p, "on", 2) == 0) {
ecc_mask = PMD_PROTECTION;
*p += 2;
} else if (memcmp(*p, "off", 3) == 0) {
ecc_mask = 0;
*p += 3;
}
} }
__early_param("nocache", early_nocache);
__early_param("nowb", early_nowrite);
__early_param("cachepolicy=", early_cachepolicy);
__early_param("ecc=", early_ecc);
static int __init noalign_setup(char *__unused) static int __init noalign_setup(char *__unused)
{ {
cr_alignment &= ~CR1_A; cr_alignment &= ~CR1_A;
...@@ -57,8 +109,6 @@ static int __init noalign_setup(char *__unused) ...@@ -57,8 +109,6 @@ static int __init noalign_setup(char *__unused)
} }
__setup("noalign", noalign_setup); __setup("noalign", noalign_setup);
__setup("nocache", nocache_setup);
__setup("nowb", nowrite_setup);
#define FIRST_KERNEL_PGD_NR (FIRST_USER_PGD_NR + USER_PTRS_PER_PGD) #define FIRST_KERNEL_PGD_NR (FIRST_USER_PGD_NR + USER_PTRS_PER_PGD)
...@@ -231,32 +281,20 @@ static struct mem_types mem_types[] __initdata = { ...@@ -231,32 +281,20 @@ static struct mem_types mem_types[] __initdata = {
.domain = DOMAIN_IO, .domain = DOMAIN_IO,
}, },
[MT_CACHECLEAN] = { [MT_CACHECLEAN] = {
.prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
L_PTE_CACHEABLE | L_PTE_BUFFERABLE,
.prot_l1 = PMD_TYPE_TABLE | PMD_BIT4,
.prot_sect = PMD_TYPE_SECT | PMD_BIT4, .prot_sect = PMD_TYPE_SECT | PMD_BIT4,
.domain = DOMAIN_KERNEL, .domain = DOMAIN_KERNEL,
}, },
[MT_MINICLEAN] = { [MT_MINICLEAN] = {
.prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
L_PTE_CACHEABLE,
.prot_l1 = PMD_TYPE_TABLE | PMD_BIT4,
.prot_sect = PMD_TYPE_SECT | PMD_BIT4 | PMD_SECT_MINICACHE, .prot_sect = PMD_TYPE_SECT | PMD_BIT4 | PMD_SECT_MINICACHE,
.domain = DOMAIN_KERNEL, .domain = DOMAIN_KERNEL,
}, },
[MT_VECTORS] = { [MT_VECTORS] = {
.prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY | .prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
L_PTE_CACHEABLE | L_PTE_BUFFERABLE |
L_PTE_EXEC, L_PTE_EXEC,
.prot_l1 = PMD_TYPE_TABLE | PMD_BIT4, .prot_l1 = PMD_TYPE_TABLE | PMD_BIT4,
.prot_sect = PMD_TYPE_SECT | PMD_BIT4,
.domain = DOMAIN_USER, .domain = DOMAIN_USER,
}, },
[MT_MEMORY] = { [MT_MEMORY] = {
.prot_pte = L_PTE_PRESENT | L_PTE_YOUNG | L_PTE_DIRTY |
L_PTE_CACHEABLE | L_PTE_BUFFERABLE |
L_PTE_EXEC | L_PTE_WRITE,
.prot_l1 = PMD_TYPE_TABLE | PMD_BIT4,
.prot_sect = PMD_TYPE_SECT | PMD_BIT4 | PMD_SECT_AP_WRITE, .prot_sect = PMD_TYPE_SECT | PMD_BIT4 | PMD_SECT_AP_WRITE,
.domain = DOMAIN_KERNEL, .domain = DOMAIN_KERNEL,
} }
...@@ -268,37 +306,50 @@ static struct mem_types mem_types[] __initdata = { ...@@ -268,37 +306,50 @@ static struct mem_types mem_types[] __initdata = {
static void __init build_mem_type_table(void) static void __init build_mem_type_table(void)
{ {
int cpu_arch = cpu_architecture(); int cpu_arch = cpu_architecture();
#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH const char *policy;
int writethrough = 1;
#else
int writethrough = 0;
#endif
int writealloc = 0, ecc = 0;
if (cpu_arch < CPU_ARCH_ARMv5) { /*
writealloc = 0; * ARMv5 can use ECC memory.
ecc = 0; */
if (cpu_arch == CPU_ARCH_ARMv5) {
mem_types[MT_VECTORS].prot_l1 |= ecc_mask;
mem_types[MT_MEMORY].prot_sect |= ecc_mask;
} else {
mem_types[MT_MINICLEAN].prot_sect &= ~PMD_SECT_TEX(1); mem_types[MT_MINICLEAN].prot_sect &= ~PMD_SECT_TEX(1);
if (cachepolicy == PMD_SECT_WBWA)
cachepolicy = PMD_SECT_WB;
ecc_mask = 0;
} }
if (writethrough) { mem_types[MT_MEMORY].prot_sect |= cachepolicy;
switch (cachepolicy) {
default:
case PMD_SECT_UNCACHED:
policy = "uncached";
break;
case PMD_SECT_BUFFERED:
mem_types[MT_VECTORS].prot_pte |= PTE_BUFFERABLE;
policy = "buffered";
break;
case PMD_SECT_WT:
mem_types[MT_VECTORS].prot_pte |= PTE_BUFFERABLE|PTE_CACHEABLE;
mem_types[MT_CACHECLEAN].prot_sect |= PMD_SECT_WT; mem_types[MT_CACHECLEAN].prot_sect |= PMD_SECT_WT;
mem_types[MT_VECTORS].prot_sect |= PMD_SECT_WT; policy = "write through";
mem_types[MT_MEMORY].prot_sect |= PMD_SECT_WT; break;
} else { case PMD_SECT_WB:
mem_types[MT_VECTORS].prot_pte |= PTE_BUFFERABLE|PTE_CACHEABLE;
mem_types[MT_CACHECLEAN].prot_sect |= PMD_SECT_WB; mem_types[MT_CACHECLEAN].prot_sect |= PMD_SECT_WB;
mem_types[MT_VECTORS].prot_sect |= PMD_SECT_WB; policy = "write back";
break;
if (writealloc) case PMD_SECT_WBWA:
mem_types[MT_MEMORY].prot_sect |= PMD_SECT_WBWA; mem_types[MT_VECTORS].prot_pte |= PTE_BUFFERABLE|PTE_CACHEABLE;
else mem_types[MT_CACHECLEAN].prot_sect |= PMD_SECT_WB;
mem_types[MT_MEMORY].prot_sect |= PMD_SECT_WB; policy = "write back, write allocate";
} break;
if (ecc) {
mem_types[MT_VECTORS].prot_sect |= PMD_PROTECTION;
mem_types[MT_MEMORY].prot_sect |= PMD_PROTECTION;
} }
printk("Memory policy: ECC %sabled, Data cache %s\n",
ecc_mask ? "en" : "dis", policy);
} }
/* /*
...@@ -330,6 +381,14 @@ static void __init create_mapping(struct map_desc *md) ...@@ -330,6 +381,14 @@ static void __init create_mapping(struct map_desc *md)
off = md->physical - virt; off = md->physical - virt;
length = md->length; length = md->length;
if (mem_types[md->type].prot_l1 == 0 &&
(virt & 0xfffff || (virt + off) & 0xfffff || (virt + length) & 0xfffff)) {
printk(KERN_WARNING "MM: map for 0x%08lx at 0x%08lx can not "
"be mapped using pages, ignoring.\n",
md->physical, md->virtual);
return;
}
while ((virt & 0xfffff || (virt + off) & 0xfffff) && length >= PAGE_SIZE) { while ((virt & 0xfffff || (virt + off) & 0xfffff) && length >= PAGE_SIZE) {
alloc_init_page(virt, virt + off, prot_l1, prot_pte); alloc_init_page(virt, virt + off, prot_l1, prot_pte);
......
...@@ -35,6 +35,9 @@ SECTIONS ...@@ -35,6 +35,9 @@ SECTIONS
__setup_start = .; __setup_start = .;
*(.init.setup) *(.init.setup)
__setup_end = .; __setup_end = .;
__early_begin = .;
*(__early_param)
__early_end = .;
__start___param = .; __start___param = .;
*(__param) *(__param)
__stop___param = .; __stop___param = .;
......
...@@ -202,4 +202,17 @@ struct meminfo { ...@@ -202,4 +202,17 @@ struct meminfo {
extern struct meminfo meminfo; extern struct meminfo meminfo;
/*
* Early command line parameters.
*/
struct early_params {
const char *arg;
void (*fn)(char **p);
};
#define __early_param(name,fn) \
static struct early_params __early_##fn \
__attribute__((section("__early_param"), unused)) = \
{ name, fn }
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment