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