Commit 91ad64a8 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'trace-v5.6-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace

Pull tracing and bootconfig updates:
 "Fixes and changes to bootconfig before it goes live in a release.

  Change in API of bootconfig (before it comes live in a release):
  - Have a magic value "BOOTCONFIG" in initrd to know a bootconfig
    exists
  - Set CONFIG_BOOT_CONFIG to 'n' by default
  - Show error if "bootconfig" on cmdline but not compiled in
  - Prevent redefining the same value
  - Have a way to append values
  - Added a SELECT BLK_DEV_INITRD to fix a build failure

  Synthetic event fixes:
  - Switch to raw_smp_processor_id() for recording CPU value in preempt
    section. (No care for what the value actually is)
  - Fix samples always recording u64 values
  - Fix endianess
  - Check number of values matches number of fields
  - Fix a printing bug

  Fix of trace_printk() breaking postponed start up tests

  Make a function static that is only used in a single file"

* tag 'trace-v5.6-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace:
  bootconfig: Fix CONFIG_BOOTTIME_TRACING dependency issue
  bootconfig: Add append value operator support
  bootconfig: Prohibit re-defining value on same key
  bootconfig: Print array as multiple commands for legacy command line
  bootconfig: Reject subkey and value on same parent key
  tools/bootconfig: Remove unneeded error message silencer
  bootconfig: Add bootconfig magic word for indicating bootconfig explicitly
  bootconfig: Set CONFIG_BOOT_CONFIG=n by default
  tracing: Clear trace_state when starting trace
  bootconfig: Mark boot_config_checksum() static
  tracing: Disable trace_printk() on post poned tests
  tracing: Have synthetic event test use raw_smp_processor_id()
  tracing: Fix number printing bug in print_synth_event()
  tracing: Check that number of vals matches number of synth event fields
  tracing: Make synth_event trace functions endian-correct
  tracing: Make sure synth_event_trace() example always uses u64
