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
19 BOOT_BSS
20 char cmdline_val[MAX_CMDLINE_VAL_LEN];
21 
22 /* workaround because string literals are not supported by C parser */
23 const char cmdline_str_max_num_nodes[]  = {'m', 'a', 'x', '_', 'n', 'u', 'm', '_', 'n', 'o', 'd', 'e', 's', 0};
24 const char cmdline_str_num_sh_frames[]  = {'n', 'u', 'm', '_', 's', 'h', '_', 'f', 'r', 'a', 'm', 'e', 's', 0};
25 const char cmdline_str_disable_iommu[]  = {'d', 'i', 's', 'a', 'b', 'l', 'e', '_', 'i', 'o', 'm', 'm', 'u', 0};
26 
is_space(char c)27 static int is_space(char c)
28 {
29     return c <= ' ';
30 }
31 
parse_opt(const char * cmdline,const char * opt,char * value,int bufsize)32 static 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 
parse_bool(const char * cmdline,const char * opt)62 static 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 
parse_uint16_array(char * str,uint16_t * array,int array_size)82 static 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 
cmdline_parse(const char * cmdline,cmdline_opt_t * cmdline_opt)106 void 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 #if defined(CONFIG_PRINTING) || defined(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