diff options
Diffstat (limited to 'scripts')
35 files changed, 3008 insertions, 258 deletions
diff --git a/scripts/.gitignore b/scripts/.gitignore index 65f362d931b5..fb070fa1038f 100644 --- a/scripts/.gitignore +++ b/scripts/.gitignore @@ -10,3 +10,4 @@ ihex2fw recordmcount docproc sortextable +asn1_compiler diff --git a/scripts/Makefile b/scripts/Makefile index a55b0067758a..01e7adb838d9 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -16,8 +16,10 @@ hostprogs-$(CONFIG_VT) += conmakehash hostprogs-$(CONFIG_IKCONFIG) += bin2c hostprogs-$(BUILD_C_RECORDMCOUNT) += recordmcount hostprogs-$(CONFIG_BUILDTIME_EXTABLE_SORT) += sortextable +hostprogs-$(CONFIG_ASN1) += asn1_compiler HOSTCFLAGS_sortextable.o = -I$(srctree)/tools/include +HOSTCFLAGS_asn1_compiler.o = -I$(srctree)/include always := $(hostprogs-y) $(hostprogs-m) diff --git a/scripts/Makefile.asm-generic b/scripts/Makefile.asm-generic index 40caf3c26cd5..d17e0ea911ed 100644 --- a/scripts/Makefile.asm-generic +++ b/scripts/Makefile.asm-generic @@ -5,7 +5,7 @@ # and for each file listed in this file with generic-y creates # a small wrapper file in $(obj) (arch/$(SRCARCH)/include/generated/asm) -kbuild-file := $(srctree)/arch/$(SRCARCH)/include/asm/Kbuild +kbuild-file := $(srctree)/arch/$(SRCARCH)/include/$(src)/Kbuild -include $(kbuild-file) include scripts/Kbuild.include diff --git a/scripts/Makefile.build b/scripts/Makefile.build index ff1720d28d0c..0e801c3cdaf8 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -354,6 +354,17 @@ quiet_cmd_cpp_lds_S = LDS $@ $(obj)/%.lds: $(src)/%.lds.S FORCE $(call if_changed_dep,cpp_lds_S) +# ASN.1 grammar +# --------------------------------------------------------------------------- +quiet_cmd_asn1_compiler = ASN.1 $@ + cmd_asn1_compiler = $(objtree)/scripts/asn1_compiler $< \ + $(subst .h,.c,$@) $(subst .c,.h,$@) + +.PRECIOUS: $(objtree)/$(obj)/%-asn1.c $(objtree)/$(obj)/%-asn1.h + +$(obj)/%-asn1.c $(obj)/%-asn1.h: $(src)/%.asn1 $(objtree)/scripts/asn1_compiler + $(call cmd,asn1_compiler) + # Build the compiled-in targets # --------------------------------------------------------------------------- diff --git a/scripts/Makefile.fwinst b/scripts/Makefile.fwinst index c3f69ae275d1..4d908d16c035 100644 --- a/scripts/Makefile.fwinst +++ b/scripts/Makefile.fwinst @@ -27,7 +27,7 @@ endif installed-mod-fw := $(addprefix $(INSTALL_FW_PATH)/,$(mod-fw)) installed-fw := $(addprefix $(INSTALL_FW_PATH)/,$(fw-shipped-all)) -installed-fw-dirs := $(sort $(dir $(installed-fw))) $(INSTALL_FW_PATH)/. +installed-fw-dirs := $(sort $(dir $(installed-fw))) $(INSTALL_FW_PATH)/./ # Workaround for make < 3.81, where .SECONDEXPANSION doesn't work. PHONY += $(INSTALL_FW_PATH)/$$(%) install-all-dirs @@ -42,7 +42,7 @@ quiet_cmd_install = INSTALL $(subst $(srctree)/,,$@) $(installed-fw-dirs): $(call cmd,mkdir) -$(installed-fw): $(INSTALL_FW_PATH)/%: $(obj)/% | $$(dir $(INSTALL_FW_PATH)/%) +$(installed-fw): $(INSTALL_FW_PATH)/%: $(obj)/% | $(INSTALL_FW_PATH)/$$(dir %) $(call cmd,install) PHONY += __fw_install __fw_modinst FORCE diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 0be6f110cce7..bdf42fdf64c9 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -266,6 +266,9 @@ $(obj)/%.dtb.S: $(obj)/%.dtb quiet_cmd_dtc = DTC $@ cmd_dtc = $(objtree)/scripts/dtc/dtc -O dtb -o $@ -b 0 $(DTC_FLAGS) -d $(depfile) $< +$(obj)/%.dtb: $(src)/%.dts FORCE + $(call if_changed_dep,dtc) + # Bzip2 # --------------------------------------------------------------------------- diff --git a/scripts/Makefile.modinst b/scripts/Makefile.modinst index efa5d940e632..ecbb44797e28 100644 --- a/scripts/Makefile.modinst +++ b/scripts/Makefile.modinst @@ -9,15 +9,16 @@ include scripts/Kbuild.include # -__modules := $(sort $(shell grep -h '\.ko' /dev/null $(wildcard $(MODVERDIR)/*.mod))) +__modules := $(sort $(shell grep -h '\.ko$$' /dev/null $(wildcard $(MODVERDIR)/*.mod))) modules := $(patsubst %.o,%.ko,$(wildcard $(__modules:.ko=.o))) PHONY += $(modules) __modinst: $(modules) @: +# Don't stop modules_install if we can't sign external modules. quiet_cmd_modules_install = INSTALL $@ - cmd_modules_install = mkdir -p $(2); cp $@ $(2) ; $(mod_strip_cmd) $(2)/$(notdir $@) + cmd_modules_install = mkdir -p $(2); cp $@ $(2) ; $(mod_strip_cmd) $(2)/$(notdir $@) ; $(mod_sign_cmd) $(2)/$(notdir $@) $(patsubst %,|| true,$(KBUILD_EXTMOD)) # Modules built outside the kernel source tree go into extra by default INSTALL_MOD_DIR ?= extra diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost index 08dce14f2dc8..a1cb0222ebe6 100644 --- a/scripts/Makefile.modpost +++ b/scripts/Makefile.modpost @@ -60,7 +60,7 @@ kernelsymfile := $(objtree)/Module.symvers modulesymfile := $(firstword $(KBUILD_EXTMOD))/Module.symvers # Step 1), find all modules listed in $(MODVERDIR)/ -__modules := $(sort $(shell grep -h '\.ko' /dev/null $(wildcard $(MODVERDIR)/*.mod))) +__modules := $(sort $(shell grep -h '\.ko$$' /dev/null $(wildcard $(MODVERDIR)/*.mod))) modules := $(patsubst %.o,%.ko, $(wildcard $(__modules:.ko=.o))) # Stop after building .o files if NOFINAL is set. Makes compile tests quicker diff --git a/scripts/Makefile.modsign b/scripts/Makefile.modsign new file mode 100644 index 000000000000..abfda626dbad --- /dev/null +++ b/scripts/Makefile.modsign @@ -0,0 +1,32 @@ +# ========================================================================== +# Signing modules +# ========================================================================== + +PHONY := __modsign +__modsign: + +include scripts/Kbuild.include + +__modules := $(sort $(shell grep -h '\.ko' /dev/null $(wildcard $(MODVERDIR)/*.mod))) +modules := $(patsubst %.o,%.ko,$(wildcard $(__modules:.ko=.o))) + +PHONY += $(modules) +__modsign: $(modules) + @: + +quiet_cmd_sign_ko = SIGN [M] $(2)/$(notdir $@) + cmd_sign_ko = $(mod_sign_cmd) $(2)/$(notdir $@) + +# Modules built outside the kernel source tree go into extra by default +INSTALL_MOD_DIR ?= extra +ext-mod-dir = $(INSTALL_MOD_DIR)$(subst $(patsubst %/,%,$(KBUILD_EXTMOD)),,$(@D)) + +modinst_dir = $(if $(KBUILD_EXTMOD),$(ext-mod-dir),kernel/$(@D)) + +$(modules): + $(call cmd,sign_ko,$(MODLIB)/$(modinst_dir)) + +# Declare the contents of the .PHONY variable as phony. We keep that +# information in a variable se we can use it in if_changed and friends. + +.PHONY: $(PHONY) diff --git a/scripts/asn1_compiler.c b/scripts/asn1_compiler.c new file mode 100644 index 000000000000..db0e5cd34c70 --- /dev/null +++ b/scripts/asn1_compiler.c @@ -0,0 +1,1545 @@ +/* Simplified ASN.1 notation parser + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <ctype.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <linux/asn1_ber_bytecode.h> + +enum token_type { + DIRECTIVE_ABSENT, + DIRECTIVE_ALL, + DIRECTIVE_ANY, + DIRECTIVE_APPLICATION, + DIRECTIVE_AUTOMATIC, + DIRECTIVE_BEGIN, + DIRECTIVE_BIT, + DIRECTIVE_BMPString, + DIRECTIVE_BOOLEAN, + DIRECTIVE_BY, + DIRECTIVE_CHARACTER, + DIRECTIVE_CHOICE, + DIRECTIVE_CLASS, + DIRECTIVE_COMPONENT, + DIRECTIVE_COMPONENTS, + DIRECTIVE_CONSTRAINED, + DIRECTIVE_CONTAINING, + DIRECTIVE_DEFAULT, + DIRECTIVE_DEFINED, + DIRECTIVE_DEFINITIONS, + DIRECTIVE_EMBEDDED, + DIRECTIVE_ENCODED, + DIRECTIVE_ENCODING_CONTROL, + DIRECTIVE_END, + DIRECTIVE_ENUMERATED, + DIRECTIVE_EXCEPT, + DIRECTIVE_EXPLICIT, + DIRECTIVE_EXPORTS, + DIRECTIVE_EXTENSIBILITY, + DIRECTIVE_EXTERNAL, + DIRECTIVE_FALSE, + DIRECTIVE_FROM, + DIRECTIVE_GeneralString, + DIRECTIVE_GeneralizedTime, + DIRECTIVE_GraphicString, + DIRECTIVE_IA5String, + DIRECTIVE_IDENTIFIER, + DIRECTIVE_IMPLICIT, + DIRECTIVE_IMPLIED, + DIRECTIVE_IMPORTS, + DIRECTIVE_INCLUDES, + DIRECTIVE_INSTANCE, + DIRECTIVE_INSTRUCTIONS, + DIRECTIVE_INTEGER, + DIRECTIVE_INTERSECTION, + DIRECTIVE_ISO646String, + DIRECTIVE_MAX, + DIRECTIVE_MIN, + DIRECTIVE_MINUS_INFINITY, + DIRECTIVE_NULL, + DIRECTIVE_NumericString, + DIRECTIVE_OBJECT, + DIRECTIVE_OCTET, + DIRECTIVE_OF, + DIRECTIVE_OPTIONAL, + DIRECTIVE_ObjectDescriptor, + DIRECTIVE_PATTERN, + DIRECTIVE_PDV, + DIRECTIVE_PLUS_INFINITY, + DIRECTIVE_PRESENT, + DIRECTIVE_PRIVATE, + DIRECTIVE_PrintableString, + DIRECTIVE_REAL, + DIRECTIVE_RELATIVE_OID, + DIRECTIVE_SEQUENCE, + DIRECTIVE_SET, + DIRECTIVE_SIZE, + DIRECTIVE_STRING, + DIRECTIVE_SYNTAX, + DIRECTIVE_T61String, + DIRECTIVE_TAGS, + DIRECTIVE_TRUE, + DIRECTIVE_TeletexString, + DIRECTIVE_UNION, + DIRECTIVE_UNIQUE, + DIRECTIVE_UNIVERSAL, + DIRECTIVE_UTCTime, + DIRECTIVE_UTF8String, + DIRECTIVE_UniversalString, + DIRECTIVE_VideotexString, + DIRECTIVE_VisibleString, + DIRECTIVE_WITH, + NR__DIRECTIVES, + TOKEN_ASSIGNMENT = NR__DIRECTIVES, + TOKEN_OPEN_CURLY, + TOKEN_CLOSE_CURLY, + TOKEN_OPEN_SQUARE, + TOKEN_CLOSE_SQUARE, + TOKEN_OPEN_ACTION, + TOKEN_CLOSE_ACTION, + TOKEN_COMMA, + TOKEN_NUMBER, + TOKEN_TYPE_NAME, + TOKEN_ELEMENT_NAME, + NR__TOKENS +}; + +static const unsigned char token_to_tag[NR__TOKENS] = { + /* EOC goes first */ + [DIRECTIVE_BOOLEAN] = ASN1_BOOL, + [DIRECTIVE_INTEGER] = ASN1_INT, + [DIRECTIVE_BIT] = ASN1_BTS, + [DIRECTIVE_OCTET] = ASN1_OTS, + [DIRECTIVE_NULL] = ASN1_NULL, + [DIRECTIVE_OBJECT] = ASN1_OID, + [DIRECTIVE_ObjectDescriptor] = ASN1_ODE, + [DIRECTIVE_EXTERNAL] = ASN1_EXT, + [DIRECTIVE_REAL] = ASN1_REAL, + [DIRECTIVE_ENUMERATED] = ASN1_ENUM, + [DIRECTIVE_EMBEDDED] = 0, + [DIRECTIVE_UTF8String] = ASN1_UTF8STR, + [DIRECTIVE_RELATIVE_OID] = ASN1_RELOID, + /* 14 */ + /* 15 */ + [DIRECTIVE_SEQUENCE] = ASN1_SEQ, + [DIRECTIVE_SET] = ASN1_SET, + [DIRECTIVE_NumericString] = ASN1_NUMSTR, + [DIRECTIVE_PrintableString] = ASN1_PRNSTR, + [DIRECTIVE_T61String] = ASN1_TEXSTR, + [DIRECTIVE_TeletexString] = ASN1_TEXSTR, + [DIRECTIVE_VideotexString] = ASN1_VIDSTR, + [DIRECTIVE_IA5String] = ASN1_IA5STR, + [DIRECTIVE_UTCTime] = ASN1_UNITIM, + [DIRECTIVE_GeneralizedTime] = ASN1_GENTIM, + [DIRECTIVE_GraphicString] = ASN1_GRASTR, + [DIRECTIVE_VisibleString] = ASN1_VISSTR, + [DIRECTIVE_GeneralString] = ASN1_GENSTR, + [DIRECTIVE_UniversalString] = ASN1_UNITIM, + [DIRECTIVE_CHARACTER] = ASN1_CHRSTR, + [DIRECTIVE_BMPString] = ASN1_BMPSTR, +}; + +static const char asn1_classes[4][5] = { + [ASN1_UNIV] = "UNIV", + [ASN1_APPL] = "APPL", + [ASN1_CONT] = "CONT", + [ASN1_PRIV] = "PRIV" +}; + +static const char asn1_methods[2][5] = { + [ASN1_UNIV] = "PRIM", + [ASN1_APPL] = "CONS" +}; + +static const char *const asn1_universal_tags[32] = { + "EOC", + "BOOL", + "INT", + "BTS", + "OTS", + "NULL", + "OID", + "ODE", + "EXT", + "REAL", + "ENUM", + "EPDV", + "UTF8STR", + "RELOID", + NULL, /* 14 */ + NULL, /* 15 */ + "SEQ", + "SET", + "NUMSTR", + "PRNSTR", + "TEXSTR", + "VIDSTR", + "IA5STR", + "UNITIM", + "GENTIM", + "GRASTR", + "VISSTR", + "GENSTR", + "UNISTR", + "CHRSTR", + "BMPSTR", + NULL /* 31 */ +}; + +static const char *filename; +static const char *grammar_name; +static const char *outputname; +static const char *headername; + +static const char *const directives[NR__DIRECTIVES] = { +#define _(X) [DIRECTIVE_##X] = #X + _(ABSENT), + _(ALL), + _(ANY), + _(APPLICATION), + _(AUTOMATIC), + _(BEGIN), + _(BIT), + _(BMPString), + _(BOOLEAN), + _(BY), + _(CHARACTER), + _(CHOICE), + _(CLASS), + _(COMPONENT), + _(COMPONENTS), + _(CONSTRAINED), + _(CONTAINING), + _(DEFAULT), + _(DEFINED), + _(DEFINITIONS), + _(EMBEDDED), + _(ENCODED), + [DIRECTIVE_ENCODING_CONTROL] = "ENCODING-CONTROL", + _(END), + _(ENUMERATED), + _(EXCEPT), + _(EXPLICIT), + _(EXPORTS), + _(EXTENSIBILITY), + _(EXTERNAL), + _(FALSE), + _(FROM), + _(GeneralString), + _(GeneralizedTime), + _(GraphicString), + _(IA5String), + _(IDENTIFIER), + _(IMPLICIT), + _(IMPLIED), + _(IMPORTS), + _(INCLUDES), + _(INSTANCE), + _(INSTRUCTIONS), + _(INTEGER), + _(INTERSECTION), + _(ISO646String), + _(MAX), + _(MIN), + [DIRECTIVE_MINUS_INFINITY] = "MINUS-INFINITY", + [DIRECTIVE_NULL] = "NULL", + _(NumericString), + _(OBJECT), + _(OCTET), + _(OF), + _(OPTIONAL), + _(ObjectDescriptor), + _(PATTERN), + _(PDV), + [DIRECTIVE_PLUS_INFINITY] = "PLUS-INFINITY", + _(PRESENT), + _(PRIVATE), + _(PrintableString), + _(REAL), + [DIRECTIVE_RELATIVE_OID] = "RELATIVE-OID", + _(SEQUENCE), + _(SET), + _(SIZE), + _(STRING), + _(SYNTAX), + _(T61String), + _(TAGS), + _(TRUE), + _(TeletexString), + _(UNION), + _(UNIQUE), + _(UNIVERSAL), + _(UTCTime), + _(UTF8String), + _(UniversalString), + _(VideotexString), + _(VisibleString), + _(WITH) +}; + +struct action { + struct action *next; + unsigned char index; + char name[]; +}; + +static struct action *action_list; +static unsigned nr_actions; + +struct token { + unsigned short line; + enum token_type token_type : 8; + unsigned char size; + struct action *action; + const char *value; + struct type *type; +}; + +static struct token *token_list; +static unsigned nr_tokens; + +static int directive_compare(const void *_key, const void *_pdir) +{ + const struct token *token = _key; + const char *const *pdir = _pdir, *dir = *pdir; + size_t dlen, clen; + int val; + + dlen = strlen(dir); + clen = (dlen < token->size) ? dlen : token->size; + + //printf("cmp(%*.*s,%s) = ", + // (int)token->size, (int)token->size, token->value, + // dir); + + val = memcmp(token->value, dir, clen); + if (val != 0) { + //printf("%d [cmp]\n", val); + return val; + } + + if (dlen == token->size) { + //printf("0\n"); + return 0; + } + //printf("%d\n", (int)dlen - (int)token->size); + return dlen - token->size; /* shorter -> negative */ +} + +/* + * Tokenise an ASN.1 grammar + */ +static void tokenise(char *buffer, char *end) +{ + struct token *tokens; + char *line, *nl, *p, *q; + unsigned tix, lineno; + + /* Assume we're going to have half as many tokens as we have + * characters + */ + token_list = tokens = calloc((end - buffer) / 2, sizeof(struct token)); + if (!tokens) { + perror(NULL); + exit(1); + } + tix = 0; + + lineno = 0; + while (buffer < end) { + /* First of all, break out a line */ + lineno++; + line = buffer; + nl = memchr(line, '\n', end - buffer); + if (!nl) { + buffer = nl = end; + } else { + buffer = nl + 1; + *nl = '\0'; + } + + /* Remove "--" comments */ + p = line; + next_comment: + while ((p = memchr(p, '-', nl - p))) { + if (p[1] == '-') { + /* Found a comment; see if there's a terminator */ + q = p + 2; + while ((q = memchr(q, '-', nl - q))) { + if (q[1] == '-') { + /* There is - excise the comment */ + q += 2; + memmove(p, q, nl - q); + goto next_comment; + } + q++; + } + *p = '\0'; + nl = p; + break; + } else { + p++; + } + } + + p = line; + while (p < nl) { + /* Skip white space */ + while (p < nl && isspace(*p)) + *(p++) = 0; + if (p >= nl) + break; + + tokens[tix].line = lineno; + tokens[tix].value = p; + + /* Handle string tokens */ + if (isalpha(*p)) { + const char **dir; + + /* Can be a directive, type name or element + * name. Find the end of the name. + */ + q = p + 1; + while (q < nl && (isalnum(*q) || *q == '-' || *q == '_')) + q++; + tokens[tix].size = q - p; + p = q; + + /* If it begins with a lowercase letter then + * it's an element name + */ + if (islower(tokens[tix].value[0])) { + tokens[tix++].token_type = TOKEN_ELEMENT_NAME; + continue; + } + + /* Otherwise we need to search the directive + * table + */ + dir = bsearch(&tokens[tix], directives, + sizeof(directives) / sizeof(directives[1]), + sizeof(directives[1]), + directive_compare); + if (dir) { + tokens[tix++].token_type = dir - directives; + continue; + } + + tokens[tix++].token_type = TOKEN_TYPE_NAME; + continue; + } + + /* Handle numbers */ + if (isdigit(*p)) { + /* Find the end of the number */ + q = p + 1; + while (q < nl && (isdigit(*q))) + q++; + tokens[tix].size = q - p; + p = q; + tokens[tix++].token_type = TOKEN_NUMBER; + continue; + } + + if (nl - p >= 3) { + if (memcmp(p, "::=", 3) == 0) { + p += 3; + tokens[tix].size = 3; + tokens[tix++].token_type = TOKEN_ASSIGNMENT; + continue; + } + } + + if (nl - p >= 2) { + if (memcmp(p, "({", 2) == 0) { + p += 2; + tokens[tix].size = 2; + tokens[tix++].token_type = TOKEN_OPEN_ACTION; + continue; + } + if (memcmp(p, "})", 2) == 0) { + p += 2; + tokens[tix].size = 2; + tokens[tix++].token_type = TOKEN_CLOSE_ACTION; + continue; + } + } + + if (nl - p >= 1) { + tokens[tix].size = 1; + switch (*p) { + case '{': + p += 1; + tokens[tix++].token_type = TOKEN_OPEN_CURLY; + continue; + case '}': + p += 1; + tokens[tix++].token_type = TOKEN_CLOSE_CURLY; + continue; + case '[': + p += 1; + tokens[tix++].token_type = TOKEN_OPEN_SQUARE; + continue; + case ']': + p += 1; + tokens[tix++].token_type = TOKEN_CLOSE_SQUARE; + continue; + case ',': + p += 1; + tokens[tix++].token_type = TOKEN_COMMA; + continue; + default: + break; + } + } + + fprintf(stderr, "%s:%u: Unknown character in grammar: '%c'\n", + filename, lineno, *p); + exit(1); + } + } + + nr_tokens = tix; + printf("Extracted %u tokens\n", nr_tokens); + +#if 0 + { + int n; + for (n = 0; n < nr_tokens; n++) + printf("Token %3u: '%*.*s'\n", + n, + (int)token_list[n].size, (int)token_list[n].size, + token_list[n].value); + } +#endif +} + +static void build_type_list(void); +static void parse(void); +static void render(FILE *out, FILE *hdr); + +/* + * + */ +int main(int argc, char **argv) +{ + struct stat st; + ssize_t readlen; + FILE *out, *hdr; + char *buffer, *p; + int fd; + + if (argc != 4) { + fprintf(stderr, "Format: %s <grammar-file> <c-file> <hdr-file>\n", + argv[0]); + exit(2); + } + + filename = argv[1]; + outputname = argv[2]; + headername = argv[3]; + + fd = open(filename, O_RDONLY); + if (fd < 0) { + perror(filename); + exit(1); + } + + if (fstat(fd, &st) < 0) { + perror(filename); + exit(1); + } + + if (!(buffer = malloc(st.st_size + 1))) { + perror(NULL); + exit(1); + } + + if ((readlen = read(fd, buffer, st.st_size)) < 0) { + perror(filename); + exit(1); + } + + if (close(fd) < 0) { + perror(filename); + exit(1); + } + + if (readlen != st.st_size) { + fprintf(stderr, "%s: Short read\n", filename); + exit(1); + } + + p = strrchr(argv[1], '/'); + p = p ? p + 1 : argv[1]; + grammar_name = strdup(p); + if (!p) { + perror(NULL); + exit(1); + } + p = strchr(grammar_name, '.'); + if (p) + *p = '\0'; + + buffer[readlen] = 0; + tokenise(buffer, buffer + readlen); + build_type_list(); + parse(); + + out = fopen(outputname, "w"); + if (!out) { + perror(outputname); + exit(1); + } + + hdr = fopen(headername, "w"); + if (!out) { + perror(headername); + exit(1); + } + + render(out, hdr); + + if (fclose(out) < 0) { + perror(outputname); + exit(1); + } + + if (fclose(hdr) < 0) { + perror(headername); + exit(1); + } + + return 0; +} + +enum compound { + NOT_COMPOUND, + SET, + SET_OF, + SEQUENCE, + SEQUENCE_OF, + CHOICE, + ANY, + TYPE_REF, + TAG_OVERRIDE +}; + +struct element { + struct type *type_def; + struct token *name; + struct token *type; + struct action *action; + struct element *children; + struct element *next; + struct element *render_next; + struct element *list_next; + uint8_t n_elements; + enum compound compound : 8; + enum asn1_class class : 8; + enum asn1_method method : 8; + uint8_t tag; + unsigned entry_index; + unsigned flags; +#define ELEMENT_IMPLICIT 0x0001 +#define ELEMENT_EXPLICIT 0x0002 +#define ELEMENT_MARKED 0x0004 +#define ELEMENT_RENDERED 0x0008 +#define ELEMENT_SKIPPABLE 0x0010 +#define ELEMENT_CONDITIONAL 0x0020 +}; + +struct type { + struct token *name; + struct token *def; + struct element *element; + unsigned ref_count; + unsigned flags; +#define TYPE_STOP_MARKER 0x0001 +#define TYPE_BEGIN 0x0002 +}; + +static struct type *type_list; +static struct type **type_index; +static unsigned nr_types; + +static int type_index_compare(const void *_a, const void *_b) +{ + const struct type *const *a = _a, *const *b = _b; + + if ((*a)->name->size != (*b)->name->size) + return (*a)->name->size - (*b)->name->size; + else + return memcmp((*a)->name->value, (*b)->name->value, + (*a)->name->size); +} + +static int type_finder(const void *_key, const void *_ti) +{ + const struct token *token = _key; + const struct type *const *ti = _ti; + const struct type *type = *ti; + + if (token->size != type->name->size) + return token->size - type->name->size; + else + return memcmp(token->value, type->name->value, + token->size); +} + +/* + * Build up a list of types and a sorted index to that list. + */ +static void build_type_list(void) +{ + struct type *types; + unsigned nr, t, n; + + nr = 0; + for (n = 0; n < nr_tokens - 1; n++) + if (token_list[n + 0].token_type == TOKEN_TYPE_NAME && + token_list[n + 1].token_type == TOKEN_ASSIGNMENT) + nr++; + + if (nr == 0) { + fprintf(stderr, "%s: No defined types\n", filename); + exit(1); + } + + nr_types = nr; + types = type_list = calloc(nr + 1, sizeof(type_list[0])); + if (!type_list) { + perror(NULL); + exit(1); + } + type_index = calloc(nr, sizeof(type_index[0])); + if (!type_index) { + perror(NULL); + exit(1); + } + + t = 0; + types[t].flags |= TYPE_BEGIN; + for (n = 0; n < nr_tokens - 1; n++) { + if (token_list[n + 0].token_type == TOKEN_TYPE_NAME && + token_list[n + 1].token_type == TOKEN_ASSIGNMENT) { + types[t].name = &token_list[n]; + type_index[t] = &types[t]; + t++; + } + } + types[t].name = &token_list[n + 1]; + types[t].flags |= TYPE_STOP_MARKER; + + qsort(type_index, nr, sizeof(type_index[0]), type_index_compare); + + printf("Extracted %u types\n", nr_types); +#if 0 + for (n = 0; n < nr_types; n++) { + struct type *type = type_index[n]; + printf("- %*.*s\n", + (int)type->name->size, + (int)type->name->size, + type->name->value); + } +#endif +} + +static struct element *parse_type(struct token **_cursor, struct token *stop, + struct token *name); + +/* + * Parse the token stream + */ +static void parse(void) +{ + struct token *cursor; + struct type *type; + + /* Parse one type definition statement at a time */ + type = type_list; + do { + cursor = type->name; + + if (cursor[0].token_type != TOKEN_TYPE_NAME || + cursor[1].token_type != TOKEN_ASSIGNMENT) + abort(); + cursor += 2; + + type->element = parse_type(&cursor, type[1].name, NULL); + type->element->type_def = type; + + if (cursor != type[1].name) { + fprintf(stderr, "%s:%d: Parse error at token '%*.*s'\n", + filename, cursor->line, + (int)cursor->size, (int)cursor->size, cursor->value); + exit(1); + } + + } while (type++, !(type->flags & TYPE_STOP_MARKER)); + + printf("Extracted %u actions\n", nr_actions); +} + +static struct element *element_list; + +static struct element *alloc_elem(struct token *type) +{ + struct element *e = calloc(1, sizeof(*e)); + if (!e) { + perror(NULL); + exit(1); + } + e->list_next = element_list; + element_list = e; + return e; +} + +static struct element *parse_compound(struct token **_cursor, struct token *end, + int alternates); + +/* + * Parse one type definition statement + */ +static struct element *parse_type(struct token **_cursor, struct token *end, + struct token *name) +{ + struct element *top, *element; + struct action *action, **ppaction; + struct token *cursor = *_cursor; + struct type **ref; + char *p; + int labelled = 0, implicit = 0; + + top = element = alloc_elem(cursor); + element->class = ASN1_UNIV; + element->method = ASN1_PRIM; + element->tag = token_to_tag[cursor->token_type]; + element->name = name; + + /* Extract the tag value if one given */ + if (cursor->token_type == TOKEN_OPEN_SQUARE) { + cursor++; + if (cursor >= end) + goto overrun_error; + switch (cursor->token_type) { + case DIRECTIVE_UNIVERSAL: + element->class = ASN1_UNIV; + cursor++; + break; + case DIRECTIVE_APPLICATION: + element->class = ASN1_APPL; + cursor++; + break; + case TOKEN_NUMBER: + element->class = ASN1_CONT; + break; + case DIRECTIVE_PRIVATE: + element->class = ASN1_PRIV; + cursor++; + break; + default: + fprintf(stderr, "%s:%d: Unrecognised tag class token '%*.*s'\n", + filename, cursor->line, + (int)cursor->size, (int)cursor->size, cursor->value); + exit(1); + } + + if (cursor >= end) + goto overrun_error; + if (cursor->token_type != TOKEN_NUMBER) { + fprintf(stderr, "%s:%d: Missing tag number '%*.*s'\n", + filename, cursor->line, + (int)cursor->size, (int)cursor->size, cursor->value); + exit(1); + } + + element->tag &= ~0x1f; + element->tag |= strtoul(cursor->value, &p, 10); + if (p - cursor->value != cursor->size) + abort(); + cursor++; + + if (cursor >= end) + goto overrun_error; + if (cursor->token_type != TOKEN_CLOSE_SQUARE) { + fprintf(stderr, "%s:%d: Missing closing square bracket '%*.*s'\n", + filename, cursor->line, + (int)cursor->size, (int)cursor->size, cursor->value); + exit(1); + } + cursor++; + if (cursor >= end) + goto overrun_error; + labelled = 1; + } + + /* Handle implicit and explicit markers */ + if (cursor->token_type == DIRECTIVE_IMPLICIT) { + element->flags |= ELEMENT_IMPLICIT; + implicit = 1; + cursor++; + if (cursor >= end) + goto overrun_error; + } else if (cursor->token_type == DIRECTIVE_EXPLICIT) { + element->flags |= ELEMENT_EXPLICIT; + cursor++; + if (cursor >= end) + goto overrun_error; + } + + if (labelled) { + if (!implicit) + element->method |= ASN1_CONS; + element->compound = implicit ? TAG_OVERRIDE : SEQUENCE; + element->children = alloc_elem(cursor); + element = element->children; + element->class = ASN1_UNIV; + element->method = ASN1_PRIM; + element->tag = token_to_tag[cursor->token_type]; + element->name = name; + } + + /* Extract the type we're expecting here */ + element->type = cursor; + switch (cursor->token_type) { + case DIRECTIVE_ANY: + element->compound = ANY; + cursor++; + break; + + case DIRECTIVE_NULL: + case DIRECTIVE_BOOLEAN: + case DIRECTIVE_ENUMERATED: + case DIRECTIVE_INTEGER: + element->compound = NOT_COMPOUND; + cursor++; + break; + + case DIRECTIVE_EXTERNAL: + element->method = ASN1_CONS; + + case DIRECTIVE_BMPString: + case DIRECTIVE_GeneralString: + case DIRECTIVE_GraphicString: + case DIRECTIVE_IA5String: + case DIRECTIVE_ISO646String: + case DIRECTIVE_NumericString: + case DIRECTIVE_PrintableString: + case DIRECTIVE_T61String: + case DIRECTIVE_TeletexString: + case DIRECTIVE_UniversalString: + case DIRECTIVE_UTF8String: + case DIRECTIVE_VideotexString: + case DIRECTIVE_VisibleString: + case DIRECTIVE_ObjectDescriptor: + case DIRECTIVE_GeneralizedTime: + case DIRECTIVE_UTCTime: + element->compound = NOT_COMPOUND; + cursor++; + break; + + case DIRECTIVE_BIT: + case DIRECTIVE_OCTET: + element->compound = NOT_COMPOUND; + cursor++; + if (cursor >= end) + goto overrun_error; + if (cursor->token_type != DIRECTIVE_STRING) + goto parse_error; + cursor++; + break; + + case DIRECTIVE_OBJECT: + element->compound = NOT_COMPOUND; + cursor++; + if (cursor >= end) + goto overrun_error; + if (cursor->token_type != DIRECTIVE_IDENTIFIER) + goto parse_error; + cursor++; + break; + + case TOKEN_TYPE_NAME: + element->compound = TYPE_REF; + ref = bsearch(cursor, type_index, nr_types, sizeof(type_index[0]), + type_finder); + if (!ref) { + fprintf(stderr, "%s:%d: Type '%*.*s' undefined\n", + filename, cursor->line, + (int)cursor->size, (int)cursor->size, cursor->value); + exit(1); + } + cursor->type = *ref; + (*ref)->ref_count++; + cursor++; + break; + + case DIRECTIVE_CHOICE: + element->compound = CHOICE; + cursor++; + element->children = parse_compound(&cursor, end, 1); + break; + + case DIRECTIVE_SEQUENCE: + element->compound = SEQUENCE; + element->method = ASN1_CONS; + cursor++; + if (cursor >= end) + goto overrun_error; + if (cursor->token_type == DIRECTIVE_OF) { + element->compound = SEQUENCE_OF; + cursor++; + if (cursor >= end) + goto overrun_error; + element->children = parse_type(&cursor, end, NULL); + } else { + element->children = parse_compound(&cursor, end, 0); + } + break; + + case DIRECTIVE_SET: + element->compound = SET; + element->method = ASN1_CONS; + cursor++; + if (cursor >= end) + goto overrun_error; + if (cursor->token_type == DIRECTIVE_OF) { + element->compound = SET_OF; + cursor++; + if (cursor >= end) + goto parse_error; + element->children = parse_type(&cursor, end, NULL); + } else { + element->children = parse_compound(&cursor, end, 1); + } + break; + + default: + fprintf(stderr, "%s:%d: Token '%*.*s' does not introduce a type\n", + filename, cursor->line, + (int)cursor->size, (int)cursor->size, cursor->value); + exit(1); + } + + /* Handle elements that are optional */ + if (cursor < end && (cursor->token_type == DIRECTIVE_OPTIONAL || + cursor->token_type == DIRECTIVE_DEFAULT) + ) { + cursor++; + top->flags |= ELEMENT_SKIPPABLE; + } + + if (cursor < end && cursor->token_type == TOKEN_OPEN_ACTION) { + cursor++; + if (cursor >= end) + goto overrun_error; + if (cursor->token_type != TOKEN_ELEMENT_NAME) { + fprintf(stderr, "%s:%d: Token '%*.*s' is not an action function name\n", + filename, cursor->line, + (int)cursor->size, (int)cursor->size, cursor->value); + exit(1); + } + + action = malloc(sizeof(struct action) + cursor->size + 1); + if (!action) { + perror(NULL); + exit(1); + } + action->index = 0; + memcpy(action->name, cursor->value, cursor->size); + action->name[cursor->size] = 0; + + for (ppaction = &action_list; + *ppaction; + ppaction = &(*ppaction)->next + ) { + int cmp = strcmp(action->name, (*ppaction)->name); + if (cmp == 0) { + free(action); + action = *ppaction; + goto found; + } + if (cmp < 0) { + action->next = *ppaction; + *ppaction = action; + nr_actions++; + goto found; + } + } + action->next = NULL; + *ppaction = action; + nr_actions++; + found: + + element->action = action; + cursor->action = action; + cursor++; + if (cursor >= end) + goto overrun_error; + if (cursor->token_type != TOKEN_CLOSE_ACTION) { + fprintf(stderr, "%s:%d: Missing close action, got '%*.*s'\n", + filename, cursor->line, + (int)cursor->size, (int)cursor->size, cursor->value); + exit(1); + } + cursor++; + } + + *_cursor = cursor; + return top; + +parse_error: + fprintf(stderr, "%s:%d: Unexpected token '%*.*s'\n", + filename, cursor->line, + (int)cursor->size, (int)cursor->size, cursor->value); + exit(1); + +overrun_error: + fprintf(stderr, "%s: Unexpectedly hit EOF\n", filename); + exit(1); +} + +/* + * Parse a compound type list + */ +static struct element *parse_compound(struct token **_cursor, struct token *end, + int alternates) +{ + struct element *children, **child_p = &children, *element; + struct token *cursor = *_cursor, *name; + + if (cursor->token_type != TOKEN_OPEN_CURLY) { + fprintf(stderr, "%s:%d: Expected compound to start with brace not '%*.*s'\n", + filename, cursor->line, + (int)cursor->size, (int)cursor->size, cursor->value); + exit(1); + } + cursor++; + if (cursor >= end) + goto overrun_error; + + if (cursor->token_type == TOKEN_OPEN_CURLY) { + fprintf(stderr, "%s:%d: Empty compound\n", + filename, cursor->line); + exit(1); + } + + for (;;) { + name = NULL; + if (cursor->token_type == TOKEN_ELEMENT_NAME) { + name = cursor; + cursor++; + if (cursor >= end) + goto overrun_error; + } + + element = parse_type(&cursor, end, name); + if (alternates) + element->flags |= ELEMENT_SKIPPABLE | ELEMENT_CONDITIONAL; + + *child_p = element; + child_p = &element->next; + + if (cursor >= end) + goto overrun_error; + if (cursor->token_type != TOKEN_COMMA) + break; + cursor++; + if (cursor >= end) + goto overrun_error; + } + + children->flags &= ~ELEMENT_CONDITIONAL; + + if (cursor->token_type != TOKEN_CLOSE_CURLY) { + fprintf(stderr, "%s:%d: Expected compound closure, got '%*.*s'\n", + filename, cursor->line, + (int)cursor->size, (int)cursor->size, cursor->value); + exit(1); + } + cursor++; + + *_cursor = cursor; + return children; + +overrun_error: + fprintf(stderr, "%s: Unexpectedly hit EOF\n", filename); + exit(1); +} + +static void render_element(FILE *out, struct element *e, struct element *tag); +static void render_out_of_line_list(FILE *out); + +static int nr_entries; +static int render_depth = 1; +static struct element *render_list, **render_list_p = &render_list; + +__attribute__((format(printf, 2, 3))) +static void render_opcode(FILE *out, const char *fmt, ...) +{ + va_list va; + + if (out) { + fprintf(out, "\t[%4d] =%*s", nr_entries, render_depth, ""); + va_start(va, fmt); + vfprintf(out, fmt, va); + va_end(va); + } + nr_entries++; +} + +__attribute__((format(printf, 2, 3))) +static void render_more(FILE *out, const char *fmt, ...) +{ + va_list va; + + if (out) { + va_start(va, fmt); + vfprintf(out, fmt, va); + va_end(va); + } +} + +/* + * Render the grammar into a state machine definition. + */ +static void render(FILE *out, FILE *hdr) +{ + struct element *e; + struct action *action; + struct type *root; + int index; + + fprintf(hdr, "/*\n"); + fprintf(hdr, " * Automatically generated by asn1_compiler. Do not edit\n"); + fprintf(hdr, " *\n"); + fprintf(hdr, " * ASN.1 parser for %s\n", grammar_name); + fprintf(hdr, " */\n"); + fprintf(hdr, "#include <linux/asn1_decoder.h>\n"); + fprintf(hdr, "\n"); + fprintf(hdr, "extern const struct asn1_decoder %s_decoder;\n", grammar_name); + if (ferror(hdr)) { + perror(headername); + exit(1); + } + + fprintf(out, "/*\n"); + fprintf(out, " * Automatically generated by asn1_compiler. Do not edit\n"); + fprintf(out, " *\n"); + fprintf(out, " * ASN.1 parser for %s\n", grammar_name); + fprintf(out, " */\n"); + fprintf(out, "#include <linux/asn1_ber_bytecode.h>\n"); + fprintf(out, "#include \"%s-asn1.h\"\n", grammar_name); + fprintf(out, "\n"); + if (ferror(out)) { + perror(outputname); + exit(1); + } + + /* Tabulate the action functions we might have to call */ + fprintf(hdr, "\n"); + index = 0; + for (action = action_list; action; action = action->next) { + action->index = index++; + fprintf(hdr, + "extern int %s(void *, size_t, unsigned char," + " const void *, size_t);\n", + action->name); + } + fprintf(hdr, "\n"); + + fprintf(out, "enum %s_actions {\n", grammar_name); + for (action = action_list; action; action = action->next) + fprintf(out, "\tACT_%s = %u,\n", + action->name, action->index); + fprintf(out, "\tNR__%s_actions = %u\n", grammar_name, nr_actions); + fprintf(out, "};\n"); + + fprintf(out, "\n"); + fprintf(out, "static const asn1_action_t %s_action_table[NR__%s_actions] = {\n", + grammar_name, grammar_name); + for (action = action_list; action; action = action->next) + fprintf(out, "\t[%4u] = %s,\n", action->index, action->name); + fprintf(out, "};\n"); + + if (ferror(out)) { + perror(outputname); + exit(1); + } + + /* We do two passes - the first one calculates all the offsets */ + printf("Pass 1\n"); + nr_entries = 0; + root = &type_list[0]; + render_element(NULL, root->element, NULL); + render_opcode(NULL, "ASN1_OP_COMPLETE,\n"); + render_out_of_line_list(NULL); + + for (e = element_list; e; e = e->list_next) + e->flags &= ~ELEMENT_RENDERED; + + /* And then we actually render */ + printf("Pass 2\n"); + fprintf(out, "\n"); + fprintf(out, "static const unsigned char %s_machine[] = {\n", + grammar_name); + + nr_entries = 0; + root = &type_list[0]; + render_element(out, root->element, NULL); + render_opcode(out, "ASN1_OP_COMPLETE,\n"); + render_out_of_line_list(out); + + fprintf(out, "};\n"); + + fprintf(out, "\n"); + fprintf(out, "const struct asn1_decoder %s_decoder = {\n", grammar_name); + fprintf(out, "\t.machine = %s_machine,\n", grammar_name); + fprintf(out, "\t.machlen = sizeof(%s_machine),\n", grammar_name); + fprintf(out, "\t.actions = %s_action_table,\n", grammar_name); + fprintf(out, "};\n"); +} + +/* + * Render the out-of-line elements + */ +static void render_out_of_line_list(FILE *out) +{ + struct element *e, *ce; + const char *act; + int entry; + + while ((e = render_list)) { + render_list = e->render_next; + if (!render_list) + render_list_p = &render_list; + + render_more(out, "\n"); + e->entry_index = entry = nr_entries; + render_depth++; + for (ce = e->children; ce; ce = ce->next) + render_element(out, ce, NULL); + render_depth--; + + act = e->action ? "_ACT" : ""; + switch (e->compound) { + case SEQUENCE: + render_opcode(out, "ASN1_OP_END_SEQ%s,\n", act); + break; + case SEQUENCE_OF: + render_opcode(out, "ASN1_OP_END_SEQ_OF%s,\n", act); + render_opcode(out, "_jump_target(%u),\n", entry); + break; + case SET: + render_opcode(out, "ASN1_OP_END_SET%s,\n", act); + break; + case SET_OF: + render_opcode(out, "ASN1_OP_END_SET_OF%s,\n", act); + render_opcode(out, "_jump_target(%u),\n", entry); + break; + } + if (e->action) + render_opcode(out, "_action(ACT_%s),\n", + e->action->name); + render_opcode(out, "ASN1_OP_RETURN,\n"); + } +} + +/* + * Render an element. + */ +static void render_element(FILE *out, struct element *e, struct element *tag) +{ + struct element *ec; + const char *cond, *act; + int entry, skippable = 0, outofline = 0; + + if (e->flags & ELEMENT_SKIPPABLE || + (tag && tag->flags & ELEMENT_SKIPPABLE)) + skippable = 1; + + if ((e->type_def && e->type_def->ref_count > 1) || + skippable) + outofline = 1; + + if (e->type_def && out) { + render_more(out, "\t// %*.*s\n", + (int)e->type_def->name->size, (int)e->type_def->name->size, + e->type_def->name->value); + } + + /* Render the operation */ + cond = (e->flags & ELEMENT_CONDITIONAL || + (tag && tag->flags & ELEMENT_CONDITIONAL)) ? "COND_" : ""; + act = e->action ? "_ACT" : ""; + switch (e->compound) { + case ANY: + render_opcode(out, "ASN1_OP_%sMATCH_ANY%s,", cond, act); + if (e->name) + render_more(out, "\t\t// %*.*s", + (int)e->name->size, (int)e->name->size, + e->name->value); + render_more(out, "\n"); + goto dont_render_tag; + + case TAG_OVERRIDE: + render_element(out, e->children, e); + return; + + case SEQUENCE: + case SEQUENCE_OF: + case SET: + case SET_OF: + render_opcode(out, "ASN1_OP_%sMATCH%s%s,", + cond, + outofline ? "_JUMP" : "", + skippable ? "_OR_SKIP" : ""); + break; + + case CHOICE: + goto dont_render_tag; + + case TYPE_REF: + if (e->class == ASN1_UNIV && e->method == ASN1_PRIM && e->tag == 0) + goto dont_render_tag; + default: + render_opcode(out, "ASN1_OP_%sMATCH%s%s,", + cond, act, + skippable ? "_OR_SKIP" : ""); + break; + } + + if (e->name) + render_more(out, "\t\t// %*.*s", + (int)e->name->size, (int)e->name->size, + e->name->value); + render_more(out, "\n"); + + /* Render the tag */ + if (!tag) + tag = e; + if (tag->class == ASN1_UNIV && + tag->tag != 14 && + tag->tag != 15 && + tag->tag != 31) + render_opcode(out, "_tag(%s, %s, %s),\n", + asn1_classes[tag->class], + asn1_methods[tag->method | e->method], + asn1_universal_tags[tag->tag]); + else + render_opcode(out, "_tagn(%s, %s, %2u),\n", + asn1_classes[tag->class], + asn1_methods[tag->method | e->method], + tag->tag); + tag = NULL; +dont_render_tag: + + /* Deal with compound types */ + switch (e->compound) { + case TYPE_REF: + render_element(out, e->type->type->element, tag); + if (e->action) + render_opcode(out, "ASN1_OP_ACT,\n"); + break; + + case SEQUENCE: + if (outofline) { + /* Render out-of-line for multiple use or + * skipability */ + render_opcode(out, "_jump_target(%u),", e->entry_index); + if (e->type_def && e->type_def->name) + render_more(out, "\t\t// --> %*.*s", + (int)e->type_def->name->size, + (int)e->type_def->name->size, + e->type_def->name->value); + render_more(out, "\n"); + if (!(e->flags & ELEMENT_RENDERED)) { + e->flags |= ELEMENT_RENDERED; + *render_list_p = e; + render_list_p = &e->render_next; + } + return; + } else { + /* Render inline for single use */ + render_depth++; + for (ec = e->children; ec; ec = ec->next) + render_element(out, ec, NULL); + render_depth--; + render_opcode(out, "ASN1_OP_END_SEQ%s,\n", act); + } + break; + + case SEQUENCE_OF: + case SET_OF: + if (outofline) { + /* Render out-of-line for multiple use or + * skipability */ + render_opcode(out, "_jump_target(%u),", e->entry_index); + if (e->type_def && e->type_def->name) + render_more(out, "\t\t// --> %*.*s", + (int)e->type_def->name->size, + (int)e->type_def->name->size, + e->type_def->name->value); + render_more(out, "\n"); + if (!(e->flags & ELEMENT_RENDERED)) { + e->flags |= ELEMENT_RENDERED; + *render_list_p = e; + render_list_p = &e->render_next; + } + return; + } else { + /* Render inline for single use */ + entry = nr_entries; + render_depth++; + render_element(out, e->children, NULL); + render_depth--; + if (e->compound == SEQUENCE_OF) + render_opcode(out, "ASN1_OP_END_SEQ_OF%s,\n", act); + else + render_opcode(out, "ASN1_OP_END_SET_OF%s,\n", act); + render_opcode(out, "_jump_target(%u),\n", entry); + } + break; + + case SET: + /* I can't think of a nice way to do SET support without having + * a stack of bitmasks to make sure no element is repeated. + * The bitmask has also to be checked that no non-optional + * elements are left out whilst not preventing optional + * elements from being left out. + */ + fprintf(stderr, "The ASN.1 SET type is not currently supported.\n"); + exit(1); + + case CHOICE: + for (ec = e->children; ec; ec = ec->next) + render_element(out, ec, NULL); + if (!skippable) + render_opcode(out, "ASN1_OP_COND_FAIL,\n"); + if (e->action) + render_opcode(out, "ASN1_OP_ACT,\n"); + break; + + default: + break; + } + + if (e->action) + render_opcode(out, "_action(ACT_%s),\n", e->action->name); +} diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 21a9f5de0a21..1d6e4c541370 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -33,6 +33,7 @@ my %ignore_type = (); my @ignore = (); my $help = 0; my $configuration_file = ".checkpatch.conf"; +my $max_line_length = 80; sub help { my ($exitcode) = @_; @@ -51,6 +52,7 @@ Options: -f, --file treat FILE as regular source file --subjective, --strict enable more subjective tests --ignore TYPE(,TYPE2...) ignore various comma separated message types + --max-line-length=n set the maximum line length, if exceeded, warn --show-types show the message "types" in the output --root=PATH PATH to the kernel tree root --no-summary suppress the per-file summary @@ -107,6 +109,7 @@ GetOptions( 'strict!' => \$check, 'ignore=s' => \@ignore, 'show-types!' => \$show_types, + 'max-line-length=i' => \$max_line_length, 'root=s' => \$root, 'summary!' => \$summary, 'mailback!' => \$mailback, @@ -227,7 +230,11 @@ our $Inline = qr{inline|__always_inline|noinline}; our $Member = qr{->$Ident|\.$Ident|\[[^]]*\]}; our $Lval = qr{$Ident(?:$Member)*}; -our $Constant = qr{(?i:(?:[0-9]+|0x[0-9a-f]+)[ul]*)}; +our $Float_hex = qr{(?i:0x[0-9a-f]+p-?[0-9]+[fl]?)}; +our $Float_dec = qr{(?i:((?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?))}; +our $Float_int = qr{(?i:[0-9]+e-?[0-9]+[fl]?)}; +our $Float = qr{$Float_hex|$Float_dec|$Float_int}; +our $Constant = qr{(?:$Float|(?i:(?:0x[0-9a-f]+|[0-9]+)[ul]*))}; our $Assignment = qr{(?:\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=)}; our $Compare = qr{<=|>=|==|!=|<|>}; our $Operators = qr{ @@ -352,27 +359,6 @@ sub deparenthesize { $chk_signoff = 0 if ($file); -my @dep_includes = (); -my @dep_functions = (); -my $removal = "Documentation/feature-removal-schedule.txt"; -if ($tree && -f "$root/$removal") { - open(my $REMOVE, '<', "$root/$removal") || - die "$P: $removal: open failed - $!\n"; - while (<$REMOVE>) { - if (/^Check:\s+(.*\S)/) { - for my $entry (split(/[, ]+/, $1)) { - if ($entry =~ m@include/(.*)@) { - push(@dep_includes, $1); - - } elsif ($entry !~ m@/@) { - push(@dep_functions, $entry); - } - } - } - } - close($REMOVE); -} - my @rawlines = (); my @lines = (); my $vname; @@ -1412,6 +1398,8 @@ sub process { my %suppress_export; my $suppress_statement = 0; + my %camelcase = (); + # Pre-scan the patch sanitizing the lines. # Pre-scan the patch looking for any __setup documentation. # @@ -1757,6 +1745,13 @@ sub process { #print "is_start<$is_start> is_end<$is_end> length<$length>\n"; } +# discourage the addition of CONFIG_EXPERIMENTAL in Kconfig. + if ($realfile =~ /Kconfig/ && + $line =~ /.\s*depends on\s+.*\bEXPERIMENTAL\b/) { + WARN("CONFIG_EXPERIMENTAL", + "Use of CONFIG_EXPERIMENTAL is deprecated. For alternatives, see https://lkml.org/lkml/2012/10/23/580\n"); + } + if (($realfile =~ /Makefile.*/ || $realfile =~ /Kbuild.*/) && ($line =~ /\+(EXTRA_[A-Z]+FLAGS).*/)) { my $flag = $1; @@ -1774,15 +1769,15 @@ sub process { # check we are in a valid source file if not then ignore this hunk next if ($realfile !~ /\.(h|c|s|S|pl|sh)$/); -#80 column limit +#line length limit if ($line =~ /^\+/ && $prevrawline !~ /\/\*\*/ && $rawline !~ /^.\s*\*\s*\@$Ident\s/ && !($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(KERN_\S+\s*|[^"]*))?"[X\t]*"\s*(?:|,|\)\s*;)\s*$/ || $line =~ /^\+\s*"[^"]*"\s*(?:\s*|,|\)\s*;)\s*$/) && - $length > 80) + $length > $max_line_length) { WARN("LONG_LINE", - "line over 80 characters\n" . $herecurr); + "line over $max_line_length characters\n" . $herecurr); } # Check for user-visible strings broken across lines, which breaks the ability @@ -1890,8 +1885,10 @@ sub process { } if ($realfile =~ m@^(drivers/net/|net/)@ && - $rawline !~ m@^\+[ \t]*(\/\*|\*\/)@ && - $rawline =~ m@^\+[ \t]*.+\*\/[ \t]*$@) { + $rawline !~ m@^\+[ \t]*\*/[ \t]*$@ && #trailing */ + $rawline !~ m@^\+.*/\*.*\*/[ \t]*$@ && #inline /*...*/ + $rawline !~ m@^\+.*\*{2,}/[ \t]*$@ && #trailing **/ + $rawline =~ m@^\+[ \t]*.+\*\/[ \t]*$@) { #non blank */ WARN("NETWORKING_BLOCK_COMMENT_STYLE", "networking block comments put the trailing */ on a separate line\n" . $herecurr); } @@ -1910,6 +1907,12 @@ sub process { # check we are in a valid C source file if not then ignore this hunk next if ($realfile !~ /\.(h|c)$/); +# discourage the addition of CONFIG_EXPERIMENTAL in #if(def). + if ($line =~ /^\+\s*\#\s*if.*\bCONFIG_EXPERIMENTAL\b/) { + WARN("CONFIG_EXPERIMENTAL", + "Use of CONFIG_EXPERIMENTAL is deprecated. For alternatives, see https://lkml.org/lkml/2012/10/23/580\n"); + } + # check for RCS/CVS revision markers if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) { WARN("CVS_KEYWORD", @@ -2904,12 +2907,17 @@ sub process { } } -#studly caps, commented out until figure out how to distinguish between use of existing and adding new -# if (($line=~/[\w_][a-z\d]+[A-Z]/) and !($line=~/print/)) { -# print "No studly caps, use _\n"; -# print "$herecurr"; -# $clean = 0; -# } +#CamelCase + while ($line =~ m{($Constant|$Lval)}g) { + my $var = $1; + if ($var !~ /$Constant/ && + $var =~ /[A-Z]\w*[a-z]|[a-z]\w*[A-Z]/ && + !defined $camelcase{$var}) { + $camelcase{$var} = 1; + WARN("CAMELCASE", + "Avoid CamelCase: <$var>\n" . $herecurr); + } + } #no spaces allowed after \ in define if ($line=~/\#\s*define.*\\\s$/) { @@ -3011,6 +3019,17 @@ sub process { "Macros with complex values should be enclosed in parenthesis\n" . "$herectx"); } } + +# check for line continuations outside of #defines, preprocessor #, and asm + + } else { + if ($prevline !~ /^..*\\$/ && + $line !~ /^\+\s*\#.*\\$/ && # preprocessor + $line !~ /^\+.*\b(__asm__|asm)\b.*\\$/ && # asm + $line =~ /^\+.*\\$/) { + WARN("LINE_CONTINUATIONS", + "Avoid unnecessary line continuations\n" . $herecurr); + } } # do {} while (0) macro tests: @@ -3181,20 +3200,14 @@ sub process { } } -# don't include deprecated include files (uses RAW line) - for my $inc (@dep_includes) { - if ($rawline =~ m@^.\s*\#\s*include\s*\<$inc>@) { - ERROR("DEPRECATED_INCLUDE", - "Don't use <$inc>: see Documentation/feature-removal-schedule.txt\n" . $herecurr); - } +# check for unnecessary blank lines around braces + if (($line =~ /^..*}\s*$/ && $prevline =~ /^.\s*$/)) { + CHK("BRACES", + "Blank lines aren't necessary before a close brace '}'\n" . $hereprev); } - -# don't use deprecated functions - for my $func (@dep_functions) { - if ($line =~ /\b$func\b/) { - ERROR("DEPRECATED_FUNCTION", - "Don't use $func(): see Documentation/feature-removal-schedule.txt\n" . $herecurr); - } + if (($line =~ /^.\s*$/ && $prevline =~ /^..*{\s*$/)) { + CHK("BRACES", + "Blank lines aren't necessary after an open brace '{'\n" . $hereprev); } # no volatiles please @@ -3211,20 +3224,12 @@ sub process { $herecurr); } -# check for needless kfree() checks - if ($prevline =~ /\bif\s*\(([^\)]*)\)/) { - my $expr = $1; - if ($line =~ /\bkfree\(\Q$expr\E\);/) { - WARN("NEEDLESS_KFREE", - "kfree(NULL) is safe this check is probably not required\n" . $hereprev); - } - } -# check for needless usb_free_urb() checks - if ($prevline =~ /\bif\s*\(([^\)]*)\)/) { - my $expr = $1; - if ($line =~ /\busb_free_urb\(\Q$expr\E\);/) { - WARN("NEEDLESS_USB_FREE_URB", - "usb_free_urb(NULL) is safe this check is probably not required\n" . $hereprev); +# check for needless "if (<foo>) fn(<foo>)" uses + if ($prevline =~ /\bif\s*\(\s*($Lval)\s*\)/) { + my $expr = '\s*\(\s*' . quotemeta($1) . '\s*\)\s*;'; + if ($line =~ /\b(kfree|usb_free_urb|debugfs_remove(?:_recursive)?)$expr/) { + WARN('NEEDLESS_IF', + "$1(NULL) is safe this check is probably not required\n" . $hereprev); } } @@ -3342,6 +3347,12 @@ sub process { "Avoid line continuations in quoted strings\n" . $herecurr); } +# check for struct spinlock declarations + if ($line =~ /^.\s*\bstruct\s+spinlock\s+\w+\s*;/) { + WARN("USE_SPINLOCK_T", + "struct spinlock should be spinlock_t\n" . $herecurr); + } + # Check for misused memsets if ($^V && $^V ge 5.10.0 && defined $stat && @@ -3448,8 +3459,22 @@ sub process { # check for multiple semicolons if ($line =~ /;\s*;\s*$/) { - WARN("ONE_SEMICOLON", - "Statements terminations use 1 semicolon\n" . $herecurr); + WARN("ONE_SEMICOLON", + "Statements terminations use 1 semicolon\n" . $herecurr); + } + +# check for switch/default statements without a break; + if ($^V && $^V ge 5.10.0 && + defined $stat && + $stat =~ /^\+[$;\s]*(?:case[$;\s]+\w+[$;\s]*:[$;\s]*|)*[$;\s]*\bdefault[$;\s]*:[$;\s]*;/g) { + my $ctx = ''; + my $herectx = $here . "\n"; + my $cnt = statement_rawlines($stat); + for (my $n = 0; $n < $cnt; $n++) { + $herectx .= raw_line($linenr, $n) . "\n"; + } + WARN("DEFAULT_NO_BREAK", + "switch default: should use break\n" . $herectx); } # check for gcc specific __FUNCTION__ diff --git a/scripts/coccicheck b/scripts/coccicheck index 823e972149e5..1a49d1c7ecfe 100755 --- a/scripts/coccicheck +++ b/scripts/coccicheck @@ -95,6 +95,9 @@ coccinelle () { $SPATCH -D report $FLAGS -sp_file $COCCI $OPT $OPTIONS -no_show_diff || \ $SPATCH -D context $FLAGS -sp_file $COCCI $OPT $OPTIONS || \ $SPATCH -D org $FLAGS -sp_file $COCCI $OPT $OPTIONS -no_show_diff || exit 1 + elif [ "$MODE" = "rep+ctxt" ] ; then + $SPATCH -D report $FLAGS -sp_file $COCCI $OPT $OPTIONS -no_show_diff && \ + $SPATCH -D context $FLAGS -sp_file $COCCI $OPT $OPTIONS || exit 1 else $SPATCH -D $MODE $FLAGS -sp_file $COCCI $OPT $OPTIONS || exit 1 fi diff --git a/scripts/coccinelle/api/d_find_alias.cocci b/scripts/coccinelle/api/d_find_alias.cocci new file mode 100644 index 000000000000..a9694a8d3e5a --- /dev/null +++ b/scripts/coccinelle/api/d_find_alias.cocci @@ -0,0 +1,80 @@ +/// Make sure calls to d_find_alias() have a corresponding call to dput(). +// +// Keywords: d_find_alias, dput +// +// Confidence: Moderate +// URL: http://coccinelle.lip6.fr/ +// Options: -include_headers + +virtual context +virtual org +virtual patch +virtual report + +@r exists@ +local idexpression struct dentry *dent; +expression E, E1; +statement S1, S2; +position p1, p2; +@@ +( + if (!(dent@p1 = d_find_alias(...))) S1 +| + dent@p1 = d_find_alias(...) +) + +<...when != dput(dent) + when != if (...) { <+... dput(dent) ...+> } + when != true !dent || ... + when != dent = E + when != E = dent +if (!dent || ...) S2 +...> +( + return <+...dent...+>; +| + return @p2 ...; +| + dent@p2 = E1; +| + E1 = dent; +) + +@depends on context@ +local idexpression struct dentry *r.dent; +position r.p1,r.p2; +@@ +* dent@p1 = ... + ... +( +* return@p2 ...; +| +* dent@p2 +) + + +@script:python depends on org@ +p1 << r.p1; +p2 << r.p2; +@@ +cocci.print_main("Missing call to dput()",p1) +cocci.print_secs("",p2) + +@depends on patch@ +local idexpression struct dentry *r.dent; +position r.p2; +@@ +( ++ dput(dent); + return @p2 ...; +| ++ dput(dent); + dent@p2 = ...; +) + +@script:python depends on report@ +p1 << r.p1; +p2 << r.p2; +@@ +msg = "Missing call to dput() at line %s." +coccilib.report.print_report(p1[0], msg % (p2[0].line)) diff --git a/scripts/coccinelle/api/ptr_ret.cocci b/scripts/coccinelle/api/ptr_ret.cocci index cbfd08c7d8c7..15f076fdecbe 100644 --- a/scripts/coccinelle/api/ptr_ret.cocci +++ b/scripts/coccinelle/api/ptr_ret.cocci @@ -30,6 +30,13 @@ expression ptr; - if (IS_ERR(ptr)) return PTR_ERR(ptr); return 0; + return PTR_RET(ptr); +@depends on patch@ +expression ptr; +@@ + +- (IS_ERR(ptr) ? PTR_ERR(ptr) : 0) ++ PTR_RET(ptr) + @r1 depends on !patch@ expression ptr; position p1; @@ -44,6 +51,13 @@ position p2; * if@p2 (IS_ERR(ptr)) return PTR_ERR(ptr); return 0; +@r3 depends on !patch@ +expression ptr; +position p3; +@@ + +* IS_ERR@p3(ptr) ? PTR_ERR(ptr) : 0 + @script:python depends on org@ p << r1.p1; @@ @@ -57,6 +71,12 @@ p << r2.p2; coccilib.org.print_todo(p[0], "WARNING: PTR_RET can be used") +@script:python depends on org@ +p << r3.p3; +@@ + +coccilib.org.print_todo(p[0], "WARNING: PTR_RET can be used") + @script:python depends on report@ p << r1.p1; @@ @@ -68,3 +88,9 @@ p << r2.p2; @@ coccilib.report.print_report(p[0], "WARNING: PTR_RET can be used") + +@script:python depends on report@ +p << r3.p3; +@@ + +coccilib.report.print_report(p[0], "WARNING: PTR_RET can be used") diff --git a/scripts/coccinelle/tests/odd_ptr_err.cocci b/scripts/coccinelle/tests/odd_ptr_err.cocci new file mode 100644 index 000000000000..e8dd8a6b28a2 --- /dev/null +++ b/scripts/coccinelle/tests/odd_ptr_err.cocci @@ -0,0 +1,65 @@ +/// PTR_ERR should access the value just tested by IS_ERR +//# There can be false positives in the patch case, where it is the call +//# IS_ERR that is wrong. +/// +// Confidence: High +// Copyright: (C) 2012 Julia Lawall, INRIA. GPLv2. +// Copyright: (C) 2012 Gilles Muller, INRIA. GPLv2. +// URL: http://coccinelle.lip6.fr/ +// Comments: +// Options: -no_includes -include_headers + +virtual patch +virtual context +virtual org +virtual report + +@depends on patch@ +expression e,e1; +@@ + +( +if (IS_ERR(e)) { ... PTR_ERR(e) ... } +| +if (IS_ERR(e=e1)) { ... PTR_ERR(e) ... } +| +if (IS_ERR(e)) + { ... + PTR_ERR( +- e1 ++ e + ) + ... } +) + +@r depends on !patch@ +expression e,e1; +position p1,p2; +@@ + +( +if (IS_ERR(e)) { ... PTR_ERR(e) ... } +| +if (IS_ERR(e=e1)) { ... PTR_ERR(e) ... } +| +*if (IS_ERR@p1(e)) + { ... +* PTR_ERR@p2(e1) + ... } +) + +@script:python depends on org@ +p1 << r.p1; +p2 << r.p2; +@@ + +cocci.print_main("inconsistent IS_ERR and PTR_ERR",p1) +cocci.print_secs("PTR_ERR",p2) + +@script:python depends on report@ +p1 << r.p1; +p2 << r.p2; +@@ + +msg = "inconsistent IS_ERR and PTR_ERR, PTR_ERR on line %s" % (p2[0].line) +coccilib.report.print_report(p1[0],msg) diff --git a/scripts/dtc/Makefile b/scripts/dtc/Makefile index 6d1c6bb9f224..2a48022c41e7 100644 --- a/scripts/dtc/Makefile +++ b/scripts/dtc/Makefile @@ -27,3 +27,5 @@ HOSTCFLAGS_dtc-parser.tab.o := $(HOSTCFLAGS_DTC) # dependencies on generated files need to be listed explicitly $(obj)/dtc-lexer.lex.o: $(obj)/dtc-parser.tab.h +# generated files need to be cleaned explicitly +clean-files := dtc-lexer.lex.c dtc-parser.tab.c dtc-parser.tab.h diff --git a/scripts/dtc/dtc.h b/scripts/dtc/dtc.h index d501c8605f26..3e42a071070e 100644 --- a/scripts/dtc/dtc.h +++ b/scripts/dtc/dtc.h @@ -161,51 +161,27 @@ struct node { struct label *labels; }; -static inline struct label *for_each_label_next(struct label *l) -{ - do { - l = l->next; - } while (l && l->deleted); - - return l; -} - -#define for_each_label(l0, l) \ - for ((l) = (l0); (l); (l) = for_each_label_next(l)) - #define for_each_label_withdel(l0, l) \ for ((l) = (l0); (l); (l) = (l)->next) -static inline struct property *for_each_property_next(struct property *p) -{ - do { - p = p->next; - } while (p && p->deleted); - - return p; -} - -#define for_each_property(n, p) \ - for ((p) = (n)->proplist; (p); (p) = for_each_property_next(p)) +#define for_each_label(l0, l) \ + for_each_label_withdel(l0, l) \ + if (!(l)->deleted) #define for_each_property_withdel(n, p) \ for ((p) = (n)->proplist; (p); (p) = (p)->next) -static inline struct node *for_each_child_next(struct node *c) -{ - do { - c = c->next_sibling; - } while (c && c->deleted); - - return c; -} - -#define for_each_child(n, c) \ - for ((c) = (n)->children; (c); (c) = for_each_child_next(c)) +#define for_each_property(n, p) \ + for_each_property_withdel(n, p) \ + if (!(p)->deleted) #define for_each_child_withdel(n, c) \ for ((c) = (n)->children; (c); (c) = (c)->next_sibling) +#define for_each_child(n, c) \ + for_each_child_withdel(n, c) \ + if (!(c)->deleted) + void add_label(struct label **labels, char *label); void delete_labels(struct label **labels); diff --git a/scripts/headers_install.pl b/scripts/headers_install.pl index 239d22d4207b..6c353ae8a451 100644 --- a/scripts/headers_install.pl +++ b/scripts/headers_install.pl @@ -42,6 +42,9 @@ foreach my $filename (@files) { $line =~ s/(^|\s)(inline)\b/$1__$2__/g; $line =~ s/(^|\s)(asm)\b(\s|[(]|$)/$1__$2__$3/g; $line =~ s/(^|\s|[(])(volatile)\b(\s|[(]|$)/$1__$2__$3/g; + $line =~ s/#ifndef _UAPI/#ifndef /; + $line =~ s/#define _UAPI/#define /; + $line =~ s!#endif /[*] _UAPI!#endif /* !; printf {$out} "%s", $line; } close $out; diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile index 77d53999ffb9..3091794e9354 100644 --- a/scripts/kconfig/Makefile +++ b/scripts/kconfig/Makefile @@ -76,11 +76,17 @@ PHONY += allnoconfig allyesconfig allmodconfig alldefconfig randconfig allnoconfig allyesconfig allmodconfig alldefconfig randconfig: $(obj)/conf $< --$@ $(Kconfig) -PHONY += listnewconfig oldnoconfig savedefconfig defconfig +PHONY += listnewconfig olddefconfig oldnoconfig savedefconfig defconfig -listnewconfig oldnoconfig: $(obj)/conf +listnewconfig olddefconfig: $(obj)/conf $< --$@ $(Kconfig) +# oldnoconfig is an alias of olddefconfig, because people already are dependent +# on its behavior(sets new symbols to their default value but not 'n') with the +# counter-intuitive name. +oldnoconfig: $(obj)/conf + $< --olddefconfig $(Kconfig) + savedefconfig: $(obj)/conf $< --$@=defconfig $(Kconfig) @@ -114,7 +120,7 @@ help: @echo ' alldefconfig - New config with all symbols set to default' @echo ' randconfig - New config with random answer to all options' @echo ' listnewconfig - List new options' - @echo ' oldnoconfig - Same as silentoldconfig but sets new symbols to their default value' + @echo ' olddefconfig - Same as silentoldconfig but sets new symbols to their default value' # lxdialog stuff check-lxdialog := $(srctree)/$(src)/lxdialog/check-lxdialog.sh diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c index 0dc4a2c779b1..4da3b4adfad2 100644 --- a/scripts/kconfig/conf.c +++ b/scripts/kconfig/conf.c @@ -32,7 +32,7 @@ enum input_mode { defconfig, savedefconfig, listnewconfig, - oldnoconfig, + olddefconfig, } input_mode = oldaskconfig; static int indent = 1; @@ -365,7 +365,7 @@ static void conf(struct menu *menu) case P_MENU: if ((input_mode == silentoldconfig || input_mode == listnewconfig || - input_mode == oldnoconfig) && + input_mode == olddefconfig) && rootEntry != menu) { check_conf(menu); return; @@ -429,7 +429,7 @@ static void check_conf(struct menu *menu) if (sym->name && !sym_is_choice_value(sym)) { printf("%s%s\n", CONFIG_, sym->name); } - } else if (input_mode != oldnoconfig) { + } else if (input_mode != olddefconfig) { if (!conf_cnt++) printf(_("*\n* Restart config...\n*\n")); rootEntry = menu_get_parent_menu(menu); @@ -454,7 +454,13 @@ static struct option long_opts[] = { {"alldefconfig", no_argument, NULL, alldefconfig}, {"randconfig", no_argument, NULL, randconfig}, {"listnewconfig", no_argument, NULL, listnewconfig}, - {"oldnoconfig", no_argument, NULL, oldnoconfig}, + {"olddefconfig", no_argument, NULL, olddefconfig}, + /* + * oldnoconfig is an alias of olddefconfig, because people already + * are dependent on its behavior(sets new symbols to their default + * value but not 'n') with the counter-intuitive name. + */ + {"oldnoconfig", no_argument, NULL, olddefconfig}, {NULL, 0, NULL, 0} }; @@ -467,7 +473,8 @@ static void conf_usage(const char *progname) printf(" --oldaskconfig Start a new configuration using a line-oriented program\n"); printf(" --oldconfig Update a configuration using a provided .config as base\n"); printf(" --silentoldconfig Same as oldconfig, but quietly, additionally update deps\n"); - printf(" --oldnoconfig Same as silentoldconfig but set new symbols to no\n"); + printf(" --olddefconfig Same as silentoldconfig but sets new symbols to their default value\n"); + printf(" --oldnoconfig An alias of olddefconfig\n"); printf(" --defconfig <file> New config with default defined in <file>\n"); printf(" --savedefconfig <file> Save the minimal current configuration to <file>\n"); printf(" --allnoconfig New config where all options are answered with no\n"); @@ -520,7 +527,7 @@ int main(int ac, char **av) case allmodconfig: case alldefconfig: case listnewconfig: - case oldnoconfig: + case olddefconfig: break; case '?': conf_usage(progname); @@ -565,7 +572,7 @@ int main(int ac, char **av) case oldaskconfig: case oldconfig: case listnewconfig: - case oldnoconfig: + case olddefconfig: conf_read(NULL); break; case allnoconfig: @@ -645,7 +652,7 @@ int main(int ac, char **av) /* fall through */ case oldconfig: case listnewconfig: - case oldnoconfig: + case olddefconfig: case silentoldconfig: /* Update until a loop caused no more changes */ do { @@ -653,7 +660,7 @@ int main(int ac, char **av) check_conf(&rootmenu); } while (conf_cnt && (input_mode != listnewconfig && - input_mode != oldnoconfig)); + input_mode != olddefconfig)); break; } diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h index d4ecce8bc3a6..cdd48600e02a 100644 --- a/scripts/kconfig/expr.h +++ b/scripts/kconfig/expr.h @@ -12,6 +12,7 @@ extern "C" { #include <assert.h> #include <stdio.h> +#include "list.h" #ifndef __cplusplus #include <stdbool.h> #endif @@ -173,6 +174,15 @@ struct menu { #define MENU_CHANGED 0x0001 #define MENU_ROOT 0x0002 +struct jump_key { + struct list_head entries; + size_t offset; + struct menu *target; + int index; +}; + +#define JUMP_NB 9 + extern struct file *file_list; extern struct file *current_file; struct file *lookup_file(const char *name); diff --git a/scripts/kconfig/list.h b/scripts/kconfig/list.h new file mode 100644 index 000000000000..0ae730be5f49 --- /dev/null +++ b/scripts/kconfig/list.h @@ -0,0 +1,91 @@ +#ifndef LIST_H +#define LIST_H + +/* + * Copied from include/linux/... + */ + +#undef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +/** + * container_of - cast a member of a structure out to the containing structure + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + + +struct list_head { + struct list_head *next, *prev; +}; + + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *_new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = _new; + _new->next = next; + _new->prev = prev; + prev->next = _new; +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *_new, struct list_head *head) +{ + __list_add(_new, head->prev, head); +} + +#endif diff --git a/scripts/kconfig/lkc_proto.h b/scripts/kconfig/lkc_proto.h index 47fe9c340f9a..ef1a7381f956 100644 --- a/scripts/kconfig/lkc_proto.h +++ b/scripts/kconfig/lkc_proto.h @@ -21,8 +21,10 @@ P(menu_get_root_menu,struct menu *,(struct menu *menu)); P(menu_get_parent_menu,struct menu *,(struct menu *menu)); P(menu_has_help,bool,(struct menu *menu)); P(menu_get_help,const char *,(struct menu *menu)); -P(get_symbol_str, void, (struct gstr *r, struct symbol *sym)); -P(get_relations_str, struct gstr, (struct symbol **sym_arr)); +P(get_symbol_str, void, (struct gstr *r, struct symbol *sym, struct list_head + *head)); +P(get_relations_str, struct gstr, (struct symbol **sym_arr, struct list_head + *head)); P(menu_get_ext_help,void,(struct menu *menu, struct gstr *help)); /* symbol.c */ diff --git a/scripts/kconfig/lxdialog/dialog.h b/scripts/kconfig/lxdialog/dialog.h index b5211fce0d94..ee17a5264d5b 100644 --- a/scripts/kconfig/lxdialog/dialog.h +++ b/scripts/kconfig/lxdialog/dialog.h @@ -144,6 +144,7 @@ struct dialog_info { */ extern struct dialog_info dlg; extern char dialog_input_result[]; +extern int saved_x, saved_y; /* Needed in signal handler in mconf.c */ /* * Function prototypes @@ -209,7 +210,13 @@ int first_alpha(const char *string, const char *exempt); int dialog_yesno(const char *title, const char *prompt, int height, int width); int dialog_msgbox(const char *title, const char *prompt, int height, int width, int pause); -int dialog_textbox(const char *title, const char *file, int height, int width); + + +typedef void (*update_text_fn)(char *buf, size_t start, size_t end, void + *_data); +int dialog_textbox(const char *title, char *tbuf, int initial_height, + int initial_width, int *keys, int *_vscroll, int *_hscroll, + update_text_fn update_text, void *data); int dialog_menu(const char *title, const char *prompt, const void *selected, int *s_scroll); int dialog_checklist(const char *title, const char *prompt, int height, diff --git a/scripts/kconfig/lxdialog/textbox.c b/scripts/kconfig/lxdialog/textbox.c index 4e5de60a0c0d..a48bb93e0907 100644 --- a/scripts/kconfig/lxdialog/textbox.c +++ b/scripts/kconfig/lxdialog/textbox.c @@ -22,23 +22,25 @@ #include "dialog.h" static void back_lines(int n); -static void print_page(WINDOW * win, int height, int width); -static void print_line(WINDOW * win, int row, int width); +static void print_page(WINDOW *win, int height, int width, update_text_fn + update_text, void *data); +static void print_line(WINDOW *win, int row, int width); static char *get_line(void); static void print_position(WINDOW * win); static int hscroll; static int begin_reached, end_reached, page_length; -static const char *buf; -static const char *page; +static char *buf; +static char *page; /* * refresh window content */ static void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw, - int cur_y, int cur_x) + int cur_y, int cur_x, update_text_fn update_text, + void *data) { - print_page(box, boxh, boxw); + print_page(box, boxh, boxw, update_text, data); print_position(dialog); wmove(dialog, cur_y, cur_x); /* Restore cursor position */ wrefresh(dialog); @@ -47,14 +49,18 @@ static void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw, /* * Display text from a file in a dialog box. + * + * keys is a null-terminated array + * update_text() may not add or remove any '\n' or '\0' in tbuf */ -int dialog_textbox(const char *title, const char *tbuf, - int initial_height, int initial_width) +int dialog_textbox(const char *title, char *tbuf, int initial_height, + int initial_width, int *keys, int *_vscroll, int *_hscroll, + update_text_fn update_text, void *data) { int i, x, y, cur_x, cur_y, key = 0; int height, width, boxh, boxw; - int passed_end; WINDOW *dialog, *box; + bool done = false; begin_reached = 1; end_reached = 0; @@ -63,6 +69,15 @@ int dialog_textbox(const char *title, const char *tbuf, buf = tbuf; page = buf; /* page is pointer to start of page to be displayed */ + if (_vscroll && *_vscroll) { + begin_reached = 0; + + for (i = 0; i < *_vscroll; i++) + get_line(); + } + if (_hscroll) + hscroll = *_hscroll; + do_resize: getmaxyx(stdscr, height, width); if (height < 8 || width < 8) @@ -120,9 +135,10 @@ do_resize: /* Print first page of text */ attr_clear(box, boxh, boxw, dlg.dialog.atr); - refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x); + refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x, update_text, + data); - while ((key != KEY_ESC) && (key != '\n')) { + while (!done) { key = wgetch(dialog); switch (key) { case 'E': /* Exit */ @@ -130,16 +146,17 @@ do_resize: case 'X': case 'x': case 'q': - delwin(box); - delwin(dialog); - return 0; + case '\n': + done = true; + break; case 'g': /* First page */ case KEY_HOME: if (!begin_reached) { begin_reached = 1; page = buf; refresh_text_box(dialog, box, boxh, boxw, - cur_y, cur_x); + cur_y, cur_x, update_text, + data); } break; case 'G': /* Last page */ @@ -149,45 +166,18 @@ do_resize: /* point to last char in buf */ page = buf + strlen(buf); back_lines(boxh); - refresh_text_box(dialog, box, boxh, boxw, - cur_y, cur_x); + refresh_text_box(dialog, box, boxh, boxw, cur_y, + cur_x, update_text, data); break; case 'K': /* Previous line */ case 'k': case KEY_UP: - if (!begin_reached) { - back_lines(page_length + 1); - - /* We don't call print_page() here but use - * scrolling to ensure faster screen update. - * However, 'end_reached' and 'page_length' - * should still be updated, and 'page' should - * point to start of next page. This is done - * by calling get_line() in the following - * 'for' loop. */ - scrollok(box, TRUE); - wscrl(box, -1); /* Scroll box region down one line */ - scrollok(box, FALSE); - page_length = 0; - passed_end = 0; - for (i = 0; i < boxh; i++) { - if (!i) { - /* print first line of page */ - print_line(box, 0, boxw); - wnoutrefresh(box); - } else - /* Called to update 'end_reached' and 'page' */ - get_line(); - if (!passed_end) - page_length++; - if (end_reached && !passed_end) - passed_end = 1; - } + if (begin_reached) + break; - print_position(dialog); - wmove(dialog, cur_y, cur_x); /* Restore cursor position */ - wrefresh(dialog); - } + back_lines(page_length + 1); + refresh_text_box(dialog, box, boxh, boxw, cur_y, + cur_x, update_text, data); break; case 'B': /* Previous page */ case 'b': @@ -196,23 +186,18 @@ do_resize: if (begin_reached) break; back_lines(page_length + boxh); - refresh_text_box(dialog, box, boxh, boxw, - cur_y, cur_x); + refresh_text_box(dialog, box, boxh, boxw, cur_y, + cur_x, update_text, data); break; case 'J': /* Next line */ case 'j': case KEY_DOWN: - if (!end_reached) { - begin_reached = 0; - scrollok(box, TRUE); - scroll(box); /* Scroll box region up one line */ - scrollok(box, FALSE); - print_line(box, boxh - 1, boxw); - wnoutrefresh(box); - print_position(dialog); - wmove(dialog, cur_y, cur_x); /* Restore cursor position */ - wrefresh(dialog); - } + if (end_reached) + break; + + back_lines(page_length - 1); + refresh_text_box(dialog, box, boxh, boxw, cur_y, + cur_x, update_text, data); break; case KEY_NPAGE: /* Next page */ case ' ': @@ -221,8 +206,8 @@ do_resize: break; begin_reached = 0; - refresh_text_box(dialog, box, boxh, boxw, - cur_y, cur_x); + refresh_text_box(dialog, box, boxh, boxw, cur_y, + cur_x, update_text, data); break; case '0': /* Beginning of line */ case 'H': /* Scroll left */ @@ -237,8 +222,8 @@ do_resize: hscroll--; /* Reprint current page to scroll horizontally */ back_lines(page_length); - refresh_text_box(dialog, box, boxh, boxw, - cur_y, cur_x); + refresh_text_box(dialog, box, boxh, boxw, cur_y, + cur_x, update_text, data); break; case 'L': /* Scroll right */ case 'l': @@ -248,11 +233,12 @@ do_resize: hscroll++; /* Reprint current page to scroll horizontally */ back_lines(page_length); - refresh_text_box(dialog, box, boxh, boxw, - cur_y, cur_x); + refresh_text_box(dialog, box, boxh, boxw, cur_y, + cur_x, update_text, data); break; case KEY_ESC: - key = on_key_esc(dialog); + if (on_key_esc(dialog) == KEY_ESC) + done = true; break; case KEY_RESIZE: back_lines(height); @@ -260,11 +246,31 @@ do_resize: delwin(dialog); on_key_resize(); goto do_resize; + default: + for (i = 0; keys[i]; i++) { + if (key == keys[i]) { + done = true; + break; + } + } } } delwin(box); delwin(dialog); - return key; /* ESC pressed */ + if (_vscroll) { + const char *s; + + s = buf; + *_vscroll = 0; + back_lines(page_length); + while (s < page && (s = strchr(s, '\n'))) { + (*_vscroll)++; + s++; + } + } + if (_hscroll) + *_hscroll = hscroll; + return key; } /* @@ -301,12 +307,23 @@ static void back_lines(int n) } /* - * Print a new page of text. Called by dialog_textbox(). + * Print a new page of text. */ -static void print_page(WINDOW * win, int height, int width) +static void print_page(WINDOW *win, int height, int width, update_text_fn + update_text, void *data) { int i, passed_end = 0; + if (update_text) { + char *end; + + for (i = 0; i < height; i++) + get_line(); + end = page; + back_lines(height); + update_text(buf, page - buf, end - buf, data); + } + page_length = 0; for (i = 0; i < height; i++) { print_line(win, i, width); @@ -319,7 +336,7 @@ static void print_page(WINDOW * win, int height, int width) } /* - * Print a new line of text. Called by dialog_textbox() and print_page(). + * Print a new line of text. */ static void print_line(WINDOW * win, int row, int width) { @@ -357,10 +374,8 @@ static char *get_line(void) end_reached = 0; while (*page != '\n') { if (*page == '\0') { - if (!end_reached) { - end_reached = 1; - break; - } + end_reached = 1; + break; } else if (i < MAX_LEN) line[i++] = *(page++); else { @@ -373,7 +388,7 @@ static char *get_line(void) if (i <= MAX_LEN) line[i] = '\0'; if (!end_reached) - page++; /* move pass '\n' */ + page++; /* move past '\n' */ return line; } diff --git a/scripts/kconfig/lxdialog/util.c b/scripts/kconfig/lxdialog/util.c index f2375ad7ebc9..109d53117d22 100644 --- a/scripts/kconfig/lxdialog/util.c +++ b/scripts/kconfig/lxdialog/util.c @@ -23,6 +23,9 @@ #include "dialog.h" +/* Needed in signal handler in mconf.c */ +int saved_x, saved_y; + struct dialog_info dlg; static void set_mono_theme(void) @@ -273,6 +276,10 @@ int init_dialog(const char *backtitle) int height, width; initscr(); /* Init curses */ + + /* Get current cursor position for signal handler in mconf.c */ + getyx(stdscr, saved_y, saved_x); + getmaxyx(stdscr, height, width); if (height < 19 || width < 80) { endwin(); diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c index f584a281bb4c..53975cf87608 100644 --- a/scripts/kconfig/mconf.c +++ b/scripts/kconfig/mconf.c @@ -236,16 +236,19 @@ search_help[] = N_( "Result:\n" "-----------------------------------------------------------------\n" "Symbol: FOO [=m]\n" + "Type : tristate\n" "Prompt: Foo bus is used to drive the bar HW\n" - "Defined at drivers/pci/Kconfig:47\n" - "Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n" - "Location:\n" - " -> Bus options (PCI, PCMCIA, EISA, ISA)\n" - " -> PCI support (PCI [=y])\n" - " -> PCI access mode (<choice> [=y])\n" - "Selects: LIBCRC32\n" - "Selected by: BAR\n" + " Defined at drivers/pci/Kconfig:47\n" + " Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n" + " Location:\n" + " -> Bus options (PCI, PCMCIA, EISA, ISA)\n" + " -> PCI support (PCI [=y])\n" + "(1) -> PCI access mode (<choice> [=y])\n" + " Selects: LIBCRC32\n" + " Selected by: BAR\n" "-----------------------------------------------------------------\n" + "o The line 'Type:' shows the type of the configuration option for\n" + " this symbol (boolean, tristate, string, ...)\n" "o The line 'Prompt:' shows the text used in the menu structure for\n" " this symbol\n" "o The 'Defined at' line tell at what file / line number the symbol\n" @@ -254,8 +257,12 @@ search_help[] = N_( " this symbol to be visible in the menu (selectable)\n" "o The 'Location:' lines tell where in the menu structure this symbol\n" " is located\n" - " A location followed by a [=y] indicate that this is a selectable\n" - " menu item - and current value is displayed inside brackets.\n" + " A location followed by a [=y] indicates that this is a\n" + " selectable menu item - and the current value is displayed inside\n" + " brackets.\n" + " Press the key in the (#) prefix to jump directly to that\n" + " location. You will be returned to the current search results\n" + " after exiting this new menu.\n" "o The 'Selects:' line tell what symbol will be automatically\n" " selected if this symbol is selected (y or m)\n" "o The 'Selected by' line tell what symbol has selected this symbol\n" @@ -273,13 +280,15 @@ static struct menu *current_menu; static int child_count; static int single_menu_mode; static int show_all_options; -static int saved_x, saved_y; -static void conf(struct menu *menu); +static void conf(struct menu *menu, struct menu *active_menu); static void conf_choice(struct menu *menu); static void conf_string(struct menu *menu); static void conf_load(void); static void conf_save(void); +static int show_textbox_ext(const char *title, char *text, int r, int c, + int *keys, int *vscroll, int *hscroll, + update_text_fn update_text, void *data); static void show_textbox(const char *title, const char *text, int r, int c); static void show_helptext(const char *title, const char *text); static void show_help(struct menu *menu); @@ -302,12 +311,47 @@ static void set_config_filename(const char *config_filename) } +struct search_data { + struct list_head *head; + struct menu **targets; + int *keys; +}; + +static void update_text(char *buf, size_t start, size_t end, void *_data) +{ + struct search_data *data = _data; + struct jump_key *pos; + int k = 0; + + list_for_each_entry(pos, data->head, entries) { + if (pos->offset >= start && pos->offset < end) { + char header[4]; + + if (k < JUMP_NB) { + int key = '0' + (pos->index % JUMP_NB) + 1; + + sprintf(header, "(%c)", key); + data->keys[k] = key; + data->targets[k] = pos->target; + k++; + } else { + sprintf(header, " "); + } + + memcpy(buf + pos->offset, header, sizeof(header) - 1); + } + } + data->keys[k] = 0; +} + static void search_conf(void) { struct symbol **sym_arr; struct gstr res; char *dialog_input; - int dres; + int dres, vscroll = 0, hscroll = 0; + bool again; + again: dialog_clear(); dres = dialog_inputbox(_("Search Configuration Parameter"), @@ -330,10 +374,30 @@ again: dialog_input += strlen(CONFIG_); sym_arr = sym_re_search(dialog_input); - res = get_relations_str(sym_arr); + do { + LIST_HEAD(head); + struct menu *targets[JUMP_NB]; + int keys[JUMP_NB + 1], i; + struct search_data data = { + .head = &head, + .targets = targets, + .keys = keys, + }; + + res = get_relations_str(sym_arr, &head); + dres = show_textbox_ext(_("Search Results"), (char *) + str_get(&res), 0, 0, keys, &vscroll, + &hscroll, &update_text, (void *) + &data); + again = false; + for (i = 0; i < JUMP_NB && keys[i]; i++) + if (dres == keys[i]) { + conf(targets[i]->parent, targets[i]); + again = true; + } + str_free(&res); + } while (again); free(sym_arr); - show_textbox(_("Search Results"), str_get(&res), 0, 0); - str_free(&res); } static void build_conf(struct menu *menu) @@ -514,12 +578,11 @@ conf_childs: indent -= doint; } -static void conf(struct menu *menu) +static void conf(struct menu *menu, struct menu *active_menu) { struct menu *submenu; const char *prompt = menu_get_prompt(menu); struct symbol *sym; - struct menu *active_menu = NULL; int res; int s_scroll = 0; @@ -562,13 +625,13 @@ static void conf(struct menu *menu) if (single_menu_mode) submenu->data = (void *) (long) !submenu->data; else - conf(submenu); + conf(submenu, NULL); break; case 't': if (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes) conf_choice(submenu); else if (submenu->prompt->type == P_MENU) - conf(submenu); + conf(submenu, NULL); break; case 's': conf_string(submenu); @@ -607,7 +670,7 @@ static void conf(struct menu *menu) if (item_is_tag('t')) sym_toggle_tristate_value(sym); else if (item_is_tag('m')) - conf(submenu); + conf(submenu, NULL); break; case 7: search_conf(); @@ -619,10 +682,19 @@ static void conf(struct menu *menu) } } -static void show_textbox(const char *title, const char *text, int r, int c) +static int show_textbox_ext(const char *title, char *text, int r, int c, int + *keys, int *vscroll, int *hscroll, update_text_fn + update_text, void *data) { dialog_clear(); - dialog_textbox(title, text, r, c); + return dialog_textbox(title, text, r, c, keys, vscroll, hscroll, + update_text, data); +} + +static void show_textbox(const char *title, const char *text, int r, int c) +{ + show_textbox_ext(title, (char *) text, r, c, (int []) {0}, NULL, NULL, + NULL, NULL); } static void show_helptext(const char *title, const char *text) @@ -862,9 +934,6 @@ int main(int ac, char **av) single_menu_mode = 1; } - initscr(); - - getyx(stdscr, saved_y, saved_x); if (init_dialog(NULL)) { fprintf(stderr, N_("Your display is too small to run Menuconfig!\n")); fprintf(stderr, N_("It must be at least 19 lines by 80 columns.\n")); @@ -873,7 +942,7 @@ int main(int ac, char **av) set_config_filename(conf_get_configname()); do { - conf(&rootmenu); + conf(&rootmenu, NULL); res = handle_exit(); } while (res == KEY_ESC); diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index 8c2a97e60faf..e98a05c8e508 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -507,10 +507,12 @@ const char *menu_get_help(struct menu *menu) return ""; } -static void get_prompt_str(struct gstr *r, struct property *prop) +static void get_prompt_str(struct gstr *r, struct property *prop, + struct list_head *head) { int i, j; - struct menu *submenu[8], *menu; + struct menu *submenu[8], *menu, *location = NULL; + struct jump_key *jump; str_printf(r, _("Prompt: %s\n"), _(prop->text)); str_printf(r, _(" Defined at %s:%d\n"), prop->menu->file->name, @@ -521,13 +523,44 @@ static void get_prompt_str(struct gstr *r, struct property *prop) str_append(r, "\n"); } menu = prop->menu->parent; - for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent) + for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent) { + bool accessible = menu_is_visible(menu); + submenu[i++] = menu; + if (location == NULL && accessible) + location = menu; + } + if (head && location) { + jump = malloc(sizeof(struct jump_key)); + + if (menu_is_visible(prop->menu)) { + /* + * There is not enough room to put the hint at the + * beginning of the "Prompt" line. Put the hint on the + * last "Location" line even when it would belong on + * the former. + */ + jump->target = prop->menu; + } else + jump->target = location; + + if (list_empty(head)) + jump->index = 0; + else + jump->index = list_entry(head->prev, struct jump_key, + entries)->index + 1; + + list_add_tail(&jump->entries, head); + } + if (i > 0) { str_printf(r, _(" Location:\n")); for (j = 4; --i >= 0; j += 2) { menu = submenu[i]; - str_printf(r, "%*c-> %s", j, ' ', _(menu_get_prompt(menu))); + if (head && location && menu == location) + jump->offset = r->len - 1; + str_printf(r, "%*c-> %s", j, ' ', + _(menu_get_prompt(menu))); if (menu->sym) { str_printf(r, " (%s [=%s])", menu->sym->name ? menu->sym->name : _("<choice>"), @@ -538,7 +571,11 @@ static void get_prompt_str(struct gstr *r, struct property *prop) } } -void get_symbol_str(struct gstr *r, struct symbol *sym) +/* + * head is optional and may be NULL + */ +void get_symbol_str(struct gstr *r, struct symbol *sym, + struct list_head *head) { bool hit; struct property *prop; @@ -557,7 +594,7 @@ void get_symbol_str(struct gstr *r, struct symbol *sym) } } for_all_prompts(sym, prop) - get_prompt_str(r, prop); + get_prompt_str(r, prop, head); hit = false; for_all_properties(sym, prop, P_SELECT) { if (!hit) { @@ -577,14 +614,14 @@ void get_symbol_str(struct gstr *r, struct symbol *sym) str_append(r, "\n\n"); } -struct gstr get_relations_str(struct symbol **sym_arr) +struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head) { struct symbol *sym; struct gstr res = str_new(); int i; for (i = 0; sym_arr && (sym = sym_arr[i]); i++) - get_symbol_str(&res, sym); + get_symbol_str(&res, sym, head); if (!i) str_append(&res, _("No matches found.\n")); return res; @@ -603,5 +640,5 @@ void menu_get_ext_help(struct menu *menu, struct gstr *help) } str_printf(help, "%s\n", _(help_text)); if (sym) - get_symbol_str(help, sym); + get_symbol_str(help, sym, NULL); } diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c index 1704a8562a5d..87d4b15da951 100644 --- a/scripts/kconfig/nconf.c +++ b/scripts/kconfig/nconf.c @@ -721,7 +721,7 @@ again: dialog_input += strlen(CONFIG_); sym_arr = sym_re_search(dialog_input); - res = get_relations_str(sym_arr); + res = get_relations_str(sym_arr, NULL); free(sym_arr); show_scroll_win(main_window, _("Search Results"), str_get(&res)); diff --git a/scripts/kernel-doc b/scripts/kernel-doc index 01e8a8e22602..28b761567815 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -6,6 +6,7 @@ use strict; ## Copyright (C) 2000, 1 Tim Waugh <twaugh@redhat.com> ## ## Copyright (C) 2001 Simon Huggins ## ## Copyright (C) 2005-2012 Randy Dunlap ## +## Copyright (C) 2012 Dan Luedtke ## ## ## ## #define enhancements by Armin Kuster <akuster@mvista.com> ## ## Copyright (c) 2000 MontaVista Software, Inc. ## @@ -35,6 +36,8 @@ use strict; # Small fixes (like spaces vs. \s in regex) # -- Tim Jansen <tim@tjansen.de> +# 25/07/2012 - Added support for HTML5 +# -- Dan Luedtke <mail@danrl.de> # # This will read a 'c' file and scan for embedded comments in the @@ -44,12 +47,16 @@ use strict; # Note: This only supports 'c'. # usage: -# kernel-doc [ -docbook | -html | -text | -man | -list ] [ -no-doc-sections ] -# [ -function funcname [ -function funcname ...] ] c file(s)s > outputfile +# kernel-doc [ -docbook | -html | -html5 | -text | -man | -list ] +# [ -no-doc-sections ] +# [ -function funcname [ -function funcname ...] ] +# c file(s)s > outputfile # or -# [ -nofunction funcname [ -function funcname ...] ] c file(s)s > outputfile +# [ -nofunction funcname [ -function funcname ...] ] +# c file(s)s > outputfile # -# Set output format using one of -docbook -html -text or -man. Default is man. +# Set output format using one of -docbook -html -html5 -text or -man. +# Default is man. # The -list format is for internal use by docproc. # # -no-doc-sections @@ -130,6 +137,8 @@ use strict; # should document the "Context:" of the function, e.g. whether the functions # can be called form interrupts. Unlike other sections you can end it with an # empty line. +# A non-void function should have a "Return:" section describing the return +# value(s). # Example-sections should contain the string EXAMPLE so that they are marked # appropriately in DocBook. # @@ -182,6 +191,14 @@ my $local_lt = "\\\\\\\\lt:"; my $local_gt = "\\\\\\\\gt:"; my $blankline_html = $local_lt . "p" . $local_gt; # was "<p>" +# html version 5 +my %highlights_html5 = ( $type_constant, "<span class=\"const\">\$1</span>", + $type_func, "<span class=\"func\">\$1</span>", + $type_struct_xml, "<span class=\"struct\">\$1</span>", + $type_env, "<span class=\"env\">\$1</span>", + $type_param, "<span class=\"param\">\$1</span>" ); +my $blankline_html5 = $local_lt . "br /" . $local_gt; + # XML, docbook format my %highlights_xml = ( "([^=])\\\"([^\\\"<]+)\\\"", "\$1<quote>\$2</quote>", $type_constant, "<constant>\$1</constant>", @@ -300,6 +317,7 @@ my $section_default = "Description"; # default section my $section_intro = "Introduction"; my $section = $section_default; my $section_context = "Context"; +my $section_return = "Return"; my $undescribed = "-- undescribed --"; @@ -311,6 +329,10 @@ while ($ARGV[0] =~ m/^-(.*)/) { $output_mode = "html"; %highlights = %highlights_html; $blankline = $blankline_html; + } elsif ($cmd eq "-html5") { + $output_mode = "html5"; + %highlights = %highlights_html5; + $blankline = $blankline_html5; } elsif ($cmd eq "-man") { $output_mode = "man"; %highlights = %highlights_man; @@ -353,10 +375,11 @@ while ($ARGV[0] =~ m/^-(.*)/) { # continue execution near EOF; sub usage { - print "Usage: $0 [ -v ] [ -docbook | -html | -text | -man | -list ]\n"; + print "Usage: $0 [ -docbook | -html | -html5 | -text | -man | -list ]\n"; print " [ -no-doc-sections ]\n"; print " [ -function funcname [ -function funcname ...] ]\n"; print " [ -nofunction funcname [ -nofunction funcname ...] ]\n"; + print " [ -v ]\n"; print " c source file(s) > outputfile\n"; print " -v : verbose output, more warnings & other info listed\n"; exit 1; @@ -450,7 +473,8 @@ sub output_highlight { # confess "output_highlight got called with no args?\n"; # } - if ($output_mode eq "html" || $output_mode eq "xml") { + if ($output_mode eq "html" || $output_mode eq "html5" || + $output_mode eq "xml") { $contents = local_unescape($contents); # convert data read & converted thru xml_escape() into &xyz; format: $contents =~ s/\\\\\\/\&/g; @@ -460,6 +484,11 @@ sub output_highlight { die $@ if $@; # print STDERR "contents af:$contents\n"; +# strip whitespaces when generating html5 + if ($output_mode eq "html5") { + $contents =~ s/^\s+//; + $contents =~ s/\s+$//; + } foreach $line (split "\n", $contents) { if (! $output_preformatted) { $line =~ s/^\s*//; @@ -480,7 +509,7 @@ sub output_highlight { } } -#output sections in html +# output sections in html sub output_section_html(%) { my %args = %{$_[0]}; my $section; @@ -640,6 +669,239 @@ sub output_blockhead_html(%) { print "<hr>\n"; } +# output sections in html5 +sub output_section_html5(%) { + my %args = %{$_[0]}; + my $section; + + foreach $section (@{$args{'sectionlist'}}) { + print "<section>\n"; + print "<h1>$section</h1>\n"; + print "<p>\n"; + output_highlight($args{'sections'}{$section}); + print "</p>\n"; + print "</section>\n"; + } +} + +# output enum in html5 +sub output_enum_html5(%) { + my %args = %{$_[0]}; + my ($parameter); + my $count; + my $html5id; + + $html5id = $args{'enum'}; + $html5id =~ s/[^a-zA-Z0-9\-]+/_/g; + print "<article class=\"enum\" id=\"enum:". $html5id . "\">"; + print "<h1>enum " . $args{'enum'} . "</h1>\n"; + print "<ol class=\"code\">\n"; + print "<li>"; + print "<span class=\"keyword\">enum</span> "; + print "<span class=\"identifier\">" . $args{'enum'} . "</span> {"; + print "</li>\n"; + $count = 0; + foreach $parameter (@{$args{'parameterlist'}}) { + print "<li class=\"indent\">"; + print "<span class=\"param\">" . $parameter . "</span>"; + if ($count != $#{$args{'parameterlist'}}) { + $count++; + print ","; + } + print "</li>\n"; + } + print "<li>};</li>\n"; + print "</ol>\n"; + + print "<section>\n"; + print "<h1>Constants</h1>\n"; + print "<dl>\n"; + foreach $parameter (@{$args{'parameterlist'}}) { + print "<dt>" . $parameter . "</dt>\n"; + print "<dd>"; + output_highlight($args{'parameterdescs'}{$parameter}); + print "</dd>\n"; + } + print "</dl>\n"; + print "</section>\n"; + output_section_html5(@_); + print "</article>\n"; +} + +# output typedef in html5 +sub output_typedef_html5(%) { + my %args = %{$_[0]}; + my ($parameter); + my $count; + my $html5id; + + $html5id = $args{'typedef'}; + $html5id =~ s/[^a-zA-Z0-9\-]+/_/g; + print "<article class=\"typedef\" id=\"typedef:" . $html5id . "\">\n"; + print "<h1>typedef " . $args{'typedef'} . "</h1>\n"; + + print "<ol class=\"code\">\n"; + print "<li>"; + print "<span class=\"keyword\">typedef</span> "; + print "<span class=\"identifier\">" . $args{'typedef'} . "</span>"; + print "</li>\n"; + print "</ol>\n"; + output_section_html5(@_); + print "</article>\n"; +} + +# output struct in html5 +sub output_struct_html5(%) { + my %args = %{$_[0]}; + my ($parameter); + my $html5id; + + $html5id = $args{'struct'}; + $html5id =~ s/[^a-zA-Z0-9\-]+/_/g; + print "<article class=\"struct\" id=\"struct:" . $html5id . "\">\n"; + print "<hgroup>\n"; + print "<h1>" . $args{'type'} . " " . $args{'struct'} . "</h1>"; + print "<h2>". $args{'purpose'} . "</h2>\n"; + print "</hgroup>\n"; + print "<ol class=\"code\">\n"; + print "<li>"; + print "<span class=\"type\">" . $args{'type'} . "</span> "; + print "<span class=\"identifier\">" . $args{'struct'} . "</span> {"; + print "</li>\n"; + foreach $parameter (@{$args{'parameterlist'}}) { + print "<li class=\"indent\">"; + if ($parameter =~ /^#/) { + print "<span class=\"param\">" . $parameter ."</span>\n"; + print "</li>\n"; + next; + } + my $parameter_name = $parameter; + $parameter_name =~ s/\[.*//; + + ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; + $type = $args{'parametertypes'}{$parameter}; + if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { + # pointer-to-function + print "<span class=\"type\">$1</span> "; + print "<span class=\"param\">$parameter</span>"; + print "<span class=\"type\">)</span> "; + print "(<span class=\"args\">$2</span>);"; + } elsif ($type =~ m/^(.*?)\s*(:.*)/) { + # bitfield + print "<span class=\"type\">$1</span> "; + print "<span class=\"param\">$parameter</span>"; + print "<span class=\"bits\">$2</span>;"; + } else { + print "<span class=\"type\">$type</span> "; + print "<span class=\"param\">$parameter</span>;"; + } + print "</li>\n"; + } + print "<li>};</li>\n"; + print "</ol>\n"; + + print "<section>\n"; + print "<h1>Members</h1>\n"; + print "<dl>\n"; + foreach $parameter (@{$args{'parameterlist'}}) { + ($parameter =~ /^#/) && next; + + my $parameter_name = $parameter; + $parameter_name =~ s/\[.*//; + + ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; + print "<dt>" . $parameter . "</dt>\n"; + print "<dd>"; + output_highlight($args{'parameterdescs'}{$parameter_name}); + print "</dd>\n"; + } + print "</dl>\n"; + print "</section>\n"; + output_section_html5(@_); + print "</article>\n"; +} + +# output function in html5 +sub output_function_html5(%) { + my %args = %{$_[0]}; + my ($parameter, $section); + my $count; + my $html5id; + + $html5id = $args{'function'}; + $html5id =~ s/[^a-zA-Z0-9\-]+/_/g; + print "<article class=\"function\" id=\"func:". $html5id . "\">\n"; + print "<hgroup>\n"; + print "<h1>" . $args{'function'} . "</h1>"; + print "<h2>" . $args{'purpose'} . "</h2>\n"; + print "</hgroup>\n"; + print "<ol class=\"code\">\n"; + print "<li>"; + print "<span class=\"type\">" . $args{'functiontype'} . "</span> "; + print "<span class=\"identifier\">" . $args{'function'} . "</span> ("; + print "</li>"; + $count = 0; + foreach $parameter (@{$args{'parameterlist'}}) { + print "<li class=\"indent\">"; + $type = $args{'parametertypes'}{$parameter}; + if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { + # pointer-to-function + print "<span class=\"type\">$1</span> "; + print "<span class=\"param\">$parameter</span>"; + print "<span class=\"type\">)</span> "; + print "(<span class=\"args\">$2</span>)"; + } else { + print "<span class=\"type\">$type</span> "; + print "<span class=\"param\">$parameter</span>"; + } + if ($count != $#{$args{'parameterlist'}}) { + $count++; + print ","; + } + print "</li>\n"; + } + print "<li>)</li>\n"; + print "</ol>\n"; + + print "<section>\n"; + print "<h1>Arguments</h1>\n"; + print "<p>\n"; + print "<dl>\n"; + foreach $parameter (@{$args{'parameterlist'}}) { + my $parameter_name = $parameter; + $parameter_name =~ s/\[.*//; + + ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; + print "<dt>" . $parameter . "</dt>\n"; + print "<dd>"; + output_highlight($args{'parameterdescs'}{$parameter_name}); + print "</dd>\n"; + } + print "</dl>\n"; + print "</section>\n"; + output_section_html5(@_); + print "</article>\n"; +} + +# output DOC: block header in html5 +sub output_blockhead_html5(%) { + my %args = %{$_[0]}; + my ($parameter, $section); + my $count; + my $html5id; + + foreach $section (@{$args{'sectionlist'}}) { + $html5id = $section; + $html5id =~ s/[^a-zA-Z0-9\-]+/_/g; + print "<article class=\"doc\" id=\"doc:". $html5id . "\">\n"; + print "<h1>$section</h1>\n"; + print "<p>\n"; + output_highlight($args{'sections'}{$section}); + print "</p>\n"; + } + print "</article>\n"; +} + sub output_section_xml(%) { my %args = %{$_[0]}; my $section; @@ -1780,6 +2042,28 @@ sub check_sections($$$$$$) { } ## +# Checks the section describing the return value of a function. +sub check_return_section { + my $file = shift; + my $declaration_name = shift; + my $return_type = shift; + + # Ignore an empty return type (It's a macro) + # Ignore functions with a "void" return type. (But don't ignore "void *") + if (($return_type eq "") || ($return_type =~ /void\s*\w*\s*$/)) { + return; + } + + if (!defined($sections{$section_return}) || + $sections{$section_return} eq "") { + print STDERR "Warning(${file}:$.): " . + "No description found for return value of " . + "'$declaration_name'\n"; + ++$warnings; + } +} + +## # takes a function prototype and the name of the current file being # processed and spits out all the details stored in the global # arrays/hashes. @@ -1850,6 +2134,15 @@ sub dump_function($$) { my $prms = join " ", @parameterlist; check_sections($file, $declaration_name, "function", $sectcheck, $prms, ""); + # This check emits a lot of warnings at the moment, because many + # functions don't have a 'Return' doc section. So until the number + # of warnings goes sufficiently down, the check is only performed in + # verbose mode. + # TODO: always perform the check. + if ($verbose) { + check_return_section($file, $declaration_name, $return_type); + } + output_declaration($declaration_name, 'function', {'function' => $declaration_name, diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index 00f7512a217f..ff36c508a10e 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -821,6 +821,7 @@ static const char *section_white_list[] = ".debug*", ".zdebug*", /* Compressed debug sections. */ ".GCC-command-line", /* mn10300 */ + ".GCC.command.line", /* record-gcc-switches, non mn10300 */ ".mdebug*", /* alpha, score, mips etc. */ ".pdr", /* alpha, score, mips etc. */ ".stab*", @@ -857,25 +858,23 @@ static void check_section(const char *modname, struct elf_info *elf, #define ALL_INIT_DATA_SECTIONS \ ".init.setup$", ".init.rodata$", \ - ".devinit.rodata$", ".cpuinit.rodata$", ".meminit.rodata$", \ - ".init.data$", ".devinit.data$", ".cpuinit.data$", ".meminit.data$" + ".cpuinit.rodata$", ".meminit.rodata$", \ + ".init.data$", ".cpuinit.data$", ".meminit.data$" #define ALL_EXIT_DATA_SECTIONS \ - ".exit.data$", ".devexit.data$", ".cpuexit.data$", ".memexit.data$" + ".exit.data$", ".cpuexit.data$", ".memexit.data$" #define ALL_INIT_TEXT_SECTIONS \ - ".init.text$", ".devinit.text$", ".cpuinit.text$", ".meminit.text$" + ".init.text$", ".cpuinit.text$", ".meminit.text$" #define ALL_EXIT_TEXT_SECTIONS \ - ".exit.text$", ".devexit.text$", ".cpuexit.text$", ".memexit.text$" + ".exit.text$", ".cpuexit.text$", ".memexit.text$" #define ALL_PCI_INIT_SECTIONS \ ".pci_fixup_early$", ".pci_fixup_header$", ".pci_fixup_final$", \ ".pci_fixup_enable$", ".pci_fixup_resume$", \ ".pci_fixup_resume_early$", ".pci_fixup_suspend$" -#define ALL_XXXINIT_SECTIONS DEV_INIT_SECTIONS, CPU_INIT_SECTIONS, \ - MEM_INIT_SECTIONS -#define ALL_XXXEXIT_SECTIONS DEV_EXIT_SECTIONS, CPU_EXIT_SECTIONS, \ - MEM_EXIT_SECTIONS +#define ALL_XXXINIT_SECTIONS CPU_INIT_SECTIONS, MEM_INIT_SECTIONS +#define ALL_XXXEXIT_SECTIONS CPU_EXIT_SECTIONS, MEM_EXIT_SECTIONS #define ALL_INIT_SECTIONS INIT_SECTIONS, ALL_XXXINIT_SECTIONS #define ALL_EXIT_SECTIONS EXIT_SECTIONS, ALL_XXXEXIT_SECTIONS @@ -884,12 +883,10 @@ static void check_section(const char *modname, struct elf_info *elf, #define TEXT_SECTIONS ".text$" #define INIT_SECTIONS ".init.*" -#define DEV_INIT_SECTIONS ".devinit.*" #define CPU_INIT_SECTIONS ".cpuinit.*" #define MEM_INIT_SECTIONS ".meminit.*" #define EXIT_SECTIONS ".exit.*" -#define DEV_EXIT_SECTIONS ".devexit.*" #define CPU_EXIT_SECTIONS ".cpuexit.*" #define MEM_EXIT_SECTIONS ".memexit.*" @@ -978,7 +975,7 @@ const struct sectioncheck sectioncheck[] = { .mismatch = DATA_TO_ANY_EXIT, .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, }, -/* Do not reference init code/data from devinit/cpuinit/meminit code/data */ +/* Do not reference init code/data from cpuinit/meminit code/data */ { .fromsec = { ALL_XXXINIT_SECTIONS, NULL }, .tosec = { INIT_SECTIONS, NULL }, @@ -999,7 +996,7 @@ const struct sectioncheck sectioncheck[] = { .mismatch = XXXINIT_TO_SOME_INIT, .symbol_white_list = { DEFAULT_SYMBOL_WHITE_LIST, NULL }, }, -/* Do not reference exit code/data from devexit/cpuexit/memexit code/data */ +/* Do not reference exit code/data from cpuexit/memexit code/data */ { .fromsec = { ALL_XXXEXIT_SECTIONS, NULL }, .tosec = { EXIT_SECTIONS, NULL }, @@ -1088,7 +1085,7 @@ static const struct sectioncheck *section_mismatch( * Pattern 2: * Many drivers utilise a *driver container with references to * add, remove, probe functions etc. - * These functions may often be marked __devinit and we do not want to + * These functions may often be marked __cpuinit and we do not want to * warn here. * the pattern is identified by: * tosec = init or exit section diff --git a/scripts/package/buildtar b/scripts/package/buildtar index d0d748e72915..62d8234f8787 100644 --- a/scripts/package/buildtar +++ b/scripts/package/buildtar @@ -28,15 +28,15 @@ case "${1}" in file_ext="" ;; targz-pkg) - compress="gzip -c9" + compress="gzip" file_ext=".gz" ;; tarbz2-pkg) - compress="bzip2 -c9" + compress="bzip2" file_ext=".bz2" ;; tarxz-pkg) - compress="xz -c9" + compress="xz" file_ext=".xz" ;; *) diff --git a/scripts/sign-file b/scripts/sign-file new file mode 100755 index 000000000000..974a20b661b7 --- /dev/null +++ b/scripts/sign-file @@ -0,0 +1,429 @@ +#!/usr/bin/perl -w +# +# Sign a module file using the given key. +# +# Format: +# +# ./scripts/sign-file [-v] <key> <x509> <module> [<dest>] +# +# +use strict; +use FileHandle; +use IPC::Open2; + +my $verbose = 0; +if ($#ARGV >= 0 && $ARGV[0] eq "-v") { + $verbose = 1; + shift; +} + +die "Format: ./scripts/sign-file [-v] <key> <x509> <module> [<dest>]\n" + if ($#ARGV != 2 && $#ARGV != 3); + +my $private_key = $ARGV[0]; +my $x509 = $ARGV[1]; +my $module = $ARGV[2]; +my $dest = ($#ARGV == 3) ? $ARGV[3] : $ARGV[2] . "~"; + +die "Can't read private key\n" unless (-r $private_key); +die "Can't read X.509 certificate\n" unless (-r $x509); +die "Can't read module\n" unless (-r $module); + +# +# Read the kernel configuration +# +my %config = ( + CONFIG_MODULE_SIG_SHA512 => 1 + ); + +if (-r ".config") { + open(FD, "<.config") || die ".config"; + while (<FD>) { + if ($_ =~ /^(CONFIG_.*)=[ym]/) { + $config{$1} = 1; + } + } + close(FD); +} + +# +# Function to read the contents of a file into a variable. +# +sub read_file($) +{ + my ($file) = @_; + my $contents; + my $len; + + open(FD, "<$file") || die $file; + binmode FD; + my @st = stat(FD); + die $file if (!@st); + $len = read(FD, $contents, $st[7]) || die $file; + close(FD) || die $file; + die "$file: Wanted length ", $st[7], ", got ", $len, "\n" + if ($len != $st[7]); + return $contents; +} + +############################################################################### +# +# First of all, we have to parse the X.509 certificate to find certain details +# about it. +# +# We read the DER-encoded X509 certificate and parse it to extract the Subject +# name and Subject Key Identifier. Theis provides the data we need to build +# the certificate identifier. +# +# The signer's name part of the identifier is fabricated from the commonName, +# the organizationName or the emailAddress components of the X.509 subject +# name. +# +# The subject key ID is used to select which of that signer's certificates +# we're intending to use to sign the module. +# +############################################################################### +my $x509_certificate = read_file($x509); + +my $UNIV = 0 << 6; +my $APPL = 1 << 6; +my $CONT = 2 << 6; +my $PRIV = 3 << 6; + +my $CONS = 0x20; + +my $BOOLEAN = 0x01; +my $INTEGER = 0x02; +my $BIT_STRING = 0x03; +my $OCTET_STRING = 0x04; +my $NULL = 0x05; +my $OBJ_ID = 0x06; +my $UTF8String = 0x0c; +my $SEQUENCE = 0x10; +my $SET = 0x11; +my $UTCTime = 0x17; +my $GeneralizedTime = 0x18; + +my %OIDs = ( + pack("CCC", 85, 4, 3) => "commonName", + pack("CCC", 85, 4, 6) => "countryName", + pack("CCC", 85, 4, 10) => "organizationName", + pack("CCC", 85, 4, 11) => "organizationUnitName", + pack("CCCCCCCCC", 42, 134, 72, 134, 247, 13, 1, 1, 1) => "rsaEncryption", + pack("CCCCCCCCC", 42, 134, 72, 134, 247, 13, 1, 1, 5) => "sha1WithRSAEncryption", + pack("CCCCCCCCC", 42, 134, 72, 134, 247, 13, 1, 9, 1) => "emailAddress", + pack("CCC", 85, 29, 35) => "authorityKeyIdentifier", + pack("CCC", 85, 29, 14) => "subjectKeyIdentifier", + pack("CCC", 85, 29, 19) => "basicConstraints" +); + +############################################################################### +# +# Extract an ASN.1 element from a string and return information about it. +# +############################################################################### +sub asn1_extract($$@) +{ + my ($cursor, $expected_tag, $optional) = @_; + + return [ -1 ] + if ($cursor->[1] == 0 && $optional); + + die $x509, ": ", $cursor->[0], ": ASN.1 data underrun (elem ", $cursor->[1], ")\n" + if ($cursor->[1] < 2); + + my ($tag, $len) = unpack("CC", substr(${$cursor->[2]}, $cursor->[0], 2)); + + if ($expected_tag != -1 && $tag != $expected_tag) { + return [ -1 ] + if ($optional); + die $x509, ": ", $cursor->[0], ": ASN.1 unexpected tag (", $tag, + " not ", $expected_tag, ")\n"; + } + + $cursor->[0] += 2; + $cursor->[1] -= 2; + + die $x509, ": ", $cursor->[0], ": ASN.1 long tag\n" + if (($tag & 0x1f) == 0x1f); + die $x509, ": ", $cursor->[0], ": ASN.1 indefinite length\n" + if ($len == 0x80); + + if ($len > 0x80) { + my $l = $len - 0x80; + die $x509, ": ", $cursor->[0], ": ASN.1 data underrun (len len $l)\n" + if ($cursor->[1] < $l); + + if ($l == 0x1) { + $len = unpack("C", substr(${$cursor->[2]}, $cursor->[0], 1)); + } elsif ($l == 0x2) { + $len = unpack("n", substr(${$cursor->[2]}, $cursor->[0], 2)); + } elsif ($l == 0x3) { + $len = unpack("C", substr(${$cursor->[2]}, $cursor->[0], 1)) << 16; + $len = unpack("n", substr(${$cursor->[2]}, $cursor->[0] + 1, 2)); + } elsif ($l == 0x4) { + $len = unpack("N", substr(${$cursor->[2]}, $cursor->[0], 4)); + } else { + die $x509, ": ", $cursor->[0], ": ASN.1 element too long (", $l, ")\n"; + } + + $cursor->[0] += $l; + $cursor->[1] -= $l; + } + + die $x509, ": ", $cursor->[0], ": ASN.1 data underrun (", $len, ")\n" + if ($cursor->[1] < $len); + + my $ret = [ $tag, [ $cursor->[0], $len, $cursor->[2] ] ]; + $cursor->[0] += $len; + $cursor->[1] -= $len; + + return $ret; +} + +############################################################################### +# +# Retrieve the data referred to by a cursor +# +############################################################################### +sub asn1_retrieve($) +{ + my ($cursor) = @_; + my ($offset, $len, $data) = @$cursor; + return substr($$data, $offset, $len); +} + +############################################################################### +# +# Roughly parse the X.509 certificate +# +############################################################################### +my $cursor = [ 0, length($x509_certificate), \$x509_certificate ]; + +my $cert = asn1_extract($cursor, $UNIV | $CONS | $SEQUENCE); +my $tbs = asn1_extract($cert->[1], $UNIV | $CONS | $SEQUENCE); +my $version = asn1_extract($tbs->[1], $CONT | $CONS | 0, 1); +my $serial_number = asn1_extract($tbs->[1], $UNIV | $INTEGER); +my $sig_type = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE); +my $issuer = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE); +my $validity = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE); +my $subject = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE); +my $key = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE); +my $issuer_uid = asn1_extract($tbs->[1], $CONT | $CONS | 1, 1); +my $subject_uid = asn1_extract($tbs->[1], $CONT | $CONS | 2, 1); +my $extension_list = asn1_extract($tbs->[1], $CONT | $CONS | 3, 1); + +my $subject_key_id = (); +my $authority_key_id = (); + +# +# Parse the extension list +# +if ($extension_list->[0] != -1) { + my $extensions = asn1_extract($extension_list->[1], $UNIV | $CONS | $SEQUENCE); + + while ($extensions->[1]->[1] > 0) { + my $ext = asn1_extract($extensions->[1], $UNIV | $CONS | $SEQUENCE); + my $x_oid = asn1_extract($ext->[1], $UNIV | $OBJ_ID); + my $x_crit = asn1_extract($ext->[1], $UNIV | $BOOLEAN, 1); + my $x_val = asn1_extract($ext->[1], $UNIV | $OCTET_STRING); + + my $raw_oid = asn1_retrieve($x_oid->[1]); + next if (!exists($OIDs{$raw_oid})); + my $x_type = $OIDs{$raw_oid}; + + my $raw_value = asn1_retrieve($x_val->[1]); + + if ($x_type eq "subjectKeyIdentifier") { + my $vcursor = [ 0, length($raw_value), \$raw_value ]; + + $subject_key_id = asn1_extract($vcursor, $UNIV | $OCTET_STRING); + } + } +} + +############################################################################### +# +# Determine what we're going to use as the signer's name. In order of +# preference, take one of: commonName, organizationName or emailAddress. +# +############################################################################### +my $org = ""; +my $cn = ""; +my $email = ""; + +while ($subject->[1]->[1] > 0) { + my $rdn = asn1_extract($subject->[1], $UNIV | $CONS | $SET); + my $attr = asn1_extract($rdn->[1], $UNIV | $CONS | $SEQUENCE); + my $n_oid = asn1_extract($attr->[1], $UNIV | $OBJ_ID); + my $n_val = asn1_extract($attr->[1], -1); + + my $raw_oid = asn1_retrieve($n_oid->[1]); + next if (!exists($OIDs{$raw_oid})); + my $n_type = $OIDs{$raw_oid}; + + my $raw_value = asn1_retrieve($n_val->[1]); + + if ($n_type eq "organizationName") { + $org = $raw_value; + } elsif ($n_type eq "commonName") { + $cn = $raw_value; + } elsif ($n_type eq "emailAddress") { + $email = $raw_value; + } +} + +my $signers_name = $email; + +if ($org && $cn) { + # Don't use the organizationName if the commonName repeats it + if (length($org) <= length($cn) && + substr($cn, 0, length($org)) eq $org) { + $signers_name = $cn; + goto got_id_name; + } + + # Or a signifcant chunk of it + if (length($org) >= 7 && + length($cn) >= 7 && + substr($cn, 0, 7) eq substr($org, 0, 7)) { + $signers_name = $cn; + goto got_id_name; + } + + $signers_name = $org . ": " . $cn; +} elsif ($org) { + $signers_name = $org; +} elsif ($cn) { + $signers_name = $cn; +} + +got_id_name: + +die $x509, ": ", "X.509: Couldn't find the Subject Key Identifier extension\n" + if (!$subject_key_id); + +my $key_identifier = asn1_retrieve($subject_key_id->[1]); + +############################################################################### +# +# Create and attach the module signature +# +############################################################################### + +# +# Signature parameters +# +my $algo = 1; # Public-key crypto algorithm: RSA +my $hash = 0; # Digest algorithm +my $id_type = 1; # Identifier type: X.509 + +# +# Digest the data +# +my ($dgst, $prologue) = (); +if (exists $config{"CONFIG_MODULE_SIG_SHA1"}) { + $prologue = pack("C*", + 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, + 0x2B, 0x0E, 0x03, 0x02, 0x1A, + 0x05, 0x00, 0x04, 0x14); + $dgst = "-sha1"; + $hash = 2; +} elsif (exists $config{"CONFIG_MODULE_SIG_SHA224"}) { + $prologue = pack("C*", + 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, + 0x05, 0x00, 0x04, 0x1C); + $dgst = "-sha224"; + $hash = 7; +} elsif (exists $config{"CONFIG_MODULE_SIG_SHA256"}) { + $prologue = pack("C*", + 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, + 0x05, 0x00, 0x04, 0x20); + $dgst = "-sha256"; + $hash = 4; +} elsif (exists $config{"CONFIG_MODULE_SIG_SHA384"}) { + $prologue = pack("C*", + 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, + 0x05, 0x00, 0x04, 0x30); + $dgst = "-sha384"; + $hash = 5; +} elsif (exists $config{"CONFIG_MODULE_SIG_SHA512"}) { + $prologue = pack("C*", + 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, + 0x05, 0x00, 0x04, 0x40); + $dgst = "-sha512"; + $hash = 6; +} else { + die "Can't determine hash algorithm"; +} + +# +# Generate the digest and read from openssl's stdout +# +my $digest; +$digest = readpipe("openssl dgst $dgst -binary $module") || die "openssl dgst"; + +# +# Generate the binary signature, which will be just the integer that comprises +# the signature with no metadata attached. +# +my $pid; +$pid = open2(*read_from, *write_to, + "openssl rsautl -sign -inkey $private_key -keyform PEM") || + die "openssl rsautl"; +binmode write_to; +print write_to $prologue . $digest || die "pipe to openssl rsautl"; +close(write_to) || die "pipe to openssl rsautl"; + +binmode read_from; +my $signature; +read(read_from, $signature, 4096) || die "pipe from openssl rsautl"; +close(read_from) || die "pipe from openssl rsautl"; +$signature = pack("n", length($signature)) . $signature, + +waitpid($pid, 0) || die; +die "openssl rsautl died: $?" if ($? >> 8); + +# +# Build the signed binary +# +my $unsigned_module = read_file($module); + +my $magic_number = "~Module signature appended~\n"; + +my $info = pack("CCCCCxxxN", + $algo, $hash, $id_type, + length($signers_name), + length($key_identifier), + length($signature)); + +if ($verbose) { + print "Size of unsigned module: ", length($unsigned_module), "\n"; + print "Size of signer's name : ", length($signers_name), "\n"; + print "Size of key identifier : ", length($key_identifier), "\n"; + print "Size of signature : ", length($signature), "\n"; + print "Size of informaton : ", length($info), "\n"; + print "Size of magic number : ", length($magic_number), "\n"; + print "Signer's name : '", $signers_name, "'\n"; + print "Digest : $dgst\n"; +} + +open(FD, ">$dest") || die $dest; +binmode FD; +print FD + $unsigned_module, + $signers_name, + $key_identifier, + $signature, + $info, + $magic_number + ; +close FD || die $dest; + +if ($#ARGV != 3) { + rename($dest, $module) || die $module; +} diff --git a/scripts/sortextable.c b/scripts/sortextable.c index f19ddc47304c..1f10e89d15b4 100644 --- a/scripts/sortextable.c +++ b/scripts/sortextable.c @@ -248,6 +248,7 @@ do_file(char const *const fname) case EM_S390: custom_sort = sort_relative_table; break; + case EM_ARM: case EM_MIPS: break; } /* end switch */ diff --git a/scripts/tags.sh b/scripts/tags.sh index cff8faad73d1..79fdafb0d263 100755 --- a/scripts/tags.sh +++ b/scripts/tags.sh @@ -154,7 +154,9 @@ exuberant() --regex-c++='/__CLEARPAGEFLAG_NOOP\(([^,)]*).*/__ClearPage\1/' \ --regex-c++='/TESTCLEARFLAG_FALSE\(([^,)]*).*/TestClearPage\1/' \ --regex-c++='/__TESTCLEARFLAG_FALSE\(([^,)]*).*/__TestClearPage\1/' \ - --regex-c++='/_PE\(([^,)]*).*/PEVENT_ERRNO__\1/' + --regex-c++='/_PE\(([^,)]*).*/PEVENT_ERRNO__\1/' \ + --regex-c='/PCI_OP_READ\(([a-z]*[a-z]).*[1-4]\)/pci_bus_read_config_\1/' \ + --regex-c='/PCI_OP_WRITE\(([a-z]*[a-z]).*[1-4]\)/pci_bus_write_config_\1/' all_kconfigs | xargs $1 -a \ --langdef=kconfig --language-force=kconfig \ @@ -197,7 +199,9 @@ emacs() --regex='/__CLEARPAGEFLAG_NOOP\(([^,)]*).*/__ClearPage\1/' \ --regex='/TESTCLEARFLAG_FALSE\(([^,)]*).*/TestClearPage\1/' \ --regex='/__TESTCLEARFLAG_FALSE\(([^,)]*).*/__TestClearPage\1/' \ - --regex='/_PE\(([^,)]*).*/PEVENT_ERRNO__\1/' + --regex='/_PE\(([^,)]*).*/PEVENT_ERRNO__\1/' \ + --regex='/PCI_OP_READ\(([a-z]*[a-z]).*[1-4]\)/pci_bus_read_config_\1/' \ + --regex='/PCI_OP_WRITE\(([a-z]*[a-z]).*[1-4]\)/pci_bus_write_config_\1/' all_kconfigs | xargs $1 -a \ --regex='/^[ \t]*\(\(menu\)*config\)[ \t]+\([a-zA-Z0-9_]+\)/\3/' |