parents b98cce1e 2910b5aa
...@@ -62,6 +62,30 @@ Or more shorter, written as following:: ...@@ -62,6 +62,30 @@ Or more shorter, written as following::
In both styles, same key words are automatically merged when parsing it In both styles, same key words are automatically merged when parsing it
at boot time. So you can append similar trees or key-values. at boot time. So you can append similar trees or key-values.
Same-key Values
---------------
It is prohibited that two or more values or arrays share a same-key.
For example,::
foo = bar, baz
foo = qux # !ERROR! we can not re-define same key
If you want to append the value to existing key as an array member,
you can use ``+=`` operator. For example::
foo = bar, baz
foo += qux
In this case, the key ``foo`` has ``bar``, ``baz`` and ``qux``.
However, a sub-key and a value can not co-exist under a parent key.
For example, following config is NOT allowed.::
foo = value1
foo.bar = value2 # !ERROR! subkey "bar" and value "value1" can NOT co-exist
Comments Comments
-------- --------
...@@ -102,9 +126,13 @@ Boot Kernel With a Boot Config ...@@ -102,9 +126,13 @@ Boot Kernel With a Boot Config
============================== ==============================
Since the boot configuration file is loaded with initrd, it will be added Since the boot configuration file is loaded with initrd, it will be added
to the end of the initrd (initramfs) image file. The Linux kernel decodes to the end of the initrd (initramfs) image file with size, checksum and
the last part of the initrd image in memory to get the boot configuration 12-byte magic word as below.
data.
[initrd][bootconfig][size(u32)][checksum(u32)][#BOOTCONFIG\n]
The Linux kernel decodes the last part of the initrd image in memory to
get the boot configuration data.
Because of this "piggyback" method, there is no need to change or Because of this "piggyback" method, there is no need to change or
update the boot loader and the kernel image itself. update the boot loader and the kernel image itself.
......
...@@ -10,6 +10,9 @@ ...@@ -10,6 +10,9 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/types.h> #include <linux/types.h>
#define BOOTCONFIG_MAGIC "#BOOTCONFIG\n"
#define BOOTCONFIG_MAGIC_LEN 12
/* XBC tree node */ /* XBC tree node */
struct xbc_node { struct xbc_node {
u16 next; u16 next;
......
...@@ -1226,13 +1226,12 @@ endif ...@@ -1226,13 +1226,12 @@ endif
config BOOT_CONFIG config BOOT_CONFIG
bool "Boot config support" bool "Boot config support"
depends on BLK_DEV_INITRD select BLK_DEV_INITRD
default y
help help
Extra boot config allows system admin to pass a config file as Extra boot config allows system admin to pass a config file as
complemental extension of kernel cmdline when booting. complemental extension of kernel cmdline when booting.
The boot config file must be attached at the end of initramfs The boot config file must be attached at the end of initramfs
with checksum and size. with checksum, size and magic word.
See <file:Documentation/admin-guide/bootconfig.rst> for details. See <file:Documentation/admin-guide/bootconfig.rst> for details.
If unsure, say Y. If unsure, say Y.
......
...@@ -268,7 +268,6 @@ static int __init xbc_snprint_cmdline(char *buf, size_t size, ...@@ -268,7 +268,6 @@ static int __init xbc_snprint_cmdline(char *buf, size_t size,
{ {
struct xbc_node *knode, *vnode; struct xbc_node *knode, *vnode;
char *end = buf + size; char *end = buf + size;
char c = '\"';
const char *val; const char *val;
int ret; int ret;
...@@ -279,25 +278,20 @@ static int __init xbc_snprint_cmdline(char *buf, size_t size, ...@@ -279,25 +278,20 @@ static int __init xbc_snprint_cmdline(char *buf, size_t size,
return ret; return ret;
vnode = xbc_node_get_child(knode); vnode = xbc_node_get_child(knode);
ret = snprintf(buf, rest(buf, end), "%s%c", xbc_namebuf, if (!vnode) {
vnode ? '=' : ' '); ret = snprintf(buf, rest(buf, end), "%s ", xbc_namebuf);
if (ret < 0) if (ret < 0)
return ret; return ret;
buf += ret; buf += ret;
if (!vnode)
continue; continue;
}
c = '\"';
xbc_array_for_each_value(vnode, val) { xbc_array_for_each_value(vnode, val) {
ret = snprintf(buf, rest(buf, end), "%c%s", c, val); ret = snprintf(buf, rest(buf, end), "%s=\"%s\" ",
xbc_namebuf, val);
if (ret < 0) if (ret < 0)
return ret; return ret;
buf += ret; buf += ret;
c = ',';
} }
if (rest(buf, end) > 2)
strcpy(buf, "\" ");
buf += 2;
} }
return buf - (end - size); return buf - (end - size);
...@@ -335,7 +329,7 @@ static char * __init xbc_make_cmdline(const char *key) ...@@ -335,7 +329,7 @@ static char * __init xbc_make_cmdline(const char *key)
return new_cmdline; return new_cmdline;
} }
u32 boot_config_checksum(unsigned char *p, u32 size) static u32 boot_config_checksum(unsigned char *p, u32 size)
{ {
u32 ret = 0; u32 ret = 0;
...@@ -374,7 +368,11 @@ static void __init setup_boot_config(const char *cmdline) ...@@ -374,7 +368,11 @@ static void __init setup_boot_config(const char *cmdline)
if (!initrd_end) if (!initrd_end)
goto not_found; goto not_found;
hdr = (u32 *)(initrd_end - 8); data = (char *)initrd_end - BOOTCONFIG_MAGIC_LEN;
if (memcmp(data, BOOTCONFIG_MAGIC, BOOTCONFIG_MAGIC_LEN))
goto not_found;
hdr = (u32 *)(data - 8);
size = hdr[0]; size = hdr[0];
csum = hdr[1]; csum = hdr[1];
...@@ -418,6 +416,14 @@ static void __init setup_boot_config(const char *cmdline) ...@@ -418,6 +416,14 @@ static void __init setup_boot_config(const char *cmdline)
} }
#else #else
#define setup_boot_config(cmdline) do { } while (0) #define setup_boot_config(cmdline) do { } while (0)
static int __init warn_bootconfig(char *str)
{
pr_warn("WARNING: 'bootconfig' found on the kernel command line but CONFIG_BOOTCONFIG is not set.\n");
return 0;
}
early_param("bootconfig", warn_bootconfig);
#endif #endif
/* Change NUL term back to "=", to make "param" the whole string. */ /* Change NUL term back to "=", to make "param" the whole string. */
......
...@@ -143,8 +143,8 @@ if FTRACE ...@@ -143,8 +143,8 @@ if FTRACE
config BOOTTIME_TRACING config BOOTTIME_TRACING
bool "Boot-time Tracing support" bool "Boot-time Tracing support"
depends on BOOT_CONFIG && TRACING depends on TRACING
default y select BOOT_CONFIG
help help
Enable developer to setup ftrace subsystem via supplemental Enable developer to setup ftrace subsystem via supplemental
kernel cmdline at boot time for debugging (tracing) driver kernel cmdline at boot time for debugging (tracing) driver
......
...@@ -111,11 +111,11 @@ static int __init test_gen_synth_cmd(void) ...@@ -111,11 +111,11 @@ static int __init test_gen_synth_cmd(void)
/* Create some bogus values just for testing */ /* Create some bogus values just for testing */
vals[0] = 777; /* next_pid_field */ vals[0] = 777; /* next_pid_field */
vals[1] = (u64)"hula hoops"; /* next_comm_field */ vals[1] = (u64)(long)"hula hoops"; /* next_comm_field */
vals[2] = 1000000; /* ts_ns */ vals[2] = 1000000; /* ts_ns */
vals[3] = 1000; /* ts_ms */ vals[3] = 1000; /* ts_ms */
vals[4] = smp_processor_id(); /* cpu */ vals[4] = raw_smp_processor_id(); /* cpu */
vals[5] = (u64)"thneed"; /* my_string_field */ vals[5] = (u64)(long)"thneed"; /* my_string_field */
vals[6] = 598; /* my_int_field */ vals[6] = 598; /* my_int_field */
/* Now generate a gen_synth_test event */ /* Now generate a gen_synth_test event */
...@@ -218,11 +218,11 @@ static int __init test_empty_synth_event(void) ...@@ -218,11 +218,11 @@ static int __init test_empty_synth_event(void)
/* Create some bogus values just for testing */ /* Create some bogus values just for testing */
vals[0] = 777; /* next_pid_field */ vals[0] = 777; /* next_pid_field */
vals[1] = (u64)"tiddlywinks"; /* next_comm_field */ vals[1] = (u64)(long)"tiddlywinks"; /* next_comm_field */
vals[2] = 1000000; /* ts_ns */ vals[2] = 1000000; /* ts_ns */
vals[3] = 1000; /* ts_ms */ vals[3] = 1000; /* ts_ms */
vals[4] = smp_processor_id(); /* cpu */ vals[4] = raw_smp_processor_id(); /* cpu */
vals[5] = (u64)"thneed_2.0"; /* my_string_field */ vals[5] = (u64)(long)"thneed_2.0"; /* my_string_field */
vals[6] = 399; /* my_int_field */ vals[6] = 399; /* my_int_field */
/* Now trace an empty_synth_test event */ /* Now trace an empty_synth_test event */
...@@ -290,11 +290,11 @@ static int __init test_create_synth_event(void) ...@@ -290,11 +290,11 @@ static int __init test_create_synth_event(void)
/* Create some bogus values just for testing */ /* Create some bogus values just for testing */
vals[0] = 777; /* next_pid_field */ vals[0] = 777; /* next_pid_field */
vals[1] = (u64)"tiddlywinks"; /* next_comm_field */ vals[1] = (u64)(long)"tiddlywinks"; /* next_comm_field */
vals[2] = 1000000; /* ts_ns */ vals[2] = 1000000; /* ts_ns */
vals[3] = 1000; /* ts_ms */ vals[3] = 1000; /* ts_ms */
vals[4] = smp_processor_id(); /* cpu */ vals[4] = raw_smp_processor_id(); /* cpu */
vals[5] = (u64)"thneed"; /* my_string_field */ vals[5] = (u64)(long)"thneed"; /* my_string_field */
vals[6] = 398; /* my_int_field */ vals[6] = 398; /* my_int_field */
/* Now generate a create_synth_test event */ /* Now generate a create_synth_test event */
...@@ -330,7 +330,7 @@ static int __init test_add_next_synth_val(void) ...@@ -330,7 +330,7 @@ static int __init test_add_next_synth_val(void)
goto out; goto out;
/* next_comm_field */ /* next_comm_field */
ret = synth_event_add_next_val((u64)"slinky", &trace_state); ret = synth_event_add_next_val((u64)(long)"slinky", &trace_state);
if (ret) if (ret)
goto out; goto out;
...@@ -345,12 +345,12 @@ static int __init test_add_next_synth_val(void) ...@@ -345,12 +345,12 @@ static int __init test_add_next_synth_val(void)
goto out; goto out;
/* cpu */ /* cpu */
ret = synth_event_add_next_val(smp_processor_id(), &trace_state); ret = synth_event_add_next_val(raw_smp_processor_id(), &trace_state);
if (ret) if (ret)
goto out; goto out;
/* my_string_field */ /* my_string_field */
ret = synth_event_add_next_val((u64)"thneed_2.01", &trace_state); ret = synth_event_add_next_val((u64)(long)"thneed_2.01", &trace_state);
if (ret) if (ret)
goto out; goto out;
...@@ -388,7 +388,7 @@ static int __init test_add_synth_val(void) ...@@ -388,7 +388,7 @@ static int __init test_add_synth_val(void)
if (ret) if (ret)
goto out; goto out;
ret = synth_event_add_val("cpu", smp_processor_id(), &trace_state); ret = synth_event_add_val("cpu", raw_smp_processor_id(), &trace_state);
if (ret) if (ret)
goto out; goto out;
...@@ -396,12 +396,12 @@ static int __init test_add_synth_val(void) ...@@ -396,12 +396,12 @@ static int __init test_add_synth_val(void)
if (ret) if (ret)
goto out; goto out;
ret = synth_event_add_val("next_comm_field", (u64)"silly putty", ret = synth_event_add_val("next_comm_field", (u64)(long)"silly putty",
&trace_state); &trace_state);
if (ret) if (ret)
goto out; goto out;
ret = synth_event_add_val("my_string_field", (u64)"thneed_9", ret = synth_event_add_val("my_string_field", (u64)(long)"thneed_9",
&trace_state); &trace_state);
if (ret) if (ret)
goto out; goto out;
...@@ -423,13 +423,13 @@ static int __init test_trace_synth_event(void) ...@@ -423,13 +423,13 @@ static int __init test_trace_synth_event(void)
/* Trace some bogus values just for testing */ /* Trace some bogus values just for testing */
ret = synth_event_trace(create_synth_test, 7, /* number of values */ ret = synth_event_trace(create_synth_test, 7, /* number of values */
444, /* next_pid_field */ (u64)444, /* next_pid_field */
(u64)"clackers", /* next_comm_field */ (u64)(long)"clackers", /* next_comm_field */
1000000, /* ts_ns */ (u64)1000000, /* ts_ns */
1000, /* ts_ms */ (u64)1000, /* ts_ms */
smp_processor_id(), /* cpu */ (u64)raw_smp_processor_id(), /* cpu */
(u64)"Thneed", /* my_string_field */ (u64)(long)"Thneed", /* my_string_field */
999); /* my_int_field */ (u64)999); /* my_int_field */
return ret; return ret;
} }
......
...@@ -1837,6 +1837,7 @@ static __init int init_trace_selftests(void) ...@@ -1837,6 +1837,7 @@ static __init int init_trace_selftests(void)
pr_info("Running postponed tracer tests:\n"); pr_info("Running postponed tracer tests:\n");
tracing_selftest_running = true;
list_for_each_entry_safe(p, n, &postponed_selftests, list) { list_for_each_entry_safe(p, n, &postponed_selftests, list) {
/* This loop can take minutes when sanitizers are enabled, so /* This loop can take minutes when sanitizers are enabled, so
* lets make sure we allow RCU processing. * lets make sure we allow RCU processing.
...@@ -1859,6 +1860,7 @@ static __init int init_trace_selftests(void) ...@@ -1859,6 +1860,7 @@ static __init int init_trace_selftests(void)
list_del(&p->list); list_del(&p->list);
kfree(p); kfree(p);
} }
tracing_selftest_running = false;
out: out:
mutex_unlock(&trace_types_lock); mutex_unlock(&trace_types_lock);
......
...@@ -821,6 +821,29 @@ static const char *synth_field_fmt(char *type) ...@@ -821,6 +821,29 @@ static const char *synth_field_fmt(char *type)
return fmt; return fmt;
} }
static void print_synth_event_num_val(struct trace_seq *s,
char *print_fmt, char *name,
int size, u64 val, char *space)
{
switch (size) {
case 1:
trace_seq_printf(s, print_fmt, name, (u8)val, space);
break;
case 2:
trace_seq_printf(s, print_fmt, name, (u16)val, space);
break;
case 4:
trace_seq_printf(s, print_fmt, name, (u32)val, space);
break;
default:
trace_seq_printf(s, print_fmt, name, val, space);
break;
}
}
static enum print_line_t print_synth_event(struct trace_iterator *iter, static enum print_line_t print_synth_event(struct trace_iterator *iter,
int flags, int flags,
struct trace_event *event) struct trace_event *event)
...@@ -859,10 +882,13 @@ static enum print_line_t print_synth_event(struct trace_iterator *iter, ...@@ -859,10 +882,13 @@ static enum print_line_t print_synth_event(struct trace_iterator *iter,
} else { } else {
struct trace_print_flags __flags[] = { struct trace_print_flags __flags[] = {
__def_gfpflag_names, {-1, NULL} }; __def_gfpflag_names, {-1, NULL} };
char *space = (i == se->n_fields - 1 ? "" : " ");
trace_seq_printf(s, print_fmt, se->fields[i]->name, print_synth_event_num_val(s, print_fmt,
se->fields[i]->name,
se->fields[i]->size,
entry->fields[n_u64], entry->fields[n_u64],
i == se->n_fields - 1 ? "" : " "); space);
if (strcmp(se->fields[i]->type, "gfp_t") == 0) { if (strcmp(se->fields[i]->type, "gfp_t") == 0) {
trace_seq_puts(s, " ("); trace_seq_puts(s, " (");
...@@ -1805,6 +1831,8 @@ __synth_event_trace_start(struct trace_event_file *file, ...@@ -1805,6 +1831,8 @@ __synth_event_trace_start(struct trace_event_file *file,
int entry_size, fields_size = 0; int entry_size, fields_size = 0;
int ret = 0; int ret = 0;
memset(trace_state, '\0', sizeof(*trace_state));
/* /*
* Normal event tracing doesn't get called at all unless the * Normal event tracing doesn't get called at all unless the
* ENABLED bit is set (which attaches the probe thus allowing * ENABLED bit is set (which attaches the probe thus allowing
...@@ -1885,6 +1913,11 @@ int synth_event_trace(struct trace_event_file *file, unsigned int n_vals, ...) ...@@ -1885,6 +1913,11 @@ int synth_event_trace(struct trace_event_file *file, unsigned int n_vals, ...)
return ret; return ret;
} }
if (n_vals != state.event->n_fields) {
ret = -EINVAL;
goto out;
}
va_start(args, n_vals); va_start(args, n_vals);
for (i = 0, n_u64 = 0; i < state.event->n_fields; i++) { for (i = 0, n_u64 = 0; i < state.event->n_fields; i++) {
u64 val; u64 val;
...@@ -1898,12 +1931,30 @@ int synth_event_trace(struct trace_event_file *file, unsigned int n_vals, ...) ...@@ -1898,12 +1931,30 @@ int synth_event_trace(struct trace_event_file *file, unsigned int n_vals, ...)
strscpy(str_field, str_val, STR_VAR_LEN_MAX); strscpy(str_field, str_val, STR_VAR_LEN_MAX);
n_u64 += STR_VAR_LEN_MAX / sizeof(u64); n_u64 += STR_VAR_LEN_MAX / sizeof(u64);
} else { } else {
struct synth_field *field = state.event->fields[i];
switch (field->size) {
case 1:
*(u8 *)&state.entry->fields[n_u64] = (u8)val;
break;
case 2:
*(u16 *)&state.entry->fields[n_u64] = (u16)val;
break;
case 4:
*(u32 *)&state.entry->fields[n_u64] = (u32)val;
break;
default:
state.entry->fields[n_u64] = val; state.entry->fields[n_u64] = val;
break;
}
n_u64++; n_u64++;
} }
} }
va_end(args); va_end(args);
out:
__synth_event_trace_end(&state); __synth_event_trace_end(&state);
return ret; return ret;
...@@ -1942,6 +1993,11 @@ int synth_event_trace_array(struct trace_event_file *file, u64 *vals, ...@@ -1942,6 +1993,11 @@ int synth_event_trace_array(struct trace_event_file *file, u64 *vals,
return ret; return ret;
} }
if (n_vals != state.event->n_fields) {
ret = -EINVAL;
goto out;
}
for (i = 0, n_u64 = 0; i < state.event->n_fields; i++) { for (i = 0, n_u64 = 0; i < state.event->n_fields; i++) {
if (state.event->fields[i]->is_string) { if (state.event->fields[i]->is_string) {
char *str_val = (char *)(long)vals[i]; char *str_val = (char *)(long)vals[i];
...@@ -1950,11 +2006,30 @@ int synth_event_trace_array(struct trace_event_file *file, u64 *vals, ...@@ -1950,11 +2006,30 @@ int synth_event_trace_array(struct trace_event_file *file, u64 *vals,
strscpy(str_field, str_val, STR_VAR_LEN_MAX); strscpy(str_field, str_val, STR_VAR_LEN_MAX);
n_u64 += STR_VAR_LEN_MAX / sizeof(u64); n_u64 += STR_VAR_LEN_MAX / sizeof(u64);
} else { } else {
state.entry->fields[n_u64] = vals[i]; struct synth_field *field = state.event->fields[i];
u64 val = vals[i];
switch (field->size) {
case 1:
*(u8 *)&state.entry->fields[n_u64] = (u8)val;
break;
case 2:
*(u16 *)&state.entry->fields[n_u64] = (u16)val;
break;
case 4:
*(u32 *)&state.entry->fields[n_u64] = (u32)val;
break;
default:
state.entry->fields[n_u64] = val;
break;
}
n_u64++; n_u64++;
} }
} }
out:
__synth_event_trace_end(&state); __synth_event_trace_end(&state);
return ret; return ret;
...@@ -1997,8 +2072,6 @@ int synth_event_trace_start(struct trace_event_file *file, ...@@ -1997,8 +2072,6 @@ int synth_event_trace_start(struct trace_event_file *file,
if (!trace_state) if (!trace_state)
return -EINVAL; return -EINVAL;
memset(trace_state, '\0', sizeof(*trace_state));
ret = __synth_event_trace_start(file, trace_state); ret = __synth_event_trace_start(file, trace_state);
if (ret == -ENOENT) if (ret == -ENOENT)
ret = 0; /* just disabled, not really an error */ ret = 0; /* just disabled, not really an error */
...@@ -2069,8 +2142,25 @@ static int __synth_event_add_val(const char *field_name, u64 val, ...@@ -2069,8 +2142,25 @@ static int __synth_event_add_val(const char *field_name, u64 val,
str_field = (char *)&entry->fields[field->offset]; str_field = (char *)&entry->fields[field->offset];
strscpy(str_field, str_val, STR_VAR_LEN_MAX); strscpy(str_field, str_val, STR_VAR_LEN_MAX);
} else } else {
entry->fields[field->offset] = val; switch (field->size) {
case 1:
*(u8 *)&trace_state->entry->fields[field->offset] = (u8)val;
break;
case 2:
*(u16 *)&trace_state->entry->fields[field->offset] = (u16)val;
break;
case 4:
*(u32 *)&trace_state->entry->fields[field->offset] = (u32)val;
break;
default:
trace_state->entry->fields[field->offset] = val;
break;
}
}
out: out:
return ret; return ret;
} }
......
...@@ -533,7 +533,7 @@ struct xbc_node *find_match_node(struct xbc_node *node, char *k) ...@@ -533,7 +533,7 @@ struct xbc_node *find_match_node(struct xbc_node *node, char *k)
static int __init __xbc_add_key(char *k) static int __init __xbc_add_key(char *k)
{ {
struct xbc_node *node; struct xbc_node *node, *child;
if (!xbc_valid_keyword(k)) if (!xbc_valid_keyword(k))
return xbc_parse_error("Invalid keyword", k); return xbc_parse_error("Invalid keyword", k);
...@@ -543,8 +543,12 @@ static int __init __xbc_add_key(char *k) ...@@ -543,8 +543,12 @@ static int __init __xbc_add_key(char *k)
if (!last_parent) /* the first level */ if (!last_parent) /* the first level */
node = find_match_node(xbc_nodes, k); node = find_match_node(xbc_nodes, k);
else else {
node = find_match_node(xbc_node_get_child(last_parent), k); child = xbc_node_get_child(last_parent);
if (child && xbc_node_is_value(child))
return xbc_parse_error("Subkey is mixed with value", k);
node = find_match_node(child, k);
}
if (node) if (node)
last_parent = node; last_parent = node;
...@@ -574,10 +578,10 @@ static int __init __xbc_parse_keys(char *k) ...@@ -574,10 +578,10 @@ static int __init __xbc_parse_keys(char *k)
return __xbc_add_key(k); return __xbc_add_key(k);
} }
static int __init xbc_parse_kv(char **k, char *v) static int __init xbc_parse_kv(char **k, char *v, int op)
{ {
struct xbc_node *prev_parent = last_parent; struct xbc_node *prev_parent = last_parent;
struct xbc_node *node; struct xbc_node *child;
char *next; char *next;
int c, ret; int c, ret;
...@@ -585,12 +589,19 @@ static int __init xbc_parse_kv(char **k, char *v) ...@@ -585,12 +589,19 @@ static int __init xbc_parse_kv(char **k, char *v)
if (ret) if (ret)
return ret; return ret;
child = xbc_node_get_child(last_parent);
if (child) {
if (xbc_node_is_key(child))
return xbc_parse_error("Value is mixed with subkey", v);
else if (op == '=')
return xbc_parse_error("Value is redefined", v);
}
c = __xbc_parse_value(&v, &next); c = __xbc_parse_value(&v, &next);
if (c < 0) if (c < 0)
return c; return c;
node = xbc_add_sibling(v, XBC_VALUE); if (!xbc_add_sibling(v, XBC_VALUE))
if (!node)
return -ENOMEM; return -ENOMEM;
if (c == ',') { /* Array */ if (c == ',') { /* Array */
...@@ -763,7 +774,7 @@ int __init xbc_init(char *buf) ...@@ -763,7 +774,7 @@ int __init xbc_init(char *buf)
p = buf; p = buf;
do { do {
q = strpbrk(p, "{}=;\n#"); q = strpbrk(p, "{}=+;\n#");
if (!q) { if (!q) {
p = skip_spaces(p); p = skip_spaces(p);
if (*p != '\0') if (*p != '\0')
...@@ -774,8 +785,15 @@ int __init xbc_init(char *buf) ...@@ -774,8 +785,15 @@ int __init xbc_init(char *buf)
c = *q; c = *q;
*q++ = '\0'; *q++ = '\0';
switch (c) { switch (c) {
case '+':
if (*q++ != '=') {
ret = xbc_parse_error("Wrong '+' operator",
q - 2);
break;
}
/* Fall through */
case '=': case '=':
ret = xbc_parse_kv(&p, q); ret = xbc_parse_kv(&p, q, c);
break; break;
case '{': case '{':
ret = xbc_open_brace(&p, q); ret = xbc_open_brace(&p, q);
......
...@@ -4,10 +4,7 @@ ...@@ -4,10 +4,7 @@
#include <stdio.h> #include <stdio.h>
/* controllable printf */ #define printk(fmt, ...) printf(fmt, ##__VA_ARGS__)
extern int pr_output;
#define printk(fmt, ...) \
(pr_output ? printf(fmt, ##__VA_ARGS__) : 0)
#define pr_err printk #define pr_err printk
#define pr_warn printk #define pr_warn printk
......
...@@ -14,8 +14,6 @@ ...@@ -14,8 +14,6 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/bootconfig.h> #include <linux/bootconfig.h>
int pr_output = 1;
static int xbc_show_array(struct xbc_node *node) static int xbc_show_array(struct xbc_node *node)
{ {
const char *val; const char *val;
...@@ -131,15 +129,26 @@ int load_xbc_from_initrd(int fd, char **buf) ...@@ -131,15 +129,26 @@ int load_xbc_from_initrd(int fd, char **buf)
struct stat stat; struct stat stat;
int ret; int ret;
u32 size = 0, csum = 0, rcsum; u32 size = 0, csum = 0, rcsum;
char magic[BOOTCONFIG_MAGIC_LEN];
ret = fstat(fd, &stat); ret = fstat(fd, &stat);
if (ret < 0) if (ret < 0)
return -errno; return -errno;
if (stat.st_size < 8) if (stat.st_size < 8 + BOOTCONFIG_MAGIC_LEN)
return 0;
if (lseek(fd, -BOOTCONFIG_MAGIC_LEN, SEEK_END) < 0) {
pr_err("Failed to lseek: %d\n", -errno);
return -errno;
}
if (read(fd, magic, BOOTCONFIG_MAGIC_LEN) < 0)
return -errno;
/* Check the bootconfig magic bytes */
if (memcmp(magic, BOOTCONFIG_MAGIC, BOOTCONFIG_MAGIC_LEN) != 0)
return 0; return 0;
if (lseek(fd, -8, SEEK_END) < 0) { if (lseek(fd, -(8 + BOOTCONFIG_MAGIC_LEN), SEEK_END) < 0) {
pr_err("Failed to lseek: %d\n", -errno); pr_err("Failed to lseek: %d\n", -errno);
return -errno; return -errno;
} }
...@@ -150,11 +159,14 @@ int load_xbc_from_initrd(int fd, char **buf) ...@@ -150,11 +159,14 @@ int load_xbc_from_initrd(int fd, char **buf)
if (read(fd, &csum, sizeof(u32)) < 0) if (read(fd, &csum, sizeof(u32)) < 0)
return -errno; return -errno;
/* Wrong size, maybe no boot config here */ /* Wrong size error */
if (stat.st_size < size + 8) if (stat.st_size < size + 8 + BOOTCONFIG_MAGIC_LEN) {
return 0; pr_err("bootconfig size is too big\n");
return -E2BIG;
}
if (lseek(fd, stat.st_size - 8 - size, SEEK_SET) < 0) { if (lseek(fd, stat.st_size - (size + 8 + BOOTCONFIG_MAGIC_LEN),
SEEK_SET) < 0) {
pr_err("Failed to lseek: %d\n", -errno); pr_err("Failed to lseek: %d\n", -errno);
return -errno; return -errno;
} }
...@@ -163,17 +175,17 @@ int load_xbc_from_initrd(int fd, char **buf) ...@@ -163,17 +175,17 @@ int load_xbc_from_initrd(int fd, char **buf)
if (ret < 0) if (ret < 0)
return ret; return ret;
/* Wrong Checksum, maybe no boot config here */ /* Wrong Checksum */
rcsum = checksum((unsigned char *)*buf, size); rcsum = checksum((unsigned char *)*buf, size);
if (csum != rcsum) { if (csum != rcsum) {
pr_err("checksum error: %d != %d\n", csum, rcsum); pr_err("checksum error: %d != %d\n", csum, rcsum);
return 0; return -EINVAL;
} }
ret = xbc_init(*buf); ret = xbc_init(*buf);
/* Wrong data, maybe no boot config here */ /* Wrong data */
if (ret < 0) if (ret < 0)
return 0; return ret;
return size; return size;
} }
...@@ -213,20 +225,15 @@ int delete_xbc(const char *path) ...@@ -213,20 +225,15 @@ int delete_xbc(const char *path)
return -errno; return -errno;
} }
/*
* Suppress error messages in xbc_init() because it can be just a
* data which concidentally matches the size and checksum footer.
*/
pr_output = 0;
size = load_xbc_from_initrd(fd, &buf); size = load_xbc_from_initrd(fd, &buf);
pr_output = 1;
if (size < 0) { if (size < 0) {
ret = size; ret = size;
pr_err("Failed to load a boot config from initrd: %d\n", ret); pr_err("Failed to load a boot config from initrd: %d\n", ret);
} else if (size > 0) { } else if (size > 0) {
ret = fstat(fd, &stat); ret = fstat(fd, &stat);
if (!ret) if (!ret)
ret = ftruncate(fd, stat.st_size - size - 8); ret = ftruncate(fd, stat.st_size
- size - 8 - BOOTCONFIG_MAGIC_LEN);
if (ret) if (ret)
ret = -errno; ret = -errno;
} /* Ignore if there is no boot config in initrd */ } /* Ignore if there is no boot config in initrd */
...@@ -295,6 +302,12 @@ int apply_xbc(const char *path, const char *xbc_path) ...@@ -295,6 +302,12 @@ int apply_xbc(const char *path, const char *xbc_path)
pr_err("Failed to apply a boot config: %d\n", ret); pr_err("Failed to apply a boot config: %d\n", ret);
return ret; return ret;
} }
/* Write a magic word of the bootconfig */
ret = write(fd, BOOTCONFIG_MAGIC, BOOTCONFIG_MAGIC_LEN);
if (ret < 0) {
pr_err("Failed to apply a boot config magic: %d\n", ret);
return ret;
}
close(fd); close(fd);
free(data); free(data);
......
# value -> subkey pattern
key = value
key.subkey = another-value
# subkey -> value pattern
key.subkey = value
key = another-value
# Same key value is not allowed
key {
foo = value
bar = value2
}
key.foo = value
...@@ -9,7 +9,7 @@ TEMPCONF=`mktemp temp-XXXX.bconf` ...@@ -9,7 +9,7 @@ TEMPCONF=`mktemp temp-XXXX.bconf`
NG=0 NG=0
cleanup() { cleanup() {
rm -f $INITRD $TEMPCONF rm -f $INITRD $TEMPCONF $OUTFILE
exit $NG exit $NG
} }
...@@ -49,7 +49,7 @@ xpass $BOOTCONF -a $TEMPCONF $INITRD ...@@ -49,7 +49,7 @@ xpass $BOOTCONF -a $TEMPCONF $INITRD
new_size=$(stat -c %s $INITRD) new_size=$(stat -c %s $INITRD)
echo "File size check" echo "File size check"
xpass test $new_size -eq $(expr $bconf_size + $initrd_size + 9) xpass test $new_size -eq $(expr $bconf_size + $initrd_size + 9 + 12)
echo "Apply command repeat test" echo "Apply command repeat test"
xpass $BOOTCONF -a $TEMPCONF $INITRD xpass $BOOTCONF -a $TEMPCONF $INITRD
...@@ -71,7 +71,6 @@ printf " \0\0\0 \0\0\0" >> $INITRD ...@@ -71,7 +71,6 @@ printf " \0\0\0 \0\0\0" >> $INITRD
$BOOTCONF -a $TEMPCONF $INITRD > $OUTFILE 2>&1 $BOOTCONF -a $TEMPCONF $INITRD > $OUTFILE 2>&1
xfail grep -i "failed" $OUTFILE xfail grep -i "failed" $OUTFILE
xfail grep -i "error" $OUTFILE xfail grep -i "error" $OUTFILE
rm $OUTFILE
echo "Max node number check" echo "Max node number check"
...@@ -96,6 +95,19 @@ truncate -s 32764 $TEMPCONF ...@@ -96,6 +95,19 @@ truncate -s 32764 $TEMPCONF
echo "\"" >> $TEMPCONF # add 2 bytes + terminal ('\"\n\0') echo "\"" >> $TEMPCONF # add 2 bytes + terminal ('\"\n\0')
xpass $BOOTCONF -a $TEMPCONF $INITRD xpass $BOOTCONF -a $TEMPCONF $INITRD
echo "Adding same-key values"
cat > $TEMPCONF << EOF
key = bar, baz
key += qux
EOF
echo > $INITRD
xpass $BOOTCONF -a $TEMPCONF $INITRD
$BOOTCONF $INITRD > $OUTFILE
xpass grep -q "bar" $OUTFILE
xpass grep -q "baz" $OUTFILE
xpass grep -q "qux" $OUTFILE
echo "=== expected failure cases ===" echo "=== expected failure cases ==="
for i in samples/bad-* ; do for i in samples/bad-* ; do
xfail $BOOTCONF -a $i $INITRD xfail $BOOTCONF -a $i $INITRD
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment