Merge branch '2019-12-06-master-imports'

- Allow for the sysboot command, which is used to parse extlinux.conf
  files to be used without PXE support.  There is no functional change
  here aside from fixing distro boot in a few cases where we actually
  lacked the ability to parse the extlinux.conf file
- Add the x509/pkcs7 parsers from Linux, a pre-requisite to EFI Secure
  Boot support.
diff --git a/Kconfig b/Kconfig
index e22417e..92fc4fc 100644
--- a/Kconfig
+++ b/Kconfig
@@ -88,6 +88,7 @@
 	select CMD_PART if PARTITIONS
 	select CMD_PING if CMD_NET
 	select CMD_PXE if NET
+	select CMD_SYSBOOT
 	select ENV_VARS_UBOOT_CONFIG
 	select HUSH_PARSER
 	select SUPPORT_RAW_INITRD
diff --git a/Makefile b/Makefile
index 0ee0c70..0766f78 100644
--- a/Makefile
+++ b/Makefile
@@ -1889,6 +1889,7 @@
 	fi
 
 tools/version.h: include/version.h
+	$(Q)mkdir -p $(dir $@)
 	$(call if_changed,copy)
 
 envtools: scripts_basic $(version_h) $(timestamp_h) tools/version.h
@@ -1957,6 +1958,7 @@
 		-o -name '*.ko.*' -o -name '*.su' -o -name '*.pyc' \
 		-o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \
 		-o -name '*.lex.c' -o -name '*.tab.[ch]' \
+		-o -name '*.asn1.[ch]' \
 		-o -name '*.symtypes' -o -name 'modules.order' \
 		-o -name modules.builtin -o -name '.tmp_*.o.*' \
 		-o -name 'dsdt.aml' -o -name 'dsdt.asl.tmp' -o -name 'dsdt.c' \
diff --git a/cmd/Kconfig b/cmd/Kconfig
index 3b3722f..bc8318d 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -1625,6 +1625,7 @@
 config CMD_DATE
 	bool "date"
 	default y if DM_RTC
+	select LIB_DATE
 	help
 	  Enable the 'date' command for getting/setting the time/date in RTC
 	  devices.
@@ -1671,6 +1672,12 @@
 	     sound init   - set up sound system
 	     sound play   - play a sound
 
+config CMD_SYSBOOT
+	bool "sysboot"
+	select MENU
+	help
+	  Boot image via local extlinux.conf file
+
 config CMD_QFW
 	bool "qfw"
 	select QFW
diff --git a/cmd/Makefile b/cmd/Makefile
index 36b3cec..e87c2f1 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -111,7 +111,7 @@
 obj-$(CONFIG_CMD_PCI) += pci.o
 endif
 obj-$(CONFIG_CMD_PINMUX) += pinmux.o
-obj-$(CONFIG_CMD_PXE) += pxe.o
+obj-$(CONFIG_CMD_PXE) += pxe.o pxe_utils.o
 obj-$(CONFIG_CMD_WOL) += wol.o
 obj-$(CONFIG_CMD_QFW) += qfw.o
 obj-$(CONFIG_CMD_READ) += read.o
@@ -130,6 +130,7 @@
 obj-$(CONFIG_CMD_SPI) += spi.o
 obj-$(CONFIG_CMD_STRINGS) += strings.o
 obj-$(CONFIG_CMD_SMC) += smccc.o
+obj-$(CONFIG_CMD_SYSBOOT) += sysboot.o pxe_utils.o
 obj-$(CONFIG_CMD_TERMINAL) += terminal.o
 obj-$(CONFIG_CMD_TIME) += time.o
 obj-$(CONFIG_CMD_TRACE) += trace.o
diff --git a/cmd/nvedit.c b/cmd/nvedit.c
index b5da375..3420e0b 100644
--- a/cmd/nvedit.c
+++ b/cmd/nvedit.c
@@ -682,6 +682,23 @@
 }
 
 /*
+ * Like env_get, but prints an error if envvar isn't defined in the
+ * environment.  It always returns what env_get does, so it can be used in
+ * place of env_get without changing error handling otherwise.
+ */
+char *from_env(const char *envvar)
+{
+	char *ret;
+
+	ret = env_get(envvar);
+
+	if (!ret)
+		printf("missing environment variable: %s\n", envvar);
+
+	return ret;
+}
+
+/*
  * Look up variable from environment for restricted C runtime env.
  */
 int env_get_f(const char *name, char *buf, unsigned len)
diff --git a/cmd/pxe.c b/cmd/pxe.c
index 768e50a..73f1b4f 100644
--- a/cmd/pxe.c
+++ b/cmd/pxe.c
@@ -6,23 +6,10 @@
 
 #include <common.h>
 #include <command.h>
-#include <env.h>
-#include <malloc.h>
-#include <mapmem.h>
-#include <lcd.h>
-#include <linux/string.h>
-#include <linux/ctype.h>
-#include <errno.h>
-#include <linux/list.h>
-#include <fs.h>
-#include <splash.h>
-#include <asm/io.h>
 
-#include "menu.h"
-#include "cli.h"
+#include "pxe_utils.h"
 
-#define MAX_TFTP_PATH_LEN 127
-
+#ifdef CONFIG_CMD_NET
 const char *pxe_default_paths[] = {
 #ifdef CONFIG_SYS_SOC
 #ifdef CONFIG_SYS_BOARD
@@ -35,102 +22,6 @@
 	NULL
 };
 
-static bool is_pxe;
-
-/*
- * Like env_get, but prints an error if envvar isn't defined in the
- * environment.  It always returns what env_get does, so it can be used in
- * place of env_get without changing error handling otherwise.
- */
-static char *from_env(const char *envvar)
-{
-	char *ret;
-
-	ret = env_get(envvar);
-
-	if (!ret)
-		printf("missing environment variable: %s\n", envvar);
-
-	return ret;
-}
-
-#ifdef CONFIG_CMD_NET
-/*
- * Convert an ethaddr from the environment to the format used by pxelinux
- * filenames based on mac addresses. Convert's ':' to '-', and adds "01-" to
- * the beginning of the ethernet address to indicate a hardware type of
- * Ethernet. Also converts uppercase hex characters into lowercase, to match
- * pxelinux's behavior.
- *
- * Returns 1 for success, -ENOENT if 'ethaddr' is undefined in the
- * environment, or some other value < 0 on error.
- */
-static int format_mac_pxe(char *outbuf, size_t outbuf_len)
-{
-	uchar ethaddr[6];
-
-	if (outbuf_len < 21) {
-		printf("outbuf is too small (%zd < 21)\n", outbuf_len);
-
-		return -EINVAL;
-	}
-
-	if (!eth_env_get_enetaddr_by_index("eth", eth_get_dev_index(), ethaddr))
-		return -ENOENT;
-
-	sprintf(outbuf, "01-%02x-%02x-%02x-%02x-%02x-%02x",
-		ethaddr[0], ethaddr[1], ethaddr[2],
-		ethaddr[3], ethaddr[4], ethaddr[5]);
-
-	return 1;
-}
-#endif
-
-/*
- * Returns the directory the file specified in the bootfile env variable is
- * in. If bootfile isn't defined in the environment, return NULL, which should
- * be interpreted as "don't prepend anything to paths".
- */
-static int get_bootfile_path(const char *file_path, char *bootfile_path,
-			     size_t bootfile_path_size)
-{
-	char *bootfile, *last_slash;
-	size_t path_len = 0;
-
-	/* Only syslinux allows absolute paths */
-	if (file_path[0] == '/' && !is_pxe)
-		goto ret;
-
-	bootfile = from_env("bootfile");
-
-	if (!bootfile)
-		goto ret;
-
-	last_slash = strrchr(bootfile, '/');
-
-	if (last_slash == NULL)
-		goto ret;
-
-	path_len = (last_slash - bootfile) + 1;
-
-	if (bootfile_path_size < path_len) {
-		printf("bootfile_path too small. (%zd < %zd)\n",
-				bootfile_path_size, path_len);
-
-		return -1;
-	}
-
-	strncpy(bootfile_path, bootfile, path_len);
-
- ret:
-	bootfile_path[path_len] = '\0';
-
-	return 1;
-}
-
-static int (*do_getfile)(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr);
-
-#ifdef CONFIG_CMD_NET
 static int do_get_tftp(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr)
 {
 	char *tftp_argv[] = {"tftp", NULL, NULL, NULL};
@@ -141,159 +32,9 @@
 	if (do_tftpb(cmdtp, 0, 3, tftp_argv))
 		return -ENOENT;
 
-	return 1;
-}
-#endif
-
-static char *fs_argv[5];
-
-static int do_get_ext2(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr)
-{
-#ifdef CONFIG_CMD_EXT2
-	fs_argv[0] = "ext2load";
-	fs_argv[3] = file_addr;
-	fs_argv[4] = (void *)file_path;
-
-	if (!do_ext2load(cmdtp, 0, 5, fs_argv))
-		return 1;
-#endif
-	return -ENOENT;
-}
-
-static int do_get_fat(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr)
-{
-#ifdef CONFIG_CMD_FAT
-	fs_argv[0] = "fatload";
-	fs_argv[3] = file_addr;
-	fs_argv[4] = (void *)file_path;
-
-	if (!do_fat_fsload(cmdtp, 0, 5, fs_argv))
-		return 1;
-#endif
-	return -ENOENT;
-}
-
-static int do_get_any(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr)
-{
-#ifdef CONFIG_CMD_FS_GENERIC
-	fs_argv[0] = "load";
-	fs_argv[3] = file_addr;
-	fs_argv[4] = (void *)file_path;
-
-	if (!do_load(cmdtp, 0, 5, fs_argv, FS_TYPE_ANY))
-		return 1;
-#endif
-	return -ENOENT;
-}
-
-/*
- * As in pxelinux, paths to files referenced from files we retrieve are
- * relative to the location of bootfile. get_relfile takes such a path and
- * joins it with the bootfile path to get the full path to the target file. If
- * the bootfile path is NULL, we use file_path as is.
- *
- * Returns 1 for success, or < 0 on error.
- */
-static int get_relfile(cmd_tbl_t *cmdtp, const char *file_path,
-	unsigned long file_addr)
-{
-	size_t path_len;
-	char relfile[MAX_TFTP_PATH_LEN+1];
-	char addr_buf[18];
-	int err;
-
-	err = get_bootfile_path(file_path, relfile, sizeof(relfile));
-
-	if (err < 0)
-		return err;
-
-	path_len = strlen(file_path);
-	path_len += strlen(relfile);
-
-	if (path_len > MAX_TFTP_PATH_LEN) {
-		printf("Base path too long (%s%s)\n",
-					relfile,
-					file_path);
-
-		return -ENAMETOOLONG;
-	}
-
-	strcat(relfile, file_path);
-
-	printf("Retrieving file: %s\n", relfile);
-
-	sprintf(addr_buf, "%lx", file_addr);
-
-	return do_getfile(cmdtp, relfile, addr_buf);
-}
-
-/*
- * Retrieve the file at 'file_path' to the locate given by 'file_addr'. If
- * 'bootfile' was specified in the environment, the path to bootfile will be
- * prepended to 'file_path' and the resulting path will be used.
- *
- * Returns 1 on success, or < 0 for error.
- */
-static int get_pxe_file(cmd_tbl_t *cmdtp, const char *file_path,
-	unsigned long file_addr)
-{
-	unsigned long config_file_size;
-	char *tftp_filesize;
-	int err;
-	char *buf;
-
-	err = get_relfile(cmdtp, file_path, file_addr);
-
-	if (err < 0)
-		return err;
-
-	/*
-	 * the file comes without a NUL byte at the end, so find out its size
-	 * and add the NUL byte.
-	 */
-	tftp_filesize = from_env("filesize");
-
-	if (!tftp_filesize)
-		return -ENOENT;
-
-	if (strict_strtoul(tftp_filesize, 16, &config_file_size) < 0)
-		return -EINVAL;
-
-	buf = map_sysmem(file_addr + config_file_size, 1);
-	*buf = '\0';
-	unmap_sysmem(buf);
-
 	return 1;
 }
 
-#ifdef CONFIG_CMD_NET
-
-#define PXELINUX_DIR "pxelinux.cfg/"
-
-/*
- * Retrieves a file in the 'pxelinux.cfg' folder. Since this uses get_pxe_file
- * to do the hard work, the location of the 'pxelinux.cfg' folder is generated
- * from the bootfile path, as described above.
- *
- * Returns 1 on success or < 0 on error.
- */
-static int get_pxelinux_path(cmd_tbl_t *cmdtp, const char *file,
-	unsigned long pxefile_addr_r)
-{
-	size_t base_len = strlen(PXELINUX_DIR);
-	char path[MAX_TFTP_PATH_LEN+1];
-
-	if (base_len + strlen(file) > MAX_TFTP_PATH_LEN) {
-		printf("path (%s%s) too long, skipping\n",
-				PXELINUX_DIR, file);
-		return -ENAMETOOLONG;
-	}
-
-	sprintf(path, PXELINUX_DIR "%s", file);
-
-	return get_pxe_file(cmdtp, path, pxefile_addr_r);
-}
-
 /*
  * Looks for a pxe file with a name based on the pxeuuid environment variable.
  *
@@ -355,7 +96,6 @@
 
 	return -ENOENT;
 }
-
 /*
  * Entry point for the 'pxe get' command.
  * This Follows pxelinux's rules to download a config file from a tftp server.
@@ -388,7 +128,7 @@
 		return 1;
 
 	err = strict_strtoul(pxefile_addr_str, 16,
-				(unsigned long *)&pxefile_addr_r);
+			     (unsigned long *)&pxefile_addr_r);
 	if (err < 0)
 		return 1;
 
@@ -417,1386 +157,82 @@
 
 	return 1;
 }
-#endif
-
-/*
- * Wrapper to make it easier to store the file at file_path in the location
- * specified by envaddr_name. file_path will be joined to the bootfile path,
- * if any is specified.
- *
- * Returns 1 on success or < 0 on error.
- */
-static int get_relfile_envaddr(cmd_tbl_t *cmdtp, const char *file_path, const char *envaddr_name)
-{
-	unsigned long file_addr;
-	char *envaddr;
-
-	envaddr = from_env(envaddr_name);
-
-	if (!envaddr)
-		return -ENOENT;
-
-	if (strict_strtoul(envaddr, 16, &file_addr) < 0)
-		return -EINVAL;
-
-	return get_relfile(cmdtp, file_path, file_addr);
-}
 
 /*
- * A note on the pxe file parser.
- *
- * We're parsing files that use syslinux grammar, which has a few quirks.
- * String literals must be recognized based on context - there is no
- * quoting or escaping support. There's also nothing to explicitly indicate
- * when a label section completes. We deal with that by ending a label
- * section whenever we see a line that doesn't include.
- *
- * As with the syslinux family, this same file format could be reused in the
- * future for non pxe purposes. The only action it takes during parsing that
- * would throw this off is handling of include files. It assumes we're using
- * pxe, and does a tftp download of a file listed as an include file in the
- * middle of the parsing operation. That could be handled by refactoring it to
- * take a 'include file getter' function.
- */
-
-/*
- * Describes a single label given in a pxe file.
- *
- * Create these with the 'label_create' function given below.
- *
- * name - the name of the menu as given on the 'menu label' line.
- * kernel - the path to the kernel file to use for this label.
- * append - kernel command line to use when booting this label
- * initrd - path to the initrd to use for this label.
- * attempted - 0 if we haven't tried to boot this label, 1 if we have.
- * localboot - 1 if this label specified 'localboot', 0 otherwise.
- * list - lets these form a list, which a pxe_menu struct will hold.
- */
-struct pxe_label {
-	char num[4];
-	char *name;
-	char *menu;
-	char *kernel;
-	char *config;
-	char *append;
-	char *initrd;
-	char *fdt;
-	char *fdtdir;
-	int ipappend;
-	int attempted;
-	int localboot;
-	int localboot_val;
-	struct list_head list;
-};
-
-/*
- * Describes a pxe menu as given via pxe files.
- *
- * title - the name of the menu as given by a 'menu title' line.
- * default_label - the name of the default label, if any.
- * bmp - the bmp file name which is displayed in background
- * timeout - time in tenths of a second to wait for a user key-press before
- *           booting the default label.
- * prompt - if 0, don't prompt for a choice unless the timeout period is
- *          interrupted.  If 1, always prompt for a choice regardless of
- *          timeout.
- * labels - a list of labels defined for the menu.
- */
-struct pxe_menu {
-	char *title;
-	char *default_label;
-	char *bmp;
-	int timeout;
-	int prompt;
-	struct list_head labels;
-};
-
-/*
- * Allocates memory for and initializes a pxe_label. This uses malloc, so the
- * result must be free()'d to reclaim the memory.
+ * Boots a system using a pxe file
  *
- * Returns NULL if malloc fails.
+ * Returns 0 on success, 1 on error.
  */
-static struct pxe_label *label_create(void)
+static int
+do_pxe_boot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 {
-	struct pxe_label *label;
-
-	label = malloc(sizeof(struct pxe_label));
-
-	if (!label)
-		return NULL;
+	unsigned long pxefile_addr_r;
+	struct pxe_menu *cfg;
+	char *pxefile_addr_str;
 
-	memset(label, 0, sizeof(struct pxe_label));
+	do_getfile = do_get_tftp;
 
-	return label;
-}
+	if (argc == 1) {
+		pxefile_addr_str = from_env("pxefile_addr_r");
+		if (!pxefile_addr_str)
+			return 1;
 
-/*
- * Free the memory used by a pxe_label, including that used by its name,
- * kernel, append and initrd members, if they're non NULL.
- *
- * So - be sure to only use dynamically allocated memory for the members of
- * the pxe_label struct, unless you want to clean it up first. These are
- * currently only created by the pxe file parsing code.
- */
-static void label_destroy(struct pxe_label *label)
-{
-	if (label->name)
-		free(label->name);
+	} else if (argc == 2) {
+		pxefile_addr_str = argv[1];
+	} else {
+		return CMD_RET_USAGE;
+	}
 
-	if (label->kernel)
-		free(label->kernel);
+	if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) {
+		printf("Invalid pxefile address: %s\n", pxefile_addr_str);
+		return 1;
+	}
 
-	if (label->config)
-		free(label->config);
+	cfg = parse_pxefile(cmdtp, pxefile_addr_r);
 
-	if (label->append)
-		free(label->append);
+	if (!cfg) {
+		printf("Error parsing config file\n");
+		return 1;
+	}
 
-	if (label->initrd)
-		free(label->initrd);
+	handle_pxe_menu(cmdtp, cfg);
 
-	if (label->fdt)
-		free(label->fdt);
+	destroy_pxe_menu(cfg);
 
-	if (label->fdtdir)
-		free(label->fdtdir);
+	copy_filename(net_boot_file_name, "", sizeof(net_boot_file_name));
 
-	free(label);
+	return 0;
 }
 
-/*
- * Print a label and its string members if they're defined.
- *
- * This is passed as a callback to the menu code for displaying each
- * menu entry.
- */
-static void label_print(void *data)
-{
-	struct pxe_label *label = data;
-	const char *c = label->menu ? label->menu : label->name;
-
-	printf("%s:\t%s\n", label->num, c);
-}
+static cmd_tbl_t cmd_pxe_sub[] = {
+	U_BOOT_CMD_MKENT(get, 1, 1, do_pxe_get, "", ""),
+	U_BOOT_CMD_MKENT(boot, 2, 1, do_pxe_boot, "", "")
+};
 
-/*
- * Boot a label that specified 'localboot'. This requires that the 'localcmd'
- * environment variable is defined. Its contents will be executed as U-Boot
- * command.  If the label specified an 'append' line, its contents will be
- * used to overwrite the contents of the 'bootargs' environment variable prior
- * to running 'localcmd'.
- *
- * Returns 1 on success or < 0 on error.
- */
-static int label_localboot(struct pxe_label *label)
+static int do_pxe(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 {
-	char *localcmd;
+	cmd_tbl_t *cp;
 
-	localcmd = from_env("localcmd");
+	if (argc < 2)
+		return CMD_RET_USAGE;
 
-	if (!localcmd)
-		return -ENOENT;
+	is_pxe = true;
 
-	if (label->append) {
-		char bootargs[CONFIG_SYS_CBSIZE];
+	/* drop initial "pxe" arg */
+	argc--;
+	argv++;
 
-		cli_simple_process_macros(label->append, bootargs);
-		env_set("bootargs", bootargs);
-	}
+	cp = find_cmd_tbl(argv[0], cmd_pxe_sub, ARRAY_SIZE(cmd_pxe_sub));
 
-	debug("running: %s\n", localcmd);
+	if (cp)
+		return cp->cmd(cmdtp, flag, argc, argv);
 
-	return run_command_list(localcmd, strlen(localcmd), 0);
+	return CMD_RET_USAGE;
 }
 
-/*
- * Boot according to the contents of a pxe_label.
- *
- * If we can't boot for any reason, we return.  A successful boot never
- * returns.
- *
- * The kernel will be stored in the location given by the 'kernel_addr_r'
- * environment variable.
- *
- * If the label specifies an initrd file, it will be stored in the location
- * given by the 'ramdisk_addr_r' environment variable.
- *
- * If the label specifies an 'append' line, its contents will overwrite that
- * of the 'bootargs' environment variable.
- */
-static int label_boot(cmd_tbl_t *cmdtp, struct pxe_label *label)
-{
-	char *bootm_argv[] = { "bootm", NULL, NULL, NULL, NULL };
-	char initrd_str[28];
-	char mac_str[29] = "";
-	char ip_str[68] = "";
-	char *fit_addr = NULL;
-	int bootm_argc = 2;
-	int len = 0;
-	ulong kernel_addr;
-	void *buf;
-
-	label_print(label);
-
-	label->attempted = 1;
-
-	if (label->localboot) {
-		if (label->localboot_val >= 0)
-			label_localboot(label);
-		return 0;
-	}
-
-	if (label->kernel == NULL) {
-		printf("No kernel given, skipping %s\n",
-				label->name);
-		return 1;
-	}
-
-	if (label->initrd) {
-		if (get_relfile_envaddr(cmdtp, label->initrd, "ramdisk_addr_r") < 0) {
-			printf("Skipping %s for failure retrieving initrd\n",
-					label->name);
-			return 1;
-		}
-
-		bootm_argv[2] = initrd_str;
-		strncpy(bootm_argv[2], env_get("ramdisk_addr_r"), 18);
-		strcat(bootm_argv[2], ":");
-		strncat(bootm_argv[2], env_get("filesize"), 9);
-		bootm_argc = 3;
-	}
-
-	if (get_relfile_envaddr(cmdtp, label->kernel, "kernel_addr_r") < 0) {
-		printf("Skipping %s for failure retrieving kernel\n",
-				label->name);
-		return 1;
-	}
-
-	if (label->ipappend & 0x1) {
-		sprintf(ip_str, " ip=%s:%s:%s:%s",
-			env_get("ipaddr"), env_get("serverip"),
-			env_get("gatewayip"), env_get("netmask"));
-	}
-
-#ifdef CONFIG_CMD_NET
-	if (label->ipappend & 0x2) {
-		int err;
-		strcpy(mac_str, " BOOTIF=");
-		err = format_mac_pxe(mac_str + 8, sizeof(mac_str) - 8);
-		if (err < 0)
-			mac_str[0] = '\0';
-	}
+U_BOOT_CMD(pxe, 3, 1, do_pxe,
+	   "commands to get and boot from pxe files",
+	   "get - try to retrieve a pxe file using tftp\n"
+	   "pxe boot [pxefile_addr_r] - boot from the pxe file at pxefile_addr_r\n"
+);
 #endif
-
-	if ((label->ipappend & 0x3) || label->append) {
-		char bootargs[CONFIG_SYS_CBSIZE] = "";
-		char finalbootargs[CONFIG_SYS_CBSIZE];
-
-		if (strlen(label->append ?: "") +
-		    strlen(ip_str) + strlen(mac_str) + 1 > sizeof(bootargs)) {
-			printf("bootarg overflow %zd+%zd+%zd+1 > %zd\n",
-			       strlen(label->append ?: ""),
-			       strlen(ip_str), strlen(mac_str),
-			       sizeof(bootargs));
-			return 1;
-		} else {
-			if (label->append)
-				strncpy(bootargs, label->append,
-					sizeof(bootargs));
-			strcat(bootargs, ip_str);
-			strcat(bootargs, mac_str);
-
-			cli_simple_process_macros(bootargs, finalbootargs);
-			env_set("bootargs", finalbootargs);
-			printf("append: %s\n", finalbootargs);
-		}
-	}
-
-	bootm_argv[1] = env_get("kernel_addr_r");
-	/* for FIT, append the configuration identifier */
-	if (label->config) {
-		int len = strlen(bootm_argv[1]) + strlen(label->config) + 1;
-
-		fit_addr = malloc(len);
-		if (!fit_addr) {
-			printf("malloc fail (FIT address)\n");
-			return 1;
-		}
-		snprintf(fit_addr, len, "%s%s", bootm_argv[1], label->config);
-		bootm_argv[1] = fit_addr;
-	}
-
-	/*
-	 * fdt usage is optional:
-	 * It handles the following scenarios. All scenarios are exclusive
-	 *
-	 * Scenario 1: If fdt_addr_r specified and "fdt" label is defined in
-	 * pxe file, retrieve fdt blob from server. Pass fdt_addr_r to bootm,
-	 * and adjust argc appropriately.
-	 *
-	 * Scenario 2: If there is an fdt_addr specified, pass it along to
-	 * bootm, and adjust argc appropriately.
-	 *
-	 * Scenario 3: fdt blob is not available.
-	 */
-	bootm_argv[3] = env_get("fdt_addr_r");
-
-	/* if fdt label is defined then get fdt from server */
-	if (bootm_argv[3]) {
-		char *fdtfile = NULL;
-		char *fdtfilefree = NULL;
-
-		if (label->fdt) {
-			fdtfile = label->fdt;
-		} else if (label->fdtdir) {
-			char *f1, *f2, *f3, *f4, *slash;
-
-			f1 = env_get("fdtfile");
-			if (f1) {
-				f2 = "";
-				f3 = "";
-				f4 = "";
-			} else {
-				/*
-				 * For complex cases where this code doesn't
-				 * generate the correct filename, the board
-				 * code should set $fdtfile during early boot,
-				 * or the boot scripts should set $fdtfile
-				 * before invoking "pxe" or "sysboot".
-				 */
-				f1 = env_get("soc");
-				f2 = "-";
-				f3 = env_get("board");
-				f4 = ".dtb";
-			}
-
-			len = strlen(label->fdtdir);
-			if (!len)
-				slash = "./";
-			else if (label->fdtdir[len - 1] != '/')
-				slash = "/";
-			else
-				slash = "";
-
-			len = strlen(label->fdtdir) + strlen(slash) +
-				strlen(f1) + strlen(f2) + strlen(f3) +
-				strlen(f4) + 1;
-			fdtfilefree = malloc(len);
-			if (!fdtfilefree) {
-				printf("malloc fail (FDT filename)\n");
-				goto cleanup;
-			}
-
-			snprintf(fdtfilefree, len, "%s%s%s%s%s%s",
-				 label->fdtdir, slash, f1, f2, f3, f4);
-			fdtfile = fdtfilefree;
-		}
-
-		if (fdtfile) {
-			int err = get_relfile_envaddr(cmdtp, fdtfile, "fdt_addr_r");
-			free(fdtfilefree);
-			if (err < 0) {
-				printf("Skipping %s for failure retrieving fdt\n",
-						label->name);
-				goto cleanup;
-			}
-		} else {
-			bootm_argv[3] = NULL;
-		}
-	}
-
-	if (!bootm_argv[3])
-		bootm_argv[3] = env_get("fdt_addr");
-
-	if (bootm_argv[3]) {
-		if (!bootm_argv[2])
-			bootm_argv[2] = "-";
-		bootm_argc = 4;
-	}
-
-	kernel_addr = genimg_get_kernel_addr(bootm_argv[1]);
-	buf = map_sysmem(kernel_addr, 0);
-	/* Try bootm for legacy and FIT format image */
-	if (genimg_get_format(buf) != IMAGE_FORMAT_INVALID)
-		do_bootm(cmdtp, 0, bootm_argc, bootm_argv);
-#ifdef CONFIG_CMD_BOOTI
-	/* Try booting an AArch64 Linux kernel image */
-	else
-		do_booti(cmdtp, 0, bootm_argc, bootm_argv);
-#elif defined(CONFIG_CMD_BOOTZ)
-	/* Try booting a Image */
-	else
-		do_bootz(cmdtp, 0, bootm_argc, bootm_argv);
-#endif
-	unmap_sysmem(buf);
-
-cleanup:
-	if (fit_addr)
-		free(fit_addr);
-	return 1;
-}
-
-/*
- * Tokens for the pxe file parser.
- */
-enum token_type {
-	T_EOL,
-	T_STRING,
-	T_EOF,
-	T_MENU,
-	T_TITLE,
-	T_TIMEOUT,
-	T_LABEL,
-	T_KERNEL,
-	T_LINUX,
-	T_APPEND,
-	T_INITRD,
-	T_LOCALBOOT,
-	T_DEFAULT,
-	T_PROMPT,
-	T_INCLUDE,
-	T_FDT,
-	T_FDTDIR,
-	T_ONTIMEOUT,
-	T_IPAPPEND,
-	T_BACKGROUND,
-	T_INVALID
-};
-
-/*
- * A token - given by a value and a type.
- */
-struct token {
-	char *val;
-	enum token_type type;
-};
-
-/*
- * Keywords recognized.
- */
-static const struct token keywords[] = {
-	{"menu", T_MENU},
-	{"title", T_TITLE},
-	{"timeout", T_TIMEOUT},
-	{"default", T_DEFAULT},
-	{"prompt", T_PROMPT},
-	{"label", T_LABEL},
-	{"kernel", T_KERNEL},
-	{"linux", T_LINUX},
-	{"localboot", T_LOCALBOOT},
-	{"append", T_APPEND},
-	{"initrd", T_INITRD},
-	{"include", T_INCLUDE},
-	{"devicetree", T_FDT},
-	{"fdt", T_FDT},
-	{"devicetreedir", T_FDTDIR},
-	{"fdtdir", T_FDTDIR},
-	{"ontimeout", T_ONTIMEOUT,},
-	{"ipappend", T_IPAPPEND,},
-	{"background", T_BACKGROUND,},
-	{NULL, T_INVALID}
-};
-
-/*
- * Since pxe(linux) files don't have a token to identify the start of a
- * literal, we have to keep track of when we're in a state where a literal is
- * expected vs when we're in a state a keyword is expected.
- */
-enum lex_state {
-	L_NORMAL = 0,
-	L_KEYWORD,
-	L_SLITERAL
-};
-
-/*
- * get_string retrieves a string from *p and stores it as a token in
- * *t.
- *
- * get_string used for scanning both string literals and keywords.
- *
- * Characters from *p are copied into t-val until a character equal to
- * delim is found, or a NUL byte is reached. If delim has the special value of
- * ' ', any whitespace character will be used as a delimiter.
- *
- * If lower is unequal to 0, uppercase characters will be converted to
- * lowercase in the result. This is useful to make keywords case
- * insensitive.
- *
- * The location of *p is updated to point to the first character after the end
- * of the token - the ending delimiter.
- *
- * On success, the new value of t->val is returned. Memory for t->val is
- * allocated using malloc and must be free()'d to reclaim it.  If insufficient
- * memory is available, NULL is returned.
- */
-static char *get_string(char **p, struct token *t, char delim, int lower)
-{
-	char *b, *e;
-	size_t len, i;
-
-	/*
-	 * b and e both start at the beginning of the input stream.
-	 *
-	 * e is incremented until we find the ending delimiter, or a NUL byte
-	 * is reached. Then, we take e - b to find the length of the token.
-	 */
-	b = e = *p;
-
-	while (*e) {
-		if ((delim == ' ' && isspace(*e)) || delim == *e)
-			break;
-		e++;
-	}
-
-	len = e - b;
-
-	/*
-	 * Allocate memory to hold the string, and copy it in, converting
-	 * characters to lowercase if lower is != 0.
-	 */
-	t->val = malloc(len + 1);
-	if (!t->val)
-		return NULL;
-
-	for (i = 0; i < len; i++, b++) {
-		if (lower)
-			t->val[i] = tolower(*b);
-		else
-			t->val[i] = *b;
-	}
-
-	t->val[len] = '\0';
-
-	/*
-	 * Update *p so the caller knows where to continue scanning.
-	 */
-	*p = e;
-
-	t->type = T_STRING;
-
-	return t->val;
-}
-
-/*
- * Populate a keyword token with a type and value.
- */
-static void get_keyword(struct token *t)
-{
-	int i;
-
-	for (i = 0; keywords[i].val; i++) {
-		if (!strcmp(t->val, keywords[i].val)) {
-			t->type = keywords[i].type;
-			break;
-		}
-	}
-}
-
-/*
- * Get the next token.  We have to keep track of which state we're in to know
- * if we're looking to get a string literal or a keyword.
- *
- * *p is updated to point at the first character after the current token.
- */
-static void get_token(char **p, struct token *t, enum lex_state state)
-{
-	char *c = *p;
-
-	t->type = T_INVALID;
-
-	/* eat non EOL whitespace */
-	while (isblank(*c))
-		c++;
-
-	/*
-	 * eat comments. note that string literals can't begin with #, but
-	 * can contain a # after their first character.
-	 */
-	if (*c == '#') {
-		while (*c && *c != '\n')
-			c++;
-	}
-
-	if (*c == '\n') {
-		t->type = T_EOL;
-		c++;
-	} else if (*c == '\0') {
-		t->type = T_EOF;
-		c++;
-	} else if (state == L_SLITERAL) {
-		get_string(&c, t, '\n', 0);
-	} else if (state == L_KEYWORD) {
-		/*
-		 * when we expect a keyword, we first get the next string
-		 * token delimited by whitespace, and then check if it
-		 * matches a keyword in our keyword list. if it does, it's
-		 * converted to a keyword token of the appropriate type, and
-		 * if not, it remains a string token.
-		 */
-		get_string(&c, t, ' ', 1);
-		get_keyword(t);
-	}
-
-	*p = c;
-}
-
-/*
- * Increment *c until we get to the end of the current line, or EOF.
- */
-static void eol_or_eof(char **c)
-{
-	while (**c && **c != '\n')
-		(*c)++;
-}
-
-/*
- * All of these parse_* functions share some common behavior.
- *
- * They finish with *c pointing after the token they parse, and return 1 on
- * success, or < 0 on error.
- */
-
-/*
- * Parse a string literal and store a pointer it at *dst. String literals
- * terminate at the end of the line.
- */
-static int parse_sliteral(char **c, char **dst)
-{
-	struct token t;
-	char *s = *c;
-
-	get_token(c, &t, L_SLITERAL);
-
-	if (t.type != T_STRING) {
-		printf("Expected string literal: %.*s\n", (int)(*c - s), s);
-		return -EINVAL;
-	}
-
-	*dst = t.val;
-
-	return 1;
-}
-
-/*
- * Parse a base 10 (unsigned) integer and store it at *dst.
- */
-static int parse_integer(char **c, int *dst)
-{
-	struct token t;
-	char *s = *c;
-
-	get_token(c, &t, L_SLITERAL);
-
-	if (t.type != T_STRING) {
-		printf("Expected string: %.*s\n", (int)(*c - s), s);
-		return -EINVAL;
-	}
-
-	*dst = simple_strtol(t.val, NULL, 10);
-
-	free(t.val);
-
-	return 1;
-}
-
-static int parse_pxefile_top(cmd_tbl_t *cmdtp, char *p, unsigned long base,
-	struct pxe_menu *cfg, int nest_level);
-
-/*
- * Parse an include statement, and retrieve and parse the file it mentions.
- *
- * base should point to a location where it's safe to store the file, and
- * nest_level should indicate how many nested includes have occurred. For this
- * include, nest_level has already been incremented and doesn't need to be
- * incremented here.
- */
-static int handle_include(cmd_tbl_t *cmdtp, char **c, unsigned long base,
-				struct pxe_menu *cfg, int nest_level)
-{
-	char *include_path;
-	char *s = *c;
-	int err;
-	char *buf;
-	int ret;
-
-	err = parse_sliteral(c, &include_path);
-
-	if (err < 0) {
-		printf("Expected include path: %.*s\n",
-				 (int)(*c - s), s);
-		return err;
-	}
-
-	err = get_pxe_file(cmdtp, include_path, base);
-
-	if (err < 0) {
-		printf("Couldn't retrieve %s\n", include_path);
-		return err;
-	}
-
-	buf = map_sysmem(base, 0);
-	ret = parse_pxefile_top(cmdtp, buf, base, cfg, nest_level);
-	unmap_sysmem(buf);
-
-	return ret;
-}
-
-/*
- * Parse lines that begin with 'menu'.
- *
- * base and nest are provided to handle the 'menu include' case.
- *
- * base should point to a location where it's safe to store the included file.
- *
- * nest_level should be 1 when parsing the top level pxe file, 2 when parsing
- * a file it includes, 3 when parsing a file included by that file, and so on.
- */
-static int parse_menu(cmd_tbl_t *cmdtp, char **c, struct pxe_menu *cfg,
-				unsigned long base, int nest_level)
-{
-	struct token t;
-	char *s = *c;
-	int err = 0;
-
-	get_token(c, &t, L_KEYWORD);
-
-	switch (t.type) {
-	case T_TITLE:
-		err = parse_sliteral(c, &cfg->title);
-
-		break;
-
-	case T_INCLUDE:
-		err = handle_include(cmdtp, c, base, cfg,
-						nest_level + 1);
-		break;
-
-	case T_BACKGROUND:
-		err = parse_sliteral(c, &cfg->bmp);
-		break;
-
-	default:
-		printf("Ignoring malformed menu command: %.*s\n",
-				(int)(*c - s), s);
-	}
-
-	if (err < 0)
-		return err;
-
-	eol_or_eof(c);
-
-	return 1;
-}
-
-/*
- * Handles parsing a 'menu line' when we're parsing a label.
- */
-static int parse_label_menu(char **c, struct pxe_menu *cfg,
-				struct pxe_label *label)
-{
-	struct token t;
-	char *s;
-
-	s = *c;
-
-	get_token(c, &t, L_KEYWORD);
-
-	switch (t.type) {
-	case T_DEFAULT:
-		if (!cfg->default_label)
-			cfg->default_label = strdup(label->name);
-
-		if (!cfg->default_label)
-			return -ENOMEM;
-
-		break;
-	case T_LABEL:
-		parse_sliteral(c, &label->menu);
-		break;
-	default:
-		printf("Ignoring malformed menu command: %.*s\n",
-				(int)(*c - s), s);
-	}
-
-	eol_or_eof(c);
-
-	return 0;
-}
-
-/*
- * Handles parsing a 'kernel' label.
- * expecting "filename" or "<fit_filename>#cfg"
- */
-static int parse_label_kernel(char **c, struct pxe_label *label)
-{
-	char *s;
-	int err;
-
-	err = parse_sliteral(c, &label->kernel);
-	if (err < 0)
-		return err;
-
-	s = strstr(label->kernel, "#");
-	if (!s)
-		return 1;
-
-	label->config = malloc(strlen(s) + 1);
-	if (!label->config)
-		return -ENOMEM;
-
-	strcpy(label->config, s);
-	*s = 0;
-
-	return 1;
-}
-
-/*
- * Parses a label and adds it to the list of labels for a menu.
- *
- * A label ends when we either get to the end of a file, or
- * get some input we otherwise don't have a handler defined
- * for.
- *
- */
-static int parse_label(char **c, struct pxe_menu *cfg)
-{
-	struct token t;
-	int len;
-	char *s = *c;
-	struct pxe_label *label;
-	int err;
-
-	label = label_create();
-	if (!label)
-		return -ENOMEM;
-
-	err = parse_sliteral(c, &label->name);
-	if (err < 0) {
-		printf("Expected label name: %.*s\n", (int)(*c - s), s);
-		label_destroy(label);
-		return -EINVAL;
-	}
-
-	list_add_tail(&label->list, &cfg->labels);
-
-	while (1) {
-		s = *c;
-		get_token(c, &t, L_KEYWORD);
-
-		err = 0;
-		switch (t.type) {
-		case T_MENU:
-			err = parse_label_menu(c, cfg, label);
-			break;
-
-		case T_KERNEL:
-		case T_LINUX:
-			err = parse_label_kernel(c, label);
-			break;
-
-		case T_APPEND:
-			err = parse_sliteral(c, &label->append);
-			if (label->initrd)
-				break;
-			s = strstr(label->append, "initrd=");
-			if (!s)
-				break;
-			s += 7;
-			len = (int)(strchr(s, ' ') - s);
-			label->initrd = malloc(len + 1);
-			strncpy(label->initrd, s, len);
-			label->initrd[len] = '\0';
-
-			break;
-
-		case T_INITRD:
-			if (!label->initrd)
-				err = parse_sliteral(c, &label->initrd);
-			break;
-
-		case T_FDT:
-			if (!label->fdt)
-				err = parse_sliteral(c, &label->fdt);
-			break;
-
-		case T_FDTDIR:
-			if (!label->fdtdir)
-				err = parse_sliteral(c, &label->fdtdir);
-			break;
-
-		case T_LOCALBOOT:
-			label->localboot = 1;
-			err = parse_integer(c, &label->localboot_val);
-			break;
-
-		case T_IPAPPEND:
-			err = parse_integer(c, &label->ipappend);
-			break;
-
-		case T_EOL:
-			break;
-
-		default:
-			/*
-			 * put the token back! we don't want it - it's the end
-			 * of a label and whatever token this is, it's
-			 * something for the menu level context to handle.
-			 */
-			*c = s;
-			return 1;
-		}
-
-		if (err < 0)
-			return err;
-	}
-}
-
-/*
- * This 16 comes from the limit pxelinux imposes on nested includes.
- *
- * There is no reason at all we couldn't do more, but some limit helps prevent
- * infinite (until crash occurs) recursion if a file tries to include itself.
- */
-#define MAX_NEST_LEVEL 16
-
-/*
- * Entry point for parsing a menu file. nest_level indicates how many times
- * we've nested in includes.  It will be 1 for the top level menu file.
- *
- * Returns 1 on success, < 0 on error.
- */
-static int parse_pxefile_top(cmd_tbl_t *cmdtp, char *p, unsigned long base,
-				struct pxe_menu *cfg, int nest_level)
-{
-	struct token t;
-	char *s, *b, *label_name;
-	int err;
-
-	b = p;
-
-	if (nest_level > MAX_NEST_LEVEL) {
-		printf("Maximum nesting (%d) exceeded\n", MAX_NEST_LEVEL);
-		return -EMLINK;
-	}
-
-	while (1) {
-		s = p;
-
-		get_token(&p, &t, L_KEYWORD);
-
-		err = 0;
-		switch (t.type) {
-		case T_MENU:
-			cfg->prompt = 1;
-			err = parse_menu(cmdtp, &p, cfg,
-				base + ALIGN(strlen(b) + 1, 4),
-				nest_level);
-			break;
-
-		case T_TIMEOUT:
-			err = parse_integer(&p, &cfg->timeout);
-			break;
-
-		case T_LABEL:
-			err = parse_label(&p, cfg);
-			break;
-
-		case T_DEFAULT:
-		case T_ONTIMEOUT:
-			err = parse_sliteral(&p, &label_name);
-
-			if (label_name) {
-				if (cfg->default_label)
-					free(cfg->default_label);
-
-				cfg->default_label = label_name;
-			}
-
-			break;
-
-		case T_INCLUDE:
-			err = handle_include(cmdtp, &p,
-				base + ALIGN(strlen(b), 4), cfg,
-				nest_level + 1);
-			break;
-
-		case T_PROMPT:
-			eol_or_eof(&p);
-			break;
-
-		case T_EOL:
-			break;
-
-		case T_EOF:
-			return 1;
-
-		default:
-			printf("Ignoring unknown command: %.*s\n",
-							(int)(p - s), s);
-			eol_or_eof(&p);
-		}
-
-		if (err < 0)
-			return err;
-	}
-}
-
-/*
- * Free the memory used by a pxe_menu and its labels.
- */
-static void destroy_pxe_menu(struct pxe_menu *cfg)
-{
-	struct list_head *pos, *n;
-	struct pxe_label *label;
-
-	if (cfg->title)
-		free(cfg->title);
-
-	if (cfg->default_label)
-		free(cfg->default_label);
-
-	list_for_each_safe(pos, n, &cfg->labels) {
-		label = list_entry(pos, struct pxe_label, list);
-
-		label_destroy(label);
-	}
-
-	free(cfg);
-}
-
-/*
- * Entry point for parsing a pxe file. This is only used for the top level
- * file.
- *
- * Returns NULL if there is an error, otherwise, returns a pointer to a
- * pxe_menu struct populated with the results of parsing the pxe file (and any
- * files it includes). The resulting pxe_menu struct can be free()'d by using
- * the destroy_pxe_menu() function.
- */
-static struct pxe_menu *parse_pxefile(cmd_tbl_t *cmdtp, unsigned long menucfg)
-{
-	struct pxe_menu *cfg;
-	char *buf;
-	int r;
-
-	cfg = malloc(sizeof(struct pxe_menu));
-
-	if (!cfg)
-		return NULL;
-
-	memset(cfg, 0, sizeof(struct pxe_menu));
-
-	INIT_LIST_HEAD(&cfg->labels);
-
-	buf = map_sysmem(menucfg, 0);
-	r = parse_pxefile_top(cmdtp, buf, menucfg, cfg, 1);
-	unmap_sysmem(buf);
-
-	if (r < 0) {
-		destroy_pxe_menu(cfg);
-		return NULL;
-	}
-
-	return cfg;
-}
-
-/*
- * Converts a pxe_menu struct into a menu struct for use with U-Boot's generic
- * menu code.
- */
-static struct menu *pxe_menu_to_menu(struct pxe_menu *cfg)
-{
-	struct pxe_label *label;
-	struct list_head *pos;
-	struct menu *m;
-	int err;
-	int i = 1;
-	char *default_num = NULL;
-
-	/*
-	 * Create a menu and add items for all the labels.
-	 */
-	m = menu_create(cfg->title, DIV_ROUND_UP(cfg->timeout, 10),
-			cfg->prompt, label_print, NULL, NULL);
-
-	if (!m)
-		return NULL;
-
-	list_for_each(pos, &cfg->labels) {
-		label = list_entry(pos, struct pxe_label, list);
-
-		sprintf(label->num, "%d", i++);
-		if (menu_item_add(m, label->num, label) != 1) {
-			menu_destroy(m);
-			return NULL;
-		}
-		if (cfg->default_label &&
-		    (strcmp(label->name, cfg->default_label) == 0))
-			default_num = label->num;
-
-	}
-
-	/*
-	 * After we've created items for each label in the menu, set the
-	 * menu's default label if one was specified.
-	 */
-	if (default_num) {
-		err = menu_default_set(m, default_num);
-		if (err != 1) {
-			if (err != -ENOENT) {
-				menu_destroy(m);
-				return NULL;
-			}
-
-			printf("Missing default: %s\n", cfg->default_label);
-		}
-	}
-
-	return m;
-}
-
-/*
- * Try to boot any labels we have yet to attempt to boot.
- */
-static void boot_unattempted_labels(cmd_tbl_t *cmdtp, struct pxe_menu *cfg)
-{
-	struct list_head *pos;
-	struct pxe_label *label;
-
-	list_for_each(pos, &cfg->labels) {
-		label = list_entry(pos, struct pxe_label, list);
-
-		if (!label->attempted)
-			label_boot(cmdtp, label);
-	}
-}
-
-/*
- * Boot the system as prescribed by a pxe_menu.
- *
- * Use the menu system to either get the user's choice or the default, based
- * on config or user input.  If there is no default or user's choice,
- * attempted to boot labels in the order they were given in pxe files.
- * If the default or user's choice fails to boot, attempt to boot other
- * labels in the order they were given in pxe files.
- *
- * If this function returns, there weren't any labels that successfully
- * booted, or the user interrupted the menu selection via ctrl+c.
- */
-static void handle_pxe_menu(cmd_tbl_t *cmdtp, struct pxe_menu *cfg)
-{
-	void *choice;
-	struct menu *m;
-	int err;
-
-#ifdef CONFIG_CMD_BMP
-	/* display BMP if available */
-	if (cfg->bmp) {
-		if (get_relfile(cmdtp, cfg->bmp, load_addr)) {
-			run_command("cls", 0);
-			bmp_display(load_addr,
-				    BMP_ALIGN_CENTER, BMP_ALIGN_CENTER);
-		} else {
-			printf("Skipping background bmp %s for failure\n",
-			       cfg->bmp);
-		}
-	}
-#endif
-
-	m = pxe_menu_to_menu(cfg);
-	if (!m)
-		return;
-
-	err = menu_get_choice(m, &choice);
-
-	menu_destroy(m);
-
-	/*
-	 * err == 1 means we got a choice back from menu_get_choice.
-	 *
-	 * err == -ENOENT if the menu was setup to select the default but no
-	 * default was set. in that case, we should continue trying to boot
-	 * labels that haven't been attempted yet.
-	 *
-	 * otherwise, the user interrupted or there was some other error and
-	 * we give up.
-	 */
-
-	if (err == 1) {
-		err = label_boot(cmdtp, choice);
-		if (!err)
-			return;
-	} else if (err != -ENOENT) {
-		return;
-	}
-
-	boot_unattempted_labels(cmdtp, cfg);
-}
-
-#ifdef CONFIG_CMD_NET
-/*
- * Boots a system using a pxe file
- *
- * Returns 0 on success, 1 on error.
- */
-static int
-do_pxe_boot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
-{
-	unsigned long pxefile_addr_r;
-	struct pxe_menu *cfg;
-	char *pxefile_addr_str;
-
-	do_getfile = do_get_tftp;
-
-	if (argc == 1) {
-		pxefile_addr_str = from_env("pxefile_addr_r");
-		if (!pxefile_addr_str)
-			return 1;
-
-	} else if (argc == 2) {
-		pxefile_addr_str = argv[1];
-	} else {
-		return CMD_RET_USAGE;
-	}
-
-	if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) {
-		printf("Invalid pxefile address: %s\n", pxefile_addr_str);
-		return 1;
-	}
-
-	cfg = parse_pxefile(cmdtp, pxefile_addr_r);
-
-	if (cfg == NULL) {
-		printf("Error parsing config file\n");
-		return 1;
-	}
-
-	handle_pxe_menu(cmdtp, cfg);
-
-	destroy_pxe_menu(cfg);
-
-	copy_filename(net_boot_file_name, "", sizeof(net_boot_file_name));
-
-	return 0;
-}
-
-static cmd_tbl_t cmd_pxe_sub[] = {
-	U_BOOT_CMD_MKENT(get, 1, 1, do_pxe_get, "", ""),
-	U_BOOT_CMD_MKENT(boot, 2, 1, do_pxe_boot, "", "")
-};
-
-static int do_pxe(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
-{
-	cmd_tbl_t *cp;
-
-	if (argc < 2)
-		return CMD_RET_USAGE;
-
-	is_pxe = true;
-
-	/* drop initial "pxe" arg */
-	argc--;
-	argv++;
-
-	cp = find_cmd_tbl(argv[0], cmd_pxe_sub, ARRAY_SIZE(cmd_pxe_sub));
-
-	if (cp)
-		return cp->cmd(cmdtp, flag, argc, argv);
-
-	return CMD_RET_USAGE;
-}
-
-U_BOOT_CMD(
-	pxe, 3, 1, do_pxe,
-	"commands to get and boot from pxe files",
-	"get - try to retrieve a pxe file using tftp\npxe "
-	"boot [pxefile_addr_r] - boot from the pxe file at pxefile_addr_r\n"
-);
-#endif
-
-/*
- * Boots a system using a local disk syslinux/extlinux file
- *
- * Returns 0 on success, 1 on error.
- */
-static int do_sysboot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
-{
-	unsigned long pxefile_addr_r;
-	struct pxe_menu *cfg;
-	char *pxefile_addr_str;
-	char *filename;
-	int prompt = 0;
-
-	is_pxe = false;
-
-	if (argc > 1 && strstr(argv[1], "-p")) {
-		prompt = 1;
-		argc--;
-		argv++;
-	}
-
-	if (argc < 4)
-		return cmd_usage(cmdtp);
-
-	if (argc < 5) {
-		pxefile_addr_str = from_env("pxefile_addr_r");
-		if (!pxefile_addr_str)
-			return 1;
-	} else {
-		pxefile_addr_str = argv[4];
-	}
-
-	if (argc < 6)
-		filename = env_get("bootfile");
-	else {
-		filename = argv[5];
-		env_set("bootfile", filename);
-	}
-
-	if (strstr(argv[3], "ext2"))
-		do_getfile = do_get_ext2;
-	else if (strstr(argv[3], "fat"))
-		do_getfile = do_get_fat;
-	else if (strstr(argv[3], "any"))
-		do_getfile = do_get_any;
-	else {
-		printf("Invalid filesystem: %s\n", argv[3]);
-		return 1;
-	}
-	fs_argv[1] = argv[1];
-	fs_argv[2] = argv[2];
-
-	if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) {
-		printf("Invalid pxefile address: %s\n", pxefile_addr_str);
-		return 1;
-	}
-
-	if (get_pxe_file(cmdtp, filename, pxefile_addr_r) < 0) {
-		printf("Error reading config file\n");
-		return 1;
-	}
-
-	cfg = parse_pxefile(cmdtp, pxefile_addr_r);
-
-	if (cfg == NULL) {
-		printf("Error parsing config file\n");
-		return 1;
-	}
-
-	if (prompt)
-		cfg->prompt = 1;
-
-	handle_pxe_menu(cmdtp, cfg);
-
-	destroy_pxe_menu(cfg);
-
-	return 0;
-}
-
-U_BOOT_CMD(
-	sysboot, 7, 1, do_sysboot,
-	"command to get and boot from syslinux files",
-	"[-p] <interface> <dev[:part]> <ext2|fat|any> [addr] [filename]\n"
-	"    - load and parse syslinux menu file 'filename' from ext2, fat\n"
-	"      or any filesystem on 'dev' on 'interface' to address 'addr'"
-);
diff --git a/cmd/pxe_utils.c b/cmd/pxe_utils.c
new file mode 100644
index 0000000..42b584e
--- /dev/null
+++ b/cmd/pxe_utils.c
@@ -0,0 +1,1352 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2010-2011 Calxeda, Inc.
+ * Copyright (c) 2014, NVIDIA CORPORATION.  All rights reserved.
+ */
+
+#include <common.h>
+#include <env.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <lcd.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <errno.h>
+#include <linux/list.h>
+
+#include <splash.h>
+#include <asm/io.h>
+
+#include "menu.h"
+#include "cli.h"
+
+#include "pxe_utils.h"
+
+#define MAX_TFTP_PATH_LEN 127
+
+bool is_pxe;
+
+/*
+ * Convert an ethaddr from the environment to the format used by pxelinux
+ * filenames based on mac addresses. Convert's ':' to '-', and adds "01-" to
+ * the beginning of the ethernet address to indicate a hardware type of
+ * Ethernet. Also converts uppercase hex characters into lowercase, to match
+ * pxelinux's behavior.
+ *
+ * Returns 1 for success, -ENOENT if 'ethaddr' is undefined in the
+ * environment, or some other value < 0 on error.
+ */
+int format_mac_pxe(char *outbuf, size_t outbuf_len)
+{
+	uchar ethaddr[6];
+
+	if (outbuf_len < 21) {
+		printf("outbuf is too small (%zd < 21)\n", outbuf_len);
+
+		return -EINVAL;
+	}
+
+	if (!eth_env_get_enetaddr_by_index("eth", eth_get_dev_index(), ethaddr))
+		return -ENOENT;
+
+	sprintf(outbuf, "01-%02x-%02x-%02x-%02x-%02x-%02x",
+		ethaddr[0], ethaddr[1], ethaddr[2],
+		ethaddr[3], ethaddr[4], ethaddr[5]);
+
+	return 1;
+}
+
+/*
+ * Returns the directory the file specified in the bootfile env variable is
+ * in. If bootfile isn't defined in the environment, return NULL, which should
+ * be interpreted as "don't prepend anything to paths".
+ */
+static int get_bootfile_path(const char *file_path, char *bootfile_path,
+			     size_t bootfile_path_size)
+{
+	char *bootfile, *last_slash;
+	size_t path_len = 0;
+
+	/* Only syslinux allows absolute paths */
+	if (file_path[0] == '/' && !is_pxe)
+		goto ret;
+
+	bootfile = from_env("bootfile");
+
+	if (!bootfile)
+		goto ret;
+
+	last_slash = strrchr(bootfile, '/');
+
+	if (!last_slash)
+		goto ret;
+
+	path_len = (last_slash - bootfile) + 1;
+
+	if (bootfile_path_size < path_len) {
+		printf("bootfile_path too small. (%zd < %zd)\n",
+		       bootfile_path_size, path_len);
+
+		return -1;
+	}
+
+	strncpy(bootfile_path, bootfile, path_len);
+
+ ret:
+	bootfile_path[path_len] = '\0';
+
+	return 1;
+}
+
+int (*do_getfile)(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr);
+
+/*
+ * As in pxelinux, paths to files referenced from files we retrieve are
+ * relative to the location of bootfile. get_relfile takes such a path and
+ * joins it with the bootfile path to get the full path to the target file. If
+ * the bootfile path is NULL, we use file_path as is.
+ *
+ * Returns 1 for success, or < 0 on error.
+ */
+static int get_relfile(cmd_tbl_t *cmdtp, const char *file_path,
+		       unsigned long file_addr)
+{
+	size_t path_len;
+	char relfile[MAX_TFTP_PATH_LEN + 1];
+	char addr_buf[18];
+	int err;
+
+	err = get_bootfile_path(file_path, relfile, sizeof(relfile));
+
+	if (err < 0)
+		return err;
+
+	path_len = strlen(file_path);
+	path_len += strlen(relfile);
+
+	if (path_len > MAX_TFTP_PATH_LEN) {
+		printf("Base path too long (%s%s)\n", relfile, file_path);
+
+		return -ENAMETOOLONG;
+	}
+
+	strcat(relfile, file_path);
+
+	printf("Retrieving file: %s\n", relfile);
+
+	sprintf(addr_buf, "%lx", file_addr);
+
+	return do_getfile(cmdtp, relfile, addr_buf);
+}
+
+/*
+ * Retrieve the file at 'file_path' to the locate given by 'file_addr'. If
+ * 'bootfile' was specified in the environment, the path to bootfile will be
+ * prepended to 'file_path' and the resulting path will be used.
+ *
+ * Returns 1 on success, or < 0 for error.
+ */
+int get_pxe_file(cmd_tbl_t *cmdtp, const char *file_path,
+		 unsigned long file_addr)
+{
+	unsigned long config_file_size;
+	char *tftp_filesize;
+	int err;
+	char *buf;
+
+	err = get_relfile(cmdtp, file_path, file_addr);
+
+	if (err < 0)
+		return err;
+
+	/*
+	 * the file comes without a NUL byte at the end, so find out its size
+	 * and add the NUL byte.
+	 */
+	tftp_filesize = from_env("filesize");
+
+	if (!tftp_filesize)
+		return -ENOENT;
+
+	if (strict_strtoul(tftp_filesize, 16, &config_file_size) < 0)
+		return -EINVAL;
+
+	buf = map_sysmem(file_addr + config_file_size, 1);
+	*buf = '\0';
+	unmap_sysmem(buf);
+
+	return 1;
+}
+
+#define PXELINUX_DIR "pxelinux.cfg/"
+
+/*
+ * Retrieves a file in the 'pxelinux.cfg' folder. Since this uses get_pxe_file
+ * to do the hard work, the location of the 'pxelinux.cfg' folder is generated
+ * from the bootfile path, as described above.
+ *
+ * Returns 1 on success or < 0 on error.
+ */
+int get_pxelinux_path(cmd_tbl_t *cmdtp, const char *file,
+		      unsigned long pxefile_addr_r)
+{
+	size_t base_len = strlen(PXELINUX_DIR);
+	char path[MAX_TFTP_PATH_LEN + 1];
+
+	if (base_len + strlen(file) > MAX_TFTP_PATH_LEN) {
+		printf("path (%s%s) too long, skipping\n",
+		       PXELINUX_DIR, file);
+		return -ENAMETOOLONG;
+	}
+
+	sprintf(path, PXELINUX_DIR "%s", file);
+
+	return get_pxe_file(cmdtp, path, pxefile_addr_r);
+}
+
+/*
+ * Wrapper to make it easier to store the file at file_path in the location
+ * specified by envaddr_name. file_path will be joined to the bootfile path,
+ * if any is specified.
+ *
+ * Returns 1 on success or < 0 on error.
+ */
+static int get_relfile_envaddr(cmd_tbl_t *cmdtp, const char *file_path,
+			       const char *envaddr_name)
+{
+	unsigned long file_addr;
+	char *envaddr;
+
+	envaddr = from_env(envaddr_name);
+
+	if (!envaddr)
+		return -ENOENT;
+
+	if (strict_strtoul(envaddr, 16, &file_addr) < 0)
+		return -EINVAL;
+
+	return get_relfile(cmdtp, file_path, file_addr);
+}
+
+/*
+ * Allocates memory for and initializes a pxe_label. This uses malloc, so the
+ * result must be free()'d to reclaim the memory.
+ *
+ * Returns NULL if malloc fails.
+ */
+static struct pxe_label *label_create(void)
+{
+	struct pxe_label *label;
+
+	label = malloc(sizeof(struct pxe_label));
+
+	if (!label)
+		return NULL;
+
+	memset(label, 0, sizeof(struct pxe_label));
+
+	return label;
+}
+
+/*
+ * Free the memory used by a pxe_label, including that used by its name,
+ * kernel, append and initrd members, if they're non NULL.
+ *
+ * So - be sure to only use dynamically allocated memory for the members of
+ * the pxe_label struct, unless you want to clean it up first. These are
+ * currently only created by the pxe file parsing code.
+ */
+static void label_destroy(struct pxe_label *label)
+{
+	if (label->name)
+		free(label->name);
+
+	if (label->kernel)
+		free(label->kernel);
+
+	if (label->config)
+		free(label->config);
+
+	if (label->append)
+		free(label->append);
+
+	if (label->initrd)
+		free(label->initrd);
+
+	if (label->fdt)
+		free(label->fdt);
+
+	if (label->fdtdir)
+		free(label->fdtdir);
+
+	free(label);
+}
+
+/*
+ * Print a label and its string members if they're defined.
+ *
+ * This is passed as a callback to the menu code for displaying each
+ * menu entry.
+ */
+static void label_print(void *data)
+{
+	struct pxe_label *label = data;
+	const char *c = label->menu ? label->menu : label->name;
+
+	printf("%s:\t%s\n", label->num, c);
+}
+
+/*
+ * Boot a label that specified 'localboot'. This requires that the 'localcmd'
+ * environment variable is defined. Its contents will be executed as U-Boot
+ * command.  If the label specified an 'append' line, its contents will be
+ * used to overwrite the contents of the 'bootargs' environment variable prior
+ * to running 'localcmd'.
+ *
+ * Returns 1 on success or < 0 on error.
+ */
+static int label_localboot(struct pxe_label *label)
+{
+	char *localcmd;
+
+	localcmd = from_env("localcmd");
+
+	if (!localcmd)
+		return -ENOENT;
+
+	if (label->append) {
+		char bootargs[CONFIG_SYS_CBSIZE];
+
+		cli_simple_process_macros(label->append, bootargs);
+		env_set("bootargs", bootargs);
+	}
+
+	debug("running: %s\n", localcmd);
+
+	return run_command_list(localcmd, strlen(localcmd), 0);
+}
+
+/*
+ * Boot according to the contents of a pxe_label.
+ *
+ * If we can't boot for any reason, we return.  A successful boot never
+ * returns.
+ *
+ * The kernel will be stored in the location given by the 'kernel_addr_r'
+ * environment variable.
+ *
+ * If the label specifies an initrd file, it will be stored in the location
+ * given by the 'ramdisk_addr_r' environment variable.
+ *
+ * If the label specifies an 'append' line, its contents will overwrite that
+ * of the 'bootargs' environment variable.
+ */
+static int label_boot(cmd_tbl_t *cmdtp, struct pxe_label *label)
+{
+	char *bootm_argv[] = { "bootm", NULL, NULL, NULL, NULL };
+	char initrd_str[28];
+	char mac_str[29] = "";
+	char ip_str[68] = "";
+	char *fit_addr = NULL;
+	int bootm_argc = 2;
+	int len = 0;
+	ulong kernel_addr;
+	void *buf;
+
+	label_print(label);
+
+	label->attempted = 1;
+
+	if (label->localboot) {
+		if (label->localboot_val >= 0)
+			label_localboot(label);
+		return 0;
+	}
+
+	if (!label->kernel) {
+		printf("No kernel given, skipping %s\n",
+		       label->name);
+		return 1;
+	}
+
+	if (label->initrd) {
+		if (get_relfile_envaddr(cmdtp, label->initrd, "ramdisk_addr_r") < 0) {
+			printf("Skipping %s for failure retrieving initrd\n",
+			       label->name);
+			return 1;
+		}
+
+		bootm_argv[2] = initrd_str;
+		strncpy(bootm_argv[2], env_get("ramdisk_addr_r"), 18);
+		strcat(bootm_argv[2], ":");
+		strncat(bootm_argv[2], env_get("filesize"), 9);
+		bootm_argc = 3;
+	}
+
+	if (get_relfile_envaddr(cmdtp, label->kernel, "kernel_addr_r") < 0) {
+		printf("Skipping %s for failure retrieving kernel\n",
+		       label->name);
+		return 1;
+	}
+
+	if (label->ipappend & 0x1) {
+		sprintf(ip_str, " ip=%s:%s:%s:%s",
+			env_get("ipaddr"), env_get("serverip"),
+			env_get("gatewayip"), env_get("netmask"));
+	}
+
+#ifdef CONFIG_CMD_NET
+	if (label->ipappend & 0x2) {
+		int err;
+
+		strcpy(mac_str, " BOOTIF=");
+		err = format_mac_pxe(mac_str + 8, sizeof(mac_str) - 8);
+		if (err < 0)
+			mac_str[0] = '\0';
+	}
+#endif
+
+	if ((label->ipappend & 0x3) || label->append) {
+		char bootargs[CONFIG_SYS_CBSIZE] = "";
+		char finalbootargs[CONFIG_SYS_CBSIZE];
+
+		if (strlen(label->append ?: "") +
+		    strlen(ip_str) + strlen(mac_str) + 1 > sizeof(bootargs)) {
+			printf("bootarg overflow %zd+%zd+%zd+1 > %zd\n",
+			       strlen(label->append ?: ""),
+			       strlen(ip_str), strlen(mac_str),
+			       sizeof(bootargs));
+			return 1;
+		}
+
+		if (label->append)
+			strncpy(bootargs, label->append, sizeof(bootargs));
+
+		strcat(bootargs, ip_str);
+		strcat(bootargs, mac_str);
+
+		cli_simple_process_macros(bootargs, finalbootargs);
+		env_set("bootargs", finalbootargs);
+		printf("append: %s\n", finalbootargs);
+	}
+
+	bootm_argv[1] = env_get("kernel_addr_r");
+	/* for FIT, append the configuration identifier */
+	if (label->config) {
+		int len = strlen(bootm_argv[1]) + strlen(label->config) + 1;
+
+		fit_addr = malloc(len);
+		if (!fit_addr) {
+			printf("malloc fail (FIT address)\n");
+			return 1;
+		}
+		snprintf(fit_addr, len, "%s%s", bootm_argv[1], label->config);
+		bootm_argv[1] = fit_addr;
+	}
+
+	/*
+	 * fdt usage is optional:
+	 * It handles the following scenarios. All scenarios are exclusive
+	 *
+	 * Scenario 1: If fdt_addr_r specified and "fdt" label is defined in
+	 * pxe file, retrieve fdt blob from server. Pass fdt_addr_r to bootm,
+	 * and adjust argc appropriately.
+	 *
+	 * Scenario 2: If there is an fdt_addr specified, pass it along to
+	 * bootm, and adjust argc appropriately.
+	 *
+	 * Scenario 3: fdt blob is not available.
+	 */
+	bootm_argv[3] = env_get("fdt_addr_r");
+
+	/* if fdt label is defined then get fdt from server */
+	if (bootm_argv[3]) {
+		char *fdtfile = NULL;
+		char *fdtfilefree = NULL;
+
+		if (label->fdt) {
+			fdtfile = label->fdt;
+		} else if (label->fdtdir) {
+			char *f1, *f2, *f3, *f4, *slash;
+
+			f1 = env_get("fdtfile");
+			if (f1) {
+				f2 = "";
+				f3 = "";
+				f4 = "";
+			} else {
+				/*
+				 * For complex cases where this code doesn't
+				 * generate the correct filename, the board
+				 * code should set $fdtfile during early boot,
+				 * or the boot scripts should set $fdtfile
+				 * before invoking "pxe" or "sysboot".
+				 */
+				f1 = env_get("soc");
+				f2 = "-";
+				f3 = env_get("board");
+				f4 = ".dtb";
+			}
+
+			len = strlen(label->fdtdir);
+			if (!len)
+				slash = "./";
+			else if (label->fdtdir[len - 1] != '/')
+				slash = "/";
+			else
+				slash = "";
+
+			len = strlen(label->fdtdir) + strlen(slash) +
+				strlen(f1) + strlen(f2) + strlen(f3) +
+				strlen(f4) + 1;
+			fdtfilefree = malloc(len);
+			if (!fdtfilefree) {
+				printf("malloc fail (FDT filename)\n");
+				goto cleanup;
+			}
+
+			snprintf(fdtfilefree, len, "%s%s%s%s%s%s",
+				 label->fdtdir, slash, f1, f2, f3, f4);
+			fdtfile = fdtfilefree;
+		}
+
+		if (fdtfile) {
+			int err = get_relfile_envaddr(cmdtp, fdtfile,
+						      "fdt_addr_r");
+
+			free(fdtfilefree);
+			if (err < 0) {
+				printf("Skipping %s for failure retrieving fdt\n",
+				       label->name);
+				goto cleanup;
+			}
+		} else {
+			bootm_argv[3] = NULL;
+		}
+	}
+
+	if (!bootm_argv[3])
+		bootm_argv[3] = env_get("fdt_addr");
+
+	if (bootm_argv[3]) {
+		if (!bootm_argv[2])
+			bootm_argv[2] = "-";
+		bootm_argc = 4;
+	}
+
+	kernel_addr = genimg_get_kernel_addr(bootm_argv[1]);
+	buf = map_sysmem(kernel_addr, 0);
+	/* Try bootm for legacy and FIT format image */
+	if (genimg_get_format(buf) != IMAGE_FORMAT_INVALID)
+		do_bootm(cmdtp, 0, bootm_argc, bootm_argv);
+#ifdef CONFIG_CMD_BOOTI
+	/* Try booting an AArch64 Linux kernel image */
+	else
+		do_booti(cmdtp, 0, bootm_argc, bootm_argv);
+#elif defined(CONFIG_CMD_BOOTZ)
+	/* Try booting a Image */
+	else
+		do_bootz(cmdtp, 0, bootm_argc, bootm_argv);
+#endif
+	unmap_sysmem(buf);
+
+cleanup:
+	if (fit_addr)
+		free(fit_addr);
+	return 1;
+}
+
+/*
+ * Tokens for the pxe file parser.
+ */
+enum token_type {
+	T_EOL,
+	T_STRING,
+	T_EOF,
+	T_MENU,
+	T_TITLE,
+	T_TIMEOUT,
+	T_LABEL,
+	T_KERNEL,
+	T_LINUX,
+	T_APPEND,
+	T_INITRD,
+	T_LOCALBOOT,
+	T_DEFAULT,
+	T_PROMPT,
+	T_INCLUDE,
+	T_FDT,
+	T_FDTDIR,
+	T_ONTIMEOUT,
+	T_IPAPPEND,
+	T_BACKGROUND,
+	T_INVALID
+};
+
+/*
+ * A token - given by a value and a type.
+ */
+struct token {
+	char *val;
+	enum token_type type;
+};
+
+/*
+ * Keywords recognized.
+ */
+static const struct token keywords[] = {
+	{"menu", T_MENU},
+	{"title", T_TITLE},
+	{"timeout", T_TIMEOUT},
+	{"default", T_DEFAULT},
+	{"prompt", T_PROMPT},
+	{"label", T_LABEL},
+	{"kernel", T_KERNEL},
+	{"linux", T_LINUX},
+	{"localboot", T_LOCALBOOT},
+	{"append", T_APPEND},
+	{"initrd", T_INITRD},
+	{"include", T_INCLUDE},
+	{"devicetree", T_FDT},
+	{"fdt", T_FDT},
+	{"devicetreedir", T_FDTDIR},
+	{"fdtdir", T_FDTDIR},
+	{"ontimeout", T_ONTIMEOUT,},
+	{"ipappend", T_IPAPPEND,},
+	{"background", T_BACKGROUND,},
+	{NULL, T_INVALID}
+};
+
+/*
+ * Since pxe(linux) files don't have a token to identify the start of a
+ * literal, we have to keep track of when we're in a state where a literal is
+ * expected vs when we're in a state a keyword is expected.
+ */
+enum lex_state {
+	L_NORMAL = 0,
+	L_KEYWORD,
+	L_SLITERAL
+};
+
+/*
+ * get_string retrieves a string from *p and stores it as a token in
+ * *t.
+ *
+ * get_string used for scanning both string literals and keywords.
+ *
+ * Characters from *p are copied into t-val until a character equal to
+ * delim is found, or a NUL byte is reached. If delim has the special value of
+ * ' ', any whitespace character will be used as a delimiter.
+ *
+ * If lower is unequal to 0, uppercase characters will be converted to
+ * lowercase in the result. This is useful to make keywords case
+ * insensitive.
+ *
+ * The location of *p is updated to point to the first character after the end
+ * of the token - the ending delimiter.
+ *
+ * On success, the new value of t->val is returned. Memory for t->val is
+ * allocated using malloc and must be free()'d to reclaim it.  If insufficient
+ * memory is available, NULL is returned.
+ */
+static char *get_string(char **p, struct token *t, char delim, int lower)
+{
+	char *b, *e;
+	size_t len, i;
+
+	/*
+	 * b and e both start at the beginning of the input stream.
+	 *
+	 * e is incremented until we find the ending delimiter, or a NUL byte
+	 * is reached. Then, we take e - b to find the length of the token.
+	 */
+	b = *p;
+	e = *p;
+
+	while (*e) {
+		if ((delim == ' ' && isspace(*e)) || delim == *e)
+			break;
+		e++;
+	}
+
+	len = e - b;
+
+	/*
+	 * Allocate memory to hold the string, and copy it in, converting
+	 * characters to lowercase if lower is != 0.
+	 */
+	t->val = malloc(len + 1);
+	if (!t->val)
+		return NULL;
+
+	for (i = 0; i < len; i++, b++) {
+		if (lower)
+			t->val[i] = tolower(*b);
+		else
+			t->val[i] = *b;
+	}
+
+	t->val[len] = '\0';
+
+	/*
+	 * Update *p so the caller knows where to continue scanning.
+	 */
+	*p = e;
+
+	t->type = T_STRING;
+
+	return t->val;
+}
+
+/*
+ * Populate a keyword token with a type and value.
+ */
+static void get_keyword(struct token *t)
+{
+	int i;
+
+	for (i = 0; keywords[i].val; i++) {
+		if (!strcmp(t->val, keywords[i].val)) {
+			t->type = keywords[i].type;
+			break;
+		}
+	}
+}
+
+/*
+ * Get the next token.  We have to keep track of which state we're in to know
+ * if we're looking to get a string literal or a keyword.
+ *
+ * *p is updated to point at the first character after the current token.
+ */
+static void get_token(char **p, struct token *t, enum lex_state state)
+{
+	char *c = *p;
+
+	t->type = T_INVALID;
+
+	/* eat non EOL whitespace */
+	while (isblank(*c))
+		c++;
+
+	/*
+	 * eat comments. note that string literals can't begin with #, but
+	 * can contain a # after their first character.
+	 */
+	if (*c == '#') {
+		while (*c && *c != '\n')
+			c++;
+	}
+
+	if (*c == '\n') {
+		t->type = T_EOL;
+		c++;
+	} else if (*c == '\0') {
+		t->type = T_EOF;
+		c++;
+	} else if (state == L_SLITERAL) {
+		get_string(&c, t, '\n', 0);
+	} else if (state == L_KEYWORD) {
+		/*
+		 * when we expect a keyword, we first get the next string
+		 * token delimited by whitespace, and then check if it
+		 * matches a keyword in our keyword list. if it does, it's
+		 * converted to a keyword token of the appropriate type, and
+		 * if not, it remains a string token.
+		 */
+		get_string(&c, t, ' ', 1);
+		get_keyword(t);
+	}
+
+	*p = c;
+}
+
+/*
+ * Increment *c until we get to the end of the current line, or EOF.
+ */
+static void eol_or_eof(char **c)
+{
+	while (**c && **c != '\n')
+		(*c)++;
+}
+
+/*
+ * All of these parse_* functions share some common behavior.
+ *
+ * They finish with *c pointing after the token they parse, and return 1 on
+ * success, or < 0 on error.
+ */
+
+/*
+ * Parse a string literal and store a pointer it at *dst. String literals
+ * terminate at the end of the line.
+ */
+static int parse_sliteral(char **c, char **dst)
+{
+	struct token t;
+	char *s = *c;
+
+	get_token(c, &t, L_SLITERAL);
+
+	if (t.type != T_STRING) {
+		printf("Expected string literal: %.*s\n", (int)(*c - s), s);
+		return -EINVAL;
+	}
+
+	*dst = t.val;
+
+	return 1;
+}
+
+/*
+ * Parse a base 10 (unsigned) integer and store it at *dst.
+ */
+static int parse_integer(char **c, int *dst)
+{
+	struct token t;
+	char *s = *c;
+
+	get_token(c, &t, L_SLITERAL);
+
+	if (t.type != T_STRING) {
+		printf("Expected string: %.*s\n", (int)(*c - s), s);
+		return -EINVAL;
+	}
+
+	*dst = simple_strtol(t.val, NULL, 10);
+
+	free(t.val);
+
+	return 1;
+}
+
+static int parse_pxefile_top(cmd_tbl_t *cmdtp, char *p, unsigned long base,
+			     struct pxe_menu *cfg, int nest_level);
+
+/*
+ * Parse an include statement, and retrieve and parse the file it mentions.
+ *
+ * base should point to a location where it's safe to store the file, and
+ * nest_level should indicate how many nested includes have occurred. For this
+ * include, nest_level has already been incremented and doesn't need to be
+ * incremented here.
+ */
+static int handle_include(cmd_tbl_t *cmdtp, char **c, unsigned long base,
+			  struct pxe_menu *cfg, int nest_level)
+{
+	char *include_path;
+	char *s = *c;
+	int err;
+	char *buf;
+	int ret;
+
+	err = parse_sliteral(c, &include_path);
+
+	if (err < 0) {
+		printf("Expected include path: %.*s\n", (int)(*c - s), s);
+		return err;
+	}
+
+	err = get_pxe_file(cmdtp, include_path, base);
+
+	if (err < 0) {
+		printf("Couldn't retrieve %s\n", include_path);
+		return err;
+	}
+
+	buf = map_sysmem(base, 0);
+	ret = parse_pxefile_top(cmdtp, buf, base, cfg, nest_level);
+	unmap_sysmem(buf);
+
+	return ret;
+}
+
+/*
+ * Parse lines that begin with 'menu'.
+ *
+ * base and nest are provided to handle the 'menu include' case.
+ *
+ * base should point to a location where it's safe to store the included file.
+ *
+ * nest_level should be 1 when parsing the top level pxe file, 2 when parsing
+ * a file it includes, 3 when parsing a file included by that file, and so on.
+ */
+static int parse_menu(cmd_tbl_t *cmdtp, char **c, struct pxe_menu *cfg,
+		      unsigned long base, int nest_level)
+{
+	struct token t;
+	char *s = *c;
+	int err = 0;
+
+	get_token(c, &t, L_KEYWORD);
+
+	switch (t.type) {
+	case T_TITLE:
+		err = parse_sliteral(c, &cfg->title);
+
+		break;
+
+	case T_INCLUDE:
+		err = handle_include(cmdtp, c, base, cfg, nest_level + 1);
+		break;
+
+	case T_BACKGROUND:
+		err = parse_sliteral(c, &cfg->bmp);
+		break;
+
+	default:
+		printf("Ignoring malformed menu command: %.*s\n",
+		       (int)(*c - s), s);
+	}
+
+	if (err < 0)
+		return err;
+
+	eol_or_eof(c);
+
+	return 1;
+}
+
+/*
+ * Handles parsing a 'menu line' when we're parsing a label.
+ */
+static int parse_label_menu(char **c, struct pxe_menu *cfg,
+			    struct pxe_label *label)
+{
+	struct token t;
+	char *s;
+
+	s = *c;
+
+	get_token(c, &t, L_KEYWORD);
+
+	switch (t.type) {
+	case T_DEFAULT:
+		if (!cfg->default_label)
+			cfg->default_label = strdup(label->name);
+
+		if (!cfg->default_label)
+			return -ENOMEM;
+
+		break;
+	case T_LABEL:
+		parse_sliteral(c, &label->menu);
+		break;
+	default:
+		printf("Ignoring malformed menu command: %.*s\n",
+		       (int)(*c - s), s);
+	}
+
+	eol_or_eof(c);
+
+	return 0;
+}
+
+/*
+ * Handles parsing a 'kernel' label.
+ * expecting "filename" or "<fit_filename>#cfg"
+ */
+static int parse_label_kernel(char **c, struct pxe_label *label)
+{
+	char *s;
+	int err;
+
+	err = parse_sliteral(c, &label->kernel);
+	if (err < 0)
+		return err;
+
+	s = strstr(label->kernel, "#");
+	if (!s)
+		return 1;
+
+	label->config = malloc(strlen(s) + 1);
+	if (!label->config)
+		return -ENOMEM;
+
+	strcpy(label->config, s);
+	*s = 0;
+
+	return 1;
+}
+
+/*
+ * Parses a label and adds it to the list of labels for a menu.
+ *
+ * A label ends when we either get to the end of a file, or
+ * get some input we otherwise don't have a handler defined
+ * for.
+ *
+ */
+static int parse_label(char **c, struct pxe_menu *cfg)
+{
+	struct token t;
+	int len;
+	char *s = *c;
+	struct pxe_label *label;
+	int err;
+
+	label = label_create();
+	if (!label)
+		return -ENOMEM;
+
+	err = parse_sliteral(c, &label->name);
+	if (err < 0) {
+		printf("Expected label name: %.*s\n", (int)(*c - s), s);
+		label_destroy(label);
+		return -EINVAL;
+	}
+
+	list_add_tail(&label->list, &cfg->labels);
+
+	while (1) {
+		s = *c;
+		get_token(c, &t, L_KEYWORD);
+
+		err = 0;
+		switch (t.type) {
+		case T_MENU:
+			err = parse_label_menu(c, cfg, label);
+			break;
+
+		case T_KERNEL:
+		case T_LINUX:
+			err = parse_label_kernel(c, label);
+			break;
+
+		case T_APPEND:
+			err = parse_sliteral(c, &label->append);
+			if (label->initrd)
+				break;
+			s = strstr(label->append, "initrd=");
+			if (!s)
+				break;
+			s += 7;
+			len = (int)(strchr(s, ' ') - s);
+			label->initrd = malloc(len + 1);
+			strncpy(label->initrd, s, len);
+			label->initrd[len] = '\0';
+
+			break;
+
+		case T_INITRD:
+			if (!label->initrd)
+				err = parse_sliteral(c, &label->initrd);
+			break;
+
+		case T_FDT:
+			if (!label->fdt)
+				err = parse_sliteral(c, &label->fdt);
+			break;
+
+		case T_FDTDIR:
+			if (!label->fdtdir)
+				err = parse_sliteral(c, &label->fdtdir);
+			break;
+
+		case T_LOCALBOOT:
+			label->localboot = 1;
+			err = parse_integer(c, &label->localboot_val);
+			break;
+
+		case T_IPAPPEND:
+			err = parse_integer(c, &label->ipappend);
+			break;
+
+		case T_EOL:
+			break;
+
+		default:
+			/*
+			 * put the token back! we don't want it - it's the end
+			 * of a label and whatever token this is, it's
+			 * something for the menu level context to handle.
+			 */
+			*c = s;
+			return 1;
+		}
+
+		if (err < 0)
+			return err;
+	}
+}
+
+/*
+ * This 16 comes from the limit pxelinux imposes on nested includes.
+ *
+ * There is no reason at all we couldn't do more, but some limit helps prevent
+ * infinite (until crash occurs) recursion if a file tries to include itself.
+ */
+#define MAX_NEST_LEVEL 16
+
+/*
+ * Entry point for parsing a menu file. nest_level indicates how many times
+ * we've nested in includes.  It will be 1 for the top level menu file.
+ *
+ * Returns 1 on success, < 0 on error.
+ */
+static int parse_pxefile_top(cmd_tbl_t *cmdtp, char *p, unsigned long base,
+			     struct pxe_menu *cfg, int nest_level)
+{
+	struct token t;
+	char *s, *b, *label_name;
+	int err;
+
+	b = p;
+
+	if (nest_level > MAX_NEST_LEVEL) {
+		printf("Maximum nesting (%d) exceeded\n", MAX_NEST_LEVEL);
+		return -EMLINK;
+	}
+
+	while (1) {
+		s = p;
+
+		get_token(&p, &t, L_KEYWORD);
+
+		err = 0;
+		switch (t.type) {
+		case T_MENU:
+			cfg->prompt = 1;
+			err = parse_menu(cmdtp, &p, cfg,
+					 base + ALIGN(strlen(b) + 1, 4),
+					 nest_level);
+			break;
+
+		case T_TIMEOUT:
+			err = parse_integer(&p, &cfg->timeout);
+			break;
+
+		case T_LABEL:
+			err = parse_label(&p, cfg);
+			break;
+
+		case T_DEFAULT:
+		case T_ONTIMEOUT:
+			err = parse_sliteral(&p, &label_name);
+
+			if (label_name) {
+				if (cfg->default_label)
+					free(cfg->default_label);
+
+				cfg->default_label = label_name;
+			}
+
+			break;
+
+		case T_INCLUDE:
+			err = handle_include(cmdtp, &p,
+					     base + ALIGN(strlen(b), 4), cfg,
+					     nest_level + 1);
+			break;
+
+		case T_PROMPT:
+			eol_or_eof(&p);
+			break;
+
+		case T_EOL:
+			break;
+
+		case T_EOF:
+			return 1;
+
+		default:
+			printf("Ignoring unknown command: %.*s\n",
+			       (int)(p - s), s);
+			eol_or_eof(&p);
+		}
+
+		if (err < 0)
+			return err;
+	}
+}
+
+/*
+ * Free the memory used by a pxe_menu and its labels.
+ */
+void destroy_pxe_menu(struct pxe_menu *cfg)
+{
+	struct list_head *pos, *n;
+	struct pxe_label *label;
+
+	if (cfg->title)
+		free(cfg->title);
+
+	if (cfg->default_label)
+		free(cfg->default_label);
+
+	list_for_each_safe(pos, n, &cfg->labels) {
+		label = list_entry(pos, struct pxe_label, list);
+
+		label_destroy(label);
+	}
+
+	free(cfg);
+}
+
+/*
+ * Entry point for parsing a pxe file. This is only used for the top level
+ * file.
+ *
+ * Returns NULL if there is an error, otherwise, returns a pointer to a
+ * pxe_menu struct populated with the results of parsing the pxe file (and any
+ * files it includes). The resulting pxe_menu struct can be free()'d by using
+ * the destroy_pxe_menu() function.
+ */
+struct pxe_menu *parse_pxefile(cmd_tbl_t *cmdtp, unsigned long menucfg)
+{
+	struct pxe_menu *cfg;
+	char *buf;
+	int r;
+
+	cfg = malloc(sizeof(struct pxe_menu));
+
+	if (!cfg)
+		return NULL;
+
+	memset(cfg, 0, sizeof(struct pxe_menu));
+
+	INIT_LIST_HEAD(&cfg->labels);
+
+	buf = map_sysmem(menucfg, 0);
+	r = parse_pxefile_top(cmdtp, buf, menucfg, cfg, 1);
+	unmap_sysmem(buf);
+
+	if (r < 0) {
+		destroy_pxe_menu(cfg);
+		return NULL;
+	}
+
+	return cfg;
+}
+
+/*
+ * Converts a pxe_menu struct into a menu struct for use with U-Boot's generic
+ * menu code.
+ */
+static struct menu *pxe_menu_to_menu(struct pxe_menu *cfg)
+{
+	struct pxe_label *label;
+	struct list_head *pos;
+	struct menu *m;
+	int err;
+	int i = 1;
+	char *default_num = NULL;
+
+	/*
+	 * Create a menu and add items for all the labels.
+	 */
+	m = menu_create(cfg->title, DIV_ROUND_UP(cfg->timeout, 10),
+			cfg->prompt, label_print, NULL, NULL);
+
+	if (!m)
+		return NULL;
+
+	list_for_each(pos, &cfg->labels) {
+		label = list_entry(pos, struct pxe_label, list);
+
+		sprintf(label->num, "%d", i++);
+		if (menu_item_add(m, label->num, label) != 1) {
+			menu_destroy(m);
+			return NULL;
+		}
+		if (cfg->default_label &&
+		    (strcmp(label->name, cfg->default_label) == 0))
+			default_num = label->num;
+	}
+
+	/*
+	 * After we've created items for each label in the menu, set the
+	 * menu's default label if one was specified.
+	 */
+	if (default_num) {
+		err = menu_default_set(m, default_num);
+		if (err != 1) {
+			if (err != -ENOENT) {
+				menu_destroy(m);
+				return NULL;
+			}
+
+			printf("Missing default: %s\n", cfg->default_label);
+		}
+	}
+
+	return m;
+}
+
+/*
+ * Try to boot any labels we have yet to attempt to boot.
+ */
+static void boot_unattempted_labels(cmd_tbl_t *cmdtp, struct pxe_menu *cfg)
+{
+	struct list_head *pos;
+	struct pxe_label *label;
+
+	list_for_each(pos, &cfg->labels) {
+		label = list_entry(pos, struct pxe_label, list);
+
+		if (!label->attempted)
+			label_boot(cmdtp, label);
+	}
+}
+
+/*
+ * Boot the system as prescribed by a pxe_menu.
+ *
+ * Use the menu system to either get the user's choice or the default, based
+ * on config or user input.  If there is no default or user's choice,
+ * attempted to boot labels in the order they were given in pxe files.
+ * If the default or user's choice fails to boot, attempt to boot other
+ * labels in the order they were given in pxe files.
+ *
+ * If this function returns, there weren't any labels that successfully
+ * booted, or the user interrupted the menu selection via ctrl+c.
+ */
+void handle_pxe_menu(cmd_tbl_t *cmdtp, struct pxe_menu *cfg)
+{
+	void *choice;
+	struct menu *m;
+	int err;
+
+#ifdef CONFIG_CMD_BMP
+	/* display BMP if available */
+	if (cfg->bmp) {
+		if (get_relfile(cmdtp, cfg->bmp, load_addr)) {
+			run_command("cls", 0);
+			bmp_display(load_addr,
+				    BMP_ALIGN_CENTER, BMP_ALIGN_CENTER);
+		} else {
+			printf("Skipping background bmp %s for failure\n",
+			       cfg->bmp);
+		}
+	}
+#endif
+
+	m = pxe_menu_to_menu(cfg);
+	if (!m)
+		return;
+
+	err = menu_get_choice(m, &choice);
+
+	menu_destroy(m);
+
+	/*
+	 * err == 1 means we got a choice back from menu_get_choice.
+	 *
+	 * err == -ENOENT if the menu was setup to select the default but no
+	 * default was set. in that case, we should continue trying to boot
+	 * labels that haven't been attempted yet.
+	 *
+	 * otherwise, the user interrupted or there was some other error and
+	 * we give up.
+	 */
+
+	if (err == 1) {
+		err = label_boot(cmdtp, choice);
+		if (!err)
+			return;
+	} else if (err != -ENOENT) {
+		return;
+	}
+
+	boot_unattempted_labels(cmdtp, cfg);
+}
diff --git a/cmd/pxe_utils.h b/cmd/pxe_utils.h
new file mode 100644
index 0000000..a38ac81
--- /dev/null
+++ b/cmd/pxe_utils.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef __PXE_UTILS_H
+#define __PXE_UTILS_H
+
+/*
+ * A note on the pxe file parser.
+ *
+ * We're parsing files that use syslinux grammar, which has a few quirks.
+ * String literals must be recognized based on context - there is no
+ * quoting or escaping support. There's also nothing to explicitly indicate
+ * when a label section completes. We deal with that by ending a label
+ * section whenever we see a line that doesn't include.
+ *
+ * As with the syslinux family, this same file format could be reused in the
+ * future for non pxe purposes. The only action it takes during parsing that
+ * would throw this off is handling of include files. It assumes we're using
+ * pxe, and does a tftp download of a file listed as an include file in the
+ * middle of the parsing operation. That could be handled by refactoring it to
+ * take a 'include file getter' function.
+ */
+
+/*
+ * Describes a single label given in a pxe file.
+ *
+ * Create these with the 'label_create' function given below.
+ *
+ * name - the name of the menu as given on the 'menu label' line.
+ * kernel - the path to the kernel file to use for this label.
+ * append - kernel command line to use when booting this label
+ * initrd - path to the initrd to use for this label.
+ * attempted - 0 if we haven't tried to boot this label, 1 if we have.
+ * localboot - 1 if this label specified 'localboot', 0 otherwise.
+ * list - lets these form a list, which a pxe_menu struct will hold.
+ */
+struct pxe_label {
+	char num[4];
+	char *name;
+	char *menu;
+	char *kernel;
+	char *config;
+	char *append;
+	char *initrd;
+	char *fdt;
+	char *fdtdir;
+	int ipappend;
+	int attempted;
+	int localboot;
+	int localboot_val;
+	struct list_head list;
+};
+
+/*
+ * Describes a pxe menu as given via pxe files.
+ *
+ * title - the name of the menu as given by a 'menu title' line.
+ * default_label - the name of the default label, if any.
+ * bmp - the bmp file name which is displayed in background
+ * timeout - time in tenths of a second to wait for a user key-press before
+ *           booting the default label.
+ * prompt - if 0, don't prompt for a choice unless the timeout period is
+ *          interrupted.  If 1, always prompt for a choice regardless of
+ *          timeout.
+ * labels - a list of labels defined for the menu.
+ */
+struct pxe_menu {
+	char *title;
+	char *default_label;
+	char *bmp;
+	int timeout;
+	int prompt;
+	struct list_head labels;
+};
+
+extern bool is_pxe;
+
+extern int (*do_getfile)(cmd_tbl_t *cmdtp, const char *file_path,
+			 char *file_addr);
+void destroy_pxe_menu(struct pxe_menu *cfg);
+int get_pxe_file(cmd_tbl_t *cmdtp, const char *file_path,
+		 unsigned long file_addr);
+int get_pxelinux_path(cmd_tbl_t *cmdtp, const char *file,
+		      unsigned long pxefile_addr_r);
+void handle_pxe_menu(cmd_tbl_t *cmdtp, struct pxe_menu *cfg);
+struct pxe_menu *parse_pxefile(cmd_tbl_t *cmdtp, unsigned long menucfg);
+int format_mac_pxe(char *outbuf, size_t outbuf_len);
+
+#endif /* __PXE_UTILS_H */
diff --git a/cmd/sysboot.c b/cmd/sysboot.c
new file mode 100644
index 0000000..793d67d
--- /dev/null
+++ b/cmd/sysboot.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <common.h>
+#include <command.h>
+#include <env.h>
+#include <fs.h>
+#include "pxe_utils.h"
+
+static char *fs_argv[5];
+
+static int do_get_ext2(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr)
+{
+#ifdef CONFIG_CMD_EXT2
+	fs_argv[0] = "ext2load";
+	fs_argv[3] = file_addr;
+	fs_argv[4] = (void *)file_path;
+
+	if (!do_ext2load(cmdtp, 0, 5, fs_argv))
+		return 1;
+#endif
+	return -ENOENT;
+}
+
+static int do_get_fat(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr)
+{
+#ifdef CONFIG_CMD_FAT
+	fs_argv[0] = "fatload";
+	fs_argv[3] = file_addr;
+	fs_argv[4] = (void *)file_path;
+
+	if (!do_fat_fsload(cmdtp, 0, 5, fs_argv))
+		return 1;
+#endif
+	return -ENOENT;
+}
+
+static int do_get_any(cmd_tbl_t *cmdtp, const char *file_path, char *file_addr)
+{
+#ifdef CONFIG_CMD_FS_GENERIC
+	fs_argv[0] = "load";
+	fs_argv[3] = file_addr;
+	fs_argv[4] = (void *)file_path;
+
+	if (!do_load(cmdtp, 0, 5, fs_argv, FS_TYPE_ANY))
+		return 1;
+#endif
+	return -ENOENT;
+}
+
+/*
+ * Boots a system using a local disk syslinux/extlinux file
+ *
+ * Returns 0 on success, 1 on error.
+ */
+static int do_sysboot(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+	unsigned long pxefile_addr_r;
+	struct pxe_menu *cfg;
+	char *pxefile_addr_str;
+	char *filename;
+	int prompt = 0;
+
+	is_pxe = false;
+
+	if (argc > 1 && strstr(argv[1], "-p")) {
+		prompt = 1;
+		argc--;
+		argv++;
+	}
+
+	if (argc < 4)
+		return cmd_usage(cmdtp);
+
+	if (argc < 5) {
+		pxefile_addr_str = from_env("pxefile_addr_r");
+		if (!pxefile_addr_str)
+			return 1;
+	} else {
+		pxefile_addr_str = argv[4];
+	}
+
+	if (argc < 6) {
+		filename = env_get("bootfile");
+	} else {
+		filename = argv[5];
+		env_set("bootfile", filename);
+	}
+
+	if (strstr(argv[3], "ext2")) {
+		do_getfile = do_get_ext2;
+	} else if (strstr(argv[3], "fat")) {
+		do_getfile = do_get_fat;
+	} else if (strstr(argv[3], "any")) {
+		do_getfile = do_get_any;
+	} else {
+		printf("Invalid filesystem: %s\n", argv[3]);
+		return 1;
+	}
+	fs_argv[1] = argv[1];
+	fs_argv[2] = argv[2];
+
+	if (strict_strtoul(pxefile_addr_str, 16, &pxefile_addr_r) < 0) {
+		printf("Invalid pxefile address: %s\n", pxefile_addr_str);
+		return 1;
+	}
+
+	if (get_pxe_file(cmdtp, filename, pxefile_addr_r) < 0) {
+		printf("Error reading config file\n");
+		return 1;
+	}
+
+	cfg = parse_pxefile(cmdtp, pxefile_addr_r);
+
+	if (!cfg) {
+		printf("Error parsing config file\n");
+		return 1;
+	}
+
+	if (prompt)
+		cfg->prompt = 1;
+
+	handle_pxe_menu(cmdtp, cfg);
+
+	destroy_pxe_menu(cfg);
+
+	return 0;
+}
+
+U_BOOT_CMD(sysboot, 7, 1, do_sysboot,
+	   "command to get and boot from syslinux files",
+	   "[-p] <interface> <dev[:part]> <ext2|fat|any> [addr] [filename]\n"
+	   "    - load and parse syslinux menu file 'filename' from ext2, fat\n"
+	   "      or any filesystem on 'dev' on 'interface' to address 'addr'"
+);
diff --git a/doc/README.asn1 b/doc/README.asn1
new file mode 100644
index 0000000..1359b93
--- /dev/null
+++ b/doc/README.asn1
@@ -0,0 +1,40 @@
+ASN1
+====
+
+Abstract Syntax Notation One (or ASN1) is a standard by ITU-T and ISO/IEC
+and used as a description language for defining data structure in
+an independent manner.
+Any data described in ASN1 notation can be serialized (or encoded) and
+de-serialized (or decoded) with well-defined encoding rules.
+
+A combination of ASN1 compiler and ASN1 decoder library function will
+provide a function interface for parsing encoded binary into specific
+data structure:
+1) define data structure in a text file (*.asn1)
+2) define "action" routines for specific "tags" defined in (1)
+3) generate bytecode as a C file (*.asn1.[ch]) from *.asn1 file
+   with ASN1 compiler (tools/asn1_compiler)
+4) call a ASN1 decoder (asn1_ber_decoder()) with bytecode and data
+
+Usage of ASN1 compiler
+----------------------
+  asn1_compiler [-v] [-d] <grammar-file> <c-file> <hdr-file>
+
+  <grammar-file>:	ASN1 input file
+  <c-file>:		generated C file
+  <hdr-file>:		generated include file
+
+Usage of ASN1 decoder
+---------------------
+  int asn1_ber_decoder(const struct asn1_decoder *decoder, void *context,
+		       const unsigned char *data, size_t datalen);
+
+  @decoder:		bytecode binary
+  @context:		context for decoder
+  @data:		data to be parsed
+  @datalen:		size of data
+
+
+As of writing this, ASN1 compiler and decoder are used to implement
+X509 certificate parser, pcks7 message parser and RSA public key parser
+for UEFI secure boot.
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 8778cc7..89e71cc 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -7,6 +7,7 @@
 config DM_RTC
 	bool "Enable Driver Model for RTC drivers"
 	depends on DM
+	select LIB_DATE
 	help
 	  Enable drver model for real-time-clock drivers. The RTC uclass
 	  then provides the rtc_get()/rtc_set() interface, delegating to
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index f97a669..e8875ce 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -7,7 +7,6 @@
 obj-$(CONFIG_$(SPL_TPL_)DM_RTC) += rtc-uclass.o
 
 obj-$(CONFIG_RTC_AT91SAM9_RTT) += at91sam9_rtt.o
-obj-y += date.o
 obj-y += rtc-lib.o
 obj-$(CONFIG_RTC_DAVINCI) += davinci.o
 obj-$(CONFIG_RTC_DS1302) += ds1302.o
diff --git a/fs/ubifs/ubifs.c b/fs/ubifs/ubifs.c
index 67a0e8c..1ffdfe0 100644
--- a/fs/ubifs/ubifs.c
+++ b/fs/ubifs/ubifs.c
@@ -18,6 +18,7 @@
 #include "ubifs.h"
 #include <u-boot/zlib.h>
 
+#include <linux/compat.h>
 #include <linux/err.h>
 #include <linux/lzo.h>
 
@@ -70,24 +71,6 @@
 
 
 #ifdef __UBOOT__
-/* from mm/util.c */
-
-/**
- * kmemdup - duplicate region of memory
- *
- * @src: memory region to duplicate
- * @len: memory region length
- * @gfp: GFP mask to use
- */
-void *kmemdup(const void *src, size_t len, gfp_t gfp)
-{
-	void *p;
-
-	p = kmalloc(len, gfp);
-	if (p)
-		memcpy(p, src, len);
-	return p;
-}
 
 struct crypto_comp {
 	int compressor;
diff --git a/include/crypto/internal/rsa.h b/include/crypto/internal/rsa.h
new file mode 100644
index 0000000..e870133
--- /dev/null
+++ b/include/crypto/internal/rsa.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * RSA internal helpers
+ *
+ * Copyright (c) 2015, Intel Corporation
+ * Authors: Tadeusz Struk <tadeusz.struk@intel.com>
+ */
+#ifndef _RSA_HELPER_
+#define _RSA_HELPER_
+#include <linux/types.h>
+
+/**
+ * rsa_key - RSA key structure
+ * @n           : RSA modulus raw byte stream
+ * @e           : RSA public exponent raw byte stream
+ * @d           : RSA private exponent raw byte stream
+ * @p           : RSA prime factor p of n raw byte stream
+ * @q           : RSA prime factor q of n raw byte stream
+ * @dp          : RSA exponent d mod (p - 1) raw byte stream
+ * @dq          : RSA exponent d mod (q - 1) raw byte stream
+ * @qinv        : RSA CRT coefficient q^(-1) mod p raw byte stream
+ * @n_sz        : length in bytes of RSA modulus n
+ * @e_sz        : length in bytes of RSA public exponent
+ * @d_sz        : length in bytes of RSA private exponent
+ * @p_sz        : length in bytes of p field
+ * @q_sz        : length in bytes of q field
+ * @dp_sz       : length in bytes of dp field
+ * @dq_sz       : length in bytes of dq field
+ * @qinv_sz     : length in bytes of qinv field
+ */
+struct rsa_key {
+	const u8 *n;
+	const u8 *e;
+	const u8 *d;
+	const u8 *p;
+	const u8 *q;
+	const u8 *dp;
+	const u8 *dq;
+	const u8 *qinv;
+	size_t n_sz;
+	size_t e_sz;
+	size_t d_sz;
+	size_t p_sz;
+	size_t q_sz;
+	size_t dp_sz;
+	size_t dq_sz;
+	size_t qinv_sz;
+};
+
+int rsa_parse_pub_key(struct rsa_key *rsa_key, const void *key,
+		      unsigned int key_len);
+
+int rsa_parse_priv_key(struct rsa_key *rsa_key, const void *key,
+		       unsigned int key_len);
+
+extern struct crypto_template rsa_pkcs1pad_tmpl;
+#endif
diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h
new file mode 100644
index 0000000..8f5c8a7
--- /dev/null
+++ b/include/crypto/pkcs7.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* PKCS#7 crypto data parser
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#ifndef _CRYPTO_PKCS7_H
+#define _CRYPTO_PKCS7_H
+
+#ifndef __UBOOT__
+#include <linux/verification.h>
+#include <crypto/public_key.h>
+#endif
+
+struct key;
+struct pkcs7_message;
+
+/*
+ * pkcs7_parser.c
+ */
+extern struct pkcs7_message *pkcs7_parse_message(const void *data,
+						 size_t datalen);
+extern void pkcs7_free_message(struct pkcs7_message *pkcs7);
+
+extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,
+				  const void **_data, size_t *_datalen,
+				  size_t *_headerlen);
+
+#ifndef __UBOOT__
+/*
+ * pkcs7_trust.c
+ */
+extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
+				struct key *trust_keyring);
+
+/*
+ * pkcs7_verify.c
+ */
+extern int pkcs7_verify(struct pkcs7_message *pkcs7,
+			enum key_being_used_for usage);
+
+extern int pkcs7_supply_detached_data(struct pkcs7_message *pkcs7,
+				      const void *data, size_t datalen);
+#endif
+
+#endif /* _CRYPTO_PKCS7_H */
diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h
new file mode 100644
index 0000000..436a1ee
--- /dev/null
+++ b/include/crypto/public_key.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Asymmetric public-key algorithm definitions
+ *
+ * See Documentation/crypto/asymmetric-keys.txt
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#ifndef _LINUX_PUBLIC_KEY_H
+#define _LINUX_PUBLIC_KEY_H
+
+#ifdef __UBOOT__
+#include <linux/types.h>
+#else
+#include <linux/keyctl.h>
+#endif
+#include <linux/oid_registry.h>
+
+/*
+ * Cryptographic data for the public-key subtype of the asymmetric key type.
+ *
+ * Note that this may include private part of the key as well as the public
+ * part.
+ */
+struct public_key {
+	void *key;
+	u32 keylen;
+	enum OID algo;
+	void *params;
+	u32 paramlen;
+	bool key_is_private;
+	const char *id_type;
+	const char *pkey_algo;
+};
+
+extern void public_key_free(struct public_key *key);
+
+/*
+ * Public key cryptography signature data
+ */
+struct public_key_signature {
+	struct asymmetric_key_id *auth_ids[2];
+	u8 *s;			/* Signature */
+	u32 s_size;		/* Number of bytes in signature */
+	u8 *digest;
+	u8 digest_size;		/* Number of bytes in digest */
+	const char *pkey_algo;
+	const char *hash_algo;
+	const char *encoding;
+};
+
+extern void public_key_signature_free(struct public_key_signature *sig);
+
+#ifndef __UBOOT__
+extern struct asymmetric_key_subtype public_key_subtype;
+
+struct key;
+struct key_type;
+union key_payload;
+
+extern int restrict_link_by_signature(struct key *dest_keyring,
+				      const struct key_type *type,
+				      const union key_payload *payload,
+				      struct key *trust_keyring);
+
+extern int restrict_link_by_key_or_keyring(struct key *dest_keyring,
+					   const struct key_type *type,
+					   const union key_payload *payload,
+					   struct key *trusted);
+
+extern int restrict_link_by_key_or_keyring_chain(struct key *trust_keyring,
+						 const struct key_type *type,
+						 const union key_payload *payload,
+						 struct key *trusted);
+
+extern int query_asymmetric_key(const struct kernel_pkey_params *,
+				struct kernel_pkey_query *);
+
+extern int encrypt_blob(struct kernel_pkey_params *, const void *, void *);
+extern int decrypt_blob(struct kernel_pkey_params *, const void *, void *);
+extern int create_signature(struct kernel_pkey_params *, const void *, void *);
+extern int verify_signature(const struct key *,
+			    const struct public_key_signature *);
+
+int public_key_verify_signature(const struct public_key *pkey,
+				const struct public_key_signature *sig);
+#endif /* !__UBOOT__ */
+
+#endif /* _LINUX_PUBLIC_KEY_H */
diff --git a/include/env.h b/include/env.h
index b72239f..d6c2d75 100644
--- a/include/env.h
+++ b/include/env.h
@@ -113,6 +113,16 @@
  */
 char *env_get(const char *varname);
 
+/*
+ * Like env_get, but prints an error if envvar isn't defined in the
+ * environment.  It always returns what env_get does, so it can be used in
+ * place of env_get without changing error handling otherwise.
+ *
+ * @varname:	Variable to look up
+ * @return value of variable, or NULL if not found
+ */
+char *from_env(const char *envvar);
+
 /**
  * env_get_f() - Look up the value of an environment variable (early)
  *
diff --git a/include/keys/asymmetric-type.h b/include/keys/asymmetric-type.h
new file mode 100644
index 0000000..47d8391
--- /dev/null
+++ b/include/keys/asymmetric-type.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Asymmetric Public-key cryptography key type interface
+ *
+ * See Documentation/crypto/asymmetric-keys.txt
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#ifndef _KEYS_ASYMMETRIC_TYPE_H
+#define _KEYS_ASYMMETRIC_TYPE_H
+
+#ifndef __UBOOT__
+#include <linux/key-type.h>
+#include <linux/verification.h>
+
+extern struct key_type key_type_asymmetric;
+
+/*
+ * The key payload is four words.  The asymmetric-type key uses them as
+ * follows:
+ */
+enum asymmetric_payload_bits {
+	asym_crypto,		/* The data representing the key */
+	asym_subtype,		/* Pointer to an asymmetric_key_subtype struct */
+	asym_key_ids,		/* Pointer to an asymmetric_key_ids struct */
+	asym_auth		/* The key's authorisation (signature, parent key ID) */
+};
+#endif /* !__UBOOT__ */
+
+/*
+ * Identifiers for an asymmetric key ID.  We have three ways of looking up a
+ * key derived from an X.509 certificate:
+ *
+ * (1) Serial Number & Issuer.  Non-optional.  This is the only valid way to
+ *     map a PKCS#7 signature to an X.509 certificate.
+ *
+ * (2) Issuer & Subject Unique IDs.  Optional.  These were the original way to
+ *     match X.509 certificates, but have fallen into disuse in favour of (3).
+ *
+ * (3) Auth & Subject Key Identifiers.  Optional.  SKIDs are only provided on
+ *     CA keys that are intended to sign other keys, so don't appear in end
+ *     user certificates unless forced.
+ *
+ * We could also support an PGP key identifier, which is just a SHA1 sum of the
+ * public key and certain parameters, but since we don't support PGP keys at
+ * the moment, we shall ignore those.
+ *
+ * What we actually do is provide a place where binary identifiers can be
+ * stashed and then compare against them when checking for an id match.
+ */
+struct asymmetric_key_id {
+	unsigned short	len;
+	unsigned char	data[];
+};
+
+struct asymmetric_key_ids {
+	void		*id[2];
+};
+
+extern bool asymmetric_key_id_same(const struct asymmetric_key_id *kid1,
+				   const struct asymmetric_key_id *kid2);
+
+extern bool asymmetric_key_id_partial(const struct asymmetric_key_id *kid1,
+				      const struct asymmetric_key_id *kid2);
+
+extern struct asymmetric_key_id *asymmetric_key_generate_id(const void *val_1,
+							    size_t len_1,
+							    const void *val_2,
+							    size_t len_2);
+#ifndef __UBOOT__
+static inline
+const struct asymmetric_key_ids *asymmetric_key_ids(const struct key *key)
+{
+	return key->payload.data[asym_key_ids];
+}
+
+extern struct key *find_asymmetric_key(struct key *keyring,
+				       const struct asymmetric_key_id *id_0,
+				       const struct asymmetric_key_id *id_1,
+				       bool partial);
+#endif
+
+/*
+ * The payload is at the discretion of the subtype.
+ */
+
+#endif /* _KEYS_ASYMMETRIC_TYPE_H */
diff --git a/include/linux/asn1.h b/include/linux/asn1.h
new file mode 100644
index 0000000..a4d0bdd
--- /dev/null
+++ b/include/linux/asn1.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* ASN.1 BER/DER/CER encoding definitions
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#ifndef _LINUX_ASN1_H
+#define _LINUX_ASN1_H
+
+/* Class */
+enum asn1_class {
+	ASN1_UNIV	= 0,	/* Universal */
+	ASN1_APPL	= 1,	/* Application */
+	ASN1_CONT	= 2,	/* Context */
+	ASN1_PRIV	= 3	/* Private */
+};
+#define ASN1_CLASS_BITS	0xc0
+
+
+enum asn1_method {
+	ASN1_PRIM	= 0,	/* Primitive */
+	ASN1_CONS	= 1	/* Constructed */
+};
+#define ASN1_CONS_BIT	0x20
+
+/* Tag */
+enum asn1_tag {
+	ASN1_EOC	= 0,	/* End Of Contents or N/A */
+	ASN1_BOOL	= 1,	/* Boolean */
+	ASN1_INT	= 2,	/* Integer */
+	ASN1_BTS	= 3,	/* Bit String */
+	ASN1_OTS	= 4,	/* Octet String */
+	ASN1_NULL	= 5,	/* Null */
+	ASN1_OID	= 6,	/* Object Identifier  */
+	ASN1_ODE	= 7,	/* Object Description */
+	ASN1_EXT	= 8,	/* External */
+	ASN1_REAL	= 9,	/* Real float */
+	ASN1_ENUM	= 10,	/* Enumerated */
+	ASN1_EPDV	= 11,	/* Embedded PDV */
+	ASN1_UTF8STR	= 12,	/* UTF8 String */
+	ASN1_RELOID	= 13,	/* Relative OID */
+	/* 14 - Reserved */
+	/* 15 - Reserved */
+	ASN1_SEQ	= 16,	/* Sequence and Sequence of */
+	ASN1_SET	= 17,	/* Set and Set of */
+	ASN1_NUMSTR	= 18,	/* Numerical String */
+	ASN1_PRNSTR	= 19,	/* Printable String */
+	ASN1_TEXSTR	= 20,	/* T61 String / Teletext String */
+	ASN1_VIDSTR	= 21,	/* Videotex String */
+	ASN1_IA5STR	= 22,	/* IA5 String */
+	ASN1_UNITIM	= 23,	/* Universal Time */
+	ASN1_GENTIM	= 24,	/* General Time */
+	ASN1_GRASTR	= 25,	/* Graphic String */
+	ASN1_VISSTR	= 26,	/* Visible String */
+	ASN1_GENSTR	= 27,	/* General String */
+	ASN1_UNISTR	= 28,	/* Universal String */
+	ASN1_CHRSTR	= 29,	/* Character String */
+	ASN1_BMPSTR	= 30,	/* BMP String */
+	ASN1_LONG_TAG	= 31	/* Long form tag */
+};
+
+#define ASN1_INDEFINITE_LENGTH 0x80
+
+#endif /* _LINUX_ASN1_H */
diff --git a/include/linux/asn1_ber_bytecode.h b/include/linux/asn1_ber_bytecode.h
new file mode 100644
index 0000000..b383619
--- /dev/null
+++ b/include/linux/asn1_ber_bytecode.h
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* ASN.1 BER/DER/CER parsing state machine internal definitions
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#ifndef _LINUX_ASN1_BER_BYTECODE_H
+#define _LINUX_ASN1_BER_BYTECODE_H
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#endif
+#include <linux/asn1.h>
+
+typedef int (*asn1_action_t)(void *context,
+			     size_t hdrlen, /* In case of ANY type */
+			     unsigned char tag, /* In case of ANY type */
+			     const void *value, size_t vlen);
+
+struct asn1_decoder {
+	const unsigned char *machine;
+	size_t machlen;
+	const asn1_action_t *actions;
+};
+
+enum asn1_opcode {
+	/* The tag-matching ops come first and the odd-numbered slots
+	 * are for OR_SKIP ops.
+	 */
+#define ASN1_OP_MATCH__SKIP		  0x01
+#define ASN1_OP_MATCH__ACT		  0x02
+#define ASN1_OP_MATCH__JUMP		  0x04
+#define ASN1_OP_MATCH__ANY		  0x08
+#define ASN1_OP_MATCH__COND		  0x10
+
+	ASN1_OP_MATCH			= 0x00,
+	ASN1_OP_MATCH_OR_SKIP		= 0x01,
+	ASN1_OP_MATCH_ACT		= 0x02,
+	ASN1_OP_MATCH_ACT_OR_SKIP	= 0x03,
+	ASN1_OP_MATCH_JUMP		= 0x04,
+	ASN1_OP_MATCH_JUMP_OR_SKIP	= 0x05,
+	ASN1_OP_MATCH_ANY		= 0x08,
+	ASN1_OP_MATCH_ANY_OR_SKIP	= 0x09,
+	ASN1_OP_MATCH_ANY_ACT		= 0x0a,
+	ASN1_OP_MATCH_ANY_ACT_OR_SKIP	= 0x0b,
+	/* Everything before here matches unconditionally */
+
+	ASN1_OP_COND_MATCH_OR_SKIP	= 0x11,
+	ASN1_OP_COND_MATCH_ACT_OR_SKIP	= 0x13,
+	ASN1_OP_COND_MATCH_JUMP_OR_SKIP	= 0x15,
+	ASN1_OP_COND_MATCH_ANY		= 0x18,
+	ASN1_OP_COND_MATCH_ANY_OR_SKIP	= 0x19,
+	ASN1_OP_COND_MATCH_ANY_ACT	= 0x1a,
+	ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP = 0x1b,
+
+	/* Everything before here will want a tag from the data */
+#define ASN1_OP__MATCHES_TAG ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP
+
+	/* These are here to help fill up space */
+	ASN1_OP_COND_FAIL		= 0x1c,
+	ASN1_OP_COMPLETE		= 0x1d,
+	ASN1_OP_ACT			= 0x1e,
+	ASN1_OP_MAYBE_ACT		= 0x1f,
+
+	/* The following eight have bit 0 -> SET, 1 -> OF, 2 -> ACT */
+	ASN1_OP_END_SEQ			= 0x20,
+	ASN1_OP_END_SET			= 0x21,
+	ASN1_OP_END_SEQ_OF		= 0x22,
+	ASN1_OP_END_SET_OF		= 0x23,
+	ASN1_OP_END_SEQ_ACT		= 0x24,
+	ASN1_OP_END_SET_ACT		= 0x25,
+	ASN1_OP_END_SEQ_OF_ACT		= 0x26,
+	ASN1_OP_END_SET_OF_ACT		= 0x27,
+#define ASN1_OP_END__SET		  0x01
+#define ASN1_OP_END__OF			  0x02
+#define ASN1_OP_END__ACT		  0x04
+
+	ASN1_OP_RETURN			= 0x28,
+
+	ASN1_OP__NR
+};
+
+#define _tag(CLASS, CP, TAG) ((ASN1_##CLASS << 6) | (ASN1_##CP << 5) | ASN1_##TAG)
+#define _tagn(CLASS, CP, TAG) ((ASN1_##CLASS << 6) | (ASN1_##CP << 5) | TAG)
+#define _jump_target(N) (N)
+#define _action(N) (N)
+
+#endif /* _LINUX_ASN1_BER_BYTECODE_H */
diff --git a/include/linux/asn1_decoder.h b/include/linux/asn1_decoder.h
new file mode 100644
index 0000000..83f9c6e
--- /dev/null
+++ b/include/linux/asn1_decoder.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* ASN.1 decoder
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#ifndef _LINUX_ASN1_DECODER_H
+#define _LINUX_ASN1_DECODER_H
+
+#include <linux/asn1.h>
+
+struct asn1_decoder;
+
+extern int asn1_ber_decoder(const struct asn1_decoder *decoder,
+			    void *context,
+			    const unsigned char *data,
+			    size_t datalen);
+
+#endif /* _LINUX_ASN1_DECODER_H */
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 5c7e5f6..564819a 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -1,8 +1,8 @@
 #ifndef _LINUX_KERNEL_H
 #define _LINUX_KERNEL_H
 
-
 #include <linux/types.h>
+#include <linux/printk.h> /* for printf/pr_* utilities */
 
 #define USHRT_MAX	((u16)(~0U))
 #define SHRT_MAX	((s16)(USHRT_MAX>>1))
diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h
new file mode 100644
index 0000000..657d6bf
--- /dev/null
+++ b/include/linux/oid_registry.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* ASN.1 Object identifier (OID) registry
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#ifndef _LINUX_OID_REGISTRY_H
+#define _LINUX_OID_REGISTRY_H
+
+#include <linux/types.h>
+
+/*
+ * OIDs are turned into these values if possible, or OID__NR if not held here.
+ *
+ * NOTE!  Do not mess with the format of each line as this is read by
+ *	  build_OID_registry.pl to generate the data for look_up_OID().
+ */
+enum OID {
+	OID_id_dsa_with_sha1,		/* 1.2.840.10030.4.3 */
+	OID_id_dsa,			/* 1.2.840.10040.4.1 */
+	OID_id_ecdsa_with_sha1,		/* 1.2.840.10045.4.1 */
+	OID_id_ecPublicKey,		/* 1.2.840.10045.2.1 */
+
+	/* PKCS#1 {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-1(1)} */
+	OID_rsaEncryption,		/* 1.2.840.113549.1.1.1 */
+	OID_md2WithRSAEncryption,	/* 1.2.840.113549.1.1.2 */
+	OID_md3WithRSAEncryption,	/* 1.2.840.113549.1.1.3 */
+	OID_md4WithRSAEncryption,	/* 1.2.840.113549.1.1.4 */
+	OID_sha1WithRSAEncryption,	/* 1.2.840.113549.1.1.5 */
+	OID_sha256WithRSAEncryption,	/* 1.2.840.113549.1.1.11 */
+	OID_sha384WithRSAEncryption,	/* 1.2.840.113549.1.1.12 */
+	OID_sha512WithRSAEncryption,	/* 1.2.840.113549.1.1.13 */
+	OID_sha224WithRSAEncryption,	/* 1.2.840.113549.1.1.14 */
+	/* PKCS#7 {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-7(7)} */
+	OID_data,			/* 1.2.840.113549.1.7.1 */
+	OID_signed_data,		/* 1.2.840.113549.1.7.2 */
+	/* PKCS#9 {iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-9(9)} */
+	OID_email_address,		/* 1.2.840.113549.1.9.1 */
+	OID_contentType,		/* 1.2.840.113549.1.9.3 */
+	OID_messageDigest,		/* 1.2.840.113549.1.9.4 */
+	OID_signingTime,		/* 1.2.840.113549.1.9.5 */
+	OID_smimeCapabilites,		/* 1.2.840.113549.1.9.15 */
+	OID_smimeAuthenticatedAttrs,	/* 1.2.840.113549.1.9.16.2.11 */
+
+	/* {iso(1) member-body(2) us(840) rsadsi(113549) digestAlgorithm(2)} */
+	OID_md2,			/* 1.2.840.113549.2.2 */
+	OID_md4,			/* 1.2.840.113549.2.4 */
+	OID_md5,			/* 1.2.840.113549.2.5 */
+
+	/* Microsoft Authenticode & Software Publishing */
+	OID_msIndirectData,		/* 1.3.6.1.4.1.311.2.1.4 */
+	OID_msStatementType,		/* 1.3.6.1.4.1.311.2.1.11 */
+	OID_msSpOpusInfo,		/* 1.3.6.1.4.1.311.2.1.12 */
+	OID_msPeImageDataObjId,		/* 1.3.6.1.4.1.311.2.1.15 */
+	OID_msIndividualSPKeyPurpose,	/* 1.3.6.1.4.1.311.2.1.21 */
+	OID_msOutlookExpress,		/* 1.3.6.1.4.1.311.16.4 */
+
+	OID_certAuthInfoAccess,		/* 1.3.6.1.5.5.7.1.1 */
+	OID_sha1,			/* 1.3.14.3.2.26 */
+	OID_sha256,			/* 2.16.840.1.101.3.4.2.1 */
+	OID_sha384,			/* 2.16.840.1.101.3.4.2.2 */
+	OID_sha512,			/* 2.16.840.1.101.3.4.2.3 */
+	OID_sha224,			/* 2.16.840.1.101.3.4.2.4 */
+
+	/* Distinguished Name attribute IDs [RFC 2256] */
+	OID_commonName,			/* 2.5.4.3 */
+	OID_surname,			/* 2.5.4.4 */
+	OID_countryName,		/* 2.5.4.6 */
+	OID_locality,			/* 2.5.4.7 */
+	OID_stateOrProvinceName,	/* 2.5.4.8 */
+	OID_organizationName,		/* 2.5.4.10 */
+	OID_organizationUnitName,	/* 2.5.4.11 */
+	OID_title,			/* 2.5.4.12 */
+	OID_description,		/* 2.5.4.13 */
+	OID_name,			/* 2.5.4.41 */
+	OID_givenName,			/* 2.5.4.42 */
+	OID_initials,			/* 2.5.4.43 */
+	OID_generationalQualifier,	/* 2.5.4.44 */
+
+	/* Certificate extension IDs */
+	OID_subjectKeyIdentifier,	/* 2.5.29.14 */
+	OID_keyUsage,			/* 2.5.29.15 */
+	OID_subjectAltName,		/* 2.5.29.17 */
+	OID_issuerAltName,		/* 2.5.29.18 */
+	OID_basicConstraints,		/* 2.5.29.19 */
+	OID_crlDistributionPoints,	/* 2.5.29.31 */
+	OID_certPolicies,		/* 2.5.29.32 */
+	OID_authorityKeyIdentifier,	/* 2.5.29.35 */
+	OID_extKeyUsage,		/* 2.5.29.37 */
+
+	/* EC-RDSA */
+	OID_gostCPSignA,		/* 1.2.643.2.2.35.1 */
+	OID_gostCPSignB,		/* 1.2.643.2.2.35.2 */
+	OID_gostCPSignC,		/* 1.2.643.2.2.35.3 */
+	OID_gost2012PKey256,		/* 1.2.643.7.1.1.1.1 */
+	OID_gost2012PKey512,		/* 1.2.643.7.1.1.1.2 */
+	OID_gost2012Digest256,		/* 1.2.643.7.1.1.2.2 */
+	OID_gost2012Digest512,		/* 1.2.643.7.1.1.2.3 */
+	OID_gost2012Signature256,	/* 1.2.643.7.1.1.3.2 */
+	OID_gost2012Signature512,	/* 1.2.643.7.1.1.3.3 */
+	OID_gostTC26Sign256A,		/* 1.2.643.7.1.2.1.1.1 */
+	OID_gostTC26Sign256B,		/* 1.2.643.7.1.2.1.1.2 */
+	OID_gostTC26Sign256C,		/* 1.2.643.7.1.2.1.1.3 */
+	OID_gostTC26Sign256D,		/* 1.2.643.7.1.2.1.1.4 */
+	OID_gostTC26Sign512A,		/* 1.2.643.7.1.2.1.2.1 */
+	OID_gostTC26Sign512B,		/* 1.2.643.7.1.2.1.2.2 */
+	OID_gostTC26Sign512C,		/* 1.2.643.7.1.2.1.2.3 */
+
+	OID__NR
+};
+
+extern enum OID look_up_OID(const void *data, size_t datasize);
+extern int sprint_oid(const void *, size_t, char *, size_t);
+extern int sprint_OID(enum OID, char *, size_t);
+
+#endif /* _LINUX_OID_REGISTRY_H */
diff --git a/include/linux/time.h b/include/linux/time.h
index b8d298e..702dd27 100644
--- a/include/linux/time.h
+++ b/include/linux/time.h
@@ -1,6 +1,8 @@
 #ifndef _LINUX_TIME_H
 #define _LINUX_TIME_H
 
+#include <rtc.h>
+#include <vsprintf.h>
 #include <linux/types.h>
 
 #define _DEFUN(a,b,c) a(c)
@@ -150,4 +152,13 @@
     return asctime_r (localtime_r (tim_p, &tm), result);
 }
 
+/* for compatibility with linux code */
+typedef __s64 time64_t;
+
+#ifdef CONFIG_LIB_DATE
+time64_t mktime64(const unsigned int year, const unsigned int mon,
+		  const unsigned int day, const unsigned int hour,
+		  const unsigned int min, const unsigned int sec);
+#endif
+
 #endif
diff --git a/include/rtc.h b/include/rtc.h
index 7386d52..8aabfc1 100644
--- a/include/rtc.h
+++ b/include/rtc.h
@@ -18,6 +18,8 @@
 
 #ifdef CONFIG_DM_RTC
 
+struct udevice;
+
 struct rtc_ops {
 	/**
 	 * get() - get the current time
diff --git a/lib/Kconfig b/lib/Kconfig
index 55dedcb..965cf7b 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -265,6 +265,7 @@
 	  present.
 
 source lib/rsa/Kconfig
+source lib/crypto/Kconfig
 
 config TPM
 	bool "Trusted Platform Module (TPM) Support"
@@ -566,6 +567,19 @@
 
 endmenu
 
+config ASN1_COMPILER
+	bool
+
+config ASN1_DECODER
+	bool
+	help
+	  Enable asn1 decoder library.
+
+config OID_REGISTRY
+	bool
+	help
+	  Enable fast lookup object identifier registry.
+
 source lib/efi/Kconfig
 source lib/efi_loader/Kconfig
 source lib/optee/Kconfig
@@ -574,4 +588,7 @@
 	bool "enable fdtdec test"
 	depends on OF_LIBFDT
 
+config LIB_DATE
+	bool
+
 endmenu
diff --git a/lib/Makefile b/lib/Makefile
index e15a189..1fb650c 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -17,6 +17,8 @@
 obj-$(CONFIG_CMD_DHRYSTONE) += dhry/
 obj-$(CONFIG_ARCH_AT91) += at91/
 obj-$(CONFIG_OPTEE) += optee/
+obj-$(CONFIG_ASN1_DECODER) += asn1_decoder.o
+obj-y += crypto/
 
 obj-$(CONFIG_AES) += aes.o
 
@@ -117,4 +119,22 @@
 obj-y += vsprintf.o strto.o
 endif
 
+obj-y += date.o
+
+#
+# Build a fast OID lookup registry from include/linux/oid_registry.h
+#
+obj-$(CONFIG_OID_REGISTRY) += oid_registry.o
+
+$(obj)/oid_registry.o: $(obj)/oid_registry_data.c
+
+$(obj)/oid_registry_data.c: $(srctree)/include/linux/oid_registry.h \
+			    $(srctree)/scripts/build_OID_registry
+	$(call cmd,build_OID_registry)
+
+quiet_cmd_build_OID_registry = GEN     $@
+      cmd_build_OID_registry = perl $(srctree)/scripts/build_OID_registry $< $@
+
+clean-files     += oid_registry_data.c
+
 subdir-ccflags-$(CONFIG_CC_OPTIMIZE_LIBS_FOR_SPEED) += -O2
diff --git a/lib/asn1_decoder.c b/lib/asn1_decoder.c
new file mode 100644
index 0000000..db22262
--- /dev/null
+++ b/lib/asn1_decoder.c
@@ -0,0 +1,527 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Decoder for ASN.1 BER/DER/CER encoded bytestream
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#ifdef __UBOOT__
+#include <linux/compat.h>
+#else
+#include <linux/export.h>
+#endif
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#ifndef __UBOOT__
+#include <linux/module.h>
+#endif
+#include <linux/asn1_decoder.h>
+#include <linux/asn1_ber_bytecode.h>
+
+static const unsigned char asn1_op_lengths[ASN1_OP__NR] = {
+	/*					OPC TAG JMP ACT */
+	[ASN1_OP_MATCH]				= 1 + 1,
+	[ASN1_OP_MATCH_OR_SKIP]			= 1 + 1,
+	[ASN1_OP_MATCH_ACT]			= 1 + 1     + 1,
+	[ASN1_OP_MATCH_ACT_OR_SKIP]		= 1 + 1     + 1,
+	[ASN1_OP_MATCH_JUMP]			= 1 + 1 + 1,
+	[ASN1_OP_MATCH_JUMP_OR_SKIP]		= 1 + 1 + 1,
+	[ASN1_OP_MATCH_ANY]			= 1,
+	[ASN1_OP_MATCH_ANY_OR_SKIP]		= 1,
+	[ASN1_OP_MATCH_ANY_ACT]			= 1         + 1,
+	[ASN1_OP_MATCH_ANY_ACT_OR_SKIP]		= 1         + 1,
+	[ASN1_OP_COND_MATCH_OR_SKIP]		= 1 + 1,
+	[ASN1_OP_COND_MATCH_ACT_OR_SKIP]	= 1 + 1     + 1,
+	[ASN1_OP_COND_MATCH_JUMP_OR_SKIP]	= 1 + 1 + 1,
+	[ASN1_OP_COND_MATCH_ANY]		= 1,
+	[ASN1_OP_COND_MATCH_ANY_OR_SKIP]	= 1,
+	[ASN1_OP_COND_MATCH_ANY_ACT]		= 1         + 1,
+	[ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP]	= 1         + 1,
+	[ASN1_OP_COND_FAIL]			= 1,
+	[ASN1_OP_COMPLETE]			= 1,
+	[ASN1_OP_ACT]				= 1         + 1,
+	[ASN1_OP_MAYBE_ACT]			= 1         + 1,
+	[ASN1_OP_RETURN]			= 1,
+	[ASN1_OP_END_SEQ]			= 1,
+	[ASN1_OP_END_SEQ_OF]			= 1     + 1,
+	[ASN1_OP_END_SET]			= 1,
+	[ASN1_OP_END_SET_OF]			= 1     + 1,
+	[ASN1_OP_END_SEQ_ACT]			= 1         + 1,
+	[ASN1_OP_END_SEQ_OF_ACT]		= 1     + 1 + 1,
+	[ASN1_OP_END_SET_ACT]			= 1         + 1,
+	[ASN1_OP_END_SET_OF_ACT]		= 1     + 1 + 1,
+};
+
+/*
+ * Find the length of an indefinite length object
+ * @data: The data buffer
+ * @datalen: The end of the innermost containing element in the buffer
+ * @_dp: The data parse cursor (updated before returning)
+ * @_len: Where to return the size of the element.
+ * @_errmsg: Where to return a pointer to an error message on error
+ */
+static int asn1_find_indefinite_length(const unsigned char *data, size_t datalen,
+				       size_t *_dp, size_t *_len,
+				       const char **_errmsg)
+{
+	unsigned char tag, tmp;
+	size_t dp = *_dp, len, n;
+	int indef_level = 1;
+
+next_tag:
+	if (unlikely(datalen - dp < 2)) {
+		if (datalen == dp)
+			goto missing_eoc;
+		goto data_overrun_error;
+	}
+
+	/* Extract a tag from the data */
+	tag = data[dp++];
+	if (tag == ASN1_EOC) {
+		/* It appears to be an EOC. */
+		if (data[dp++] != 0)
+			goto invalid_eoc;
+		if (--indef_level <= 0) {
+			*_len = dp - *_dp;
+			*_dp = dp;
+			return 0;
+		}
+		goto next_tag;
+	}
+
+	if (unlikely((tag & 0x1f) == ASN1_LONG_TAG)) {
+		do {
+			if (unlikely(datalen - dp < 2))
+				goto data_overrun_error;
+			tmp = data[dp++];
+		} while (tmp & 0x80);
+	}
+
+	/* Extract the length */
+	len = data[dp++];
+	if (len <= 0x7f)
+		goto check_length;
+
+	if (unlikely(len == ASN1_INDEFINITE_LENGTH)) {
+		/* Indefinite length */
+		if (unlikely((tag & ASN1_CONS_BIT) == ASN1_PRIM << 5))
+			goto indefinite_len_primitive;
+		indef_level++;
+		goto next_tag;
+	}
+
+	n = len - 0x80;
+	if (unlikely(n > sizeof(len) - 1))
+		goto length_too_long;
+	if (unlikely(n > datalen - dp))
+		goto data_overrun_error;
+	len = 0;
+	for (; n > 0; n--) {
+		len <<= 8;
+		len |= data[dp++];
+	}
+check_length:
+	if (len > datalen - dp)
+		goto data_overrun_error;
+	dp += len;
+	goto next_tag;
+
+length_too_long:
+	*_errmsg = "Unsupported length";
+	goto error;
+indefinite_len_primitive:
+	*_errmsg = "Indefinite len primitive not permitted";
+	goto error;
+invalid_eoc:
+	*_errmsg = "Invalid length EOC";
+	goto error;
+data_overrun_error:
+	*_errmsg = "Data overrun error";
+	goto error;
+missing_eoc:
+	*_errmsg = "Missing EOC in indefinite len cons";
+error:
+	*_dp = dp;
+	return -1;
+}
+
+/**
+ * asn1_ber_decoder - Decoder BER/DER/CER ASN.1 according to pattern
+ * @decoder: The decoder definition (produced by asn1_compiler)
+ * @context: The caller's context (to be passed to the action functions)
+ * @data: The encoded data
+ * @datalen: The size of the encoded data
+ *
+ * Decode BER/DER/CER encoded ASN.1 data according to a bytecode pattern
+ * produced by asn1_compiler.  Action functions are called on marked tags to
+ * allow the caller to retrieve significant data.
+ *
+ * LIMITATIONS:
+ *
+ * To keep down the amount of stack used by this function, the following limits
+ * have been imposed:
+ *
+ *  (1) This won't handle datalen > 65535 without increasing the size of the
+ *	cons stack elements and length_too_long checking.
+ *
+ *  (2) The stack of constructed types is 10 deep.  If the depth of non-leaf
+ *	constructed types exceeds this, the decode will fail.
+ *
+ *  (3) The SET type (not the SET OF type) isn't really supported as tracking
+ *	what members of the set have been seen is a pain.
+ */
+int asn1_ber_decoder(const struct asn1_decoder *decoder,
+		     void *context,
+		     const unsigned char *data,
+		     size_t datalen)
+{
+	const unsigned char *machine = decoder->machine;
+	const asn1_action_t *actions = decoder->actions;
+	size_t machlen = decoder->machlen;
+	enum asn1_opcode op;
+	unsigned char tag = 0, csp = 0, jsp = 0, optag = 0, hdr = 0;
+	const char *errmsg;
+	size_t pc = 0, dp = 0, tdp = 0, len = 0;
+	int ret;
+
+	unsigned char flags = 0;
+#define FLAG_INDEFINITE_LENGTH	0x01
+#define FLAG_MATCHED		0x02
+#define FLAG_LAST_MATCHED	0x04 /* Last tag matched */
+#define FLAG_CONS		0x20 /* Corresponds to CONS bit in the opcode tag
+				      * - ie. whether or not we are going to parse
+				      *   a compound type.
+				      */
+
+#define NR_CONS_STACK 10
+	unsigned short cons_dp_stack[NR_CONS_STACK];
+	unsigned short cons_datalen_stack[NR_CONS_STACK];
+	unsigned char cons_hdrlen_stack[NR_CONS_STACK];
+#define NR_JUMP_STACK 10
+	unsigned char jump_stack[NR_JUMP_STACK];
+
+	if (datalen > 65535)
+		return -EMSGSIZE;
+
+next_op:
+	pr_debug("next_op: pc=\e[32m%zu\e[m/%zu dp=\e[33m%zu\e[m/%zu C=%d J=%d\n",
+		 pc, machlen, dp, datalen, csp, jsp);
+	if (unlikely(pc >= machlen))
+		goto machine_overrun_error;
+	op = machine[pc];
+	if (unlikely(pc + asn1_op_lengths[op] > machlen))
+		goto machine_overrun_error;
+
+	/* If this command is meant to match a tag, then do that before
+	 * evaluating the command.
+	 */
+	if (op <= ASN1_OP__MATCHES_TAG) {
+		unsigned char tmp;
+
+		/* Skip conditional matches if possible */
+		if ((op & ASN1_OP_MATCH__COND && flags & FLAG_MATCHED) ||
+		    (op & ASN1_OP_MATCH__SKIP && dp == datalen)) {
+			flags &= ~FLAG_LAST_MATCHED;
+			pc += asn1_op_lengths[op];
+			goto next_op;
+		}
+
+		flags = 0;
+		hdr = 2;
+
+		/* Extract a tag from the data */
+		if (unlikely(datalen - dp < 2))
+			goto data_overrun_error;
+		tag = data[dp++];
+		if (unlikely((tag & 0x1f) == ASN1_LONG_TAG))
+			goto long_tag_not_supported;
+
+		if (op & ASN1_OP_MATCH__ANY) {
+			pr_debug("- any %02x\n", tag);
+		} else {
+			/* Extract the tag from the machine
+			 * - Either CONS or PRIM are permitted in the data if
+			 *   CONS is not set in the op stream, otherwise CONS
+			 *   is mandatory.
+			 */
+			optag = machine[pc + 1];
+			flags |= optag & FLAG_CONS;
+
+			/* Determine whether the tag matched */
+			tmp = optag ^ tag;
+			tmp &= ~(optag & ASN1_CONS_BIT);
+			pr_debug("- match? %02x %02x %02x\n", tag, optag, tmp);
+			if (tmp != 0) {
+				/* All odd-numbered tags are MATCH_OR_SKIP. */
+				if (op & ASN1_OP_MATCH__SKIP) {
+					pc += asn1_op_lengths[op];
+					dp--;
+					goto next_op;
+				}
+				goto tag_mismatch;
+			}
+		}
+		flags |= FLAG_MATCHED;
+
+		len = data[dp++];
+		if (len > 0x7f) {
+			if (unlikely(len == ASN1_INDEFINITE_LENGTH)) {
+				/* Indefinite length */
+				if (unlikely(!(tag & ASN1_CONS_BIT)))
+					goto indefinite_len_primitive;
+				flags |= FLAG_INDEFINITE_LENGTH;
+				if (unlikely(2 > datalen - dp))
+					goto data_overrun_error;
+			} else {
+				int n = len - 0x80;
+				if (unlikely(n > 2))
+					goto length_too_long;
+				if (unlikely(n > datalen - dp))
+					goto data_overrun_error;
+				hdr += n;
+				for (len = 0; n > 0; n--) {
+					len <<= 8;
+					len |= data[dp++];
+				}
+				if (unlikely(len > datalen - dp))
+					goto data_overrun_error;
+			}
+		} else {
+			if (unlikely(len > datalen - dp))
+				goto data_overrun_error;
+		}
+
+		if (flags & FLAG_CONS) {
+			/* For expected compound forms, we stack the positions
+			 * of the start and end of the data.
+			 */
+			if (unlikely(csp >= NR_CONS_STACK))
+				goto cons_stack_overflow;
+			cons_dp_stack[csp] = dp;
+			cons_hdrlen_stack[csp] = hdr;
+			if (!(flags & FLAG_INDEFINITE_LENGTH)) {
+				cons_datalen_stack[csp] = datalen;
+				datalen = dp + len;
+			} else {
+				cons_datalen_stack[csp] = 0;
+			}
+			csp++;
+		}
+
+		pr_debug("- TAG: %02x %zu%s\n",
+			 tag, len, flags & FLAG_CONS ? " CONS" : "");
+		tdp = dp;
+	}
+
+	/* Decide how to handle the operation */
+	switch (op) {
+	case ASN1_OP_MATCH:
+	case ASN1_OP_MATCH_OR_SKIP:
+	case ASN1_OP_MATCH_ACT:
+	case ASN1_OP_MATCH_ACT_OR_SKIP:
+	case ASN1_OP_MATCH_ANY:
+	case ASN1_OP_MATCH_ANY_OR_SKIP:
+	case ASN1_OP_MATCH_ANY_ACT:
+	case ASN1_OP_MATCH_ANY_ACT_OR_SKIP:
+	case ASN1_OP_COND_MATCH_OR_SKIP:
+	case ASN1_OP_COND_MATCH_ACT_OR_SKIP:
+	case ASN1_OP_COND_MATCH_ANY:
+	case ASN1_OP_COND_MATCH_ANY_OR_SKIP:
+	case ASN1_OP_COND_MATCH_ANY_ACT:
+	case ASN1_OP_COND_MATCH_ANY_ACT_OR_SKIP:
+
+		if (!(flags & FLAG_CONS)) {
+			if (flags & FLAG_INDEFINITE_LENGTH) {
+				size_t tmp = dp;
+
+				ret = asn1_find_indefinite_length(
+					data, datalen, &tmp, &len, &errmsg);
+				if (ret < 0)
+					goto error;
+			}
+			pr_debug("- LEAF: %zu\n", len);
+		}
+
+		if (op & ASN1_OP_MATCH__ACT) {
+			unsigned char act;
+
+			if (op & ASN1_OP_MATCH__ANY)
+				act = machine[pc + 1];
+			else
+				act = machine[pc + 2];
+			ret = actions[act](context, hdr, tag, data + dp, len);
+			if (ret < 0)
+				return ret;
+		}
+
+		if (!(flags & FLAG_CONS))
+			dp += len;
+		pc += asn1_op_lengths[op];
+		goto next_op;
+
+	case ASN1_OP_MATCH_JUMP:
+	case ASN1_OP_MATCH_JUMP_OR_SKIP:
+	case ASN1_OP_COND_MATCH_JUMP_OR_SKIP:
+		pr_debug("- MATCH_JUMP\n");
+		if (unlikely(jsp == NR_JUMP_STACK))
+			goto jump_stack_overflow;
+		jump_stack[jsp++] = pc + asn1_op_lengths[op];
+		pc = machine[pc + 2];
+		goto next_op;
+
+	case ASN1_OP_COND_FAIL:
+		if (unlikely(!(flags & FLAG_MATCHED)))
+			goto tag_mismatch;
+		pc += asn1_op_lengths[op];
+		goto next_op;
+
+	case ASN1_OP_COMPLETE:
+		if (unlikely(jsp != 0 || csp != 0)) {
+			pr_err("ASN.1 decoder error: Stacks not empty at completion (%u, %u)\n",
+			       jsp, csp);
+			return -EBADMSG;
+		}
+		return 0;
+
+	case ASN1_OP_END_SET:
+	case ASN1_OP_END_SET_ACT:
+		if (unlikely(!(flags & FLAG_MATCHED)))
+			goto tag_mismatch;
+		/* fall through */
+
+	case ASN1_OP_END_SEQ:
+	case ASN1_OP_END_SET_OF:
+	case ASN1_OP_END_SEQ_OF:
+	case ASN1_OP_END_SEQ_ACT:
+	case ASN1_OP_END_SET_OF_ACT:
+	case ASN1_OP_END_SEQ_OF_ACT:
+		if (unlikely(csp <= 0))
+			goto cons_stack_underflow;
+		csp--;
+		tdp = cons_dp_stack[csp];
+		hdr = cons_hdrlen_stack[csp];
+		len = datalen;
+		datalen = cons_datalen_stack[csp];
+		pr_debug("- end cons t=%zu dp=%zu l=%zu/%zu\n",
+			 tdp, dp, len, datalen);
+		if (datalen == 0) {
+			/* Indefinite length - check for the EOC. */
+			datalen = len;
+			if (unlikely(datalen - dp < 2))
+				goto data_overrun_error;
+			if (data[dp++] != 0) {
+				if (op & ASN1_OP_END__OF) {
+					dp--;
+					csp++;
+					pc = machine[pc + 1];
+					pr_debug("- continue\n");
+					goto next_op;
+				}
+				goto missing_eoc;
+			}
+			if (data[dp++] != 0)
+				goto invalid_eoc;
+			len = dp - tdp - 2;
+		} else {
+			if (dp < len && (op & ASN1_OP_END__OF)) {
+				datalen = len;
+				csp++;
+				pc = machine[pc + 1];
+				pr_debug("- continue\n");
+				goto next_op;
+			}
+			if (dp != len)
+				goto cons_length_error;
+			len -= tdp;
+			pr_debug("- cons len l=%zu d=%zu\n", len, dp - tdp);
+		}
+
+		if (op & ASN1_OP_END__ACT) {
+			unsigned char act;
+			if (op & ASN1_OP_END__OF)
+				act = machine[pc + 2];
+			else
+				act = machine[pc + 1];
+			ret = actions[act](context, hdr, 0, data + tdp, len);
+			if (ret < 0)
+				return ret;
+		}
+		pc += asn1_op_lengths[op];
+		goto next_op;
+
+	case ASN1_OP_MAYBE_ACT:
+		if (!(flags & FLAG_LAST_MATCHED)) {
+			pc += asn1_op_lengths[op];
+			goto next_op;
+		}
+		/* fall through */
+
+	case ASN1_OP_ACT:
+		ret = actions[machine[pc + 1]](context, hdr, tag, data + tdp, len);
+		if (ret < 0)
+			return ret;
+		pc += asn1_op_lengths[op];
+		goto next_op;
+
+	case ASN1_OP_RETURN:
+		if (unlikely(jsp <= 0))
+			goto jump_stack_underflow;
+		pc = jump_stack[--jsp];
+		flags |= FLAG_MATCHED | FLAG_LAST_MATCHED;
+		goto next_op;
+
+	default:
+		break;
+	}
+
+	/* Shouldn't reach here */
+	pr_err("ASN.1 decoder error: Found reserved opcode (%u) pc=%zu\n",
+	       op, pc);
+	return -EBADMSG;
+
+data_overrun_error:
+	errmsg = "Data overrun error";
+	goto error;
+machine_overrun_error:
+	errmsg = "Machine overrun error";
+	goto error;
+jump_stack_underflow:
+	errmsg = "Jump stack underflow";
+	goto error;
+jump_stack_overflow:
+	errmsg = "Jump stack overflow";
+	goto error;
+cons_stack_underflow:
+	errmsg = "Cons stack underflow";
+	goto error;
+cons_stack_overflow:
+	errmsg = "Cons stack overflow";
+	goto error;
+cons_length_error:
+	errmsg = "Cons length error";
+	goto error;
+missing_eoc:
+	errmsg = "Missing EOC in indefinite len cons";
+	goto error;
+invalid_eoc:
+	errmsg = "Invalid length EOC";
+	goto error;
+length_too_long:
+	errmsg = "Unsupported length";
+	goto error;
+indefinite_len_primitive:
+	errmsg = "Indefinite len primitive not permitted";
+	goto error;
+tag_mismatch:
+	errmsg = "Unexpected tag";
+	goto error;
+long_tag_not_supported:
+	errmsg = "Long tag not supported";
+error:
+	pr_debug("\nASN1: %s [m=%zu d=%zu ot=%02x t=%02x l=%zu]\n",
+		 errmsg, pc, dp, optag, tag, len);
+	return -EBADMSG;
+}
+EXPORT_SYMBOL_GPL(asn1_ber_decoder);
+
+MODULE_LICENSE("GPL");
diff --git a/lib/crypto/Kconfig b/lib/crypto/Kconfig
new file mode 100644
index 0000000..2b221b9
--- /dev/null
+++ b/lib/crypto/Kconfig
@@ -0,0 +1,52 @@
+menuconfig ASYMMETRIC_KEY_TYPE
+	bool "Asymmetric (public-key cryptographic) key Support"
+	help
+	  This option provides support for a key type that holds the data for
+	  the asymmetric keys used for public key cryptographic operations such
+	  as encryption, decryption, signature generation and signature
+	  verification.
+
+if ASYMMETRIC_KEY_TYPE
+
+config ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+	bool "Asymmetric public-key crypto algorithm subtype"
+	help
+	  This option provides support for asymmetric public key type handling.
+	  If signature generation and/or verification are to be used,
+	  appropriate hash algorithms (such as SHA-1) must be available.
+	  ENOPKG will be reported if the requisite algorithm is unavailable.
+
+config RSA_PUBLIC_KEY_PARSER
+	bool "RSA public key parser"
+	depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+	select ASN1_DECODER
+	select ASN1_COMPILER
+	select OID_REGISTRY
+	help
+	  This option provides support for parsing a blob containing RSA
+	  public key data and provides the ability to instantiate a public
+	  key.
+
+config X509_CERTIFICATE_PARSER
+	bool "X.509 certificate parser"
+	depends on ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+	select ASN1_DECODER
+	select ASN1_COMPILER
+	select OID_REGISTRY
+	select LIB_DATE
+	help
+	  This option provides support for parsing X.509 format blobs for key
+	  data and provides the ability to instantiate a crypto key from a
+	  public key packet found inside the certificate.
+
+config PKCS7_MESSAGE_PARSER
+	bool "PKCS#7 message parser"
+	depends on X509_CERTIFICATE_PARSER
+	select ASN1_DECODER
+	select ASN1_COMPILER
+	select OID_REGISTRY
+	help
+	  This option provides support for parsing PKCS#7 format messages for
+	  signature data and provides the ability to verify the signature.
+
+endif # ASYMMETRIC_KEY_TYPE
diff --git a/lib/crypto/Makefile b/lib/crypto/Makefile
new file mode 100644
index 0000000..8267fee
--- /dev/null
+++ b/lib/crypto/Makefile
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Makefile for asymmetric cryptographic keys
+#
+
+obj-$(CONFIG_ASYMMETRIC_KEY_TYPE) += asymmetric_keys.o
+
+asymmetric_keys-y := asymmetric_type.o
+
+obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o
+
+#
+# RSA public key parser
+#
+obj-$(CONFIG_RSA_PUBLIC_KEY_PARSER) += rsa_public_key.o
+rsa_public_key-y := \
+	rsapubkey.asn1.o \
+	rsa_helper.o
+
+$(obj)/rsapubkey.asn1.o: $(obj)/rsapubkey.asn1.c $(obj)/rsapubkey.asn1.h
+$(obj)/rsa_helper.o: $(obj)/rsapubkey.asn1.h
+
+#
+# X.509 Certificate handling
+#
+obj-$(CONFIG_X509_CERTIFICATE_PARSER) += x509_key_parser.o
+x509_key_parser-y := \
+	x509.asn1.o \
+	x509_akid.asn1.o \
+	x509_cert_parser.o \
+	x509_public_key.o
+
+$(obj)/x509_cert_parser.o: \
+	$(obj)/x509.asn1.h \
+	$(obj)/x509_akid.asn1.h
+
+$(obj)/x509.asn1.o: $(obj)/x509.asn1.c $(obj)/x509.asn1.h
+$(obj)/x509_akid.asn1.o: $(obj)/x509_akid.asn1.c $(obj)/x509_akid.asn1.h
+
+#
+# PKCS#7 message handling
+#
+obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o
+pkcs7_message-y := \
+	pkcs7.asn1.o \
+	pkcs7_parser.o
+
+$(obj)/pkcs7_parser.o: $(obj)/pkcs7.asn1.h
+$(obj)/pkcs7.asn1.o: $(obj)/pkcs7.asn1.c $(obj)/pkcs7.asn1.h
diff --git a/lib/crypto/asymmetric_type.c b/lib/crypto/asymmetric_type.c
new file mode 100644
index 0000000..e04666c
--- /dev/null
+++ b/lib/crypto/asymmetric_type.c
@@ -0,0 +1,668 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Asymmetric public-key cryptography key type
+ *
+ * See Documentation/crypto/asymmetric-keys.txt
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+#ifndef __UBOOT__
+#include <keys/asymmetric-subtype.h>
+#include <keys/asymmetric-parser.h>
+#endif
+#include <crypto/public_key.h>
+#ifdef __UBOOT__
+#include <linux/compat.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#else
+#include <linux/seq_file.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#endif
+#ifdef __UBOOT__
+#include <keys/asymmetric-type.h>
+#else
+#include <keys/system_keyring.h>
+#include <keys/user-type.h>
+#include "asymmetric_keys.h"
+#endif
+
+MODULE_LICENSE("GPL");
+
+#ifndef __UBOOT__
+const char *const key_being_used_for[NR__KEY_BEING_USED_FOR] = {
+	[VERIFYING_MODULE_SIGNATURE]		= "mod sig",
+	[VERIFYING_FIRMWARE_SIGNATURE]		= "firmware sig",
+	[VERIFYING_KEXEC_PE_SIGNATURE]		= "kexec PE sig",
+	[VERIFYING_KEY_SIGNATURE]		= "key sig",
+	[VERIFYING_KEY_SELF_SIGNATURE]		= "key self sig",
+	[VERIFYING_UNSPECIFIED_SIGNATURE]	= "unspec sig",
+};
+EXPORT_SYMBOL_GPL(key_being_used_for);
+
+static LIST_HEAD(asymmetric_key_parsers);
+static DECLARE_RWSEM(asymmetric_key_parsers_sem);
+
+/**
+ * find_asymmetric_key - Find a key by ID.
+ * @keyring: The keys to search.
+ * @id_0: The first ID to look for or NULL.
+ * @id_1: The second ID to look for or NULL.
+ * @partial: Use partial match if true, exact if false.
+ *
+ * Find a key in the given keyring by identifier.  The preferred identifier is
+ * the id_0 and the fallback identifier is the id_1.  If both are given, the
+ * lookup is by the former, but the latter must also match.
+ */
+struct key *find_asymmetric_key(struct key *keyring,
+				const struct asymmetric_key_id *id_0,
+				const struct asymmetric_key_id *id_1,
+				bool partial)
+{
+	struct key *key;
+	key_ref_t ref;
+	const char *lookup;
+	char *req, *p;
+	int len;
+
+	BUG_ON(!id_0 && !id_1);
+
+	if (id_0) {
+		lookup = id_0->data;
+		len = id_0->len;
+	} else {
+		lookup = id_1->data;
+		len = id_1->len;
+	}
+
+	/* Construct an identifier "id:<keyid>". */
+	p = req = kmalloc(2 + 1 + len * 2 + 1, GFP_KERNEL);
+	if (!req)
+		return ERR_PTR(-ENOMEM);
+
+	if (partial) {
+		*p++ = 'i';
+		*p++ = 'd';
+	} else {
+		*p++ = 'e';
+		*p++ = 'x';
+	}
+	*p++ = ':';
+	p = bin2hex(p, lookup, len);
+	*p = 0;
+
+	pr_debug("Look up: \"%s\"\n", req);
+
+	ref = keyring_search(make_key_ref(keyring, 1),
+			     &key_type_asymmetric, req, true);
+	if (IS_ERR(ref))
+		pr_debug("Request for key '%s' err %ld\n", req, PTR_ERR(ref));
+	kfree(req);
+
+	if (IS_ERR(ref)) {
+		switch (PTR_ERR(ref)) {
+			/* Hide some search errors */
+		case -EACCES:
+		case -ENOTDIR:
+		case -EAGAIN:
+			return ERR_PTR(-ENOKEY);
+		default:
+			return ERR_CAST(ref);
+		}
+	}
+
+	key = key_ref_to_ptr(ref);
+	if (id_0 && id_1) {
+		const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
+
+		if (!kids->id[1]) {
+			pr_debug("First ID matches, but second is missing\n");
+			goto reject;
+		}
+		if (!asymmetric_key_id_same(id_1, kids->id[1])) {
+			pr_debug("First ID matches, but second does not\n");
+			goto reject;
+		}
+	}
+
+	pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key));
+	return key;
+
+reject:
+	key_put(key);
+	return ERR_PTR(-EKEYREJECTED);
+}
+EXPORT_SYMBOL_GPL(find_asymmetric_key);
+#endif /* !__UBOOT__ */
+
+/**
+ * asymmetric_key_generate_id: Construct an asymmetric key ID
+ * @val_1: First binary blob
+ * @len_1: Length of first binary blob
+ * @val_2: Second binary blob
+ * @len_2: Length of second binary blob
+ *
+ * Construct an asymmetric key ID from a pair of binary blobs.
+ */
+struct asymmetric_key_id *asymmetric_key_generate_id(const void *val_1,
+						     size_t len_1,
+						     const void *val_2,
+						     size_t len_2)
+{
+	struct asymmetric_key_id *kid;
+
+	kid = kmalloc(sizeof(struct asymmetric_key_id) + len_1 + len_2,
+		      GFP_KERNEL);
+	if (!kid)
+		return ERR_PTR(-ENOMEM);
+	kid->len = len_1 + len_2;
+	memcpy(kid->data, val_1, len_1);
+	memcpy(kid->data + len_1, val_2, len_2);
+	return kid;
+}
+EXPORT_SYMBOL_GPL(asymmetric_key_generate_id);
+
+/**
+ * asymmetric_key_id_same - Return true if two asymmetric keys IDs are the same.
+ * @kid_1, @kid_2: The key IDs to compare
+ */
+bool asymmetric_key_id_same(const struct asymmetric_key_id *kid1,
+			    const struct asymmetric_key_id *kid2)
+{
+	if (!kid1 || !kid2)
+		return false;
+	if (kid1->len != kid2->len)
+		return false;
+	return memcmp(kid1->data, kid2->data, kid1->len) == 0;
+}
+EXPORT_SYMBOL_GPL(asymmetric_key_id_same);
+
+/**
+ * asymmetric_key_id_partial - Return true if two asymmetric keys IDs
+ * partially match
+ * @kid_1, @kid_2: The key IDs to compare
+ */
+bool asymmetric_key_id_partial(const struct asymmetric_key_id *kid1,
+			       const struct asymmetric_key_id *kid2)
+{
+	if (!kid1 || !kid2)
+		return false;
+	if (kid1->len < kid2->len)
+		return false;
+	return memcmp(kid1->data + (kid1->len - kid2->len),
+		      kid2->data, kid2->len) == 0;
+}
+EXPORT_SYMBOL_GPL(asymmetric_key_id_partial);
+
+#ifndef __UBOOT__
+/**
+ * asymmetric_match_key_ids - Search asymmetric key IDs
+ * @kids: The list of key IDs to check
+ * @match_id: The key ID we're looking for
+ * @match: The match function to use
+ */
+static bool asymmetric_match_key_ids(
+	const struct asymmetric_key_ids *kids,
+	const struct asymmetric_key_id *match_id,
+	bool (*match)(const struct asymmetric_key_id *kid1,
+		      const struct asymmetric_key_id *kid2))
+{
+	int i;
+
+	if (!kids || !match_id)
+		return false;
+	for (i = 0; i < ARRAY_SIZE(kids->id); i++)
+		if (match(kids->id[i], match_id))
+			return true;
+	return false;
+}
+
+/* helper function can be called directly with pre-allocated memory */
+inline int __asymmetric_key_hex_to_key_id(const char *id,
+				   struct asymmetric_key_id *match_id,
+				   size_t hexlen)
+{
+	match_id->len = hexlen;
+	return hex2bin(match_id->data, id, hexlen);
+}
+
+/**
+ * asymmetric_key_hex_to_key_id - Convert a hex string into a key ID.
+ * @id: The ID as a hex string.
+ */
+struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id)
+{
+	struct asymmetric_key_id *match_id;
+	size_t asciihexlen;
+	int ret;
+
+	if (!*id)
+		return ERR_PTR(-EINVAL);
+	asciihexlen = strlen(id);
+	if (asciihexlen & 1)
+		return ERR_PTR(-EINVAL);
+
+	match_id = kmalloc(sizeof(struct asymmetric_key_id) + asciihexlen / 2,
+			   GFP_KERNEL);
+	if (!match_id)
+		return ERR_PTR(-ENOMEM);
+	ret = __asymmetric_key_hex_to_key_id(id, match_id, asciihexlen / 2);
+	if (ret < 0) {
+		kfree(match_id);
+		return ERR_PTR(-EINVAL);
+	}
+	return match_id;
+}
+
+/*
+ * Match asymmetric keys by an exact match on an ID.
+ */
+static bool asymmetric_key_cmp(const struct key *key,
+			       const struct key_match_data *match_data)
+{
+	const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
+	const struct asymmetric_key_id *match_id = match_data->preparsed;
+
+	return asymmetric_match_key_ids(kids, match_id,
+					asymmetric_key_id_same);
+}
+
+/*
+ * Match asymmetric keys by a partial match on an IDs.
+ */
+static bool asymmetric_key_cmp_partial(const struct key *key,
+				       const struct key_match_data *match_data)
+{
+	const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
+	const struct asymmetric_key_id *match_id = match_data->preparsed;
+
+	return asymmetric_match_key_ids(kids, match_id,
+					asymmetric_key_id_partial);
+}
+
+/*
+ * Preparse the match criterion.  If we don't set lookup_type and cmp,
+ * the default will be an exact match on the key description.
+ *
+ * There are some specifiers for matching key IDs rather than by the key
+ * description:
+ *
+ *	"id:<id>" - find a key by partial match on any available ID
+ *	"ex:<id>" - find a key by exact match on any available ID
+ *
+ * These have to be searched by iteration rather than by direct lookup because
+ * the key is hashed according to its description.
+ */
+static int asymmetric_key_match_preparse(struct key_match_data *match_data)
+{
+	struct asymmetric_key_id *match_id;
+	const char *spec = match_data->raw_data;
+	const char *id;
+	bool (*cmp)(const struct key *, const struct key_match_data *) =
+		asymmetric_key_cmp;
+
+	if (!spec || !*spec)
+		return -EINVAL;
+	if (spec[0] == 'i' &&
+	    spec[1] == 'd' &&
+	    spec[2] == ':') {
+		id = spec + 3;
+		cmp = asymmetric_key_cmp_partial;
+	} else if (spec[0] == 'e' &&
+		   spec[1] == 'x' &&
+		   spec[2] == ':') {
+		id = spec + 3;
+	} else {
+		goto default_match;
+	}
+
+	match_id = asymmetric_key_hex_to_key_id(id);
+	if (IS_ERR(match_id))
+		return PTR_ERR(match_id);
+
+	match_data->preparsed = match_id;
+	match_data->cmp = cmp;
+	match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE;
+	return 0;
+
+default_match:
+	return 0;
+}
+
+/*
+ * Free the preparsed the match criterion.
+ */
+static void asymmetric_key_match_free(struct key_match_data *match_data)
+{
+	kfree(match_data->preparsed);
+}
+
+/*
+ * Describe the asymmetric key
+ */
+static void asymmetric_key_describe(const struct key *key, struct seq_file *m)
+{
+	const struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
+	const struct asymmetric_key_ids *kids = asymmetric_key_ids(key);
+	const struct asymmetric_key_id *kid;
+	const unsigned char *p;
+	int n;
+
+	seq_puts(m, key->description);
+
+	if (subtype) {
+		seq_puts(m, ": ");
+		subtype->describe(key, m);
+
+		if (kids && kids->id[1]) {
+			kid = kids->id[1];
+			seq_putc(m, ' ');
+			n = kid->len;
+			p = kid->data;
+			if (n > 4) {
+				p += n - 4;
+				n = 4;
+			}
+			seq_printf(m, "%*phN", n, p);
+		}
+
+		seq_puts(m, " [");
+		/* put something here to indicate the key's capabilities */
+		seq_putc(m, ']');
+	}
+}
+
+/*
+ * Preparse a asymmetric payload to get format the contents appropriately for the
+ * internal payload to cut down on the number of scans of the data performed.
+ *
+ * We also generate a proposed description from the contents of the key that
+ * can be used to name the key if the user doesn't want to provide one.
+ */
+static int asymmetric_key_preparse(struct key_preparsed_payload *prep)
+{
+	struct asymmetric_key_parser *parser;
+	int ret;
+
+	pr_devel("==>%s()\n", __func__);
+
+	if (prep->datalen == 0)
+		return -EINVAL;
+
+	down_read(&asymmetric_key_parsers_sem);
+
+	ret = -EBADMSG;
+	list_for_each_entry(parser, &asymmetric_key_parsers, link) {
+		pr_debug("Trying parser '%s'\n", parser->name);
+
+		ret = parser->parse(prep);
+		if (ret != -EBADMSG) {
+			pr_debug("Parser recognised the format (ret %d)\n",
+				 ret);
+			break;
+		}
+	}
+
+	up_read(&asymmetric_key_parsers_sem);
+	pr_devel("<==%s() = %d\n", __func__, ret);
+	return ret;
+}
+
+/*
+ * Clean up the key ID list
+ */
+static void asymmetric_key_free_kids(struct asymmetric_key_ids *kids)
+{
+	int i;
+
+	if (kids) {
+		for (i = 0; i < ARRAY_SIZE(kids->id); i++)
+			kfree(kids->id[i]);
+		kfree(kids);
+	}
+}
+
+/*
+ * Clean up the preparse data
+ */
+static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep)
+{
+	struct asymmetric_key_subtype *subtype = prep->payload.data[asym_subtype];
+	struct asymmetric_key_ids *kids = prep->payload.data[asym_key_ids];
+
+	pr_devel("==>%s()\n", __func__);
+
+	if (subtype) {
+		subtype->destroy(prep->payload.data[asym_crypto],
+				 prep->payload.data[asym_auth]);
+		module_put(subtype->owner);
+	}
+	asymmetric_key_free_kids(kids);
+	kfree(prep->description);
+}
+
+/*
+ * dispose of the data dangling from the corpse of a asymmetric key
+ */
+static void asymmetric_key_destroy(struct key *key)
+{
+	struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key);
+	struct asymmetric_key_ids *kids = key->payload.data[asym_key_ids];
+	void *data = key->payload.data[asym_crypto];
+	void *auth = key->payload.data[asym_auth];
+
+	key->payload.data[asym_crypto] = NULL;
+	key->payload.data[asym_subtype] = NULL;
+	key->payload.data[asym_key_ids] = NULL;
+	key->payload.data[asym_auth] = NULL;
+
+	if (subtype) {
+		subtype->destroy(data, auth);
+		module_put(subtype->owner);
+	}
+
+	asymmetric_key_free_kids(kids);
+}
+
+static struct key_restriction *asymmetric_restriction_alloc(
+	key_restrict_link_func_t check,
+	struct key *key)
+{
+	struct key_restriction *keyres =
+		kzalloc(sizeof(struct key_restriction), GFP_KERNEL);
+
+	if (!keyres)
+		return ERR_PTR(-ENOMEM);
+
+	keyres->check = check;
+	keyres->key = key;
+	keyres->keytype = &key_type_asymmetric;
+
+	return keyres;
+}
+
+/*
+ * look up keyring restrict functions for asymmetric keys
+ */
+static struct key_restriction *asymmetric_lookup_restriction(
+	const char *restriction)
+{
+	char *restrict_method;
+	char *parse_buf;
+	char *next;
+	struct key_restriction *ret = ERR_PTR(-EINVAL);
+
+	if (strcmp("builtin_trusted", restriction) == 0)
+		return asymmetric_restriction_alloc(
+			restrict_link_by_builtin_trusted, NULL);
+
+	if (strcmp("builtin_and_secondary_trusted", restriction) == 0)
+		return asymmetric_restriction_alloc(
+			restrict_link_by_builtin_and_secondary_trusted, NULL);
+
+	parse_buf = kstrndup(restriction, PAGE_SIZE, GFP_KERNEL);
+	if (!parse_buf)
+		return ERR_PTR(-ENOMEM);
+
+	next = parse_buf;
+	restrict_method = strsep(&next, ":");
+
+	if ((strcmp(restrict_method, "key_or_keyring") == 0) && next) {
+		char *key_text;
+		key_serial_t serial;
+		struct key *key;
+		key_restrict_link_func_t link_fn =
+			restrict_link_by_key_or_keyring;
+		bool allow_null_key = false;
+
+		key_text = strsep(&next, ":");
+
+		if (next) {
+			if (strcmp(next, "chain") != 0)
+				goto out;
+
+			link_fn = restrict_link_by_key_or_keyring_chain;
+			allow_null_key = true;
+		}
+
+		if (kstrtos32(key_text, 0, &serial) < 0)
+			goto out;
+
+		if ((serial == 0) && allow_null_key) {
+			key = NULL;
+		} else {
+			key = key_lookup(serial);
+			if (IS_ERR(key)) {
+				ret = ERR_CAST(key);
+				goto out;
+			}
+		}
+
+		ret = asymmetric_restriction_alloc(link_fn, key);
+		if (IS_ERR(ret))
+			key_put(key);
+	}
+
+out:
+	kfree(parse_buf);
+	return ret;
+}
+
+int asymmetric_key_eds_op(struct kernel_pkey_params *params,
+			  const void *in, void *out)
+{
+	const struct asymmetric_key_subtype *subtype;
+	struct key *key = params->key;
+	int ret;
+
+	pr_devel("==>%s()\n", __func__);
+
+	if (key->type != &key_type_asymmetric)
+		return -EINVAL;
+	subtype = asymmetric_key_subtype(key);
+	if (!subtype ||
+	    !key->payload.data[0])
+		return -EINVAL;
+	if (!subtype->eds_op)
+		return -ENOTSUPP;
+
+	ret = subtype->eds_op(params, in, out);
+
+	pr_devel("<==%s() = %d\n", __func__, ret);
+	return ret;
+}
+
+static int asymmetric_key_verify_signature(struct kernel_pkey_params *params,
+					   const void *in, const void *in2)
+{
+	struct public_key_signature sig = {
+		.s_size		= params->in2_len,
+		.digest_size	= params->in_len,
+		.encoding	= params->encoding,
+		.hash_algo	= params->hash_algo,
+		.digest		= (void *)in,
+		.s		= (void *)in2,
+	};
+
+	return verify_signature(params->key, &sig);
+}
+
+struct key_type key_type_asymmetric = {
+	.name			= "asymmetric",
+	.preparse		= asymmetric_key_preparse,
+	.free_preparse		= asymmetric_key_free_preparse,
+	.instantiate		= generic_key_instantiate,
+	.match_preparse		= asymmetric_key_match_preparse,
+	.match_free		= asymmetric_key_match_free,
+	.destroy		= asymmetric_key_destroy,
+	.describe		= asymmetric_key_describe,
+	.lookup_restriction	= asymmetric_lookup_restriction,
+	.asym_query		= query_asymmetric_key,
+	.asym_eds_op		= asymmetric_key_eds_op,
+	.asym_verify_signature	= asymmetric_key_verify_signature,
+};
+EXPORT_SYMBOL_GPL(key_type_asymmetric);
+
+/**
+ * register_asymmetric_key_parser - Register a asymmetric key blob parser
+ * @parser: The parser to register
+ */
+int register_asymmetric_key_parser(struct asymmetric_key_parser *parser)
+{
+	struct asymmetric_key_parser *cursor;
+	int ret;
+
+	down_write(&asymmetric_key_parsers_sem);
+
+	list_for_each_entry(cursor, &asymmetric_key_parsers, link) {
+		if (strcmp(cursor->name, parser->name) == 0) {
+			pr_err("Asymmetric key parser '%s' already registered\n",
+			       parser->name);
+			ret = -EEXIST;
+			goto out;
+		}
+	}
+
+	list_add_tail(&parser->link, &asymmetric_key_parsers);
+
+	pr_notice("Asymmetric key parser '%s' registered\n", parser->name);
+	ret = 0;
+
+out:
+	up_write(&asymmetric_key_parsers_sem);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(register_asymmetric_key_parser);
+
+/**
+ * unregister_asymmetric_key_parser - Unregister a asymmetric key blob parser
+ * @parser: The parser to unregister
+ */
+void unregister_asymmetric_key_parser(struct asymmetric_key_parser *parser)
+{
+	down_write(&asymmetric_key_parsers_sem);
+	list_del(&parser->link);
+	up_write(&asymmetric_key_parsers_sem);
+
+	pr_notice("Asymmetric key parser '%s' unregistered\n", parser->name);
+}
+EXPORT_SYMBOL_GPL(unregister_asymmetric_key_parser);
+
+/*
+ * Module stuff
+ */
+static int __init asymmetric_key_init(void)
+{
+	return register_key_type(&key_type_asymmetric);
+}
+
+static void __exit asymmetric_key_cleanup(void)
+{
+	unregister_key_type(&key_type_asymmetric);
+}
+
+module_init(asymmetric_key_init);
+module_exit(asymmetric_key_cleanup);
+#endif /* !__UBOOT__ */
diff --git a/lib/crypto/pkcs7.asn1 b/lib/crypto/pkcs7.asn1
new file mode 100644
index 0000000..1eca740
--- /dev/null
+++ b/lib/crypto/pkcs7.asn1
@@ -0,0 +1,135 @@
+PKCS7ContentInfo ::= SEQUENCE {
+	contentType	ContentType ({ pkcs7_check_content_type }),
+	content		[0] EXPLICIT SignedData OPTIONAL
+}
+
+ContentType ::= OBJECT IDENTIFIER ({ pkcs7_note_OID })
+
+SignedData ::= SEQUENCE {
+	version			INTEGER ({ pkcs7_note_signeddata_version }),
+	digestAlgorithms	DigestAlgorithmIdentifiers,
+	contentInfo		ContentInfo ({ pkcs7_note_content }),
+	certificates		CHOICE {
+		certSet		[0] IMPLICIT ExtendedCertificatesAndCertificates,
+		certSequence	[2] IMPLICIT Certificates
+	} OPTIONAL ({ pkcs7_note_certificate_list }),
+	crls CHOICE {
+		crlSet		[1] IMPLICIT CertificateRevocationLists,
+		crlSequence	[3] IMPLICIT CRLSequence
+	} OPTIONAL,
+	signerInfos		SignerInfos
+}
+
+ContentInfo ::= SEQUENCE {
+	contentType	ContentType ({ pkcs7_note_OID }),
+	content		[0] EXPLICIT Data OPTIONAL
+}
+
+Data ::= ANY ({ pkcs7_note_data })
+
+DigestAlgorithmIdentifiers ::= CHOICE {
+	daSet			SET OF DigestAlgorithmIdentifier,
+	daSequence		SEQUENCE OF DigestAlgorithmIdentifier
+}
+
+DigestAlgorithmIdentifier ::= SEQUENCE {
+	algorithm   OBJECT IDENTIFIER ({ pkcs7_note_OID }),
+	parameters  ANY OPTIONAL
+}
+
+--
+-- Certificates and certificate lists
+--
+ExtendedCertificatesAndCertificates ::= SET OF ExtendedCertificateOrCertificate
+
+ExtendedCertificateOrCertificate ::= CHOICE {
+  certificate		Certificate,				-- X.509
+  extendedCertificate	[0] IMPLICIT ExtendedCertificate	-- PKCS#6
+}
+
+ExtendedCertificate ::= Certificate -- cheating
+
+Certificates ::= SEQUENCE OF Certificate
+
+CertificateRevocationLists ::= SET OF CertificateList
+
+CertificateList ::= SEQUENCE OF Certificate -- This may be defined incorrectly
+
+CRLSequence ::= SEQUENCE OF CertificateList
+
+Certificate ::= ANY ({ pkcs7_extract_cert }) -- X.509
+
+--
+-- Signer information
+--
+SignerInfos ::= CHOICE {
+	siSet		SET OF SignerInfo,
+	siSequence	SEQUENCE OF SignerInfo
+}
+
+SignerInfo ::= SEQUENCE {
+	version			INTEGER ({ pkcs7_note_signerinfo_version }),
+	sid			SignerIdentifier, -- CMS variant, not PKCS#7
+	digestAlgorithm		DigestAlgorithmIdentifier ({ pkcs7_sig_note_digest_algo }),
+	authenticatedAttributes	CHOICE {
+		aaSet		[0] IMPLICIT SetOfAuthenticatedAttribute
+					({ pkcs7_sig_note_set_of_authattrs }),
+		aaSequence	[2] EXPLICIT SEQUENCE OF AuthenticatedAttribute
+			-- Explicit because easier to compute digest on
+			-- sequence of attributes and then reuse encoded
+			-- sequence in aaSequence.
+	} OPTIONAL,
+	digestEncryptionAlgorithm
+				DigestEncryptionAlgorithmIdentifier ({ pkcs7_sig_note_pkey_algo }),
+	encryptedDigest		EncryptedDigest,
+	unauthenticatedAttributes CHOICE {
+		uaSet		[1] IMPLICIT SET OF UnauthenticatedAttribute,
+		uaSequence	[3] IMPLICIT SEQUENCE OF UnauthenticatedAttribute
+	} OPTIONAL
+} ({ pkcs7_note_signed_info })
+
+SignerIdentifier ::= CHOICE {
+	-- RFC5652 sec 5.3
+	issuerAndSerialNumber IssuerAndSerialNumber,
+        subjectKeyIdentifier [0] IMPLICIT SubjectKeyIdentifier
+}
+
+IssuerAndSerialNumber ::= SEQUENCE {
+	issuer			Name ({ pkcs7_sig_note_issuer }),
+	serialNumber		CertificateSerialNumber ({ pkcs7_sig_note_serial })
+}
+
+CertificateSerialNumber ::= INTEGER
+
+SubjectKeyIdentifier ::= OCTET STRING ({ pkcs7_sig_note_skid })
+
+SetOfAuthenticatedAttribute ::= SET OF AuthenticatedAttribute
+
+AuthenticatedAttribute ::= SEQUENCE {
+	type			OBJECT IDENTIFIER ({ pkcs7_note_OID }),
+	values			SET OF ANY ({ pkcs7_sig_note_authenticated_attr })
+}
+
+UnauthenticatedAttribute ::= SEQUENCE {
+	type			OBJECT IDENTIFIER,
+	values			SET OF ANY
+}
+
+DigestEncryptionAlgorithmIdentifier ::= SEQUENCE {
+	algorithm		OBJECT IDENTIFIER ({ pkcs7_note_OID }),
+	parameters		ANY OPTIONAL
+}
+
+EncryptedDigest ::= OCTET STRING ({ pkcs7_sig_note_signature })
+
+---
+--- X.500 Name
+---
+Name ::= SEQUENCE OF RelativeDistinguishedName
+
+RelativeDistinguishedName ::= SET OF AttributeValueAssertion
+
+AttributeValueAssertion ::= SEQUENCE {
+	attributeType		OBJECT IDENTIFIER ({ pkcs7_note_OID }),
+	attributeValue		ANY
+}
diff --git a/lib/crypto/pkcs7_parser.c b/lib/crypto/pkcs7_parser.c
new file mode 100644
index 0000000..bf9e7e8
--- /dev/null
+++ b/lib/crypto/pkcs7_parser.c
@@ -0,0 +1,693 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* PKCS#7 parser
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#define pr_fmt(fmt) "PKCS7: "fmt
+#ifdef __UBOOT__
+#include <linux/bitops.h>
+#include <linux/compat.h>
+#endif
+#include <linux/kernel.h>
+#ifndef __UBOOT__
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#endif
+#include <linux/err.h>
+#include <linux/oid_registry.h>
+#include <crypto/public_key.h>
+#include "pkcs7_parser.h"
+#include "pkcs7.asn1.h"
+
+MODULE_DESCRIPTION("PKCS#7 parser");
+MODULE_AUTHOR("Red Hat, Inc.");
+MODULE_LICENSE("GPL");
+
+struct pkcs7_parse_context {
+	struct pkcs7_message	*msg;		/* Message being constructed */
+	struct pkcs7_signed_info *sinfo;	/* SignedInfo being constructed */
+	struct pkcs7_signed_info **ppsinfo;
+	struct x509_certificate *certs;		/* Certificate cache */
+	struct x509_certificate **ppcerts;
+	unsigned long	data;			/* Start of data */
+	enum OID	last_oid;		/* Last OID encountered */
+	unsigned	x509_index;
+	unsigned	sinfo_index;
+	const void	*raw_serial;
+	unsigned	raw_serial_size;
+	unsigned	raw_issuer_size;
+	const void	*raw_issuer;
+	const void	*raw_skid;
+	unsigned	raw_skid_size;
+	bool		expect_skid;
+};
+
+/*
+ * Free a signed information block.
+ */
+static void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo)
+{
+	if (sinfo) {
+		public_key_signature_free(sinfo->sig);
+		kfree(sinfo);
+	}
+}
+
+/**
+ * pkcs7_free_message - Free a PKCS#7 message
+ * @pkcs7: The PKCS#7 message to free
+ */
+void pkcs7_free_message(struct pkcs7_message *pkcs7)
+{
+	struct x509_certificate *cert;
+	struct pkcs7_signed_info *sinfo;
+
+	if (pkcs7) {
+		while (pkcs7->certs) {
+			cert = pkcs7->certs;
+			pkcs7->certs = cert->next;
+			x509_free_certificate(cert);
+		}
+		while (pkcs7->crl) {
+			cert = pkcs7->crl;
+			pkcs7->crl = cert->next;
+			x509_free_certificate(cert);
+		}
+		while (pkcs7->signed_infos) {
+			sinfo = pkcs7->signed_infos;
+			pkcs7->signed_infos = sinfo->next;
+			pkcs7_free_signed_info(sinfo);
+		}
+		kfree(pkcs7);
+	}
+}
+EXPORT_SYMBOL_GPL(pkcs7_free_message);
+
+/*
+ * Check authenticatedAttributes are provided or not provided consistently.
+ */
+static int pkcs7_check_authattrs(struct pkcs7_message *msg)
+{
+	struct pkcs7_signed_info *sinfo;
+	bool want = false;
+
+	sinfo = msg->signed_infos;
+	if (!sinfo)
+		goto inconsistent;
+
+	if (sinfo->authattrs) {
+		want = true;
+		msg->have_authattrs = true;
+	}
+
+	for (sinfo = sinfo->next; sinfo; sinfo = sinfo->next)
+		if (!!sinfo->authattrs != want)
+			goto inconsistent;
+	return 0;
+
+inconsistent:
+	pr_warn("Inconsistently supplied authAttrs\n");
+	return -EINVAL;
+}
+
+/**
+ * pkcs7_parse_message - Parse a PKCS#7 message
+ * @data: The raw binary ASN.1 encoded message to be parsed
+ * @datalen: The size of the encoded message
+ */
+struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen)
+{
+	struct pkcs7_parse_context *ctx;
+	struct pkcs7_message *msg = ERR_PTR(-ENOMEM);
+	int ret;
+
+	ctx = kzalloc(sizeof(struct pkcs7_parse_context), GFP_KERNEL);
+	if (!ctx)
+		goto out_no_ctx;
+	ctx->msg = kzalloc(sizeof(struct pkcs7_message), GFP_KERNEL);
+	if (!ctx->msg)
+		goto out_no_msg;
+	ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
+	if (!ctx->sinfo)
+		goto out_no_sinfo;
+	ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature),
+				  GFP_KERNEL);
+	if (!ctx->sinfo->sig)
+		goto out_no_sig;
+
+	ctx->data = (unsigned long)data;
+	ctx->ppcerts = &ctx->certs;
+	ctx->ppsinfo = &ctx->msg->signed_infos;
+
+	/* Attempt to decode the signature */
+	ret = asn1_ber_decoder(&pkcs7_decoder, ctx, data, datalen);
+	if (ret < 0) {
+		msg = ERR_PTR(ret);
+		goto out;
+	}
+
+	ret = pkcs7_check_authattrs(ctx->msg);
+	if (ret < 0) {
+		msg = ERR_PTR(ret);
+		goto out;
+	}
+
+	msg = ctx->msg;
+	ctx->msg = NULL;
+
+out:
+	while (ctx->certs) {
+		struct x509_certificate *cert = ctx->certs;
+		ctx->certs = cert->next;
+		x509_free_certificate(cert);
+	}
+out_no_sig:
+	pkcs7_free_signed_info(ctx->sinfo);
+out_no_sinfo:
+	pkcs7_free_message(ctx->msg);
+out_no_msg:
+	kfree(ctx);
+out_no_ctx:
+	return msg;
+}
+EXPORT_SYMBOL_GPL(pkcs7_parse_message);
+
+/**
+ * pkcs7_get_content_data - Get access to the PKCS#7 content
+ * @pkcs7: The preparsed PKCS#7 message to access
+ * @_data: Place to return a pointer to the data
+ * @_data_len: Place to return the data length
+ * @_headerlen: Size of ASN.1 header not included in _data
+ *
+ * Get access to the data content of the PKCS#7 message.  The size of the
+ * header of the ASN.1 object that contains it is also provided and can be used
+ * to adjust *_data and *_data_len to get the entire object.
+ *
+ * Returns -ENODATA if the data object was missing from the message.
+ */
+int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,
+			   const void **_data, size_t *_data_len,
+			   size_t *_headerlen)
+{
+	if (!pkcs7->data)
+		return -ENODATA;
+
+	*_data = pkcs7->data;
+	*_data_len = pkcs7->data_len;
+	if (_headerlen)
+		*_headerlen = pkcs7->data_hdrlen;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(pkcs7_get_content_data);
+
+/*
+ * Note an OID when we find one for later processing when we know how
+ * to interpret it.
+ */
+int pkcs7_note_OID(void *context, size_t hdrlen,
+		   unsigned char tag,
+		   const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	ctx->last_oid = look_up_OID(value, vlen);
+	if (ctx->last_oid == OID__NR) {
+		char buffer[50];
+		sprint_oid(value, vlen, buffer, sizeof(buffer));
+		printk("PKCS7: Unknown OID: [%lu] %s\n",
+		       (unsigned long)value - ctx->data, buffer);
+	}
+	return 0;
+}
+
+/*
+ * Note the digest algorithm for the signature.
+ */
+int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen,
+			       unsigned char tag,
+			       const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	switch (ctx->last_oid) {
+	case OID_md4:
+		ctx->sinfo->sig->hash_algo = "md4";
+		break;
+	case OID_md5:
+		ctx->sinfo->sig->hash_algo = "md5";
+		break;
+	case OID_sha1:
+		ctx->sinfo->sig->hash_algo = "sha1";
+		break;
+	case OID_sha256:
+		ctx->sinfo->sig->hash_algo = "sha256";
+		break;
+	case OID_sha384:
+		ctx->sinfo->sig->hash_algo = "sha384";
+		break;
+	case OID_sha512:
+		ctx->sinfo->sig->hash_algo = "sha512";
+		break;
+	case OID_sha224:
+		ctx->sinfo->sig->hash_algo = "sha224";
+		break;
+	default:
+		printk("Unsupported digest algo: %u\n", ctx->last_oid);
+		return -ENOPKG;
+	}
+	return 0;
+}
+
+/*
+ * Note the public key algorithm for the signature.
+ */
+int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen,
+			     unsigned char tag,
+			     const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	switch (ctx->last_oid) {
+	case OID_rsaEncryption:
+		ctx->sinfo->sig->pkey_algo = "rsa";
+		ctx->sinfo->sig->encoding = "pkcs1";
+		break;
+	default:
+		printk("Unsupported pkey algo: %u\n", ctx->last_oid);
+		return -ENOPKG;
+	}
+	return 0;
+}
+
+/*
+ * We only support signed data [RFC2315 sec 9].
+ */
+int pkcs7_check_content_type(void *context, size_t hdrlen,
+			     unsigned char tag,
+			     const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	if (ctx->last_oid != OID_signed_data) {
+		pr_warn("Only support pkcs7_signedData type\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * Note the SignedData version
+ */
+int pkcs7_note_signeddata_version(void *context, size_t hdrlen,
+				  unsigned char tag,
+				  const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+	unsigned version;
+
+	if (vlen != 1)
+		goto unsupported;
+
+	ctx->msg->version = version = *(const u8 *)value;
+	switch (version) {
+	case 1:
+		/* PKCS#7 SignedData [RFC2315 sec 9.1]
+		 * CMS ver 1 SignedData [RFC5652 sec 5.1]
+		 */
+		break;
+	case 3:
+		/* CMS ver 3 SignedData [RFC2315 sec 5.1] */
+		break;
+	default:
+		goto unsupported;
+	}
+
+	return 0;
+
+unsupported:
+	pr_warn("Unsupported SignedData version\n");
+	return -EINVAL;
+}
+
+/*
+ * Note the SignerInfo version
+ */
+int pkcs7_note_signerinfo_version(void *context, size_t hdrlen,
+				  unsigned char tag,
+				  const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+	unsigned version;
+
+	if (vlen != 1)
+		goto unsupported;
+
+	version = *(const u8 *)value;
+	switch (version) {
+	case 1:
+		/* PKCS#7 SignerInfo [RFC2315 sec 9.2]
+		 * CMS ver 1 SignerInfo [RFC5652 sec 5.3]
+		 */
+		if (ctx->msg->version != 1)
+			goto version_mismatch;
+		ctx->expect_skid = false;
+		break;
+	case 3:
+		/* CMS ver 3 SignerInfo [RFC2315 sec 5.3] */
+		if (ctx->msg->version == 1)
+			goto version_mismatch;
+		ctx->expect_skid = true;
+		break;
+	default:
+		goto unsupported;
+	}
+
+	return 0;
+
+unsupported:
+	pr_warn("Unsupported SignerInfo version\n");
+	return -EINVAL;
+version_mismatch:
+	pr_warn("SignedData-SignerInfo version mismatch\n");
+	return -EBADMSG;
+}
+
+/*
+ * Extract a certificate and store it in the context.
+ */
+int pkcs7_extract_cert(void *context, size_t hdrlen,
+		       unsigned char tag,
+		       const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+	struct x509_certificate *x509;
+
+	if (tag != ((ASN1_UNIV << 6) | ASN1_CONS_BIT | ASN1_SEQ)) {
+		pr_debug("Cert began with tag %02x at %lu\n",
+			 tag, (unsigned long)ctx - ctx->data);
+		return -EBADMSG;
+	}
+
+	/* We have to correct for the header so that the X.509 parser can start
+	 * from the beginning.  Note that since X.509 stipulates DER, there
+	 * probably shouldn't be an EOC trailer - but it is in PKCS#7 (which
+	 * stipulates BER).
+	 */
+	value -= hdrlen;
+	vlen += hdrlen;
+
+	if (((u8*)value)[1] == 0x80)
+		vlen += 2; /* Indefinite length - there should be an EOC */
+
+	x509 = x509_cert_parse(value, vlen);
+	if (IS_ERR(x509))
+		return PTR_ERR(x509);
+
+	x509->index = ++ctx->x509_index;
+	pr_debug("Got cert %u for %s\n", x509->index, x509->subject);
+	pr_debug("- fingerprint %*phN\n", x509->id->len, x509->id->data);
+
+	*ctx->ppcerts = x509;
+	ctx->ppcerts = &x509->next;
+	return 0;
+}
+
+/*
+ * Save the certificate list
+ */
+int pkcs7_note_certificate_list(void *context, size_t hdrlen,
+				unsigned char tag,
+				const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	pr_devel("Got cert list (%02x)\n", tag);
+
+	*ctx->ppcerts = ctx->msg->certs;
+	ctx->msg->certs = ctx->certs;
+	ctx->certs = NULL;
+	ctx->ppcerts = &ctx->certs;
+	return 0;
+}
+
+/*
+ * Note the content type.
+ */
+int pkcs7_note_content(void *context, size_t hdrlen,
+		       unsigned char tag,
+		       const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	if (ctx->last_oid != OID_data &&
+	    ctx->last_oid != OID_msIndirectData) {
+		pr_warn("Unsupported data type %d\n", ctx->last_oid);
+		return -EINVAL;
+	}
+
+	ctx->msg->data_type = ctx->last_oid;
+	return 0;
+}
+
+/*
+ * Extract the data from the message and store that and its content type OID in
+ * the context.
+ */
+int pkcs7_note_data(void *context, size_t hdrlen,
+		    unsigned char tag,
+		    const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	pr_debug("Got data\n");
+
+	ctx->msg->data = value;
+	ctx->msg->data_len = vlen;
+	ctx->msg->data_hdrlen = hdrlen;
+	return 0;
+}
+
+/*
+ * Parse authenticated attributes.
+ */
+int pkcs7_sig_note_authenticated_attr(void *context, size_t hdrlen,
+				      unsigned char tag,
+				      const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+	struct pkcs7_signed_info *sinfo = ctx->sinfo;
+	enum OID content_type;
+
+	pr_devel("AuthAttr: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value);
+
+	switch (ctx->last_oid) {
+	case OID_contentType:
+		if (__test_and_set_bit(sinfo_has_content_type, &sinfo->aa_set))
+			goto repeated;
+		content_type = look_up_OID(value, vlen);
+		if (content_type != ctx->msg->data_type) {
+			pr_warn("Mismatch between global data type (%d) and sinfo %u (%d)\n",
+				ctx->msg->data_type, sinfo->index,
+				content_type);
+			return -EBADMSG;
+		}
+		return 0;
+
+	case OID_signingTime:
+		if (__test_and_set_bit(sinfo_has_signing_time, &sinfo->aa_set))
+			goto repeated;
+		/* Should we check that the signing time is consistent
+		 * with the signer's X.509 cert?
+		 */
+		return x509_decode_time(&sinfo->signing_time,
+					hdrlen, tag, value, vlen);
+
+	case OID_messageDigest:
+		if (__test_and_set_bit(sinfo_has_message_digest, &sinfo->aa_set))
+			goto repeated;
+		if (tag != ASN1_OTS)
+			return -EBADMSG;
+		sinfo->msgdigest = value;
+		sinfo->msgdigest_len = vlen;
+		return 0;
+
+	case OID_smimeCapabilites:
+		if (__test_and_set_bit(sinfo_has_smime_caps, &sinfo->aa_set))
+			goto repeated;
+#ifdef __UBOOT__ /* OID_data is needed for authenticated UEFI variables */
+		if (ctx->msg->data_type != OID_msIndirectData &&
+		    ctx->msg->data_type != OID_data) {
+#else
+		if (ctx->msg->data_type != OID_msIndirectData) {
+#endif
+			pr_warn("S/MIME Caps only allowed with Authenticode\n");
+			return -EKEYREJECTED;
+		}
+		return 0;
+
+		/* Microsoft SpOpusInfo seems to be contain cont[0] 16-bit BE
+		 * char URLs and cont[1] 8-bit char URLs.
+		 *
+		 * Microsoft StatementType seems to contain a list of OIDs that
+		 * are also used as extendedKeyUsage types in X.509 certs.
+		 */
+	case OID_msSpOpusInfo:
+		if (__test_and_set_bit(sinfo_has_ms_opus_info, &sinfo->aa_set))
+			goto repeated;
+		goto authenticode_check;
+	case OID_msStatementType:
+		if (__test_and_set_bit(sinfo_has_ms_statement_type, &sinfo->aa_set))
+			goto repeated;
+	authenticode_check:
+		if (ctx->msg->data_type != OID_msIndirectData) {
+			pr_warn("Authenticode AuthAttrs only allowed with Authenticode\n");
+			return -EKEYREJECTED;
+		}
+		/* I'm not sure how to validate these */
+		return 0;
+	default:
+		return 0;
+	}
+
+repeated:
+	/* We permit max one item per AuthenticatedAttribute and no repeats */
+	pr_warn("Repeated/multivalue AuthAttrs not permitted\n");
+	return -EKEYREJECTED;
+}
+
+/*
+ * Note the set of auth attributes for digestion purposes [RFC2315 sec 9.3]
+ */
+int pkcs7_sig_note_set_of_authattrs(void *context, size_t hdrlen,
+				    unsigned char tag,
+				    const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+	struct pkcs7_signed_info *sinfo = ctx->sinfo;
+
+	if (!test_bit(sinfo_has_content_type, &sinfo->aa_set) ||
+	    !test_bit(sinfo_has_message_digest, &sinfo->aa_set)) {
+		pr_warn("Missing required AuthAttr\n");
+		return -EBADMSG;
+	}
+
+	if (ctx->msg->data_type != OID_msIndirectData &&
+	    test_bit(sinfo_has_ms_opus_info, &sinfo->aa_set)) {
+		pr_warn("Unexpected Authenticode AuthAttr\n");
+		return -EBADMSG;
+	}
+
+	/* We need to switch the 'CONT 0' to a 'SET OF' when we digest */
+	sinfo->authattrs = value - (hdrlen - 1);
+	sinfo->authattrs_len = vlen + (hdrlen - 1);
+	return 0;
+}
+
+/*
+ * Note the issuing certificate serial number
+ */
+int pkcs7_sig_note_serial(void *context, size_t hdrlen,
+			  unsigned char tag,
+			  const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+	ctx->raw_serial = value;
+	ctx->raw_serial_size = vlen;
+	return 0;
+}
+
+/*
+ * Note the issuer's name
+ */
+int pkcs7_sig_note_issuer(void *context, size_t hdrlen,
+			  unsigned char tag,
+			  const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+	ctx->raw_issuer = value;
+	ctx->raw_issuer_size = vlen;
+	return 0;
+}
+
+/*
+ * Note the issuing cert's subjectKeyIdentifier
+ */
+int pkcs7_sig_note_skid(void *context, size_t hdrlen,
+			unsigned char tag,
+			const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	pr_devel("SKID: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value);
+
+	ctx->raw_skid = value;
+	ctx->raw_skid_size = vlen;
+	return 0;
+}
+
+/*
+ * Note the signature data
+ */
+int pkcs7_sig_note_signature(void *context, size_t hdrlen,
+			     unsigned char tag,
+			     const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+
+	ctx->sinfo->sig->s = kmemdup(value, vlen, GFP_KERNEL);
+	if (!ctx->sinfo->sig->s)
+		return -ENOMEM;
+
+	ctx->sinfo->sig->s_size = vlen;
+	return 0;
+}
+
+/*
+ * Note a signature information block
+ */
+int pkcs7_note_signed_info(void *context, size_t hdrlen,
+			   unsigned char tag,
+			   const void *value, size_t vlen)
+{
+	struct pkcs7_parse_context *ctx = context;
+	struct pkcs7_signed_info *sinfo = ctx->sinfo;
+	struct asymmetric_key_id *kid;
+
+	if (ctx->msg->data_type == OID_msIndirectData && !sinfo->authattrs) {
+		pr_warn("Authenticode requires AuthAttrs\n");
+		return -EBADMSG;
+	}
+
+	/* Generate cert issuer + serial number key ID */
+	if (!ctx->expect_skid) {
+		kid = asymmetric_key_generate_id(ctx->raw_serial,
+						 ctx->raw_serial_size,
+						 ctx->raw_issuer,
+						 ctx->raw_issuer_size);
+	} else {
+		kid = asymmetric_key_generate_id(ctx->raw_skid,
+						 ctx->raw_skid_size,
+						 "", 0);
+	}
+	if (IS_ERR(kid))
+		return PTR_ERR(kid);
+
+	pr_devel("SINFO KID: %u [%*phN]\n", kid->len, kid->len, kid->data);
+
+	sinfo->sig->auth_ids[0] = kid;
+	sinfo->index = ++ctx->sinfo_index;
+	*ctx->ppsinfo = sinfo;
+	ctx->ppsinfo = &sinfo->next;
+	ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL);
+	if (!ctx->sinfo)
+		return -ENOMEM;
+	ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature),
+				  GFP_KERNEL);
+	if (!ctx->sinfo->sig)
+		return -ENOMEM;
+	return 0;
+}
diff --git a/lib/crypto/pkcs7_parser.h b/lib/crypto/pkcs7_parser.h
new file mode 100644
index 0000000..6565fdc
--- /dev/null
+++ b/lib/crypto/pkcs7_parser.h
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* PKCS#7 crypto data parser internal definitions
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#include <linux/oid_registry.h>
+#include <crypto/pkcs7.h>
+#include "x509_parser.h"
+
+#define kenter(FMT, ...) \
+	pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
+#define kleave(FMT, ...) \
+	pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
+
+struct pkcs7_signed_info {
+	struct pkcs7_signed_info *next;
+	struct x509_certificate *signer; /* Signing certificate (in msg->certs) */
+	unsigned	index;
+	bool		unsupported_crypto;	/* T if not usable due to missing crypto */
+	bool		blacklisted;
+
+	/* Message digest - the digest of the Content Data (or NULL) */
+	const void	*msgdigest;
+	unsigned	msgdigest_len;
+
+	/* Authenticated Attribute data (or NULL) */
+	unsigned	authattrs_len;
+	const void	*authattrs;
+	unsigned long	aa_set;
+#define	sinfo_has_content_type		0
+#define	sinfo_has_signing_time		1
+#define	sinfo_has_message_digest	2
+#define sinfo_has_smime_caps		3
+#define	sinfo_has_ms_opus_info		4
+#define	sinfo_has_ms_statement_type	5
+	time64_t	signing_time;
+
+	/* Message signature.
+	 *
+	 * This contains the generated digest of _either_ the Content Data or
+	 * the Authenticated Attributes [RFC2315 9.3].  If the latter, one of
+	 * the attributes contains the digest of the the Content Data within
+	 * it.
+	 *
+	 * THis also contains the issuing cert serial number and issuer's name
+	 * [PKCS#7 or CMS ver 1] or issuing cert's SKID [CMS ver 3].
+	 */
+	struct public_key_signature *sig;
+};
+
+struct pkcs7_message {
+	struct x509_certificate *certs;	/* Certificate list */
+	struct x509_certificate *crl;	/* Revocation list */
+	struct pkcs7_signed_info *signed_infos;
+	u8		version;	/* Version of cert (1 -> PKCS#7 or CMS; 3 -> CMS) */
+	bool		have_authattrs;	/* T if have authattrs */
+
+	/* Content Data (or NULL) */
+	enum OID	data_type;	/* Type of Data */
+	size_t		data_len;	/* Length of Data */
+	size_t		data_hdrlen;	/* Length of Data ASN.1 header */
+	const void	*data;		/* Content Data (or 0) */
+};
diff --git a/lib/crypto/public_key.c b/lib/crypto/public_key.c
new file mode 100644
index 0000000..6343774
--- /dev/null
+++ b/lib/crypto/public_key.c
@@ -0,0 +1,376 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* In-software asymmetric public-key crypto subtype
+ *
+ * See Documentation/crypto/asymmetric-keys.txt
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#define pr_fmt(fmt) "PKEY: "fmt
+#ifdef __UBOOT__
+#include <linux/compat.h>
+#else
+#include <linux/module.h>
+#include <linux/export.h>
+#endif
+#include <linux/kernel.h>
+#ifndef __UBOOT__
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+#include <linux/scatterlist.h>
+#include <keys/asymmetric-subtype.h>
+#endif
+#include <crypto/public_key.h>
+#ifndef __UBOOT__
+#include <crypto/akcipher.h>
+#endif
+
+MODULE_DESCRIPTION("In-software asymmetric public-key subtype");
+MODULE_AUTHOR("Red Hat, Inc.");
+MODULE_LICENSE("GPL");
+
+#ifndef __UBOOT__
+/*
+ * Provide a part of a description of the key for /proc/keys.
+ */
+static void public_key_describe(const struct key *asymmetric_key,
+				struct seq_file *m)
+{
+	struct public_key *key = asymmetric_key->payload.data[asym_crypto];
+
+	if (key)
+		seq_printf(m, "%s.%s", key->id_type, key->pkey_algo);
+}
+#endif
+
+/*
+ * Destroy a public key algorithm key.
+ */
+void public_key_free(struct public_key *key)
+{
+	if (key) {
+		kfree(key->key);
+		kfree(key->params);
+		kfree(key);
+	}
+}
+EXPORT_SYMBOL_GPL(public_key_free);
+
+#ifdef __UBOOT__
+/*
+ * from <linux>/crypto/asymmetric_keys/signature.c
+ *
+ * Destroy a public key signature.
+ */
+void public_key_signature_free(struct public_key_signature *sig)
+{
+	int i;
+
+	if (sig) {
+		for (i = 0; i < ARRAY_SIZE(sig->auth_ids); i++)
+			free(sig->auth_ids[i]);
+		free(sig->s);
+		free(sig->digest);
+		free(sig);
+	}
+}
+EXPORT_SYMBOL_GPL(public_key_signature_free);
+
+#else
+/*
+ * Destroy a public key algorithm key.
+ */
+static void public_key_destroy(void *payload0, void *payload3)
+{
+	public_key_free(payload0);
+	public_key_signature_free(payload3);
+}
+
+/*
+ * Determine the crypto algorithm name.
+ */
+static
+int software_key_determine_akcipher(const char *encoding,
+				    const char *hash_algo,
+				    const struct public_key *pkey,
+				    char alg_name[CRYPTO_MAX_ALG_NAME])
+{
+	int n;
+
+	if (strcmp(encoding, "pkcs1") == 0) {
+		/* The data wangled by the RSA algorithm is typically padded
+		 * and encoded in some manner, such as EMSA-PKCS1-1_5 [RFC3447
+		 * sec 8.2].
+		 */
+		if (!hash_algo)
+			n = snprintf(alg_name, CRYPTO_MAX_ALG_NAME,
+				     "pkcs1pad(%s)",
+				     pkey->pkey_algo);
+		else
+			n = snprintf(alg_name, CRYPTO_MAX_ALG_NAME,
+				     "pkcs1pad(%s,%s)",
+				     pkey->pkey_algo, hash_algo);
+		return n >= CRYPTO_MAX_ALG_NAME ? -EINVAL : 0;
+	}
+
+	if (strcmp(encoding, "raw") == 0) {
+		strcpy(alg_name, pkey->pkey_algo);
+		return 0;
+	}
+
+	return -ENOPKG;
+}
+
+static u8 *pkey_pack_u32(u8 *dst, u32 val)
+{
+	memcpy(dst, &val, sizeof(val));
+	return dst + sizeof(val);
+}
+
+/*
+ * Query information about a key.
+ */
+static int software_key_query(const struct kernel_pkey_params *params,
+			      struct kernel_pkey_query *info)
+{
+	struct crypto_akcipher *tfm;
+	struct public_key *pkey = params->key->payload.data[asym_crypto];
+	char alg_name[CRYPTO_MAX_ALG_NAME];
+	u8 *key, *ptr;
+	int ret, len;
+
+	ret = software_key_determine_akcipher(params->encoding,
+					      params->hash_algo,
+					      pkey, alg_name);
+	if (ret < 0)
+		return ret;
+
+	tfm = crypto_alloc_akcipher(alg_name, 0, 0);
+	if (IS_ERR(tfm))
+		return PTR_ERR(tfm);
+
+	key = kmalloc(pkey->keylen + sizeof(u32) * 2 + pkey->paramlen,
+		      GFP_KERNEL);
+	if (!key)
+		goto error_free_tfm;
+	memcpy(key, pkey->key, pkey->keylen);
+	ptr = key + pkey->keylen;
+	ptr = pkey_pack_u32(ptr, pkey->algo);
+	ptr = pkey_pack_u32(ptr, pkey->paramlen);
+	memcpy(ptr, pkey->params, pkey->paramlen);
+
+	if (pkey->key_is_private)
+		ret = crypto_akcipher_set_priv_key(tfm, key, pkey->keylen);
+	else
+		ret = crypto_akcipher_set_pub_key(tfm, key, pkey->keylen);
+	if (ret < 0)
+		goto error_free_key;
+
+	len = crypto_akcipher_maxsize(tfm);
+	info->key_size = len * 8;
+	info->max_data_size = len;
+	info->max_sig_size = len;
+	info->max_enc_size = len;
+	info->max_dec_size = len;
+	info->supported_ops = (KEYCTL_SUPPORTS_ENCRYPT |
+			       KEYCTL_SUPPORTS_VERIFY);
+	if (pkey->key_is_private)
+		info->supported_ops |= (KEYCTL_SUPPORTS_DECRYPT |
+					KEYCTL_SUPPORTS_SIGN);
+	ret = 0;
+
+error_free_key:
+	kfree(key);
+error_free_tfm:
+	crypto_free_akcipher(tfm);
+	pr_devel("<==%s() = %d\n", __func__, ret);
+	return ret;
+}
+
+/*
+ * Do encryption, decryption and signing ops.
+ */
+static int software_key_eds_op(struct kernel_pkey_params *params,
+			       const void *in, void *out)
+{
+	const struct public_key *pkey = params->key->payload.data[asym_crypto];
+	struct akcipher_request *req;
+	struct crypto_akcipher *tfm;
+	struct crypto_wait cwait;
+	struct scatterlist in_sg, out_sg;
+	char alg_name[CRYPTO_MAX_ALG_NAME];
+	char *key, *ptr;
+	int ret;
+
+	pr_devel("==>%s()\n", __func__);
+
+	ret = software_key_determine_akcipher(params->encoding,
+					      params->hash_algo,
+					      pkey, alg_name);
+	if (ret < 0)
+		return ret;
+
+	tfm = crypto_alloc_akcipher(alg_name, 0, 0);
+	if (IS_ERR(tfm))
+		return PTR_ERR(tfm);
+
+	req = akcipher_request_alloc(tfm, GFP_KERNEL);
+	if (!req)
+		goto error_free_tfm;
+
+	key = kmalloc(pkey->keylen + sizeof(u32) * 2 + pkey->paramlen,
+		      GFP_KERNEL);
+	if (!key)
+		goto error_free_req;
+
+	memcpy(key, pkey->key, pkey->keylen);
+	ptr = key + pkey->keylen;
+	ptr = pkey_pack_u32(ptr, pkey->algo);
+	ptr = pkey_pack_u32(ptr, pkey->paramlen);
+	memcpy(ptr, pkey->params, pkey->paramlen);
+
+	if (pkey->key_is_private)
+		ret = crypto_akcipher_set_priv_key(tfm, key, pkey->keylen);
+	else
+		ret = crypto_akcipher_set_pub_key(tfm, key, pkey->keylen);
+	if (ret)
+		goto error_free_key;
+
+	sg_init_one(&in_sg, in, params->in_len);
+	sg_init_one(&out_sg, out, params->out_len);
+	akcipher_request_set_crypt(req, &in_sg, &out_sg, params->in_len,
+				   params->out_len);
+	crypto_init_wait(&cwait);
+	akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
+				      CRYPTO_TFM_REQ_MAY_SLEEP,
+				      crypto_req_done, &cwait);
+
+	/* Perform the encryption calculation. */
+	switch (params->op) {
+	case kernel_pkey_encrypt:
+		ret = crypto_akcipher_encrypt(req);
+		break;
+	case kernel_pkey_decrypt:
+		ret = crypto_akcipher_decrypt(req);
+		break;
+	case kernel_pkey_sign:
+		ret = crypto_akcipher_sign(req);
+		break;
+	default:
+		BUG();
+	}
+
+	ret = crypto_wait_req(ret, &cwait);
+	if (ret == 0)
+		ret = req->dst_len;
+
+error_free_key:
+	kfree(key);
+error_free_req:
+	akcipher_request_free(req);
+error_free_tfm:
+	crypto_free_akcipher(tfm);
+	pr_devel("<==%s() = %d\n", __func__, ret);
+	return ret;
+}
+
+/*
+ * Verify a signature using a public key.
+ */
+int public_key_verify_signature(const struct public_key *pkey,
+				const struct public_key_signature *sig)
+{
+	struct crypto_wait cwait;
+	struct crypto_akcipher *tfm;
+	struct akcipher_request *req;
+	struct scatterlist src_sg[2];
+	char alg_name[CRYPTO_MAX_ALG_NAME];
+	char *key, *ptr;
+	int ret;
+
+	pr_devel("==>%s()\n", __func__);
+
+	BUG_ON(!pkey);
+	BUG_ON(!sig);
+	BUG_ON(!sig->s);
+
+	ret = software_key_determine_akcipher(sig->encoding,
+					      sig->hash_algo,
+					      pkey, alg_name);
+	if (ret < 0)
+		return ret;
+
+	tfm = crypto_alloc_akcipher(alg_name, 0, 0);
+	if (IS_ERR(tfm))
+		return PTR_ERR(tfm);
+
+	ret = -ENOMEM;
+	req = akcipher_request_alloc(tfm, GFP_KERNEL);
+	if (!req)
+		goto error_free_tfm;
+
+	key = kmalloc(pkey->keylen + sizeof(u32) * 2 + pkey->paramlen,
+		      GFP_KERNEL);
+	if (!key)
+		goto error_free_req;
+
+	memcpy(key, pkey->key, pkey->keylen);
+	ptr = key + pkey->keylen;
+	ptr = pkey_pack_u32(ptr, pkey->algo);
+	ptr = pkey_pack_u32(ptr, pkey->paramlen);
+	memcpy(ptr, pkey->params, pkey->paramlen);
+
+	if (pkey->key_is_private)
+		ret = crypto_akcipher_set_priv_key(tfm, key, pkey->keylen);
+	else
+		ret = crypto_akcipher_set_pub_key(tfm, key, pkey->keylen);
+	if (ret)
+		goto error_free_key;
+
+	sg_init_table(src_sg, 2);
+	sg_set_buf(&src_sg[0], sig->s, sig->s_size);
+	sg_set_buf(&src_sg[1], sig->digest, sig->digest_size);
+	akcipher_request_set_crypt(req, src_sg, NULL, sig->s_size,
+				   sig->digest_size);
+	crypto_init_wait(&cwait);
+	akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG |
+				      CRYPTO_TFM_REQ_MAY_SLEEP,
+				      crypto_req_done, &cwait);
+	ret = crypto_wait_req(crypto_akcipher_verify(req), &cwait);
+
+error_free_key:
+	kfree(key);
+error_free_req:
+	akcipher_request_free(req);
+error_free_tfm:
+	crypto_free_akcipher(tfm);
+	pr_devel("<==%s() = %d\n", __func__, ret);
+	if (WARN_ON_ONCE(ret > 0))
+		ret = -EINVAL;
+	return ret;
+}
+EXPORT_SYMBOL_GPL(public_key_verify_signature);
+
+static int public_key_verify_signature_2(const struct key *key,
+					 const struct public_key_signature *sig)
+{
+	const struct public_key *pk = key->payload.data[asym_crypto];
+	return public_key_verify_signature(pk, sig);
+}
+
+/*
+ * Public key algorithm asymmetric key subtype
+ */
+struct asymmetric_key_subtype public_key_subtype = {
+	.owner			= THIS_MODULE,
+	.name			= "public_key",
+	.name_len		= sizeof("public_key") - 1,
+	.describe		= public_key_describe,
+	.destroy		= public_key_destroy,
+	.query			= software_key_query,
+	.eds_op			= software_key_eds_op,
+	.verify_signature	= public_key_verify_signature_2,
+};
+EXPORT_SYMBOL_GPL(public_key_subtype);
+#endif /* !__UBOOT__ */
diff --git a/lib/crypto/rsa_helper.c b/lib/crypto/rsa_helper.c
new file mode 100644
index 0000000..aca627a
--- /dev/null
+++ b/lib/crypto/rsa_helper.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RSA key extract helper
+ *
+ * Copyright (c) 2015, Intel Corporation
+ * Authors: Tadeusz Struk <tadeusz.struk@intel.com>
+ */
+#ifndef __UBOOT__
+#include <linux/kernel.h>
+#include <linux/export.h>
+#endif
+#include <linux/err.h>
+#ifndef __UBOOT__
+#include <linux/fips.h>
+#endif
+#include <crypto/internal/rsa.h>
+#include "rsapubkey.asn1.h"
+#ifndef __UBOOT__
+#include "rsaprivkey.asn1.h"
+#endif
+
+int rsa_get_n(void *context, size_t hdrlen, unsigned char tag,
+	      const void *value, size_t vlen)
+{
+	struct rsa_key *key = context;
+#ifndef __UBOOT__
+	const u8 *ptr = value;
+	size_t n_sz = vlen;
+#endif
+
+	/* invalid key provided */
+	if (!value || !vlen)
+		return -EINVAL;
+
+#ifndef __UBOOT__
+	if (fips_enabled) {
+		while (n_sz && !*ptr) {
+			ptr++;
+			n_sz--;
+		}
+
+		/* In FIPS mode only allow key size 2K and higher */
+		if (n_sz < 256) {
+			pr_err("RSA: key size not allowed in FIPS mode\n");
+			return -EINVAL;
+		}
+	}
+#endif
+
+	key->n = value;
+	key->n_sz = vlen;
+
+	return 0;
+}
+
+int rsa_get_e(void *context, size_t hdrlen, unsigned char tag,
+	      const void *value, size_t vlen)
+{
+	struct rsa_key *key = context;
+
+	/* invalid key provided */
+	if (!value || !key->n_sz || !vlen || vlen > key->n_sz)
+		return -EINVAL;
+
+	key->e = value;
+	key->e_sz = vlen;
+
+	return 0;
+}
+
+int rsa_get_d(void *context, size_t hdrlen, unsigned char tag,
+	      const void *value, size_t vlen)
+{
+	struct rsa_key *key = context;
+
+	/* invalid key provided */
+	if (!value || !key->n_sz || !vlen || vlen > key->n_sz)
+		return -EINVAL;
+
+	key->d = value;
+	key->d_sz = vlen;
+
+	return 0;
+}
+
+int rsa_get_p(void *context, size_t hdrlen, unsigned char tag,
+	      const void *value, size_t vlen)
+{
+	struct rsa_key *key = context;
+
+	/* invalid key provided */
+	if (!value || !vlen || vlen > key->n_sz)
+		return -EINVAL;
+
+	key->p = value;
+	key->p_sz = vlen;
+
+	return 0;
+}
+
+int rsa_get_q(void *context, size_t hdrlen, unsigned char tag,
+	      const void *value, size_t vlen)
+{
+	struct rsa_key *key = context;
+
+	/* invalid key provided */
+	if (!value || !vlen || vlen > key->n_sz)
+		return -EINVAL;
+
+	key->q = value;
+	key->q_sz = vlen;
+
+	return 0;
+}
+
+int rsa_get_dp(void *context, size_t hdrlen, unsigned char tag,
+	       const void *value, size_t vlen)
+{
+	struct rsa_key *key = context;
+
+	/* invalid key provided */
+	if (!value || !vlen || vlen > key->n_sz)
+		return -EINVAL;
+
+	key->dp = value;
+	key->dp_sz = vlen;
+
+	return 0;
+}
+
+int rsa_get_dq(void *context, size_t hdrlen, unsigned char tag,
+	       const void *value, size_t vlen)
+{
+	struct rsa_key *key = context;
+
+	/* invalid key provided */
+	if (!value || !vlen || vlen > key->n_sz)
+		return -EINVAL;
+
+	key->dq = value;
+	key->dq_sz = vlen;
+
+	return 0;
+}
+
+int rsa_get_qinv(void *context, size_t hdrlen, unsigned char tag,
+		 const void *value, size_t vlen)
+{
+	struct rsa_key *key = context;
+
+	/* invalid key provided */
+	if (!value || !vlen || vlen > key->n_sz)
+		return -EINVAL;
+
+	key->qinv = value;
+	key->qinv_sz = vlen;
+
+	return 0;
+}
+
+/**
+ * rsa_parse_pub_key() - decodes the BER encoded buffer and stores in the
+ *                       provided struct rsa_key, pointers to the raw key as is,
+ *                       so that the caller can copy it or MPI parse it, etc.
+ *
+ * @rsa_key:	struct rsa_key key representation
+ * @key:	key in BER format
+ * @key_len:	length of key
+ *
+ * Return:	0 on success or error code in case of error
+ */
+int rsa_parse_pub_key(struct rsa_key *rsa_key, const void *key,
+		      unsigned int key_len)
+{
+	return asn1_ber_decoder(&rsapubkey_decoder, rsa_key, key, key_len);
+}
+EXPORT_SYMBOL_GPL(rsa_parse_pub_key);
+
+#ifndef __UBOOT__
+/**
+ * rsa_parse_priv_key() - decodes the BER encoded buffer and stores in the
+ *                        provided struct rsa_key, pointers to the raw key
+ *                        as is, so that the caller can copy it or MPI parse it,
+ *                        etc.
+ *
+ * @rsa_key:	struct rsa_key key representation
+ * @key:	key in BER format
+ * @key_len:	length of key
+ *
+ * Return:	0 on success or error code in case of error
+ */
+int rsa_parse_priv_key(struct rsa_key *rsa_key, const void *key,
+		       unsigned int key_len)
+{
+	return asn1_ber_decoder(&rsaprivkey_decoder, rsa_key, key, key_len);
+}
+EXPORT_SYMBOL_GPL(rsa_parse_priv_key);
+#endif
diff --git a/lib/crypto/rsapubkey.asn1 b/lib/crypto/rsapubkey.asn1
new file mode 100644
index 0000000..725498e
--- /dev/null
+++ b/lib/crypto/rsapubkey.asn1
@@ -0,0 +1,4 @@
+RsaPubKey ::= SEQUENCE {
+	n INTEGER ({ rsa_get_n }),
+	e INTEGER ({ rsa_get_e })
+}
diff --git a/lib/crypto/x509.asn1 b/lib/crypto/x509.asn1
new file mode 100644
index 0000000..5c9f4e4
--- /dev/null
+++ b/lib/crypto/x509.asn1
@@ -0,0 +1,60 @@
+Certificate ::= SEQUENCE {
+	tbsCertificate		TBSCertificate ({ x509_note_tbs_certificate }),
+	signatureAlgorithm	AlgorithmIdentifier,
+	signature		BIT STRING ({ x509_note_signature })
+	}
+
+TBSCertificate ::= SEQUENCE {
+	version           [ 0 ]	Version DEFAULT,
+	serialNumber		CertificateSerialNumber ({ x509_note_serial }),
+	signature		AlgorithmIdentifier ({ x509_note_pkey_algo }),
+	issuer			Name ({ x509_note_issuer }),
+	validity		Validity,
+	subject			Name ({ x509_note_subject }),
+	subjectPublicKeyInfo	SubjectPublicKeyInfo,
+	issuerUniqueID    [ 1 ]	IMPLICIT UniqueIdentifier OPTIONAL,
+	subjectUniqueID   [ 2 ]	IMPLICIT UniqueIdentifier OPTIONAL,
+	extensions        [ 3 ]	Extensions OPTIONAL
+	}
+
+Version ::= INTEGER
+CertificateSerialNumber ::= INTEGER
+
+AlgorithmIdentifier ::= SEQUENCE {
+	algorithm		OBJECT IDENTIFIER ({ x509_note_OID }),
+	parameters		ANY OPTIONAL ({ x509_note_params })
+}
+
+Name ::= SEQUENCE OF RelativeDistinguishedName
+
+RelativeDistinguishedName ::= SET OF AttributeValueAssertion
+
+AttributeValueAssertion ::= SEQUENCE {
+	attributeType		OBJECT IDENTIFIER ({ x509_note_OID }),
+	attributeValue		ANY ({ x509_extract_name_segment })
+	}
+
+Validity ::= SEQUENCE {
+	notBefore		Time ({ x509_note_not_before }),
+	notAfter		Time ({ x509_note_not_after })
+	}
+
+Time ::= CHOICE {
+	utcTime			UTCTime,
+	generalTime		GeneralizedTime
+	}
+
+SubjectPublicKeyInfo ::= SEQUENCE {
+	algorithm		AlgorithmIdentifier,
+	subjectPublicKey	BIT STRING ({ x509_extract_key_data })
+	}
+
+UniqueIdentifier ::= BIT STRING
+
+Extensions ::= SEQUENCE OF Extension
+
+Extension ::= SEQUENCE {
+	extnid			OBJECT IDENTIFIER ({ x509_note_OID }),
+	critical		BOOLEAN DEFAULT,
+	extnValue		OCTET STRING ({ x509_process_extension })
+	}
diff --git a/lib/crypto/x509_akid.asn1 b/lib/crypto/x509_akid.asn1
new file mode 100644
index 0000000..1a33231
--- /dev/null
+++ b/lib/crypto/x509_akid.asn1
@@ -0,0 +1,35 @@
+-- X.509 AuthorityKeyIdentifier
+-- rfc5280 section 4.2.1.1
+
+AuthorityKeyIdentifier ::= SEQUENCE {
+	keyIdentifier			[0] IMPLICIT KeyIdentifier		OPTIONAL,
+	authorityCertIssuer		[1] IMPLICIT GeneralNames		OPTIONAL,
+	authorityCertSerialNumber	[2] IMPLICIT CertificateSerialNumber	OPTIONAL
+	}
+
+KeyIdentifier ::= OCTET STRING ({ x509_akid_note_kid })
+
+CertificateSerialNumber ::= INTEGER ({ x509_akid_note_serial })
+
+GeneralNames ::= SEQUENCE OF GeneralName
+
+GeneralName ::= CHOICE {
+	otherName			[0] ANY,
+	rfc822Name			[1] IA5String,
+	dNSName				[2] IA5String,
+	x400Address			[3] ANY,
+	directoryName			[4] Name ({ x509_akid_note_name }),
+	ediPartyName			[5] ANY,
+	uniformResourceIdentifier	[6] IA5String,
+	iPAddress			[7] OCTET STRING,
+	registeredID			[8] OBJECT IDENTIFIER
+	}
+
+Name ::= SEQUENCE OF RelativeDistinguishedName
+
+RelativeDistinguishedName ::= SET OF AttributeValueAssertion
+
+AttributeValueAssertion ::= SEQUENCE {
+	attributeType		OBJECT IDENTIFIER ({ x509_note_OID }),
+	attributeValue		ANY ({ x509_extract_name_segment })
+	}
diff --git a/lib/crypto/x509_cert_parser.c b/lib/crypto/x509_cert_parser.c
new file mode 100644
index 0000000..e6d2a42
--- /dev/null
+++ b/lib/crypto/x509_cert_parser.c
@@ -0,0 +1,697 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* X.509 certificate parser
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#define pr_fmt(fmt) "X.509: "fmt
+#include <linux/kernel.h>
+#ifndef __UBOOT__
+#include <linux/export.h>
+#include <linux/slab.h>
+#endif
+#include <linux/err.h>
+#include <linux/oid_registry.h>
+#ifdef __UBOOT__
+#include <linux/string.h>
+#endif
+#include <crypto/public_key.h>
+#include "x509_parser.h"
+#include "x509.asn1.h"
+#include "x509_akid.asn1.h"
+
+struct x509_parse_context {
+	struct x509_certificate	*cert;		/* Certificate being constructed */
+	unsigned long	data;			/* Start of data */
+	const void	*cert_start;		/* Start of cert content */
+	const void	*key;			/* Key data */
+	size_t		key_size;		/* Size of key data */
+	const void	*params;		/* Key parameters */
+	size_t		params_size;		/* Size of key parameters */
+	enum OID	key_algo;		/* Public key algorithm */
+	enum OID	last_oid;		/* Last OID encountered */
+	enum OID	algo_oid;		/* Algorithm OID */
+	unsigned char	nr_mpi;			/* Number of MPIs stored */
+	u8		o_size;			/* Size of organizationName (O) */
+	u8		cn_size;		/* Size of commonName (CN) */
+	u8		email_size;		/* Size of emailAddress */
+	u16		o_offset;		/* Offset of organizationName (O) */
+	u16		cn_offset;		/* Offset of commonName (CN) */
+	u16		email_offset;		/* Offset of emailAddress */
+	unsigned	raw_akid_size;
+	const void	*raw_akid;		/* Raw authorityKeyId in ASN.1 */
+	const void	*akid_raw_issuer;	/* Raw directoryName in authorityKeyId */
+	unsigned	akid_raw_issuer_size;
+};
+
+/*
+ * Free an X.509 certificate
+ */
+void x509_free_certificate(struct x509_certificate *cert)
+{
+	if (cert) {
+		public_key_free(cert->pub);
+		public_key_signature_free(cert->sig);
+		kfree(cert->issuer);
+		kfree(cert->subject);
+		kfree(cert->id);
+		kfree(cert->skid);
+		kfree(cert);
+	}
+}
+EXPORT_SYMBOL_GPL(x509_free_certificate);
+
+/*
+ * Parse an X.509 certificate
+ */
+struct x509_certificate *x509_cert_parse(const void *data, size_t datalen)
+{
+	struct x509_certificate *cert;
+	struct x509_parse_context *ctx;
+	struct asymmetric_key_id *kid;
+	long ret;
+
+	ret = -ENOMEM;
+	cert = kzalloc(sizeof(struct x509_certificate), GFP_KERNEL);
+	if (!cert)
+		goto error_no_cert;
+	cert->pub = kzalloc(sizeof(struct public_key), GFP_KERNEL);
+	if (!cert->pub)
+		goto error_no_ctx;
+	cert->sig = kzalloc(sizeof(struct public_key_signature), GFP_KERNEL);
+	if (!cert->sig)
+		goto error_no_ctx;
+	ctx = kzalloc(sizeof(struct x509_parse_context), GFP_KERNEL);
+	if (!ctx)
+		goto error_no_ctx;
+
+	ctx->cert = cert;
+	ctx->data = (unsigned long)data;
+
+	/* Attempt to decode the certificate */
+	ret = asn1_ber_decoder(&x509_decoder, ctx, data, datalen);
+	if (ret < 0)
+		goto error_decode;
+
+	/* Decode the AuthorityKeyIdentifier */
+	if (ctx->raw_akid) {
+		pr_devel("AKID: %u %*phN\n",
+			 ctx->raw_akid_size, ctx->raw_akid_size, ctx->raw_akid);
+		ret = asn1_ber_decoder(&x509_akid_decoder, ctx,
+				       ctx->raw_akid, ctx->raw_akid_size);
+		if (ret < 0) {
+			pr_warn("Couldn't decode AuthKeyIdentifier\n");
+			goto error_decode;
+		}
+	}
+
+	ret = -ENOMEM;
+	cert->pub->key = kmemdup(ctx->key, ctx->key_size, GFP_KERNEL);
+	if (!cert->pub->key)
+		goto error_decode;
+
+	cert->pub->keylen = ctx->key_size;
+
+	cert->pub->params = kmemdup(ctx->params, ctx->params_size, GFP_KERNEL);
+	if (!cert->pub->params)
+		goto error_decode;
+
+	cert->pub->paramlen = ctx->params_size;
+	cert->pub->algo = ctx->key_algo;
+
+	/* Grab the signature bits */
+	ret = x509_get_sig_params(cert);
+	if (ret < 0)
+		goto error_decode;
+
+	/* Generate cert issuer + serial number key ID */
+	kid = asymmetric_key_generate_id(cert->raw_serial,
+					 cert->raw_serial_size,
+					 cert->raw_issuer,
+					 cert->raw_issuer_size);
+	if (IS_ERR(kid)) {
+		ret = PTR_ERR(kid);
+		goto error_decode;
+	}
+	cert->id = kid;
+
+#ifndef __UBOOT__
+	/* Detect self-signed certificates */
+	ret = x509_check_for_self_signed(cert);
+	if (ret < 0)
+		goto error_decode;
+#endif
+
+	kfree(ctx);
+	return cert;
+
+error_decode:
+	kfree(ctx);
+error_no_ctx:
+	x509_free_certificate(cert);
+error_no_cert:
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(x509_cert_parse);
+
+/*
+ * Note an OID when we find one for later processing when we know how
+ * to interpret it.
+ */
+int x509_note_OID(void *context, size_t hdrlen,
+	     unsigned char tag,
+	     const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+
+	ctx->last_oid = look_up_OID(value, vlen);
+	if (ctx->last_oid == OID__NR) {
+		char buffer[50];
+		sprint_oid(value, vlen, buffer, sizeof(buffer));
+		pr_debug("Unknown OID: [%lu] %s\n",
+			 (unsigned long)value - ctx->data, buffer);
+	}
+	return 0;
+}
+
+/*
+ * Save the position of the TBS data so that we can check the signature over it
+ * later.
+ */
+int x509_note_tbs_certificate(void *context, size_t hdrlen,
+			      unsigned char tag,
+			      const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+
+	pr_debug("x509_note_tbs_certificate(,%zu,%02x,%ld,%zu)!\n",
+		 hdrlen, tag, (unsigned long)value - ctx->data, vlen);
+
+	ctx->cert->tbs = value - hdrlen;
+	ctx->cert->tbs_size = vlen + hdrlen;
+	return 0;
+}
+
+/*
+ * Record the public key algorithm
+ */
+int x509_note_pkey_algo(void *context, size_t hdrlen,
+			unsigned char tag,
+			const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+
+	pr_debug("PubKey Algo: %u\n", ctx->last_oid);
+
+	switch (ctx->last_oid) {
+	case OID_md2WithRSAEncryption:
+	case OID_md3WithRSAEncryption:
+	default:
+		return -ENOPKG; /* Unsupported combination */
+
+	case OID_md4WithRSAEncryption:
+		ctx->cert->sig->hash_algo = "md4";
+		goto rsa_pkcs1;
+
+	case OID_sha1WithRSAEncryption:
+		ctx->cert->sig->hash_algo = "sha1";
+		goto rsa_pkcs1;
+
+	case OID_sha256WithRSAEncryption:
+		ctx->cert->sig->hash_algo = "sha256";
+		goto rsa_pkcs1;
+
+	case OID_sha384WithRSAEncryption:
+		ctx->cert->sig->hash_algo = "sha384";
+		goto rsa_pkcs1;
+
+	case OID_sha512WithRSAEncryption:
+		ctx->cert->sig->hash_algo = "sha512";
+		goto rsa_pkcs1;
+
+	case OID_sha224WithRSAEncryption:
+		ctx->cert->sig->hash_algo = "sha224";
+		goto rsa_pkcs1;
+
+	case OID_gost2012Signature256:
+		ctx->cert->sig->hash_algo = "streebog256";
+		goto ecrdsa;
+
+	case OID_gost2012Signature512:
+		ctx->cert->sig->hash_algo = "streebog512";
+		goto ecrdsa;
+	}
+
+rsa_pkcs1:
+	ctx->cert->sig->pkey_algo = "rsa";
+	ctx->cert->sig->encoding = "pkcs1";
+	ctx->algo_oid = ctx->last_oid;
+	return 0;
+ecrdsa:
+	ctx->cert->sig->pkey_algo = "ecrdsa";
+	ctx->cert->sig->encoding = "raw";
+	ctx->algo_oid = ctx->last_oid;
+	return 0;
+}
+
+/*
+ * Note the whereabouts and type of the signature.
+ */
+int x509_note_signature(void *context, size_t hdrlen,
+			unsigned char tag,
+			const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+
+	pr_debug("Signature type: %u size %zu\n", ctx->last_oid, vlen);
+
+	if (ctx->last_oid != ctx->algo_oid) {
+		pr_warn("Got cert with pkey (%u) and sig (%u) algorithm OIDs\n",
+			ctx->algo_oid, ctx->last_oid);
+		return -EINVAL;
+	}
+
+	if (strcmp(ctx->cert->sig->pkey_algo, "rsa") == 0 ||
+	    strcmp(ctx->cert->sig->pkey_algo, "ecrdsa") == 0) {
+		/* Discard the BIT STRING metadata */
+		if (vlen < 1 || *(const u8 *)value != 0)
+			return -EBADMSG;
+
+		value++;
+		vlen--;
+	}
+
+	ctx->cert->raw_sig = value;
+	ctx->cert->raw_sig_size = vlen;
+	return 0;
+}
+
+/*
+ * Note the certificate serial number
+ */
+int x509_note_serial(void *context, size_t hdrlen,
+		     unsigned char tag,
+		     const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+	ctx->cert->raw_serial = value;
+	ctx->cert->raw_serial_size = vlen;
+	return 0;
+}
+
+/*
+ * Note some of the name segments from which we'll fabricate a name.
+ */
+int x509_extract_name_segment(void *context, size_t hdrlen,
+			      unsigned char tag,
+			      const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+
+	switch (ctx->last_oid) {
+	case OID_commonName:
+		ctx->cn_size = vlen;
+		ctx->cn_offset = (unsigned long)value - ctx->data;
+		break;
+	case OID_organizationName:
+		ctx->o_size = vlen;
+		ctx->o_offset = (unsigned long)value - ctx->data;
+		break;
+	case OID_email_address:
+		ctx->email_size = vlen;
+		ctx->email_offset = (unsigned long)value - ctx->data;
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+/*
+ * Fabricate and save the issuer and subject names
+ */
+static int x509_fabricate_name(struct x509_parse_context *ctx, size_t hdrlen,
+			       unsigned char tag,
+			       char **_name, size_t vlen)
+{
+	const void *name, *data = (const void *)ctx->data;
+	size_t namesize;
+	char *buffer;
+
+	if (*_name)
+		return -EINVAL;
+
+	/* Empty name string if no material */
+	if (!ctx->cn_size && !ctx->o_size && !ctx->email_size) {
+		buffer = kmalloc(1, GFP_KERNEL);
+		if (!buffer)
+			return -ENOMEM;
+		buffer[0] = 0;
+		goto done;
+	}
+
+	if (ctx->cn_size && ctx->o_size) {
+		/* Consider combining O and CN, but use only the CN if it is
+		 * prefixed by the O, or a significant portion thereof.
+		 */
+		namesize = ctx->cn_size;
+		name = data + ctx->cn_offset;
+		if (ctx->cn_size >= ctx->o_size &&
+		    memcmp(data + ctx->cn_offset, data + ctx->o_offset,
+			   ctx->o_size) == 0)
+			goto single_component;
+		if (ctx->cn_size >= 7 &&
+		    ctx->o_size >= 7 &&
+		    memcmp(data + ctx->cn_offset, data + ctx->o_offset, 7) == 0)
+			goto single_component;
+
+		buffer = kmalloc(ctx->o_size + 2 + ctx->cn_size + 1,
+				 GFP_KERNEL);
+		if (!buffer)
+			return -ENOMEM;
+
+		memcpy(buffer,
+		       data + ctx->o_offset, ctx->o_size);
+		buffer[ctx->o_size + 0] = ':';
+		buffer[ctx->o_size + 1] = ' ';
+		memcpy(buffer + ctx->o_size + 2,
+		       data + ctx->cn_offset, ctx->cn_size);
+		buffer[ctx->o_size + 2 + ctx->cn_size] = 0;
+		goto done;
+
+	} else if (ctx->cn_size) {
+		namesize = ctx->cn_size;
+		name = data + ctx->cn_offset;
+	} else if (ctx->o_size) {
+		namesize = ctx->o_size;
+		name = data + ctx->o_offset;
+	} else {
+		namesize = ctx->email_size;
+		name = data + ctx->email_offset;
+	}
+
+single_component:
+	buffer = kmalloc(namesize + 1, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+	memcpy(buffer, name, namesize);
+	buffer[namesize] = 0;
+
+done:
+	*_name = buffer;
+	ctx->cn_size = 0;
+	ctx->o_size = 0;
+	ctx->email_size = 0;
+	return 0;
+}
+
+int x509_note_issuer(void *context, size_t hdrlen,
+		     unsigned char tag,
+		     const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+	ctx->cert->raw_issuer = value;
+	ctx->cert->raw_issuer_size = vlen;
+	return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->issuer, vlen);
+}
+
+int x509_note_subject(void *context, size_t hdrlen,
+		      unsigned char tag,
+		      const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+	ctx->cert->raw_subject = value;
+	ctx->cert->raw_subject_size = vlen;
+	return x509_fabricate_name(ctx, hdrlen, tag, &ctx->cert->subject, vlen);
+}
+
+/*
+ * Extract the parameters for the public key
+ */
+int x509_note_params(void *context, size_t hdrlen,
+		     unsigned char tag,
+		     const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+
+	/*
+	 * AlgorithmIdentifier is used three times in the x509, we should skip
+	 * first and ignore third, using second one which is after subject and
+	 * before subjectPublicKey.
+	 */
+	if (!ctx->cert->raw_subject || ctx->key)
+		return 0;
+	ctx->params = value - hdrlen;
+	ctx->params_size = vlen + hdrlen;
+	return 0;
+}
+
+/*
+ * Extract the data for the public key algorithm
+ */
+int x509_extract_key_data(void *context, size_t hdrlen,
+			  unsigned char tag,
+			  const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+
+	ctx->key_algo = ctx->last_oid;
+	if (ctx->last_oid == OID_rsaEncryption)
+		ctx->cert->pub->pkey_algo = "rsa";
+	else if (ctx->last_oid == OID_gost2012PKey256 ||
+		 ctx->last_oid == OID_gost2012PKey512)
+		ctx->cert->pub->pkey_algo = "ecrdsa";
+	else
+		return -ENOPKG;
+
+	/* Discard the BIT STRING metadata */
+	if (vlen < 1 || *(const u8 *)value != 0)
+		return -EBADMSG;
+	ctx->key = value + 1;
+	ctx->key_size = vlen - 1;
+	return 0;
+}
+
+/* The keyIdentifier in AuthorityKeyIdentifier SEQUENCE is tag(CONT,PRIM,0) */
+#define SEQ_TAG_KEYID (ASN1_CONT << 6)
+
+/*
+ * Process certificate extensions that are used to qualify the certificate.
+ */
+int x509_process_extension(void *context, size_t hdrlen,
+			   unsigned char tag,
+			   const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+	struct asymmetric_key_id *kid;
+	const unsigned char *v = value;
+
+	pr_debug("Extension: %u\n", ctx->last_oid);
+
+	if (ctx->last_oid == OID_subjectKeyIdentifier) {
+		/* Get hold of the key fingerprint */
+		if (ctx->cert->skid || vlen < 3)
+			return -EBADMSG;
+		if (v[0] != ASN1_OTS || v[1] != vlen - 2)
+			return -EBADMSG;
+		v += 2;
+		vlen -= 2;
+
+		ctx->cert->raw_skid_size = vlen;
+		ctx->cert->raw_skid = v;
+		kid = asymmetric_key_generate_id(v, vlen, "", 0);
+		if (IS_ERR(kid))
+			return PTR_ERR(kid);
+		ctx->cert->skid = kid;
+		pr_debug("subjkeyid %*phN\n", kid->len, kid->data);
+		return 0;
+	}
+
+	if (ctx->last_oid == OID_authorityKeyIdentifier) {
+		/* Get hold of the CA key fingerprint */
+		ctx->raw_akid = v;
+		ctx->raw_akid_size = vlen;
+		return 0;
+	}
+
+	return 0;
+}
+
+/**
+ * x509_decode_time - Decode an X.509 time ASN.1 object
+ * @_t: The time to fill in
+ * @hdrlen: The length of the object header
+ * @tag: The object tag
+ * @value: The object value
+ * @vlen: The size of the object value
+ *
+ * Decode an ASN.1 universal time or generalised time field into a struct the
+ * kernel can handle and check it for validity.  The time is decoded thus:
+ *
+ *	[RFC5280 §4.1.2.5]
+ *	CAs conforming to this profile MUST always encode certificate validity
+ *	dates through the year 2049 as UTCTime; certificate validity dates in
+ *	2050 or later MUST be encoded as GeneralizedTime.  Conforming
+ *	applications MUST be able to process validity dates that are encoded in
+ *	either UTCTime or GeneralizedTime.
+ */
+int x509_decode_time(time64_t *_t,  size_t hdrlen,
+		     unsigned char tag,
+		     const unsigned char *value, size_t vlen)
+{
+	static const unsigned char month_lengths[] = { 31, 28, 31, 30, 31, 30,
+						       31, 31, 30, 31, 30, 31 };
+	const unsigned char *p = value;
+	unsigned year, mon, day, hour, min, sec, mon_len;
+
+#define dec2bin(X) ({ unsigned char x = (X) - '0'; if (x > 9) goto invalid_time; x; })
+#define DD2bin(P) ({ unsigned x = dec2bin(P[0]) * 10 + dec2bin(P[1]); P += 2; x; })
+
+	if (tag == ASN1_UNITIM) {
+		/* UTCTime: YYMMDDHHMMSSZ */
+		if (vlen != 13)
+			goto unsupported_time;
+		year = DD2bin(p);
+		if (year >= 50)
+			year += 1900;
+		else
+			year += 2000;
+	} else if (tag == ASN1_GENTIM) {
+		/* GenTime: YYYYMMDDHHMMSSZ */
+		if (vlen != 15)
+			goto unsupported_time;
+		year = DD2bin(p) * 100 + DD2bin(p);
+		if (year >= 1950 && year <= 2049)
+			goto invalid_time;
+	} else {
+		goto unsupported_time;
+	}
+
+	mon  = DD2bin(p);
+	day = DD2bin(p);
+	hour = DD2bin(p);
+	min  = DD2bin(p);
+	sec  = DD2bin(p);
+
+	if (*p != 'Z')
+		goto unsupported_time;
+
+	if (year < 1970 ||
+	    mon < 1 || mon > 12)
+		goto invalid_time;
+
+	mon_len = month_lengths[mon - 1];
+	if (mon == 2) {
+		if (year % 4 == 0) {
+			mon_len = 29;
+			if (year % 100 == 0) {
+				mon_len = 28;
+				if (year % 400 == 0)
+					mon_len = 29;
+			}
+		}
+	}
+
+	if (day < 1 || day > mon_len ||
+	    hour > 24 || /* ISO 8601 permits 24:00:00 as midnight tomorrow */
+	    min > 59 ||
+	    sec > 60) /* ISO 8601 permits leap seconds [X.680 46.3] */
+		goto invalid_time;
+
+	*_t = mktime64(year, mon, day, hour, min, sec);
+	return 0;
+
+unsupported_time:
+	pr_debug("Got unsupported time [tag %02x]: '%*phN'\n",
+		 tag, (int)vlen, value);
+	return -EBADMSG;
+invalid_time:
+	pr_debug("Got invalid time [tag %02x]: '%*phN'\n",
+		 tag, (int)vlen, value);
+	return -EBADMSG;
+}
+EXPORT_SYMBOL_GPL(x509_decode_time);
+
+int x509_note_not_before(void *context, size_t hdrlen,
+			 unsigned char tag,
+			 const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+	return x509_decode_time(&ctx->cert->valid_from, hdrlen, tag, value, vlen);
+}
+
+int x509_note_not_after(void *context, size_t hdrlen,
+			unsigned char tag,
+			const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+	return x509_decode_time(&ctx->cert->valid_to, hdrlen, tag, value, vlen);
+}
+
+/*
+ * Note a key identifier-based AuthorityKeyIdentifier
+ */
+int x509_akid_note_kid(void *context, size_t hdrlen,
+		       unsigned char tag,
+		       const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+	struct asymmetric_key_id *kid;
+
+	pr_debug("AKID: keyid: %*phN\n", (int)vlen, value);
+
+	if (ctx->cert->sig->auth_ids[1])
+		return 0;
+
+	kid = asymmetric_key_generate_id(value, vlen, "", 0);
+	if (IS_ERR(kid))
+		return PTR_ERR(kid);
+	pr_debug("authkeyid %*phN\n", kid->len, kid->data);
+	ctx->cert->sig->auth_ids[1] = kid;
+	return 0;
+}
+
+/*
+ * Note a directoryName in an AuthorityKeyIdentifier
+ */
+int x509_akid_note_name(void *context, size_t hdrlen,
+			unsigned char tag,
+			const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+
+	pr_debug("AKID: name: %*phN\n", (int)vlen, value);
+
+	ctx->akid_raw_issuer = value;
+	ctx->akid_raw_issuer_size = vlen;
+	return 0;
+}
+
+/*
+ * Note a serial number in an AuthorityKeyIdentifier
+ */
+int x509_akid_note_serial(void *context, size_t hdrlen,
+			  unsigned char tag,
+			  const void *value, size_t vlen)
+{
+	struct x509_parse_context *ctx = context;
+	struct asymmetric_key_id *kid;
+
+	pr_debug("AKID: serial: %*phN\n", (int)vlen, value);
+
+	if (!ctx->akid_raw_issuer || ctx->cert->sig->auth_ids[0])
+		return 0;
+
+	kid = asymmetric_key_generate_id(value,
+					 vlen,
+					 ctx->akid_raw_issuer,
+					 ctx->akid_raw_issuer_size);
+	if (IS_ERR(kid))
+		return PTR_ERR(kid);
+
+	pr_debug("authkeyid %*phN\n", kid->len, kid->data);
+	ctx->cert->sig->auth_ids[0] = kid;
+	return 0;
+}
diff --git a/lib/crypto/x509_parser.h b/lib/crypto/x509_parser.h
new file mode 100644
index 0000000..c233f13
--- /dev/null
+++ b/lib/crypto/x509_parser.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* X.509 certificate parser internal definitions
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#include <linux/time.h>
+#include <crypto/public_key.h>
+#include <keys/asymmetric-type.h>
+
+struct x509_certificate {
+	struct x509_certificate *next;
+	struct x509_certificate *signer;	/* Certificate that signed this one */
+	struct public_key *pub;			/* Public key details */
+	struct public_key_signature *sig;	/* Signature parameters */
+	char		*issuer;		/* Name of certificate issuer */
+	char		*subject;		/* Name of certificate subject */
+	struct asymmetric_key_id *id;		/* Issuer + Serial number */
+	struct asymmetric_key_id *skid;		/* Subject + subjectKeyId (optional) */
+	time64_t	valid_from;
+	time64_t	valid_to;
+	const void	*tbs;			/* Signed data */
+	unsigned	tbs_size;		/* Size of signed data */
+	unsigned	raw_sig_size;		/* Size of sigature */
+	const void	*raw_sig;		/* Signature data */
+	const void	*raw_serial;		/* Raw serial number in ASN.1 */
+	unsigned	raw_serial_size;
+	unsigned	raw_issuer_size;
+	const void	*raw_issuer;		/* Raw issuer name in ASN.1 */
+	const void	*raw_subject;		/* Raw subject name in ASN.1 */
+	unsigned	raw_subject_size;
+	unsigned	raw_skid_size;
+	const void	*raw_skid;		/* Raw subjectKeyId in ASN.1 */
+	unsigned	index;
+	bool		seen;			/* Infinite recursion prevention */
+	bool		verified;
+	bool		self_signed;		/* T if self-signed (check unsupported_sig too) */
+	bool		unsupported_key;	/* T if key uses unsupported crypto */
+	bool		unsupported_sig;	/* T if signature uses unsupported crypto */
+	bool		blacklisted;
+};
+
+/*
+ * x509_cert_parser.c
+ */
+extern void x509_free_certificate(struct x509_certificate *cert);
+extern struct x509_certificate *x509_cert_parse(const void *data, size_t datalen);
+extern int x509_decode_time(time64_t *_t,  size_t hdrlen,
+			    unsigned char tag,
+			    const unsigned char *value, size_t vlen);
+
+/*
+ * x509_public_key.c
+ */
+extern int x509_get_sig_params(struct x509_certificate *cert);
+extern int x509_check_for_self_signed(struct x509_certificate *cert);
diff --git a/lib/crypto/x509_public_key.c b/lib/crypto/x509_public_key.c
new file mode 100644
index 0000000..04bdb67
--- /dev/null
+++ b/lib/crypto/x509_public_key.c
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Instantiate a public key crypto key from an X.509 Certificate
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#define pr_fmt(fmt) "X.509: "fmt
+#ifdef __UBOOT__
+#include <common.h>
+#include <linux/compat.h>
+#include <linux/errno.h>
+#else
+#include <linux/module.h>
+#endif
+#include <linux/kernel.h>
+#ifndef __UBOOT__
+#include <linux/slab.h>
+#include <keys/asymmetric-subtype.h>
+#include <keys/asymmetric-parser.h>
+#include <keys/system_keyring.h>
+#include <crypto/hash.h>
+#include "asymmetric_keys.h"
+#endif
+#include "x509_parser.h"
+
+/*
+ * Set up the signature parameters in an X.509 certificate.  This involves
+ * digesting the signed data and extracting the signature.
+ */
+int x509_get_sig_params(struct x509_certificate *cert)
+{
+	struct public_key_signature *sig = cert->sig;
+#ifndef __UBOOT__
+	struct crypto_shash *tfm;
+	struct shash_desc *desc;
+	size_t desc_size;
+#endif
+	int ret;
+
+	pr_devel("==>%s()\n", __func__);
+
+	if (!cert->pub->pkey_algo)
+		cert->unsupported_key = true;
+
+	if (!sig->pkey_algo)
+		cert->unsupported_sig = true;
+
+	/* We check the hash if we can - even if we can't then verify it */
+	if (!sig->hash_algo) {
+		cert->unsupported_sig = true;
+		return 0;
+	}
+
+	sig->s = kmemdup(cert->raw_sig, cert->raw_sig_size, GFP_KERNEL);
+	if (!sig->s)
+		return -ENOMEM;
+
+	sig->s_size = cert->raw_sig_size;
+
+#ifdef __UBOOT__
+	/*
+	 * Note:
+	 * This part (filling sig->digest) should be implemented if
+	 * x509_check_for_self_signed() is enabled x509_cert_parse().
+	 * Currently, this check won't affect UEFI secure boot.
+	 */
+	ret = 0;
+#else
+	/* Allocate the hashing algorithm we're going to need and find out how
+	 * big the hash operational data will be.
+	 */
+	tfm = crypto_alloc_shash(sig->hash_algo, 0, 0);
+	if (IS_ERR(tfm)) {
+		if (PTR_ERR(tfm) == -ENOENT) {
+			cert->unsupported_sig = true;
+			return 0;
+		}
+		return PTR_ERR(tfm);
+	}
+
+	desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
+	sig->digest_size = crypto_shash_digestsize(tfm);
+
+	ret = -ENOMEM;
+	sig->digest = kmalloc(sig->digest_size, GFP_KERNEL);
+	if (!sig->digest)
+		goto error;
+
+	desc = kzalloc(desc_size, GFP_KERNEL);
+	if (!desc)
+		goto error;
+
+	desc->tfm = tfm;
+
+	ret = crypto_shash_digest(desc, cert->tbs, cert->tbs_size, sig->digest);
+	if (ret < 0)
+		goto error_2;
+
+	ret = is_hash_blacklisted(sig->digest, sig->digest_size, "tbs");
+	if (ret == -EKEYREJECTED) {
+		pr_err("Cert %*phN is blacklisted\n",
+		       sig->digest_size, sig->digest);
+		cert->blacklisted = true;
+		ret = 0;
+	}
+
+error_2:
+	kfree(desc);
+error:
+	crypto_free_shash(tfm);
+#endif /* __UBOOT__ */
+	pr_devel("<==%s() = %d\n", __func__, ret);
+	return ret;
+}
+
+#ifndef __UBOOT__
+/*
+ * Check for self-signedness in an X.509 cert and if found, check the signature
+ * immediately if we can.
+ */
+int x509_check_for_self_signed(struct x509_certificate *cert)
+{
+	int ret = 0;
+
+	pr_devel("==>%s()\n", __func__);
+
+	if (cert->raw_subject_size != cert->raw_issuer_size ||
+	    memcmp(cert->raw_subject, cert->raw_issuer,
+		   cert->raw_issuer_size) != 0)
+		goto not_self_signed;
+
+	if (cert->sig->auth_ids[0] || cert->sig->auth_ids[1]) {
+		/* If the AKID is present it may have one or two parts.  If
+		 * both are supplied, both must match.
+		 */
+		bool a = asymmetric_key_id_same(cert->skid, cert->sig->auth_ids[1]);
+		bool b = asymmetric_key_id_same(cert->id, cert->sig->auth_ids[0]);
+
+		if (!a && !b)
+			goto not_self_signed;
+
+		ret = -EKEYREJECTED;
+		if (((a && !b) || (b && !a)) &&
+		    cert->sig->auth_ids[0] && cert->sig->auth_ids[1])
+			goto out;
+	}
+
+	ret = -EKEYREJECTED;
+	if (strcmp(cert->pub->pkey_algo, cert->sig->pkey_algo) != 0)
+		goto out;
+
+	ret = public_key_verify_signature(cert->pub, cert->sig);
+	if (ret < 0) {
+		if (ret == -ENOPKG) {
+			cert->unsupported_sig = true;
+			ret = 0;
+		}
+		goto out;
+	}
+
+	pr_devel("Cert Self-signature verified");
+	cert->self_signed = true;
+
+out:
+	pr_devel("<==%s() = %d\n", __func__, ret);
+	return ret;
+
+not_self_signed:
+	pr_devel("<==%s() = 0 [not]\n", __func__);
+	return 0;
+}
+
+/*
+ * Attempt to parse a data blob for a key as an X509 certificate.
+ */
+static int x509_key_preparse(struct key_preparsed_payload *prep)
+{
+	struct asymmetric_key_ids *kids;
+	struct x509_certificate *cert;
+	const char *q;
+	size_t srlen, sulen;
+	char *desc = NULL, *p;
+	int ret;
+
+	cert = x509_cert_parse(prep->data, prep->datalen);
+	if (IS_ERR(cert))
+		return PTR_ERR(cert);
+
+	pr_devel("Cert Issuer: %s\n", cert->issuer);
+	pr_devel("Cert Subject: %s\n", cert->subject);
+
+	if (cert->unsupported_key) {
+		ret = -ENOPKG;
+		goto error_free_cert;
+	}
+
+	pr_devel("Cert Key Algo: %s\n", cert->pub->pkey_algo);
+	pr_devel("Cert Valid period: %lld-%lld\n", cert->valid_from, cert->valid_to);
+
+	cert->pub->id_type = "X509";
+
+	if (cert->unsupported_sig) {
+		public_key_signature_free(cert->sig);
+		cert->sig = NULL;
+	} else {
+		pr_devel("Cert Signature: %s + %s\n",
+			 cert->sig->pkey_algo, cert->sig->hash_algo);
+	}
+
+	/* Don't permit addition of blacklisted keys */
+	ret = -EKEYREJECTED;
+	if (cert->blacklisted)
+		goto error_free_cert;
+
+	/* Propose a description */
+	sulen = strlen(cert->subject);
+	if (cert->raw_skid) {
+		srlen = cert->raw_skid_size;
+		q = cert->raw_skid;
+	} else {
+		srlen = cert->raw_serial_size;
+		q = cert->raw_serial;
+	}
+
+	ret = -ENOMEM;
+	desc = kmalloc(sulen + 2 + srlen * 2 + 1, GFP_KERNEL);
+	if (!desc)
+		goto error_free_cert;
+	p = memcpy(desc, cert->subject, sulen);
+	p += sulen;
+	*p++ = ':';
+	*p++ = ' ';
+	p = bin2hex(p, q, srlen);
+	*p = 0;
+
+	kids = kmalloc(sizeof(struct asymmetric_key_ids), GFP_KERNEL);
+	if (!kids)
+		goto error_free_desc;
+	kids->id[0] = cert->id;
+	kids->id[1] = cert->skid;
+
+	/* We're pinning the module by being linked against it */
+	__module_get(public_key_subtype.owner);
+	prep->payload.data[asym_subtype] = &public_key_subtype;
+	prep->payload.data[asym_key_ids] = kids;
+	prep->payload.data[asym_crypto] = cert->pub;
+	prep->payload.data[asym_auth] = cert->sig;
+	prep->description = desc;
+	prep->quotalen = 100;
+
+	/* We've finished with the certificate */
+	cert->pub = NULL;
+	cert->id = NULL;
+	cert->skid = NULL;
+	cert->sig = NULL;
+	desc = NULL;
+	ret = 0;
+
+error_free_desc:
+	kfree(desc);
+error_free_cert:
+	x509_free_certificate(cert);
+	return ret;
+}
+
+static struct asymmetric_key_parser x509_key_parser = {
+	.owner	= THIS_MODULE,
+	.name	= "x509",
+	.parse	= x509_key_preparse,
+};
+
+/*
+ * Module stuff
+ */
+static int __init x509_key_init(void)
+{
+	return register_asymmetric_key_parser(&x509_key_parser);
+}
+
+static void __exit x509_key_exit(void)
+{
+	unregister_asymmetric_key_parser(&x509_key_parser);
+}
+
+module_init(x509_key_init);
+module_exit(x509_key_exit);
+#endif /* !__UBOOT__ */
+
+MODULE_DESCRIPTION("X.509 certificate parser");
+MODULE_AUTHOR("Red Hat, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/date.c b/lib/date.c
similarity index 81%
rename from drivers/rtc/date.c
rename to lib/date.c
index c57317d..0456de7 100644
--- a/drivers/rtc/date.c
+++ b/lib/date.c
@@ -8,9 +8,9 @@
 #include <command.h>
 #include <errno.h>
 #include <rtc.h>
+#include <linux/time.h>
 
-#if defined(CONFIG_CMD_DATE) || defined(CONFIG_DM_RTC) || \
-				defined(CONFIG_TIMESTAMP)
+#if defined(CONFIG_LIB_DATE) || defined(CONFIG_TIMESTAMP)
 
 #define FEBRUARY		2
 #define	STARTOFTIME		1970
@@ -97,4 +97,23 @@
 	return (hours * 60 + tm->tm_min) * 60 + tm->tm_sec;
 }
 
+#endif /* CONFIG_LIB_DATE || CONFIG_TIMESTAMP */
+
+#ifdef CONFIG_LIB_DATE
+/* for compatibility with linux code */
+time64_t mktime64(const unsigned int year, const unsigned int mon,
+		  const unsigned int day, const unsigned int hour,
+		  const unsigned int min, const unsigned int sec)
+{
+	struct rtc_time time;
+
+	time.tm_year = year;
+	time.tm_mon = mon;
+	time.tm_mday = day;
+	time.tm_hour = hour;
+	time.tm_min = min;
+	time.tm_sec = sec;
+
+	return (time64_t)rtc_mktime((const struct rtc_time *)&time);
+}
 #endif
diff --git a/lib/linux_compat.c b/lib/linux_compat.c
index 81ea8fb..3f440de 100644
--- a/lib/linux_compat.c
+++ b/lib/linux_compat.c
@@ -40,3 +40,22 @@
 {
 	return malloc_cache_aligned(obj->sz);
 }
+
+/**
+ * kmemdup - duplicate region of memory
+ *
+ * @src: memory region to duplicate
+ * @len: memory region length
+ * @gfp: GFP mask to use
+ *
+ * Return: newly allocated copy of @src or %NULL in case of error
+ */
+void *kmemdup(const void *src, size_t len, gfp_t gfp)
+{
+	void *p;
+
+	p = kmalloc(len, gfp);
+	if (p)
+		memcpy(p, src, len);
+	return p;
+}
diff --git a/lib/oid_registry.c b/lib/oid_registry.c
new file mode 100644
index 0000000..209edc7
--- /dev/null
+++ b/lib/oid_registry.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* ASN.1 Object identifier (OID) registry
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#ifdef __UBOOT__
+#include <linux/compat.h>
+#else
+#include <linux/module.h>
+#include <linux/export.h>
+#endif
+#include <linux/oid_registry.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/bug.h>
+#include "oid_registry_data.c"
+
+MODULE_DESCRIPTION("OID Registry");
+MODULE_AUTHOR("Red Hat, Inc.");
+MODULE_LICENSE("GPL");
+
+/**
+ * look_up_OID - Find an OID registration for the specified data
+ * @data: Binary representation of the OID
+ * @datasize: Size of the binary representation
+ */
+enum OID look_up_OID(const void *data, size_t datasize)
+{
+	const unsigned char *octets = data;
+	enum OID oid;
+	unsigned char xhash;
+	unsigned i, j, k, hash;
+	size_t len;
+
+	/* Hash the OID data */
+	hash = datasize - 1;
+
+	for (i = 0; i < datasize; i++)
+		hash += octets[i] * 33;
+	hash = (hash >> 24) ^ (hash >> 16) ^ (hash >> 8) ^ hash;
+	hash &= 0xff;
+
+	/* Binary search the OID registry.  OIDs are stored in ascending order
+	 * of hash value then ascending order of size and then in ascending
+	 * order of reverse value.
+	 */
+	i = 0;
+	k = OID__NR;
+	while (i < k) {
+		j = (i + k) / 2;
+
+		xhash = oid_search_table[j].hash;
+		if (xhash > hash) {
+			k = j;
+			continue;
+		}
+		if (xhash < hash) {
+			i = j + 1;
+			continue;
+		}
+
+		oid = oid_search_table[j].oid;
+		len = oid_index[oid + 1] - oid_index[oid];
+		if (len > datasize) {
+			k = j;
+			continue;
+		}
+		if (len < datasize) {
+			i = j + 1;
+			continue;
+		}
+
+		/* Variation is most likely to be at the tail end of the
+		 * OID, so do the comparison in reverse.
+		 */
+		while (len > 0) {
+			unsigned char a = oid_data[oid_index[oid] + --len];
+			unsigned char b = octets[len];
+			if (a > b) {
+				k = j;
+				goto next;
+			}
+			if (a < b) {
+				i = j + 1;
+				goto next;
+			}
+		}
+		return oid;
+	next:
+		;
+	}
+
+	return OID__NR;
+}
+EXPORT_SYMBOL_GPL(look_up_OID);
+
+/*
+ * sprint_OID - Print an Object Identifier into a buffer
+ * @data: The encoded OID to print
+ * @datasize: The size of the encoded OID
+ * @buffer: The buffer to render into
+ * @bufsize: The size of the buffer
+ *
+ * The OID is rendered into the buffer in "a.b.c.d" format and the number of
+ * bytes is returned.  -EBADMSG is returned if the data could not be intepreted
+ * and -ENOBUFS if the buffer was too small.
+ */
+int sprint_oid(const void *data, size_t datasize, char *buffer, size_t bufsize)
+{
+	const unsigned char *v = data, *end = v + datasize;
+	unsigned long num;
+	unsigned char n;
+	size_t ret;
+	int count;
+
+	if (v >= end)
+		goto bad;
+
+	n = *v++;
+	ret = count = snprintf(buffer, bufsize, "%u.%u", n / 40, n % 40);
+	if (count >= bufsize)
+		return -ENOBUFS;
+	buffer += count;
+	bufsize -= count;
+
+	while (v < end) {
+		num = 0;
+		n = *v++;
+		if (!(n & 0x80)) {
+			num = n;
+		} else {
+			num = n & 0x7f;
+			do {
+				if (v >= end)
+					goto bad;
+				n = *v++;
+				num <<= 7;
+				num |= n & 0x7f;
+			} while (n & 0x80);
+		}
+		ret += count = snprintf(buffer, bufsize, ".%lu", num);
+		if (count >= bufsize)
+			return -ENOBUFS;
+		buffer += count;
+		bufsize -= count;
+	}
+
+	return ret;
+
+bad:
+	snprintf(buffer, bufsize, "(bad)");
+	return -EBADMSG;
+}
+EXPORT_SYMBOL_GPL(sprint_oid);
+
+/**
+ * sprint_OID - Print an Object Identifier into a buffer
+ * @oid: The OID to print
+ * @buffer: The buffer to render into
+ * @bufsize: The size of the buffer
+ *
+ * The OID is rendered into the buffer in "a.b.c.d" format and the number of
+ * bytes is returned.
+ */
+int sprint_OID(enum OID oid, char *buffer, size_t bufsize)
+{
+	int ret;
+
+	BUG_ON(oid >= OID__NR);
+
+	ret = sprint_oid(oid_data + oid_index[oid],
+			 oid_index[oid + 1] - oid_index[oid],
+			 buffer, bufsize);
+	BUG_ON(ret == -EBADMSG);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(sprint_OID);
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index f7a0412..26eb701 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -328,10 +328,10 @@
 # ASN.1 grammar
 # ---------------------------------------------------------------------------
 quiet_cmd_asn1_compiler = ASN.1   $@
-      cmd_asn1_compiler = $(objtree)/scripts/asn1_compiler $< \
+      cmd_asn1_compiler = $(objtree)/tools/asn1_compiler $< \
 				$(subst .h,.c,$@) $(subst .c,.h,$@)
 
-$(obj)/%-asn1.c $(obj)/%-asn1.h: $(src)/%.asn1 $(objtree)/scripts/asn1_compiler
+$(obj)/%.asn1.c $(obj)/%.asn1.h: $(src)/%.asn1 $(objtree)/tools/asn1_compiler
 	$(call cmd,asn1_compiler)
 
 # Build the compiled-in targets
diff --git a/scripts/build_OID_registry b/scripts/build_OID_registry
new file mode 100755
index 0000000..d7fc32e
--- /dev/null
+++ b/scripts/build_OID_registry
@@ -0,0 +1,203 @@
+#!/usr/bin/perl -w
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Build a static ASN.1 Object Identified (OID) registry
+#
+# Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+# Written by David Howells (dhowells@redhat.com)
+#
+
+use strict;
+
+my @names = ();
+my @oids = ();
+
+if ($#ARGV != 1) {
+    print STDERR "Format: ", $0, " <in-h-file> <out-c-file>\n";
+    exit(2);
+}
+
+#
+# Open the file to read from
+#
+open IN_FILE, "<$ARGV[0]" || die;
+while (<IN_FILE>) {
+    chomp;
+    if (m!\s+OID_([a-zA-z][a-zA-Z0-9_]+),\s+/[*]\s+([012][.0-9]*)\s+[*]/!) {
+	push @names, $1;
+	push @oids, $2;
+    }
+}
+close IN_FILE || die;
+
+#
+# Open the files to write into
+#
+open C_FILE, ">$ARGV[1]" or die;
+print C_FILE "/*\n";
+print C_FILE " * Automatically generated by ", $0, ".  Do not edit\n";
+print C_FILE " */\n";
+
+#
+# Split the data up into separate lists and also determine the lengths of the
+# encoded data arrays.
+#
+my @indices = ();
+my @lengths = ();
+my $total_length = 0;
+
+for (my $i = 0; $i <= $#names; $i++) {
+    my $name = $names[$i];
+    my $oid = $oids[$i];
+
+    my @components = split(/[.]/, $oid);
+
+    # Determine the encoded length of this OID
+    my $size = $#components;
+    for (my $loop = 2; $loop <= $#components; $loop++) {
+	my $c = $components[$loop];
+
+	# We will base128 encode the number
+	my $tmp = ($c == 0) ? 0 : int(log($c)/log(2));
+	$tmp = int($tmp / 7);
+	$size += $tmp;
+    }
+    push @lengths, $size;
+    push @indices, $total_length;
+    $total_length += $size;
+}
+
+#
+# Emit the look-up-by-OID index table
+#
+print C_FILE "\n";
+if ($total_length <= 255) {
+    print C_FILE "static const unsigned char oid_index[OID__NR + 1] = {\n";
+} else {
+    print C_FILE "static const unsigned short oid_index[OID__NR + 1] = {\n";
+}
+for (my $i = 0; $i <= $#names; $i++) {
+    print C_FILE "\t[OID_", $names[$i], "] = ", $indices[$i], ",\n"
+}
+print C_FILE "\t[OID__NR] = ", $total_length, "\n";
+print C_FILE "};\n";
+
+#
+# Encode the OIDs
+#
+my @encoded_oids = ();
+
+for (my $i = 0; $i <= $#names; $i++) {
+    my @octets = ();
+
+    my @components = split(/[.]/, $oids[$i]);
+
+    push @octets, $components[0] * 40 + $components[1];
+
+    for (my $loop = 2; $loop <= $#components; $loop++) {
+	my $c = $components[$loop];
+
+	# Base128 encode the number
+	my $tmp = ($c == 0) ? 0 : int(log($c)/log(2));
+	$tmp = int($tmp / 7);
+
+	for (; $tmp > 0; $tmp--) {
+	    push @octets, (($c >> $tmp * 7) & 0x7f) | 0x80;
+	}
+	push @octets, $c & 0x7f;
+    }
+
+    push @encoded_oids, \@octets;
+}
+
+#
+# Create a hash value for each OID
+#
+my @hash_values = ();
+for (my $i = 0; $i <= $#names; $i++) {
+    my @octets = @{$encoded_oids[$i]};
+
+    my $hash = $#octets;
+    foreach (@octets) {
+	$hash += $_ * 33;
+    }
+
+    $hash = ($hash >> 24) ^ ($hash >> 16) ^ ($hash >> 8) ^ ($hash);
+
+    push @hash_values, $hash & 0xff;
+}
+
+#
+# Emit the OID data
+#
+print C_FILE "\n";
+print C_FILE "static const unsigned char oid_data[", $total_length, "] = {\n";
+for (my $i = 0; $i <= $#names; $i++) {
+    my @octets = @{$encoded_oids[$i]};
+    print C_FILE "\t";
+    print C_FILE $_, ", " foreach (@octets);
+    print C_FILE "\t// ", $names[$i];
+    print C_FILE "\n";
+}
+print C_FILE "};\n";
+
+#
+# Build the search index table (ordered by length then hash then content)
+#
+my @index_table = ( 0 .. $#names );
+
+@index_table = sort {
+    my @octets_a = @{$encoded_oids[$a]};
+    my @octets_b = @{$encoded_oids[$b]};
+
+    return $hash_values[$a] <=> $hash_values[$b]
+	if ($hash_values[$a] != $hash_values[$b]);
+    return $#octets_a <=> $#octets_b
+	if ($#octets_a != $#octets_b);
+    for (my $i = $#octets_a; $i >= 0; $i--) {
+	return $octets_a[$i] <=> $octets_b[$i]
+	    if ($octets_a[$i] != $octets_b[$i]);
+    }
+    return 0;
+
+} @index_table;
+
+#
+# Emit the search index and hash value table
+#
+print C_FILE "\n";
+print C_FILE "static const struct {\n";
+print C_FILE "\tunsigned char hash;\n";
+if ($#names <= 255) {
+    print C_FILE "\tenum OID oid : 8;\n";
+} else {
+    print C_FILE "\tenum OID oid : 16;\n";
+}
+print C_FILE "} oid_search_table[OID__NR] = {\n";
+for (my $i = 0; $i <= $#names; $i++) {
+    my @octets = @{$encoded_oids[$index_table[$i]]};
+    printf(C_FILE "\t[%3u] = { %3u, OID_%-35s }, // ",
+	   $i,
+	   $hash_values[$index_table[$i]],
+	   $names[$index_table[$i]]);
+    printf C_FILE "%02x", $_ foreach (@octets);
+    print C_FILE "\n";
+}
+print C_FILE "};\n";
+
+#
+# Emit the OID debugging name table
+#
+#print C_FILE "\n";
+#print C_FILE "const char *const oid_name_table[OID__NR + 1] = {\n";
+#
+#for (my $i = 0; $i <= $#names; $i++) {
+#    print C_FILE "\t\"", $names[$i], "\",\n"
+#}
+#print C_FILE "\t\"Unknown-OID\"\n";
+#print C_FILE "};\n";
+
+#
+# Polish off
+#
+close C_FILE or die;
diff --git a/test/Kconfig b/test/Kconfig
index f53629a..cb51b46 100644
--- a/test/Kconfig
+++ b/test/Kconfig
@@ -12,7 +12,23 @@
 	default y
 	help
 	  Enables the 'ut lib' command which tests library functions like
-	  memcat(), memcyp(), memmove().
+	  memcat(), memcyp(), memmove() and ASN1 compiler/decoder.
+
+if UT_LIB
+
+config UT_LIB_ASN1
+	bool "Unit test for asn1 compiler and decoder function"
+	default y
+	imply ASYMMETRIC_KEY_TYPE
+	imply ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+	imply X509_CERTIFICATE_PARSER
+	imply PKCS7_MESSAGE_PARSER
+	imply RSA_PUBLIC_KEY_PARSER
+	help
+	  Enables a test which exercises asn1 compiler and decoder function
+	  via various parsers.
+
+endif
 
 config UT_TIME
 	bool "Unit tests for time functions"
diff --git a/test/lib/Makefile b/test/lib/Makefile
index b13aaca..72d2ec7 100644
--- a/test/lib/Makefile
+++ b/test/lib/Makefile
@@ -7,3 +7,4 @@
 obj-y += lmb.o
 obj-y += string.o
 obj-$(CONFIG_ERRNO_STR) += test_errno_str.o
+obj-$(CONFIG_UT_LIB_ASN1) += asn1.o
diff --git a/test/lib/asn1.c b/test/lib/asn1.c
new file mode 100644
index 0000000..d2b3f67
--- /dev/null
+++ b/test/lib/asn1.c
@@ -0,0 +1,392 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2019 Linaro Limited
+ * Author: AKASHI Takahiro
+ *
+ * Unit test for asn1 compiler and asn1 decoder function via various parsers
+ */
+
+#include <common.h>
+#include <command.h>
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+#ifdef CONFIG_PKCS7_MESSAGE_PARSER
+#include "../../lib/crypto/pkcs7_parser.h"
+#else
+#ifdef CONFIG_X509_CERTIFICATE_PARSER
+#include "../../lib/crypto/x509_parser.h"
+#endif
+#endif
+
+#ifdef CONFIG_X509_CERTIFICATE_PARSER
+static const unsigned char cert_data[] = {
+	0x30, 0x82, 0x03, 0xc7, 0x30, 0x82, 0x02, 0xaf, 0xa0, 0x03, 0x02, 0x01,
+	0x02, 0x02, 0x09, 0x00, 0xd7, 0x17, 0x0a, 0x76, 0xd5, 0xd3, 0x4d, 0xeb,
+	0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+	0x0b, 0x05, 0x00, 0x30, 0x7a, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+	0x04, 0x06, 0x13, 0x02, 0x4a, 0x50, 0x31, 0x0e, 0x30, 0x0c, 0x06, 0x03,
+	0x55, 0x04, 0x08, 0x0c, 0x05, 0x54, 0x6f, 0x6b, 0x79, 0x6f, 0x31, 0x0e,
+	0x30, 0x0c, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x05, 0x54, 0x6f, 0x6b,
+	0x79, 0x6f, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c,
+	0x06, 0x4c, 0x69, 0x6e, 0x61, 0x72, 0x6f, 0x31, 0x0b, 0x30, 0x09, 0x06,
+	0x03, 0x55, 0x04, 0x0b, 0x0c, 0x02, 0x53, 0x57, 0x31, 0x0f, 0x30, 0x0d,
+	0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x06, 0x54, 0x65, 0x73, 0x74, 0x65,
+	0x72, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+	0x0d, 0x01, 0x09, 0x01, 0x16, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x40, 0x74,
+	0x65, 0x73, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x30, 0x1e, 0x17, 0x0d, 0x31,
+	0x39, 0x31, 0x30, 0x31, 0x38, 0x30, 0x33, 0x31, 0x33, 0x33, 0x31, 0x5a,
+	0x17, 0x0d, 0x32, 0x30, 0x31, 0x30, 0x31, 0x37, 0x30, 0x33, 0x31, 0x33,
+	0x33, 0x31, 0x5a, 0x30, 0x7a, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+	0x04, 0x06, 0x13, 0x02, 0x4a, 0x50, 0x31, 0x0e, 0x30, 0x0c, 0x06, 0x03,
+	0x55, 0x04, 0x08, 0x0c, 0x05, 0x54, 0x6f, 0x6b, 0x79, 0x6f, 0x31, 0x0e,
+	0x30, 0x0c, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x05, 0x54, 0x6f, 0x6b,
+	0x79, 0x6f, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c,
+	0x06, 0x4c, 0x69, 0x6e, 0x61, 0x72, 0x6f, 0x31, 0x0b, 0x30, 0x09, 0x06,
+	0x03, 0x55, 0x04, 0x0b, 0x0c, 0x02, 0x53, 0x57, 0x31, 0x0f, 0x30, 0x0d,
+	0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x06, 0x54, 0x65, 0x73, 0x74, 0x65,
+	0x72, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+	0x0d, 0x01, 0x09, 0x01, 0x16, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x40, 0x74,
+	0x65, 0x73, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x30, 0x82, 0x01, 0x22, 0x30,
+	0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+	0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02,
+	0x82, 0x01, 0x01, 0x00, 0x9f, 0x37, 0x4d, 0x95, 0x7e, 0x36, 0xb7, 0xaf,
+	0xf4, 0xd6, 0xce, 0x39, 0x04, 0xee, 0xbf, 0x36, 0xb2, 0xcc, 0xa3, 0x8b,
+	0x9e, 0xac, 0x62, 0x8a, 0xe9, 0xae, 0x18, 0xcf, 0xe8, 0x95, 0xfd, 0xcb,
+	0xad, 0x34, 0x8a, 0x5f, 0x55, 0xe6, 0x0c, 0x5e, 0xf8, 0x76, 0xc1, 0xa2,
+	0xc3, 0xd4, 0x73, 0x13, 0x8a, 0x71, 0x1b, 0xfd, 0x58, 0x27, 0xea, 0x4d,
+	0x41, 0xff, 0x63, 0xbb, 0xad, 0x97, 0x62, 0xba, 0xe4, 0xe5, 0x97, 0x45,
+	0xa3, 0x5b, 0xd5, 0x5b, 0x53, 0x55, 0x10, 0x19, 0xfa, 0xac, 0xbd, 0xdb,
+	0x77, 0x62, 0x23, 0x50, 0x3f, 0x35, 0xdb, 0x8a, 0xf6, 0xee, 0x7a, 0x31,
+	0xec, 0x92, 0xf5, 0x78, 0x35, 0x92, 0x76, 0x3c, 0x5f, 0xe7, 0xee, 0xc9,
+	0xed, 0x01, 0x1c, 0x42, 0x55, 0xd6, 0x7e, 0xa6, 0xca, 0x7c, 0xd1, 0x15,
+	0x16, 0x87, 0x7c, 0x99, 0x63, 0xc0, 0xa9, 0x25, 0x49, 0xbc, 0x4e, 0xdc,
+	0x2d, 0x4b, 0xcb, 0x52, 0xd7, 0x67, 0xe9, 0x83, 0x6b, 0x5e, 0x5b, 0x48,
+	0x80, 0x33, 0xe9, 0xcc, 0xe8, 0xfe, 0x19, 0xc8, 0xc2, 0x61, 0x74, 0x52,
+	0x25, 0x92, 0x48, 0xea, 0xad, 0x15, 0x16, 0x64, 0x6e, 0x53, 0x30, 0x77,
+	0xa2, 0xef, 0x61, 0x92, 0x1b, 0x5e, 0xbe, 0x07, 0xf2, 0x3c, 0xf8, 0x35,
+	0x7d, 0x76, 0x4f, 0x78, 0xa9, 0x2a, 0xf1, 0x32, 0xff, 0xec, 0x89, 0xa9,
+	0x22, 0x4c, 0x3d, 0xc8, 0x65, 0xca, 0xf4, 0xa2, 0x6d, 0x3f, 0xa4, 0x0a,
+	0xfa, 0x9e, 0xe4, 0xf0, 0xdb, 0x39, 0xb1, 0xf9, 0xf0, 0xfb, 0x04, 0x81,
+	0x44, 0xa7, 0xd7, 0x61, 0xdf, 0x2d, 0x13, 0x45, 0x2c, 0xae, 0xf0, 0x0e,
+	0xc4, 0x07, 0x5d, 0x7d, 0x2b, 0xb2, 0x20, 0x75, 0x33, 0x6b, 0x5b, 0xf7,
+	0xe7, 0x17, 0x51, 0xf1, 0xab, 0xc1, 0x9e, 0xc6, 0xf0, 0x30, 0xc6, 0x25,
+	0x26, 0x3e, 0xd7, 0xd7, 0xa3, 0xcc, 0x15, 0x95, 0x02, 0x03, 0x01, 0x00,
+	0x01, 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+	0x04, 0x16, 0x04, 0x14, 0x45, 0x8a, 0x76, 0xf7, 0x4f, 0xf4, 0x0e, 0xa0,
+	0xf2, 0x02, 0xe1, 0xe7, 0xe9, 0xc7, 0x7d, 0x51, 0x55, 0x92, 0x33, 0xcd,
+	0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80,
+	0x14, 0x45, 0x8a, 0x76, 0xf7, 0x4f, 0xf4, 0x0e, 0xa0, 0xf2, 0x02, 0xe1,
+	0xe7, 0xe9, 0xc7, 0x7d, 0x51, 0x55, 0x92, 0x33, 0xcd, 0x30, 0x0c, 0x06,
+	0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30,
+	0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
+	0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x47, 0x93, 0x82, 0x0e, 0x8a,
+	0x70, 0x9d, 0x6c, 0x7a, 0xdb, 0x04, 0xb4, 0xc9, 0xef, 0x98, 0x28, 0xc6,
+	0xd9, 0x53, 0x90, 0xc8, 0x25, 0x83, 0x07, 0x23, 0xe7, 0x59, 0x38, 0xc1,
+	0xc0, 0x50, 0x28, 0x99, 0x92, 0xfb, 0x21, 0x24, 0x72, 0xe5, 0xa6, 0x57,
+	0x30, 0x31, 0xb3, 0xdf, 0xa0, 0x17, 0xa9, 0x73, 0x9c, 0x39, 0x83, 0xfb,
+	0xe4, 0xfa, 0x20, 0x1d, 0xfa, 0x33, 0x20, 0x0c, 0x72, 0x2a, 0x50, 0x40,
+	0xbd, 0x2d, 0x33, 0xa2, 0xfc, 0x06, 0xf9, 0xfe, 0x86, 0x4f, 0x50, 0x1d,
+	0x65, 0x37, 0xe9, 0x30, 0x33, 0x82, 0xa1, 0x75, 0x8f, 0x5d, 0x33, 0x84,
+	0x0d, 0xf2, 0x09, 0x04, 0xc0, 0x7a, 0x12, 0x79, 0xdb, 0x4f, 0x77, 0x04,
+	0xe4, 0xd8, 0x0b, 0x87, 0x19, 0xba, 0xb7, 0x3c, 0xa6, 0x45, 0xaa, 0x91,
+	0x62, 0x7f, 0x01, 0x7d, 0xc6, 0x20, 0x6d, 0x71, 0x15, 0x74, 0x5e, 0x87,
+	0xb3, 0x60, 0x17, 0x9c, 0xc0, 0xed, 0x01, 0x4b, 0xb3, 0x23, 0x24, 0xc1,
+	0xcb, 0x7a, 0x83, 0x03, 0x26, 0x2d, 0xde, 0x47, 0xc5, 0x11, 0x94, 0x28,
+	0x27, 0x15, 0x92, 0x00, 0x8b, 0x2e, 0x51, 0x42, 0xca, 0x4b, 0x4a, 0x2c,
+	0x51, 0x37, 0x56, 0xd0, 0xbc, 0x33, 0xd5, 0xd5, 0x3e, 0x79, 0x5c, 0x3f,
+	0x9d, 0x6e, 0xb1, 0xe9, 0x71, 0xf1, 0x2c, 0xe9, 0xb4, 0x88, 0x2c, 0xd2,
+	0x49, 0x97, 0xce, 0x29, 0x94, 0x16, 0xc9, 0xf9, 0x64, 0x0e, 0xd0, 0xd9,
+	0x7a, 0x53, 0x10, 0x1a, 0xee, 0x83, 0x73, 0x93, 0x1b, 0xdf, 0x8a, 0x77,
+	0xc0, 0x56, 0x63, 0xab, 0x5a, 0x65, 0xc5, 0xc5, 0x3b, 0xf3, 0x30, 0x80,
+	0xfc, 0x38, 0x8b, 0xc9, 0xcd, 0xc3, 0x4f, 0x2e, 0x2d, 0x67, 0xcc, 0x17,
+	0x18, 0x9b, 0x3e, 0xc6, 0x47, 0x03, 0xfc, 0x35, 0xa8, 0x35, 0x06, 0x5a,
+	0x77, 0xe5, 0x97, 0x71, 0xbb, 0x27, 0x93, 0x0d, 0x1f, 0x0e, 0x8c
+};
+
+static unsigned int cert_data_len = 971;
+
+/**
+ * lib_asn1_x509() - unit test for asn1 decoder function
+ * with x509 certificate parser
+ *
+ * @uts:	unit test state
+ * Return:	0 = success, 1 = failure
+ */
+static int lib_asn1_x509(struct unit_test_state *uts)
+{
+	struct x509_certificate *cert;
+
+	cert = x509_cert_parse(cert_data, cert_data_len);
+
+	ut_assertf(cert != NULL, "decoding failed\n");
+	ut_assertf(!strcmp(cert->subject, "Linaro: Tester"),
+		   "subject doesn't match\n");
+	ut_assertf(!strcmp(cert->issuer, "Linaro: Tester"),
+		   "issuer doesn't match\n");
+	ut_assertf(cert->pub, "public key doesn't exist\n");
+	ut_assertf(cert->pub->keylen == 0x10e, "key length doesn't match\n");
+	ut_assertf(!strcmp(cert->pub->pkey_algo, "rsa"), "algo isn't rsa\n");
+	ut_assertf(cert->valid_from == 0x5da92ddb,
+		   "valid_from doesn't match\n");
+	ut_assertf(cert->valid_to == 0x5f8a615b, "valid_to doesn't match\n");
+
+	x509_free_certificate(cert);
+
+	return CMD_RET_SUCCESS;
+}
+
+LIB_TEST(lib_asn1_x509, 0);
+#endif /* CONFIG_X509_CERTIFICATE_PARSER */
+
+#ifdef CONFIG_PKCS7_MESSAGE_PARSER
+/*
+ * sbsign --key priv.pem --cert cert.pem --detach --out Image.pk Image
+ */
+static const unsigned char image_pk7[] = {
+	0x30, 0x82, 0x07, 0x0f, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+	0x01, 0x07, 0x02, 0xa0, 0x82, 0x07, 0x00, 0x30, 0x82, 0x06, 0xfc, 0x02,
+	0x01, 0x01, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+	0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x30, 0x78, 0x06, 0x0a, 0x2b,
+	0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x01, 0x04, 0xa0, 0x6a, 0x30,
+	0x68, 0x30, 0x33, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37,
+	0x02, 0x01, 0x0f, 0x30, 0x25, 0x03, 0x01, 0x00, 0xa0, 0x20, 0xa2, 0x1e,
+	0x80, 0x1c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x4f, 0x00, 0x62,
+	0x00, 0x73, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x74, 0x00, 0x65,
+	0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09,
+	0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04,
+	0x20, 0x9e, 0x90, 0x99, 0x6d, 0xf2, 0xb5, 0x3d, 0x3f, 0xfc, 0x38, 0xb6,
+	0xf2, 0x1f, 0xd2, 0x24, 0x88, 0x43, 0x77, 0x7d, 0xc1, 0x2c, 0x9e, 0x8a,
+	0xf6, 0xf7, 0xdd, 0x9e, 0x9c, 0x5f, 0x18, 0x36, 0xc5, 0xa0, 0x82, 0x03,
+	0xcb, 0x30, 0x82, 0x03, 0xc7, 0x30, 0x82, 0x02, 0xaf, 0xa0, 0x03, 0x02,
+	0x01, 0x02, 0x02, 0x09, 0x00, 0xd7, 0x17, 0x0a, 0x76, 0xd5, 0xd3, 0x4d,
+	0xeb, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+	0x01, 0x0b, 0x05, 0x00, 0x30, 0x7a, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+	0x55, 0x04, 0x06, 0x13, 0x02, 0x4a, 0x50, 0x31, 0x0e, 0x30, 0x0c, 0x06,
+	0x03, 0x55, 0x04, 0x08, 0x0c, 0x05, 0x54, 0x6f, 0x6b, 0x79, 0x6f, 0x31,
+	0x0e, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x05, 0x54, 0x6f,
+	0x6b, 0x79, 0x6f, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a,
+	0x0c, 0x06, 0x4c, 0x69, 0x6e, 0x61, 0x72, 0x6f, 0x31, 0x0b, 0x30, 0x09,
+	0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x02, 0x53, 0x57, 0x31, 0x0f, 0x30,
+	0x0d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x06, 0x54, 0x65, 0x73, 0x74,
+	0x65, 0x72, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+	0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x40,
+	0x74, 0x65, 0x73, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x30, 0x1e, 0x17, 0x0d,
+	0x31, 0x39, 0x31, 0x30, 0x31, 0x38, 0x30, 0x33, 0x31, 0x33, 0x33, 0x31,
+	0x5a, 0x17, 0x0d, 0x32, 0x30, 0x31, 0x30, 0x31, 0x37, 0x30, 0x33, 0x31,
+	0x33, 0x33, 0x31, 0x5a, 0x30, 0x7a, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+	0x55, 0x04, 0x06, 0x13, 0x02, 0x4a, 0x50, 0x31, 0x0e, 0x30, 0x0c, 0x06,
+	0x03, 0x55, 0x04, 0x08, 0x0c, 0x05, 0x54, 0x6f, 0x6b, 0x79, 0x6f, 0x31,
+	0x0e, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x05, 0x54, 0x6f,
+	0x6b, 0x79, 0x6f, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a,
+	0x0c, 0x06, 0x4c, 0x69, 0x6e, 0x61, 0x72, 0x6f, 0x31, 0x0b, 0x30, 0x09,
+	0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x02, 0x53, 0x57, 0x31, 0x0f, 0x30,
+	0x0d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x06, 0x54, 0x65, 0x73, 0x74,
+	0x65, 0x72, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+	0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x40,
+	0x74, 0x65, 0x73, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x30, 0x82, 0x01, 0x22,
+	0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+	0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+	0x02, 0x82, 0x01, 0x01, 0x00, 0x9f, 0x37, 0x4d, 0x95, 0x7e, 0x36, 0xb7,
+	0xaf, 0xf4, 0xd6, 0xce, 0x39, 0x04, 0xee, 0xbf, 0x36, 0xb2, 0xcc, 0xa3,
+	0x8b, 0x9e, 0xac, 0x62, 0x8a, 0xe9, 0xae, 0x18, 0xcf, 0xe8, 0x95, 0xfd,
+	0xcb, 0xad, 0x34, 0x8a, 0x5f, 0x55, 0xe6, 0x0c, 0x5e, 0xf8, 0x76, 0xc1,
+	0xa2, 0xc3, 0xd4, 0x73, 0x13, 0x8a, 0x71, 0x1b, 0xfd, 0x58, 0x27, 0xea,
+	0x4d, 0x41, 0xff, 0x63, 0xbb, 0xad, 0x97, 0x62, 0xba, 0xe4, 0xe5, 0x97,
+	0x45, 0xa3, 0x5b, 0xd5, 0x5b, 0x53, 0x55, 0x10, 0x19, 0xfa, 0xac, 0xbd,
+	0xdb, 0x77, 0x62, 0x23, 0x50, 0x3f, 0x35, 0xdb, 0x8a, 0xf6, 0xee, 0x7a,
+	0x31, 0xec, 0x92, 0xf5, 0x78, 0x35, 0x92, 0x76, 0x3c, 0x5f, 0xe7, 0xee,
+	0xc9, 0xed, 0x01, 0x1c, 0x42, 0x55, 0xd6, 0x7e, 0xa6, 0xca, 0x7c, 0xd1,
+	0x15, 0x16, 0x87, 0x7c, 0x99, 0x63, 0xc0, 0xa9, 0x25, 0x49, 0xbc, 0x4e,
+	0xdc, 0x2d, 0x4b, 0xcb, 0x52, 0xd7, 0x67, 0xe9, 0x83, 0x6b, 0x5e, 0x5b,
+	0x48, 0x80, 0x33, 0xe9, 0xcc, 0xe8, 0xfe, 0x19, 0xc8, 0xc2, 0x61, 0x74,
+	0x52, 0x25, 0x92, 0x48, 0xea, 0xad, 0x15, 0x16, 0x64, 0x6e, 0x53, 0x30,
+	0x77, 0xa2, 0xef, 0x61, 0x92, 0x1b, 0x5e, 0xbe, 0x07, 0xf2, 0x3c, 0xf8,
+	0x35, 0x7d, 0x76, 0x4f, 0x78, 0xa9, 0x2a, 0xf1, 0x32, 0xff, 0xec, 0x89,
+	0xa9, 0x22, 0x4c, 0x3d, 0xc8, 0x65, 0xca, 0xf4, 0xa2, 0x6d, 0x3f, 0xa4,
+	0x0a, 0xfa, 0x9e, 0xe4, 0xf0, 0xdb, 0x39, 0xb1, 0xf9, 0xf0, 0xfb, 0x04,
+	0x81, 0x44, 0xa7, 0xd7, 0x61, 0xdf, 0x2d, 0x13, 0x45, 0x2c, 0xae, 0xf0,
+	0x0e, 0xc4, 0x07, 0x5d, 0x7d, 0x2b, 0xb2, 0x20, 0x75, 0x33, 0x6b, 0x5b,
+	0xf7, 0xe7, 0x17, 0x51, 0xf1, 0xab, 0xc1, 0x9e, 0xc6, 0xf0, 0x30, 0xc6,
+	0x25, 0x26, 0x3e, 0xd7, 0xd7, 0xa3, 0xcc, 0x15, 0x95, 0x02, 0x03, 0x01,
+	0x00, 0x01, 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+	0x0e, 0x04, 0x16, 0x04, 0x14, 0x45, 0x8a, 0x76, 0xf7, 0x4f, 0xf4, 0x0e,
+	0xa0, 0xf2, 0x02, 0xe1, 0xe7, 0xe9, 0xc7, 0x7d, 0x51, 0x55, 0x92, 0x33,
+	0xcd, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+	0x80, 0x14, 0x45, 0x8a, 0x76, 0xf7, 0x4f, 0xf4, 0x0e, 0xa0, 0xf2, 0x02,
+	0xe1, 0xe7, 0xe9, 0xc7, 0x7d, 0x51, 0x55, 0x92, 0x33, 0xcd, 0x30, 0x0c,
+	0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff,
+	0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+	0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x47, 0x93, 0x82, 0x0e,
+	0x8a, 0x70, 0x9d, 0x6c, 0x7a, 0xdb, 0x04, 0xb4, 0xc9, 0xef, 0x98, 0x28,
+	0xc6, 0xd9, 0x53, 0x90, 0xc8, 0x25, 0x83, 0x07, 0x23, 0xe7, 0x59, 0x38,
+	0xc1, 0xc0, 0x50, 0x28, 0x99, 0x92, 0xfb, 0x21, 0x24, 0x72, 0xe5, 0xa6,
+	0x57, 0x30, 0x31, 0xb3, 0xdf, 0xa0, 0x17, 0xa9, 0x73, 0x9c, 0x39, 0x83,
+	0xfb, 0xe4, 0xfa, 0x20, 0x1d, 0xfa, 0x33, 0x20, 0x0c, 0x72, 0x2a, 0x50,
+	0x40, 0xbd, 0x2d, 0x33, 0xa2, 0xfc, 0x06, 0xf9, 0xfe, 0x86, 0x4f, 0x50,
+	0x1d, 0x65, 0x37, 0xe9, 0x30, 0x33, 0x82, 0xa1, 0x75, 0x8f, 0x5d, 0x33,
+	0x84, 0x0d, 0xf2, 0x09, 0x04, 0xc0, 0x7a, 0x12, 0x79, 0xdb, 0x4f, 0x77,
+	0x04, 0xe4, 0xd8, 0x0b, 0x87, 0x19, 0xba, 0xb7, 0x3c, 0xa6, 0x45, 0xaa,
+	0x91, 0x62, 0x7f, 0x01, 0x7d, 0xc6, 0x20, 0x6d, 0x71, 0x15, 0x74, 0x5e,
+	0x87, 0xb3, 0x60, 0x17, 0x9c, 0xc0, 0xed, 0x01, 0x4b, 0xb3, 0x23, 0x24,
+	0xc1, 0xcb, 0x7a, 0x83, 0x03, 0x26, 0x2d, 0xde, 0x47, 0xc5, 0x11, 0x94,
+	0x28, 0x27, 0x15, 0x92, 0x00, 0x8b, 0x2e, 0x51, 0x42, 0xca, 0x4b, 0x4a,
+	0x2c, 0x51, 0x37, 0x56, 0xd0, 0xbc, 0x33, 0xd5, 0xd5, 0x3e, 0x79, 0x5c,
+	0x3f, 0x9d, 0x6e, 0xb1, 0xe9, 0x71, 0xf1, 0x2c, 0xe9, 0xb4, 0x88, 0x2c,
+	0xd2, 0x49, 0x97, 0xce, 0x29, 0x94, 0x16, 0xc9, 0xf9, 0x64, 0x0e, 0xd0,
+	0xd9, 0x7a, 0x53, 0x10, 0x1a, 0xee, 0x83, 0x73, 0x93, 0x1b, 0xdf, 0x8a,
+	0x77, 0xc0, 0x56, 0x63, 0xab, 0x5a, 0x65, 0xc5, 0xc5, 0x3b, 0xf3, 0x30,
+	0x80, 0xfc, 0x38, 0x8b, 0xc9, 0xcd, 0xc3, 0x4f, 0x2e, 0x2d, 0x67, 0xcc,
+	0x17, 0x18, 0x9b, 0x3e, 0xc6, 0x47, 0x03, 0xfc, 0x35, 0xa8, 0x35, 0x06,
+	0x5a, 0x77, 0xe5, 0x97, 0x71, 0xbb, 0x27, 0x93, 0x0d, 0x1f, 0x0e, 0x8c,
+	0x31, 0x82, 0x02, 0x9b, 0x30, 0x82, 0x02, 0x97, 0x02, 0x01, 0x01, 0x30,
+	0x81, 0x87, 0x30, 0x7a, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
+	0x06, 0x13, 0x02, 0x4a, 0x50, 0x31, 0x0e, 0x30, 0x0c, 0x06, 0x03, 0x55,
+	0x04, 0x08, 0x0c, 0x05, 0x54, 0x6f, 0x6b, 0x79, 0x6f, 0x31, 0x0e, 0x30,
+	0x0c, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x05, 0x54, 0x6f, 0x6b, 0x79,
+	0x6f, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06,
+	0x4c, 0x69, 0x6e, 0x61, 0x72, 0x6f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+	0x55, 0x04, 0x0b, 0x0c, 0x02, 0x53, 0x57, 0x31, 0x0f, 0x30, 0x0d, 0x06,
+	0x03, 0x55, 0x04, 0x03, 0x0c, 0x06, 0x54, 0x65, 0x73, 0x74, 0x65, 0x72,
+	0x31, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+	0x01, 0x09, 0x01, 0x16, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x40, 0x74, 0x65,
+	0x73, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x02, 0x09, 0x00, 0xd7, 0x17, 0x0a,
+	0x76, 0xd5, 0xd3, 0x4d, 0xeb, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
+	0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0xa0, 0x81, 0xe5, 0x30,
+	0x19, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x03,
+	0x31, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02,
+	0x01, 0x04, 0x30, 0x1c, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+	0x01, 0x09, 0x05, 0x31, 0x0f, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x30, 0x31,
+	0x38, 0x30, 0x35, 0x35, 0x35, 0x32, 0x36, 0x5a, 0x30, 0x2f, 0x06, 0x09,
+	0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x04, 0x31, 0x22, 0x04,
+	0x20, 0x13, 0xe9, 0x2d, 0xcd, 0x35, 0x43, 0xe0, 0x13, 0x34, 0xc5, 0x67,
+	0xde, 0xdd, 0x75, 0xdc, 0x62, 0x97, 0x76, 0x7d, 0x5b, 0xa0, 0xb4, 0x4d,
+	0x4f, 0xef, 0xb8, 0xa7, 0x95, 0x50, 0xcb, 0x0f, 0xec, 0x30, 0x79, 0x06,
+	0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x0f, 0x31, 0x6c,
+	0x30, 0x6a, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
+	0x04, 0x01, 0x2a, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
+	0x03, 0x04, 0x01, 0x16, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+	0x65, 0x03, 0x04, 0x01, 0x02, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48,
+	0x86, 0xf7, 0x0d, 0x03, 0x07, 0x30, 0x0e, 0x06, 0x08, 0x2a, 0x86, 0x48,
+	0x86, 0xf7, 0x0d, 0x03, 0x02, 0x02, 0x02, 0x00, 0x80, 0x30, 0x0d, 0x06,
+	0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x03, 0x02, 0x02, 0x01, 0x40,
+	0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x07, 0x30, 0x0d, 0x06,
+	0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x03, 0x02, 0x02, 0x01, 0x28,
+	0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+	0x01, 0x05, 0x00, 0x04, 0x82, 0x01, 0x00, 0x38, 0x40, 0x09, 0xc7, 0xc4,
+	0xf7, 0x78, 0x48, 0x75, 0x1e, 0xb2, 0x50, 0x95, 0x0a, 0x52, 0xee, 0x57,
+	0x60, 0xc5, 0xf4, 0xdb, 0xca, 0x67, 0xb0, 0x19, 0xad, 0x68, 0xb1, 0xe1,
+	0x1e, 0xb7, 0xf6, 0x53, 0x3d, 0x13, 0xb1, 0x11, 0x37, 0xa7, 0x6e, 0x9b,
+	0x18, 0x1d, 0x0e, 0xbd, 0xc4, 0xb2, 0xd0, 0x36, 0x6c, 0x0c, 0x5a, 0x11,
+	0x50, 0xcc, 0xdb, 0x1f, 0x6b, 0xcb, 0x28, 0x80, 0xd5, 0x3c, 0x4f, 0x93,
+	0x0b, 0xd1, 0x45, 0x75, 0xa1, 0x89, 0x00, 0x71, 0x7d, 0x55, 0xcc, 0x1c,
+	0x0a, 0xc9, 0xc4, 0xe6, 0x87, 0xf2, 0x87, 0x0d, 0x2e, 0x79, 0x71, 0x85,
+	0x01, 0xd7, 0x32, 0x87, 0x9a, 0x11, 0xc6, 0x9a, 0xbb, 0x0a, 0x7b, 0xce,
+	0xfe, 0xc8, 0xee, 0x10, 0x3c, 0xa6, 0x47, 0xdd, 0xbb, 0xa7, 0xf5, 0x19,
+	0x50, 0xd5, 0x2a, 0x11, 0x44, 0x2f, 0x65, 0x09, 0x69, 0x50, 0xfa, 0xbd,
+	0x02, 0xe4, 0x90, 0xdc, 0x2a, 0x7c, 0xdb, 0x82, 0x03, 0xa5, 0x28, 0x91,
+	0x74, 0x7c, 0xd3, 0x83, 0xc8, 0x11, 0x1a, 0x14, 0x1b, 0xba, 0xb1, 0x82,
+	0xbd, 0x53, 0xad, 0x9c, 0x34, 0x05, 0xfa, 0x2d, 0x14, 0x58, 0x5e, 0x50,
+	0x64, 0x60, 0x5c, 0x21, 0x7c, 0xe6, 0xf0, 0x2b, 0xa2, 0xec, 0xe5, 0xeb,
+	0xda, 0x88, 0xe2, 0x19, 0x36, 0x96, 0x65, 0xf7, 0x4c, 0x62, 0x9b, 0x75,
+	0x24, 0xb4, 0xb1, 0x34, 0x83, 0xba, 0x05, 0x01, 0xd8, 0xe1, 0x33, 0xd3,
+	0x1a, 0xd6, 0x09, 0x84, 0x31, 0xd0, 0x67, 0xf3, 0x3b, 0x0e, 0x19, 0x98,
+	0x7e, 0x07, 0xdc, 0xe1, 0xd8, 0x45, 0x84, 0xa2, 0xdd, 0x8a, 0x04, 0x6a,
+	0x43, 0xcf, 0xff, 0x7c, 0x9e, 0x83, 0xa8, 0x5d, 0xbc, 0x1f, 0x45, 0x86,
+	0x5b, 0x2d, 0xcd, 0x9d, 0xa0, 0xba, 0x4d, 0xd2, 0xc6, 0xb9, 0xc5, 0x34,
+	0x39, 0x29, 0x20, 0xee, 0x27, 0x60, 0x46, 0x9c, 0x62, 0xbe, 0xf2
+};
+
+static unsigned int image_pk7_len = 1811;
+
+/**
+ * lib_asn1_pkcs7() - unit test for asn1 decoder function
+ * with pkcs7 message parser
+ *
+ * @uts:	unit test state
+ * Return:	0 = success, 1 = failure
+ */
+static int lib_asn1_pkcs7(struct unit_test_state *uts)
+{
+	struct pkcs7_message *pkcs7;
+
+	pkcs7 = pkcs7_parse_message(image_pk7, image_pk7_len);
+
+	ut_assertf(pkcs7 != NULL, "decoding failed\n");
+	ut_assertf(pkcs7->data_len == 104, "signature size doesn't match\n");
+	ut_assertf(pkcs7->signed_infos != NULL, "sign-info doesn't exist\n");
+	ut_assertf(pkcs7->signed_infos->msgdigest_len == 32,
+		   "digest size doesn't match\n");
+	ut_assertf(pkcs7->signed_infos->aa_set == 0xf,
+		   "authenticated attributes doesn't match\n");
+
+	pkcs7_free_message(pkcs7);
+
+	return CMD_RET_SUCCESS;
+}
+
+LIB_TEST(lib_asn1_pkcs7, 0);
+#endif /* CONFIG_PKCS7_MESSAGE_PARSER */
+
+#ifdef CONFIG_RSA_PUBLIC_KEY_PARSER
+#include <crypto/internal/rsa.h>
+
+/*
+ * openssl genrsa 2048 -out private.pem
+ * openssl rsa -in private.pem -pubout -outform der -out public.der
+ * dd if=public.der of=public.raw bs=24 skip=1
+ */
+static const unsigned char public_key[] = {
+	0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xca, 0x25, 0x23,
+	0xe0, 0x0a, 0x4d, 0x8f, 0x56, 0xfc, 0xc9, 0x06, 0x4c, 0xcc, 0x94, 0x43,
+	0xe0, 0x56, 0x44, 0x6e, 0x37, 0x54, 0x87, 0x12, 0x84, 0xf9, 0x07, 0x4f,
+	0xe4, 0x23, 0x40, 0xc3, 0x43, 0x84, 0x37, 0x86, 0xd3, 0x9d, 0x95, 0x1c,
+	0xe4, 0x8a, 0x66, 0x02, 0x09, 0xe2, 0x3d, 0xce, 0x2c, 0xc6, 0x02, 0x6a,
+	0xd4, 0x65, 0x61, 0xff, 0x85, 0x6f, 0x88, 0x63, 0xba, 0x31, 0x62, 0x1e,
+	0xb7, 0x95, 0xe9, 0x08, 0x3c, 0xe9, 0x35, 0xde, 0xfd, 0x65, 0x92, 0xb8,
+	0x9e, 0x71, 0xa4, 0xcd, 0x47, 0xfd, 0x04, 0x26, 0xb9, 0x78, 0xbf, 0x05,
+	0x0d, 0xfc, 0x00, 0x84, 0x08, 0xfc, 0xc4, 0x4b, 0xea, 0xf5, 0x97, 0x68,
+	0x0d, 0x97, 0xd7, 0xff, 0x4f, 0x92, 0x82, 0xd7, 0xbb, 0xef, 0xb7, 0x67,
+	0x8e, 0x72, 0x54, 0xe8, 0xc5, 0x9e, 0xfd, 0xd8, 0x38, 0xe9, 0xbe, 0x19,
+	0x37, 0x5b, 0x36, 0x8b, 0xbf, 0x49, 0xa1, 0x59, 0x3a, 0x9d, 0xad, 0x92,
+	0x08, 0x0b, 0xe3, 0xa4, 0xa4, 0x7d, 0xd3, 0x70, 0xc0, 0xb8, 0xfb, 0xc7,
+	0xda, 0xd3, 0x19, 0x86, 0x37, 0x9a, 0xcd, 0xab, 0x30, 0x96, 0xab, 0xa4,
+	0xa2, 0x31, 0xa0, 0x38, 0xfb, 0xbf, 0x85, 0xd3, 0x24, 0x39, 0xed, 0xbf,
+	0xe1, 0x31, 0xed, 0x6c, 0x39, 0xc1, 0xe5, 0x05, 0x2e, 0x12, 0x30, 0x36,
+	0x73, 0x5d, 0x62, 0xf3, 0x82, 0xaf, 0x38, 0xc8, 0xca, 0xfa, 0xa1, 0x99,
+	0x57, 0x3c, 0xe1, 0xc1, 0x7b, 0x05, 0x0b, 0xcc, 0x2e, 0xa9, 0x10, 0xc8,
+	0x68, 0xbd, 0x27, 0xb6, 0x19, 0x9c, 0xd2, 0xad, 0xb3, 0x1f, 0xca, 0x35,
+	0x6e, 0x84, 0x23, 0xa1, 0xe9, 0xa4, 0x4c, 0xab, 0x19, 0x09, 0x79, 0x6e,
+	0x3c, 0x7b, 0x74, 0xfc, 0x33, 0x05, 0xcf, 0xa4, 0x2e, 0xeb, 0x55, 0x60,
+	0x05, 0xc7, 0xcf, 0x3f, 0x92, 0xac, 0x2d, 0x69, 0x0b, 0x19, 0x16, 0x79,
+	0x75, 0x02, 0x03, 0x01, 0x00, 0x01
+};
+
+static unsigned int public_key_len = 270;
+
+/**
+ * lib_asn1_pkey() - unit test for asn1 decoder function
+ * with RSA public key parser
+ *
+ * @uts:	unit test state
+ * Return:	0 = success, 1 = failure
+ */
+static int lib_asn1_pkey(struct unit_test_state *uts)
+{
+	struct rsa_key pkey;
+	int ret;
+
+	ret = rsa_parse_pub_key(&pkey, public_key, public_key_len);
+
+	ut_assertf(ret == 0, "decoding failed (%d)\n", ret);
+	ut_assertf(pkey.n_sz == 257, "public key modulus size doesn't match\n");
+	ut_assertf(pkey.e_sz == 3, "public key exponent size doesn't match\n");
+	ut_assertf(pkey.e[0] == 0x01 && pkey.e[1] == 0x00 && pkey.e[2] == 0x01,
+		   "public key exponent doesn't match\n");
+
+	return CMD_RET_SUCCESS;
+}
+
+LIB_TEST(lib_asn1_pkey, 0);
+#endif /* CONFIG_RSA_PUBLIC_KEY_PARSER */
diff --git a/tools/Makefile b/tools/Makefile
index 24581ad..345bc84 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -207,6 +207,9 @@
 
 hostprogs-$(CONFIG_MIPS) += mips-relocs
 
+hostprogs-$(CONFIG_ASN1_COMPILER)	+= asn1_compiler
+HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include
+
 # We build some files with extra pedantic flags to try to minimize things
 # that won't build on some weird host compiler -- though there are lots of
 # exceptions for files that aren't complaint.
diff --git a/tools/asn1_compiler.c b/tools/asn1_compiler.c
new file mode 100644
index 0000000..adabd41
--- /dev/null
+++ b/tools/asn1_compiler.c
@@ -0,0 +1,1611 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Simplified ASN.1 notation parser
+ *
+ * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.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;
+	char		*name;
+	unsigned char	index;
+};
+
+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;
+	char		*content;
+	struct type	*type;
+};
+
+static struct token *token_list;
+static unsigned nr_tokens;
+static bool verbose_opt;
+static bool debug_opt;
+
+#define verbose(fmt, ...) do { if (verbose_opt) printf(fmt, ## __VA_ARGS__); } while (0)
+#define debug(fmt, ...) do { if (debug_opt) printf(fmt, ## __VA_ARGS__); } while (0)
+
+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;
+
+	//debug("cmp(%s,%s) = ", token->content, dir);
+
+	val = memcmp(token->content, dir, clen);
+	if (val != 0) {
+		//debug("%d [cmp]\n", val);
+		return val;
+	}
+
+	if (dlen == token->size) {
+		//debug("0\n");
+		return 0;
+	}
+	//debug("%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, *start, *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;
+			start = 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;
+
+				tokens[tix].content = malloc(tokens[tix].size + 1);
+				if (!tokens[tix].content) {
+					perror(NULL);
+					exit(1);
+				}
+				memcpy(tokens[tix].content, start, tokens[tix].size);
+				tokens[tix].content[tokens[tix].size] = 0;
+				
+				/* If it begins with a lowercase letter then
+				 * it's an element name
+				 */
+				if (islower(tokens[tix].content[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].content = malloc(tokens[tix].size + 1);
+				if (!tokens[tix].content) {
+					perror(NULL);
+					exit(1);
+				}
+				memcpy(tokens[tix].content, start, tokens[tix].size);
+				tokens[tix].content[tokens[tix].size] = 0;
+				tokens[tix++].token_type = TOKEN_NUMBER;
+				continue;
+			}
+
+			if (nl - p >= 3) {
+				if (memcmp(p, "::=", 3) == 0) {
+					p += 3;
+					tokens[tix].size = 3;
+					tokens[tix].content = "::=";
+					tokens[tix++].token_type = TOKEN_ASSIGNMENT;
+					continue;
+				}
+			}
+
+			if (nl - p >= 2) {
+				if (memcmp(p, "({", 2) == 0) {
+					p += 2;
+					tokens[tix].size = 2;
+					tokens[tix].content = "({";
+					tokens[tix++].token_type = TOKEN_OPEN_ACTION;
+					continue;
+				}
+				if (memcmp(p, "})", 2) == 0) {
+					p += 2;
+					tokens[tix].size = 2;
+					tokens[tix].content = "})";
+					tokens[tix++].token_type = TOKEN_CLOSE_ACTION;
+					continue;
+				}
+			}
+
+			if (nl - p >= 1) {
+				tokens[tix].size = 1;
+				switch (*p) {
+				case '{':
+					p += 1;
+					tokens[tix].content = "{";
+					tokens[tix++].token_type = TOKEN_OPEN_CURLY;
+					continue;
+				case '}':
+					p += 1;
+					tokens[tix].content = "}";
+					tokens[tix++].token_type = TOKEN_CLOSE_CURLY;
+					continue;
+				case '[':
+					p += 1;
+					tokens[tix].content = "[";
+					tokens[tix++].token_type = TOKEN_OPEN_SQUARE;
+					continue;
+				case ']':
+					p += 1;
+					tokens[tix].content = "]";
+					tokens[tix++].token_type = TOKEN_CLOSE_SQUARE;
+					continue;
+				case ',':
+					p += 1;
+					tokens[tix].content = ",";
+					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;
+	verbose("Extracted %u tokens\n", nr_tokens);
+
+#if 0
+	{
+		int n;
+		for (n = 0; n < nr_tokens; n++)
+			debug("Token %3u: '%s'\n", n, token_list[n].content);
+	}
+#endif
+}
+
+static void build_type_list(void);
+static void parse(void);
+static void dump_elements(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;
+	char *kbuild_verbose;
+	int fd;
+
+	kbuild_verbose = getenv("KBUILD_VERBOSE");
+	if (kbuild_verbose)
+		verbose_opt = atoi(kbuild_verbose);
+
+	while (argc > 4) {
+		if (strcmp(argv[1], "-v") == 0)
+			verbose_opt = true;
+		else if (strcmp(argv[1], "-d") == 0)
+			debug_opt = true;
+		else
+			break;
+		memmove(&argv[1], &argv[2], (argc - 2) * sizeof(char *));
+		argc--;
+	}
+
+	if (argc != 4) {
+		fprintf(stderr, "Format: %s [-v] [-d] <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();
+	dump_elements();
+
+	out = fopen(outputname, "w");
+	if (!out) {
+		perror(outputname);
+		exit(1);
+	}
+
+	hdr = fopen(headername, "w");
+	if (!hdr) {
+		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_TAG_SPECIFIED	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->content, (*b)->name->content,
+			      (*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->content, type->name->content,
+			      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);
+
+	verbose("Extracted %u types\n", nr_types);
+#if 0
+	for (n = 0; n < nr_types; n++) {
+		struct type *type = type_index[n];
+		debug("- %*.*s\n", type->name->content);
+	}
+#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, cursor->content);
+			exit(1);
+		}
+
+	} while (type++, !(type->flags & TYPE_STOP_MARKER));
+
+	verbose("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, cursor->content);
+			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, cursor->content);
+			exit(1);
+		}
+
+		element->tag &= ~0x1f;
+		element->tag |= strtoul(cursor->content, &p, 10);
+		element->flags |= ELEMENT_TAG_SPECIFIED;
+		if (p - cursor->content != 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, cursor->content);
+			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, cursor->content);
+			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, cursor->content);
+		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, cursor->content);
+			exit(1);
+		}
+
+		action = malloc(sizeof(struct action));
+		if (!action) {
+			perror(NULL);
+			exit(1);
+		}
+		action->index = 0;
+		action->name = cursor->content;
+
+		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, cursor->content);
+			exit(1);
+		}
+		cursor++;
+	}
+
+	*_cursor = cursor;
+	return top;
+
+parse_error:
+	fprintf(stderr, "%s:%d: Unexpected token '%s'\n",
+		filename, cursor->line, cursor->content);
+	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, cursor->content);
+		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, cursor->content);
+		exit(1);
+	}
+	cursor++;
+
+	*_cursor = cursor;
+	return children;
+
+overrun_error:
+	fprintf(stderr, "%s: Unexpectedly hit EOF\n", filename);
+	exit(1);
+}
+
+static void dump_element(const struct element *e, int level)
+{
+	const struct element *c;
+	const struct type *t = e->type_def;
+	const char *name = e->name ? e->name->content : ".";
+	const char *tname = t && t->name ? t->name->content : ".";
+	char tag[32];
+
+	if (e->class == 0 && e->method == 0 && e->tag == 0)
+		strcpy(tag, "<...>");
+	else if (e->class == ASN1_UNIV)
+		sprintf(tag, "%s %s %s",
+			asn1_classes[e->class],
+			asn1_methods[e->method],
+			asn1_universal_tags[e->tag]);
+	else
+		sprintf(tag, "%s %s %u",
+			asn1_classes[e->class],
+			asn1_methods[e->method],
+			e->tag);
+
+	printf("%c%c%c%c%c %c %*s[*] \e[33m%s\e[m %s %s \e[35m%s\e[m\n",
+	       e->flags & ELEMENT_IMPLICIT ? 'I' : '-',
+	       e->flags & ELEMENT_EXPLICIT ? 'E' : '-',
+	       e->flags & ELEMENT_TAG_SPECIFIED ? 'T' : '-',
+	       e->flags & ELEMENT_SKIPPABLE ? 'S' : '-',
+	       e->flags & ELEMENT_CONDITIONAL ? 'C' : '-',
+	       "-tTqQcaro"[e->compound],
+	       level, "",
+	       tag,
+	       tname,
+	       name,
+	       e->action ? e->action->name : "");
+	if (e->compound == TYPE_REF)
+		dump_element(e->type->type->element, level + 3);
+	else
+		for (c = e->children; c; c = c->next)
+			dump_element(c, level + 3);
+}
+
+static void dump_elements(void)
+{
+	if (debug_opt)
+		dump_element(type_list[0].element, 0);
+}
+
+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 */
+	verbose("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 */
+	verbose("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;
+		default:
+			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, *x;
+	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", e->type_def->name->content);
+	}
+
+	/* 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%s,",
+			      cond, act, skippable ? "_OR_SKIP" : "");
+		if (e->name)
+			render_more(out, "\t\t// %s", e->name->content);
+		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;
+	}
+
+	x = tag ?: e;
+	if (x->name)
+		render_more(out, "\t\t// %s", x->name->content);
+	render_more(out, "\n");
+
+	/* Render the tag */
+	if (!tag || !(tag->flags & ELEMENT_TAG_SPECIFIED))
+		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_%sACT,\n",
+				      skippable ? "MAYBE_" : "");
+		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",
+					    e->type_def->name->content);
+			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",
+					    e->type_def->name->content);
+			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, ec);
+		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);
+}