earlycon: Use a pointer table to fix __earlycon_table stride

Commit 99492c39f3 ("earlycon: Fix __earlycon_table stride") tried to fix
__earlycon_table stride by forcing the earlycon_id struct alignment to 32
and asking the linker to 32-byte align the __earlycon_table symbol.  This
fix was based on commit 07fca0e57f ("tracing: Properly align linker
defined symbols") which tried a similar fix for the tracing subsystem.

However, this fix doesn't quite work because there is no guarantee that
gcc will place structures packed into an array format.  In fact, gcc 4.9
chooses to 64-byte align these structs by inserting additional padding
between the entries because it has no clue that they are supposed to be in
an array.  If we are unlucky, the linker will assign symbol
"__earlycon_table" to a 32-byte aligned address which does not correspond
to the 64-byte aligned contents of section "__earlycon_table".

To address this same problem, the fix to the tracing system was
subsequently re-implemented using a more robust table of pointers approach
by commits:
 3d56e331b6 ("tracing: Replace syscall_meta_data struct array with pointer array")
 6549864629 ("tracepoints: Fix section alignment using pointer array")
 e4a9ea5ee7 ("tracing: Replace trace_event struct array with pointer array")

Let's use this same "array of pointers to structs" approach for
EARLYCON_TABLE.

Fixes: 99492c39f3 ("earlycon: Fix __earlycon_table stride")
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Suggested-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-by: Rob Herring <robh@kernel.org>
Tested-by: Guenter Roeck <groeck@chromium.org>
Reviewed-by: Guenter Roeck <groeck@chromium.org>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Daniel Kurtz 2018-04-06 17:21:53 -06:00 коммит произвёл Greg Kroah-Hartman
Родитель ef9604b622
Коммит dd709e72cb
4 изменённых файлов: 24 добавлений и 12 удалений

Просмотреть файл

@ -942,7 +942,7 @@ int __init early_init_dt_scan_chosen_stdout(void)
int offset; int offset;
const char *p, *q, *options = NULL; const char *p, *q, *options = NULL;
int l; int l;
const struct earlycon_id *match; const struct earlycon_id **p_match;
const void *fdt = initial_boot_params; const void *fdt = initial_boot_params;
offset = fdt_path_offset(fdt, "/chosen"); offset = fdt_path_offset(fdt, "/chosen");
@ -969,7 +969,10 @@ int __init early_init_dt_scan_chosen_stdout(void)
return 0; return 0;
} }
for (match = __earlycon_table; match < __earlycon_table_end; match++) { for (p_match = __earlycon_table; p_match < __earlycon_table_end;
p_match++) {
const struct earlycon_id *match = *p_match;
if (!match->compatible[0]) if (!match->compatible[0])
continue; continue;

Просмотреть файл

@ -169,7 +169,7 @@ static int __init register_earlycon(char *buf, const struct earlycon_id *match)
*/ */
int __init setup_earlycon(char *buf) int __init setup_earlycon(char *buf)
{ {
const struct earlycon_id *match; const struct earlycon_id **p_match;
if (!buf || !buf[0]) if (!buf || !buf[0])
return -EINVAL; return -EINVAL;
@ -177,7 +177,9 @@ int __init setup_earlycon(char *buf)
if (early_con.flags & CON_ENABLED) if (early_con.flags & CON_ENABLED)
return -EALREADY; return -EALREADY;
for (match = __earlycon_table; match < __earlycon_table_end; match++) { for (p_match = __earlycon_table; p_match < __earlycon_table_end;
p_match++) {
const struct earlycon_id *match = *p_match;
size_t len = strlen(match->name); size_t len = strlen(match->name);
if (strncmp(buf, match->name, len)) if (strncmp(buf, match->name, len))

Просмотреть файл

@ -188,7 +188,7 @@
#endif #endif
#ifdef CONFIG_SERIAL_EARLYCON #ifdef CONFIG_SERIAL_EARLYCON
#define EARLYCON_TABLE() STRUCT_ALIGN(); \ #define EARLYCON_TABLE() . = ALIGN(8); \
VMLINUX_SYMBOL(__earlycon_table) = .; \ VMLINUX_SYMBOL(__earlycon_table) = .; \
KEEP(*(__earlycon_table)) \ KEEP(*(__earlycon_table)) \
VMLINUX_SYMBOL(__earlycon_table_end) = .; VMLINUX_SYMBOL(__earlycon_table_end) = .;

Просмотреть файл

@ -351,10 +351,10 @@ struct earlycon_id {
char name[16]; char name[16];
char compatible[128]; char compatible[128];
int (*setup)(struct earlycon_device *, const char *options); int (*setup)(struct earlycon_device *, const char *options);
} __aligned(32); };
extern const struct earlycon_id __earlycon_table[]; extern const struct earlycon_id *__earlycon_table[];
extern const struct earlycon_id __earlycon_table_end[]; extern const struct earlycon_id *__earlycon_table_end[];
#if defined(CONFIG_SERIAL_EARLYCON) && !defined(MODULE) #if defined(CONFIG_SERIAL_EARLYCON) && !defined(MODULE)
#define EARLYCON_USED_OR_UNUSED __used #define EARLYCON_USED_OR_UNUSED __used
@ -362,12 +362,19 @@ extern const struct earlycon_id __earlycon_table_end[];
#define EARLYCON_USED_OR_UNUSED __maybe_unused #define EARLYCON_USED_OR_UNUSED __maybe_unused
#endif #endif
#define OF_EARLYCON_DECLARE(_name, compat, fn) \ #define _OF_EARLYCON_DECLARE(_name, compat, fn, unique_id) \
static const struct earlycon_id __UNIQUE_ID(__earlycon_##_name) \ static const struct earlycon_id unique_id \
EARLYCON_USED_OR_UNUSED __section(__earlycon_table) \ EARLYCON_USED_OR_UNUSED __initconst \
= { .name = __stringify(_name), \ = { .name = __stringify(_name), \
.compatible = compat, \ .compatible = compat, \
.setup = fn } .setup = fn }; \
static const struct earlycon_id EARLYCON_USED_OR_UNUSED \
__section(__earlycon_table) \
* const __PASTE(__p, unique_id) = &unique_id
#define OF_EARLYCON_DECLARE(_name, compat, fn) \
_OF_EARLYCON_DECLARE(_name, compat, fn, \
__UNIQUE_ID(__earlycon_##_name))
#define EARLYCON_DECLARE(_name, fn) OF_EARLYCON_DECLARE(_name, "", fn) #define EARLYCON_DECLARE(_name, fn) OF_EARLYCON_DECLARE(_name, "", fn)