1/*
2 * Copyright 2014, General Dynamics C4 Systems
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7#include <config.h>
8#include <util.h>
9#include <machine/io.h>
10#include <arch/kernel/cmdline.h>
11#include <arch/kernel/boot_sys.h>
12#include <linker.h>
13#include <plat/machine/io.h>
14
15/* 'cmdline_val' is declared globally because of a C-subset restriction.
16 * It is only used in cmdline_parse(), which therefore is non-reentrant.
17 */
18#define MAX_CMDLINE_VAL_LEN 1000
19BOOT_BSS
20char cmdline_val[MAX_CMDLINE_VAL_LEN];
21
22/* workaround because string literals are not supported by C parser */
23const char cmdline_str_max_num_nodes[]  = {'m', 'a', 'x', '_', 'n', 'u', 'm', '_', 'n', 'o', 'd', 'e', 's', 0};
24const char cmdline_str_num_sh_frames[]  = {'n', 'u', 'm', '_', 's', 'h', '_', 'f', 'r', 'a', 'm', 'e', 's', 0};
25const char cmdline_str_disable_iommu[]  = {'d', 'i', 's', 'a', 'b', 'l', 'e', '_', 'i', 'o', 'm', 'm', 'u', 0};
26
27static int is_space(char c)
28{
29    return c <= ' ';
30}
31
32static int UNUSED parse_opt(const char *cmdline, const char *opt, char *value, int bufsize)
33{
34    int len = -1;
35    const char *optptr = NULL;
36
37    while (true) {
38        for (; is_space(*cmdline) && (*cmdline != 0); cmdline++);
39        if (*cmdline == 0) {
40            break;
41        }
42
43        for (optptr = opt; *optptr && *cmdline && (*cmdline != '=') && !is_space(*cmdline)
44             && (*optptr == *cmdline); optptr++, cmdline++);
45
46        if (*optptr == '\0' && *cmdline == '=') {
47            cmdline++;
48
49            for (len = 0; !is_space(*cmdline) && (len < bufsize - 1); cmdline++, len++) {
50                value[len] = *cmdline;
51            }
52            if (bufsize) {
53                value[len] = '\0';
54            }
55        }
56        for (; !is_space(*cmdline); cmdline++);
57    }
58
59    return len;
60}
61
62static int parse_bool(const char *cmdline, const char *opt)
63{
64    const char *optptr = NULL;
65
66    while (1) {
67        for (; is_space(*cmdline) && (*cmdline != 0); cmdline++);
68        if (*cmdline == 0) {
69            return 0;
70        }
71
72        for (optptr = opt; *optptr && *cmdline && !is_space(*cmdline) && (*optptr == *cmdline); optptr++, cmdline++);
73
74        if (*optptr == '\0' && is_space(*cmdline)) {
75            return 1;
76        } else {
77            for (; !is_space(*cmdline); cmdline++);
78        }
79    }
80}
81
82static void UNUSED parse_uint16_array(char *str, uint16_t *array, int array_size)
83{
84    char *last;
85    int   i = 0;
86    int   v;
87
88    while (str && i < array_size) {
89        for (last = str; *str && *str != ','; str++);
90        if (*str == 0) {
91            str = 0;
92        } else {
93            *str = 0;
94            str++;
95        }
96        v = str_to_long(last);
97        if (v == -1) {
98            array[i] = 0;
99        } else {
100            array[i] = v;
101        }
102        i++;
103    }
104}
105
106void cmdline_parse(const char *cmdline, cmdline_opt_t *cmdline_opt)
107{
108#if defined(CONFIG_PRINTING) || defined(CONFIG_DEBUG_BUILD)
109    /* use BIOS data area to read serial configuration. The BDA is not
110     * fully standardized and parts are absolete. See http://wiki.osdev.org/Memory_Map_(x86)#BIOS_Data_Area_.28BDA.29
111     * for an explanation */
112    const unsigned short *bda_port = (unsigned short *)0x400;
113    const unsigned short *bda_equi = (unsigned short *)0x410;
114    int const bda_ports_count       = (*bda_equi >> 9) & 0x7;
115#endif
116
117#ifdef CONFIG_PRINTING
118    /* initialise to default or use BDA if available */
119    cmdline_opt->console_port = bda_ports_count && *bda_port ? *bda_port : 0x3f8;
120
121    if (parse_opt(cmdline, "console_port", cmdline_val, MAX_CMDLINE_VAL_LEN) != -1) {
122        parse_uint16_array(cmdline_val, &cmdline_opt->console_port, 1);
123    }
124
125    /* initialise console ports to enable debug output */
126    if (cmdline_opt->console_port) {
127        serial_init(cmdline_opt->console_port);
128        x86KSconsolePort = cmdline_opt->console_port;
129    }
130
131    /* only start printing here after having parsed/set/initialised the console_port */
132    printf("\nBoot config: parsing cmdline '%s'\n", cmdline);
133
134    if (cmdline_opt->console_port) {
135        printf("Boot config: console_port = 0x%x\n", cmdline_opt->console_port);
136    }
137#endif
138
139#ifdef CONFIG_DEBUG_BUILD
140    /* initialise to default or use BDA if available */
141    cmdline_opt->debug_port = bda_ports_count && *bda_port ? *bda_port : 0x3f8;
142    if (parse_opt(cmdline, "debug_port", cmdline_val, MAX_CMDLINE_VAL_LEN) != -1) {
143        parse_uint16_array(cmdline_val, &cmdline_opt->debug_port, 1);
144    }
145
146    /* initialise debug ports */
147    if (cmdline_opt->debug_port) {
148        serial_init(cmdline_opt->debug_port);
149        x86KSdebugPort = cmdline_opt->debug_port;
150        printf("Boot config: debug_port = 0x%x\n", cmdline_opt->debug_port);
151    }
152#endif
153
154    cmdline_opt->disable_iommu = parse_bool(cmdline, cmdline_str_disable_iommu);
155    printf("Boot config: disable_iommu = %s\n", cmdline_opt->disable_iommu ? "true" : "false");
156}
157