summaryrefslogtreecommitdiff
path: root/cleopatre/devkit/plcdrv/src/boot_params.c
blob: 1e6d9587dae926106078a5714b410d411b40f071 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
/* Cleopatre project {{{
 *
 * Copyright (C) 2011 Spidcom
 *
 * <<<Licence>>>
 *
 * }}} */
/**
 * \file    src/boot_params.c
 * \brief   Support for passing boot parameters to Cesar.
 * \ingroup plcdrv
 *
 */

#include "boot_params.h"

#include <linux/kernel.h>
#include <linux/string.h>

/** Marker indicating the start of the boot parameters area. */
#define BOOT_PARAMS_MARKER    0xB007B007

/** Delimiter between two parameters in the boot parameters string. */
#define BOOT_PARAMS_DELIMITER    " "

/** Delimiter between the key and the value of a boot parameter in the boot
 *  parameters string. */
#define BOOT_PARAMS_KEY_VALUE_DELIMITER    "="

/** Size of the string representing the value of the "mem" boot parameter.
 *  len(size_str) + len("k"|"M") + '\0' */
#define BOOT_PARAMS_MEM_VAL_STR_SIZE    (20 + 1 + 1)

/** Size of the string representing the value of a clock frequency boot
 * parameter.
 *  len(size_str) + '\0' */
#define BOOT_PARAMS_CLK_MHZ_VAL_STR_SIZE  (3 + 1)


char custom_boot_params[BOOT_PARAMS_STR_SIZE] = "";

/**
 * Add a boot parameter to the boot parameters string.
 *
 * \param  params_str  boot parameters string.
 * \param  param_name  name of the parameter to add.
 * \param  param_value  value of the parameter to add.
 *                      Pass NULL if the parameter has no value.
 * \return  0, on success. -1, on error.
 */
static int plcdrv_add_boot_param(char *params_str, const char *param_name,
                                 const char *param_value)
{
    size_t len;

    BUG_ON(params_str == NULL);
    BUG_ON(param_name == NULL);

    len = strlen(params_str);

    /* If params_str is not empty, add a delimiter before adding the boot param. */
    if (len > 0)
    {
        len += strlen(BOOT_PARAMS_DELIMITER);
        if (len >= BOOT_PARAMS_STR_SIZE)
            goto overflow;
        strcat(params_str, BOOT_PARAMS_DELIMITER);
    }

    /* Add param_name. */
    len += strlen(param_name);
    if (len >= BOOT_PARAMS_STR_SIZE)
        goto overflow;
    strcat(params_str, param_name);

    /* Add param_value, if any. */
    if (param_value != NULL)
    {
        len += strlen(BOOT_PARAMS_KEY_VALUE_DELIMITER) + strlen(param_value);
        if (len >= BOOT_PARAMS_STR_SIZE)
            goto overflow;
        strcat(params_str, BOOT_PARAMS_KEY_VALUE_DELIMITER);
        strcat(params_str, param_value);
    }

    return 0;

overflow:
    printk (KERN_ERR
            "Boot parameters string too long (while adding default parameter)\n");
    return -1;
}

/**
 * Convert a numeric memory size value to a string representation.
 * Eventually add a "k" or "M".
 *
 * \param  str  destination string.
 * \param  size  size of the string.
 * \param  mem  memory size.
 * \return  0, on success. -1, on error.
 */
static int plcdrv_mem_to_str(char *str, size_t size, uint32_t mem)
{
    char *symbol = "";
    int ret;

    if ((mem % 1024) == 0)
    {
        mem /= 1024;
        symbol = "k";

        if ((mem % 1024) == 0)
        {
            mem /= 1024;
            symbol = "M";
        }
    }

    ret = snprintf(str, size, "%u%s", mem, symbol);

    if ((ret <= -1) || (ret >= size))
    {
        return -1;
    }

    return 0;
}

/**
 * Convert a numeric clk_mhz size value to a string representation.
 *
 * \param  str  destination string.
 * \param  size  size of the string.
 * \param  sysclock  the clock frequency.
 * \return  0, on success. -1, on error.
 */
static int plcdrv_clk_to_str(char *str, size_t size, uint32_t clk_mhz)
{
    int ret;
    ret = snprintf(str, size, "%u", clk_mhz);
    if ((ret <= -1) || (ret >= size))
    {
        return -1;
    }
    return 0;
}

/**
 * Add the default boot parameters to the final boot parameters string.
 *
 * \param  params_str  final parameters string.
 * \return  0, on success. -1, on error.
 */
