| 1 | /* |
| 2 | * Tag parsing. |
| 3 | * |
| 4 | * Copyright (C) 1995-2001 Russell King |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License version 2 as |
| 8 | * published by the Free Software Foundation. |
| 9 | */ |
| 10 | |
| 11 | /* |
| 12 | * This is the traditional way of passing data to the kernel at boot time. Rather |
| 13 | * than passing a fixed inflexible structure to the kernel, we pass a list |
| 14 | * of variable-sized tags to the kernel. The first tag must be a ATAG_CORE |
| 15 | * tag for the list to be recognised (to distinguish the tagged list from |
| 16 | * a param_struct). The list is terminated with a zero-length tag (this tag |
| 17 | * is not parsed in any way). |
| 18 | */ |
| 19 | |
| 20 | #include <linux/init.h> |
| 21 | #include <linux/kernel.h> |
| 22 | #include <linux/fs.h> |
| 23 | #include <linux/root_dev.h> |
| 24 | #include <linux/screen_info.h> |
| 25 | #include <linux/memblock.h> |
| 26 | |
| 27 | #include <asm/setup.h> |
| 28 | #include <asm/system_info.h> |
| 29 | #include <asm/page.h> |
| 30 | #include <asm/mach/arch.h> |
| 31 | |
| 32 | #include "atags.h" |
| 33 | |
| 34 | static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE; |
| 35 | |
| 36 | #ifndef MEM_SIZE |
| 37 | #define MEM_SIZE (16*1024*1024) |
| 38 | #endif |
| 39 | |
| 40 | static struct { |
| 41 | struct tag_header hdr1; |
| 42 | struct tag_core core; |
| 43 | struct tag_header hdr2; |
| 44 | struct tag_mem32 mem; |
| 45 | struct tag_header hdr3; |
| 46 | } default_tags __initdata = { |
| 47 | { tag_size(tag_core), ATAG_CORE }, |
| 48 | { 1, PAGE_SIZE, 0xff }, |
| 49 | { tag_size(tag_mem32), ATAG_MEM }, |
| 50 | { MEM_SIZE }, |
| 51 | { 0, ATAG_NONE } |
| 52 | }; |
| 53 | |
| 54 | static int __init parse_tag_core(const struct tag *tag) |
| 55 | { |
| 56 | if (tag->hdr.size > 2) { |
| 57 | if ((tag->u.core.flags & 1) == 0) |
| 58 | root_mountflags &= ~MS_RDONLY; |
| 59 | ROOT_DEV = old_decode_dev(tag->u.core.rootdev); |
| 60 | } |
| 61 | return 0; |
| 62 | } |
| 63 | |
| 64 | __tagtable(ATAG_CORE, parse_tag_core); |
| 65 | |
| 66 | static int __init parse_tag_mem32(const struct tag *tag) |
| 67 | { |
| 68 | return arm_add_memory(tag->u.mem.start, tag->u.mem.size); |
| 69 | } |
| 70 | |
| 71 | __tagtable(ATAG_MEM, parse_tag_mem32); |
| 72 | |
| 73 | #if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE) |
| 74 | static int __init parse_tag_videotext(const struct tag *tag) |
| 75 | { |
| 76 | screen_info.orig_x = tag->u.videotext.x; |
| 77 | screen_info.orig_y = tag->u.videotext.y; |
| 78 | screen_info.orig_video_page = tag->u.videotext.video_page; |
| 79 | screen_info.orig_video_mode = tag->u.videotext.video_mode; |
| 80 | screen_info.orig_video_cols = tag->u.videotext.video_cols; |
| 81 | screen_info.orig_video_ega_bx = tag->u.videotext.video_ega_bx; |
| 82 | screen_info.orig_video_lines = tag->u.videotext.video_lines; |
| 83 | screen_info.orig_video_isVGA = tag->u.videotext.video_isvga; |
| 84 | screen_info.orig_video_points = tag->u.videotext.video_points; |
| 85 | return 0; |
| 86 | } |
| 87 | |
| 88 | __tagtable(ATAG_VIDEOTEXT, parse_tag_videotext); |
| 89 | #endif |
| 90 | |
| 91 | #ifdef CONFIG_BLK_DEV_RAM |
| 92 | static int __init parse_tag_ramdisk(const struct tag *tag) |
| 93 | { |
| 94 | extern int rd_size, rd_image_start, rd_prompt, rd_doload; |
| 95 | |
| 96 | rd_image_start = tag->u.ramdisk.start; |
| 97 | rd_doload = (tag->u.ramdisk.flags & 1) == 0; |
| 98 | rd_prompt = (tag->u.ramdisk.flags & 2) == 0; |
| 99 | |
| 100 | if (tag->u.ramdisk.size) |
| 101 | rd_size = tag->u.ramdisk.size; |
| 102 | |
| 103 | return 0; |
| 104 | } |
| 105 | |
| 106 | __tagtable(ATAG_RAMDISK, parse_tag_ramdisk); |
| 107 | #endif |
| 108 | |
| 109 | static int __init parse_tag_serialnr(const struct tag *tag) |
| 110 | { |
| 111 | system_serial_low = tag->u.serialnr.low; |
| 112 | system_serial_high = tag->u.serialnr.high; |
| 113 | return 0; |
| 114 | } |
| 115 | |
| 116 | __tagtable(ATAG_SERIAL, parse_tag_serialnr); |
| 117 | |
| 118 | static int __init parse_tag_revision(const struct tag *tag) |
| 119 | { |
| 120 | system_rev = tag->u.revision.rev; |
| 121 | return 0; |
| 122 | } |
| 123 | |
| 124 | __tagtable(ATAG_REVISION, parse_tag_revision); |
| 125 | |
| 126 | static int __init parse_tag_cmdline(const struct tag *tag) |
| 127 | { |
| 128 | #if defined(CONFIG_CMDLINE_EXTEND) |
| 129 | strlcat(default_command_line, " ", COMMAND_LINE_SIZE); |
| 130 | strlcat(default_command_line, tag->u.cmdline.cmdline, |
| 131 | COMMAND_LINE_SIZE); |
| 132 | #elif defined(CONFIG_CMDLINE_FORCE) |
| 133 | pr_warn("Ignoring tag cmdline (using the default kernel command line)\n"); |
| 134 | #else |
| 135 | strlcpy(default_command_line, tag->u.cmdline.cmdline, |
| 136 | COMMAND_LINE_SIZE); |
| 137 | #endif |
| 138 | return 0; |
| 139 | } |
| 140 | |
| 141 | __tagtable(ATAG_CMDLINE, parse_tag_cmdline); |
| 142 | |
| 143 | /* |
| 144 | * Scan the tag table for this tag, and call its parse function. |
| 145 | * The tag table is built by the linker from all the __tagtable |
| 146 | * declarations. |
| 147 | */ |
| 148 | static int __init parse_tag(const struct tag *tag) |
| 149 | { |
| 150 | extern struct tagtable __tagtable_begin, __tagtable_end; |
| 151 | struct tagtable *t; |
| 152 | |
| 153 | for (t = &__tagtable_begin; t < &__tagtable_end; t++) |
| 154 | if (tag->hdr.tag == t->tag) { |
| 155 | t->parse(tag); |
| 156 | break; |
| 157 | } |
| 158 | |
| 159 | return t < &__tagtable_end; |
| 160 | } |
| 161 | |
| 162 | /* |
| 163 | * Parse all tags in the list, checking both the global and architecture |
| 164 | * specific tag tables. |
| 165 | */ |
| 166 | static void __init parse_tags(const struct tag *t) |
| 167 | { |
| 168 | for (; t->hdr.size; t = tag_next(t)) |
| 169 | if (!parse_tag(t)) |
| 170 | pr_warn("Ignoring unrecognised tag 0x%08x\n", |
| 171 | t->hdr.tag); |
| 172 | } |
| 173 | |
| 174 | static void __init squash_mem_tags(struct tag *tag) |
| 175 | { |
| 176 | for (; tag->hdr.size; tag = tag_next(tag)) |
| 177 | if (tag->hdr.tag == ATAG_MEM) |
| 178 | tag->hdr.tag = ATAG_NONE; |
| 179 | } |
| 180 | |
| 181 | const struct machine_desc * __init |
| 182 | setup_machine_tags(phys_addr_t __atags_pointer, unsigned int machine_nr) |
| 183 | { |
| 184 | struct tag *tags = (struct tag *)&default_tags; |
| 185 | const struct machine_desc *mdesc = NULL, *p; |
| 186 | char *from = default_command_line; |
| 187 | |
| 188 | default_tags.mem.start = PHYS_OFFSET; |
| 189 | |
| 190 | /* |
| 191 | * locate machine in the list of supported machines. |
| 192 | */ |
| 193 | for_each_machine_desc(p) |
| 194 | if (machine_nr == p->nr) { |
| 195 | pr_info("Machine: %s\n", p->name); |
| 196 | mdesc = p; |
| 197 | break; |
| 198 | } |
| 199 | |
| 200 | if (!mdesc) { |
| 201 | early_print("\nError: unrecognized/unsupported machine ID" |
| 202 | " (r1 = 0x%08x).\n\n", machine_nr); |
| 203 | dump_machine_table(); /* does not return */ |
| 204 | } |
| 205 | |
| 206 | if (__atags_pointer) |
| 207 | tags = phys_to_virt(__atags_pointer); |
| 208 | else if (mdesc->atag_offset) |
| 209 | tags = (void *)(PAGE_OFFSET + mdesc->atag_offset); |
| 210 | |
| 211 | #if defined(CONFIG_DEPRECATED_PARAM_STRUCT) |
| 212 | /* |
| 213 | * If we have the old style parameters, convert them to |
| 214 | * a tag list. |
| 215 | */ |
| 216 | if (tags->hdr.tag != ATAG_CORE) |
| 217 | convert_to_tag_list(tags); |
| 218 | #endif |
| 219 | if (tags->hdr.tag != ATAG_CORE) { |
| 220 | early_print("Warning: Neither atags nor dtb found\n"); |
| 221 | tags = (struct tag *)&default_tags; |
| 222 | } |
| 223 | |
| 224 | if (mdesc->fixup) |
| 225 | mdesc->fixup(tags, &from); |
| 226 | |
| 227 | if (tags->hdr.tag == ATAG_CORE) { |
| 228 | if (memblock_phys_mem_size()) |
| 229 | squash_mem_tags(tags); |
| 230 | save_atags(tags); |
| 231 | parse_tags(tags); |
| 232 | } |
| 233 | |
| 234 | /* parse_early_param needs a boot_command_line */ |
| 235 | strlcpy(boot_command_line, from, COMMAND_LINE_SIZE); |
| 236 | |
| 237 | return mdesc; |
| 238 | } |