root/arch/arm/kernel/atags_parse.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Tag parsing.
 *
 * Copyright (C) 1995-2001 Russell King
 */

/*
 * This is the traditional way of passing data to the kernel at boot time.  Rather
 * than passing a fixed inflexible structure to the kernel, we pass a list
 * of variable-sized tags to the kernel.  The first tag must be a ATAG_CORE
 * tag for the list to be recognised (to distinguish the tagged list from
 * a param_struct).  The list is terminated with a zero-length tag (this tag
 * is not parsed in any way).
 */

#include <linux/init.h>
#include <linux/initrd.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/root_dev.h>
#include <linux/screen_info.h>
#include <linux/memblock.h>
#include <uapi/linux/mount.h>

#include <asm/setup.h>
#include <asm/system_info.h>
#include <asm/page.h>
#include <asm/mach/arch.h>

#include "atags.h"

static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;

#ifndef MEM_SIZE
#define MEM_SIZE        (16*1024*1024)
#endif

static struct {
        struct tag_header hdr1;
        struct tag_core   core;
        struct tag_header hdr2;
        struct tag_mem32  mem;
        struct tag_header hdr3;
} default_tags __initdata = {
        { tag_size(tag_core), ATAG_CORE },
        { 1, PAGE_SIZE, 0xff },
        { tag_size(tag_mem32), ATAG_MEM },
        { MEM_SIZE },
        { 0, ATAG_NONE }
};

static int __init parse_tag_core(const struct tag *tag)
{
        if (tag->hdr.size > 2) {
                if ((tag->u.core.flags & 1) == 0)
                        root_mountflags &= ~MS_RDONLY;
                ROOT_DEV = old_decode_dev(tag->u.core.rootdev);
        }
        return 0;
}

__tagtable(ATAG_CORE, parse_tag_core);

static int __init parse_tag_mem32(const struct tag *tag)
{
        return arm_add_memory(tag->u.mem.start, tag->u.mem.size);
}

__tagtable(ATAG_MEM, parse_tag_mem32);

#if defined(CONFIG_ARCH_FOOTBRIDGE) && defined(CONFIG_VGA_CONSOLE)
static int __init parse_tag_videotext(const struct tag *tag)
{
        vgacon_screen_info.orig_x            = tag->u.videotext.x;
        vgacon_screen_info.orig_y            = tag->u.videotext.y;
        vgacon_screen_info.orig_video_page   = tag->u.videotext.video_page;
        vgacon_screen_info.orig_video_mode   = tag->u.videotext.video_mode;
        vgacon_screen_info.orig_video_cols   = tag->u.videotext.video_cols;
        vgacon_screen_info.orig_video_ega_bx = tag->u.videotext.video_ega_bx;
        vgacon_screen_info.orig_video_lines  = tag->u.videotext.video_lines;
        vgacon_screen_info.orig_video_isVGA  = tag->u.videotext.video_isvga;
        vgacon_screen_info.orig_video_points = tag->u.videotext.video_points;
        return 0;
}

__tagtable(ATAG_VIDEOTEXT, parse_tag_videotext);
#endif

#ifdef CONFIG_BLK_DEV_RAM
static int __init parse_tag_ramdisk(const struct tag *tag)
{
        rd_image_start = tag->u.ramdisk.start;

        if (tag->u.ramdisk.size)
                rd_size = tag->u.ramdisk.size;

        return 0;
}

__tagtable(ATAG_RAMDISK, parse_tag_ramdisk);
#endif

static int __init parse_tag_serialnr(const struct tag *tag)
{
        system_serial_low = tag->u.serialnr.low;
        system_serial_high = tag->u.serialnr.high;
        return 0;
}

__tagtable(ATAG_SERIAL, parse_tag_serialnr);

static int __init parse_tag_revision(const struct tag *tag)
{
        system_rev = tag->u.revision.rev;
        return 0;
}

__tagtable(ATAG_REVISION, parse_tag_revision);

static int __init parse_tag_cmdline(const struct tag *tag)
{
#if defined(CONFIG_CMDLINE_EXTEND)
        strlcat(default_command_line, " ", COMMAND_LINE_SIZE);
        strlcat(default_command_line, tag->u.cmdline.cmdline,
                COMMAND_LINE_SIZE);
#elif defined(CONFIG_CMDLINE_FORCE)
        pr_warn("Ignoring tag cmdline (using the default kernel command line)\n");
#else
        strscpy(default_command_line, tag->u.cmdline.cmdline,
                COMMAND_LINE_SIZE);
#endif
        return 0;
}

__tagtable(ATAG_CMDLINE, parse_tag_cmdline);

/*
 * Scan the tag table for this tag, and call its parse function.
 * The tag table is built by the linker from all the __tagtable
 * declarations.
 */
static int __init parse_tag(const struct tag *tag)
{
        extern struct tagtable __tagtable_begin, __tagtable_end;
        struct tagtable *t;

        for (t = &__tagtable_begin; t < &__tagtable_end; t++)
                if (tag->hdr.tag == t->tag) {
                        t->parse(tag);
                        break;
                }

        return t < &__tagtable_end;
}

/*
 * Parse all tags in the list, checking both the global and architecture
 * specific tag tables.
 */
static void __init parse_tags(const struct tag *t)
{
        for (; t->hdr.size; t = tag_next(t))
                if (!parse_tag(t))
                        pr_warn("Ignoring unrecognised tag 0x%08x\n",
                                t->hdr.tag);
}

static void __init squash_mem_tags(struct tag *tag)
{
        for (; tag->hdr.size; tag = tag_next(tag))
                if (tag->hdr.tag == ATAG_MEM)
                        tag->hdr.tag = ATAG_NONE;
}

const struct machine_desc * __init
setup_machine_tags(void *atags_vaddr, unsigned int machine_nr)
{
        struct tag *tags = (struct tag *)&default_tags;
        const struct machine_desc *mdesc = NULL, *p;
        char *from = default_command_line;

        default_tags.mem.start = PHYS_OFFSET;

        /*
         * locate machine in the list of supported machines.
         */
        for_each_machine_desc(p)
                if (machine_nr == p->nr) {
                        pr_info("Machine: %s\n", p->name);
                        mdesc = p;
                        break;
                }

        if (!mdesc)
                return NULL;

        if (atags_vaddr)
                tags = atags_vaddr;
        else if (mdesc->atag_offset)
                tags = (void *)(PAGE_OFFSET + mdesc->atag_offset);

#if defined(CONFIG_DEPRECATED_PARAM_STRUCT)
        /*
         * If we have the old style parameters, convert them to
         * a tag list.
         */
        if (tags->hdr.tag != ATAG_CORE)
                convert_to_tag_list(tags);
#endif
        if (tags->hdr.tag != ATAG_CORE) {
                early_print("Warning: Neither atags nor dtb found\n");
                tags = (struct tag *)&default_tags;
        }

        if (mdesc->fixup)
                mdesc->fixup(tags, &from);

        if (tags->hdr.tag == ATAG_CORE) {
                if (memblock_phys_mem_size())
                        squash_mem_tags(tags);
                save_atags(tags);
                parse_tags(tags);
        }

        /* parse_early_param needs a boot_command_line */
        strscpy(boot_command_line, from, COMMAND_LINE_SIZE);

        return mdesc;
}