static int plcdrv_add_default_boot_params(char *params_str)
{
    //"mem"
    {
        //Value of the "mem" parameter as a string.
        char mem_val_str[BOOT_PARAMS_MEM_VAL_STR_SIZE] = "";

        if (plcdrv_mem_to_str(mem_val_str, sizeof(mem_val_str),
                              spc300_plc_mem_size) != 0)
            return -1;

        if (plcdrv_add_boot_param(params_str, "mem", mem_val_str) != 0)
            return -1;
    }
    //"sysclk_mhz"
    {
        //Value of the "sysclk_mhz" parameter as a string.
        char sysclk_val_str[BOOT_PARAMS_CLK_MHZ_VAL_STR_SIZE] = "";

        if (plcdrv_clk_to_str(sysclk_val_str, sizeof(sysclk_val_str),
                              PLC_SYSCLOCK_MHZ) != 0)
            return -1;

        if (plcdrv_add_boot_param(params_str, "sysclk_mhz", sysclk_val_str)
            != 0)
            return -1;
    }
    //"uartclk_mhz"
    {
        //Value of the "uartclk_mhz" parameter as a string.
        char uartclk_val_str[BOOT_PARAMS_CLK_MHZ_VAL_STR_SIZE] = "";

        if (plcdrv_clk_to_str(uartclk_val_str, sizeof(uartclk_val_str),
                              PLC_UARTCLOCK_MHZ) != 0)
            return -1;

        if (plcdrv_add_boot_param(params_str, "uartclk_mhz", uartclk_val_str)
            != 0)
            return -1;
    }


    return 0;
}

/**
 * Add the customized boot parameters to the final boot parameters string.
 *
 * \param  params_str  final boot parameters string.
 * \return  0, on success. -1, on error.
 */
static int plcdrv_add_custom_boot_params(char *params_str)
{
    int len;

    if (strlen(custom_boot_params) == 0)
        return 0;

    len = strlen(params_str);

    if (len > 0)
    {
        len += strlen(BOOT_PARAMS_DELIMITER);
        if (len >= BOOT_PARAMS_STR_SIZE)
            goto overflow;

        strcat(params_str, BOOT_PARAMS_DELIMITER);
    }

    len += strlen(custom_boot_params);
    if (len >= BOOT_PARAMS_STR_SIZE)
        goto overflow;

    strcat(params_str, custom_boot_params);

    return 0;

overflow:
    printk (KERN_ERR
            "Boot parameters string too long (after adding custom parameters)\n");
    return -1;
}

/**
 * Build the boot parameters string.
 *
 * \param  boot_params_str  boot parameters string.
 * \return  0, on success. -1, on error.
 */
static int plcdrv_build_boot_params(char *boot_params_str)
{
    if ((plcdrv_add_default_boot_params(boot_params_str) != 0)
        || (plcdrv_add_custom_boot_params(boot_params_str) != 0))
    {
        return -1;
    }

    return 0;
}

/**
 * Write the boot params to the boot params area expected by Cesar code.
 *
 * \param  boot_params_area  the start of the boot params area.
 * \param  boot_params_str  the boot params string.
 */
static void plcdrv_write_boot_params(uint8_t *boot_params_area,
                                     const char *boot_params_str)
{
    uint32_t *src;
    uint32_t *dst;
    uint len;
    uint i;

    dst = (uint32_t *)boot_params_area;

    //Set the marker indicating the start of the boot parameters area.
    *dst++ = BOOT_PARAMS_MARKER;

    len = strlen(boot_params_str);

    //This condition is enforced during the building of the boot parameters
    //string.
    BUG_ON(len >= BOOT_PARAMS_STR_SIZE);

    //Copy the parameters to the destination area, and convert them to
    //big-endian, in the process.
#if ((BOOT_PARAMS_STR_SIZE % 4) != 0)
#error BOOT_PARAMS_STR_SIZE must be a multiple of 4
#endif
    src = (uint32_t *)&boot_params_str[0];
    for (i = 0; i <= len; i += 4)
    {
        *dst++ = cpu_to_be32p(src++);
    }
}

/**
 * Pass the boot parameters to Cesar.
 *
 * \param  bin_eof  end of the .bin file.
 * \return  0, on success. -1, on error.
 */
int plcdrv_pass_boot_params(uint8_t *bin_eof)
{
    char boot_params_str[BOOT_PARAMS_STR_SIZE] = "";
    uint8_t *boot_params_area;

    BUG_ON(bin_eof == NULL);

    if (plcdrv_build_boot_params(boot_params_str) != 0)
    {
        return -1;
    }

    //The boot parameters area must be written at the first 4-bytes-aligned
    //address superior or equal to bin_eof.
    boot_params_area = (uint8_t *)ALIGN((u32)bin_eof, 4);
    plcdrv_write_boot_params(boot_params_area, boot_params_str);

    printk (KERN_INFO "PLC boot parameters: \"%s\"\n", boot_params_str);

    return 0;
}