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