diff mbox series

lib: Add a YAML emitter

Message ID 20200213232338.3746869-1-chris@chris-wilson.co.uk (mailing list archive)
State New, archived
Headers show
Series lib: Add a YAML emitter | expand

Commit Message

Chris Wilson Feb. 13, 2020, 11:23 p.m. UTC
Provide a library to generate correct YAML for use in structured debugfs
or similar information dumps. This will be useful to pull our large
information dumps into a forwards compatible, parse-able file-format by
forcing some structure upon ourselves!

Originally from https://github.com/yaml/libyaml/blob/master/src/emitter.c
under a permissive MIT licence.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Jani Nikula <jani.nikula@intel.com>
Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>

---
Converting to kerneldoc is about the last major task. It uses an
opencoded stack struct which might be nice to convert to a library,
maybe just use XArray?

It has been dogfooded to convert i915_engine_info (and friends)
https://patchwork.freedesktop.org/patch/353209/?series=73403&rev=1
---
 include/linux/yaml-events.h |  259 ++++
 include/linux/yaml.h        |  188 +++
 lib/Kconfig                 |   11 +
 lib/Makefile                |    2 +
 lib/yaml/Makefile           |    8 +
 lib/yaml/test-yaml.c        |  123 ++
 lib/yaml/yaml-emitter.c     | 2539 +++++++++++++++++++++++++++++++++++
 lib/yaml/yaml-emitter.h     |  140 ++
 lib/yaml/yaml-events.c      |  424 ++++++
 lib/yaml/yaml-simple.c      |  227 ++++
 10 files changed, 3921 insertions(+)
 create mode 100644 include/linux/yaml-events.h
 create mode 100644 include/linux/yaml.h
 create mode 100644 lib/yaml/Makefile
 create mode 100644 lib/yaml/test-yaml.c
 create mode 100644 lib/yaml/yaml-emitter.c
 create mode 100644 lib/yaml/yaml-emitter.h
 create mode 100644 lib/yaml/yaml-events.c
 create mode 100644 lib/yaml/yaml-simple.c

Comments

Jani Nikula Feb. 14, 2020, 7:57 a.m. UTC | #1
On Thu, 13 Feb 2020, Chris Wilson <chris@chris-wilson.co.uk> wrote:
> Provide a library to generate correct YAML for use in structured debugfs
> or similar information dumps. This will be useful to pull our large
> information dumps into a forwards compatible, parse-able file-format by
> forcing some structure upon ourselves!

Acked-by: Jani Nikula <jani.nikula@intel.com>

I like the idea. I like the prospect of converting *all* of our bigger
debugfs files to have some structure. Why not smaller ones too, to be
uniform. Not entirely sold on using it in sysfs, like the example code
did, but even that might be justified for capturing a state.

For many things I do like json, but yaml is definitely more human
readable as-is and more suitable for debugfs.

I expect the biggest uphill to be merging the bulk of stuff to
lib/. It's a lot of code in one go.

BR,
Jani.
Chris Wilson Feb. 14, 2020, 12:48 p.m. UTC | #2
Quoting Jani Nikula (2020-02-14 07:57:09)
> On Thu, 13 Feb 2020, Chris Wilson <chris@chris-wilson.co.uk> wrote:
> > Provide a library to generate correct YAML for use in structured debugfs
> > or similar information dumps. This will be useful to pull our large
> > information dumps into a forwards compatible, parse-able file-format by
> > forcing some structure upon ourselves!
> 
> Acked-by: Jani Nikula <jani.nikula@intel.com>
> 
> I like the idea. I like the prospect of converting *all* of our bigger
> debugfs files to have some structure. Why not smaller ones too, to be
> uniform. Not entirely sold on using it in sysfs, like the example code
> did, but even that might be justified for capturing a state.

Fair enough, I shall not mention sysfs in the first pass :)
And I'll do a couple more large and small debugfs to help flesh out the
API and demonstrate its usecases.
-Chris
diff mbox series

Patch

diff --git a/include/linux/yaml-events.h b/include/linux/yaml-events.h
new file mode 100644
index 000000000000..33d1bb227e65
--- /dev/null
+++ b/include/linux/yaml-events.h
@@ -0,0 +1,259 @@ 
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (c) 2006-2016 Kirill Simonov
+ * Copyright (c) 2017-2019 Ingy döt Net
+ * Copyright (c) 2020 Intel Corporation
+ */
+
+#ifndef __LINUX_YAML_EVENTS_H__
+#define __LINUX_YAML_EVENTS_H__
+
+#include <linux/gfp.h>
+#include <linux/types.h>
+#include <linux/list.h>
+
+/** The version directive data. */
+struct yaml_version_directive {
+	int major;
+	int minor;
+};
+
+/** The tag directive data. */
+struct yaml_tag_directive {
+	char *handle;
+	char *prefix;
+};
+
+/** The pointer position. */
+struct yaml_mark {
+	size_t index;
+	size_t line;
+	size_t column;
+};
+
+enum yaml_event_type {
+	YAML_NO_EVENT = 0,
+
+	YAML_STREAM_START_EVENT,
+	YAML_STREAM_END_EVENT,
+
+	YAML_DOCUMENT_START_EVENT,
+	YAML_DOCUMENT_END_EVENT,
+
+	YAML_ALIAS_EVENT,
+	YAML_SCALAR_EVENT,
+
+	YAML_SEQUENCE_START_EVENT,
+	YAML_SEQUENCE_END_EVENT,
+
+	YAML_MAPPING_START_EVENT,
+	YAML_MAPPING_END_EVENT
+};
+
+struct yaml_event {
+	enum yaml_event_type type;
+	struct list_head link;
+
+	union {
+		/** The stream parameters (for YAML_STREAM_START_EVENT). */
+		struct {
+		} stream_start;
+
+		/** The document parameters (for YAML_DOCUMENT_START_EVENT). */
+		struct {
+			struct yaml_version_directive *version;
+
+			struct {
+				struct yaml_tag_directive *start;
+				struct yaml_tag_directive *end;
+			} tags;
+
+			int implicit;
+		} document_start;
+
+		/** The document end parameters (for YAML_DOCUMENT_END_EVENT). */
+		struct {
+			int implicit;
+		} document_end;
+
+		/** The alias parameters (for YAML_ALIAS_EVENT). */
+		struct {
+			char *anchor;
+		} alias;
+
+		/** The scalar parameters (for YAML_SCALAR_EVENT). */
+		struct {
+			char *anchor;
+			char *tag;
+			char *value;
+			size_t length;
+			int plain_implicit;
+			int quoted_implicit;
+			enum yaml_scalar_style style;
+		} scalar;
+
+		/** The sequence parameters (for YAML_SEQUENCE_START_EVENT). */
+		struct {
+			char *anchor;
+			char *tag;
+			int implicit;
+			enum yaml_sequence_style style;
+		} sequence_start;
+
+		/** The mapping parameters (for YAML_MAPPING_START_EVENT). */
+		struct {
+			char *anchor;
+			char *tag;
+			int implicit;
+			enum yaml_mapping_style style;
+		} mapping_start;
+	};
+
+	struct yaml_mark start_mark;
+	struct yaml_mark end_mark;
+};
+
+struct yaml_event *yaml_stream_start_event_create(gfp_t gfp);
+struct yaml_event *yaml_stream_end_event_create(gfp_t gfp);
+
+/**
+ * Create the DOCUMENT-START event.
+ *
+ * The @a implicit argument is considered as a stylistic parameter and may be
+ * ignored by the emitter.
+ *
+ * @param[in]       version_directive       The %YAML directive value or
+ *                                          @c NULL.
+ * @param[in]       tag_directives_start    The beginning of the %TAG
+ *                                          directives list.
+ * @param[in]       tag_directives_end      The end of the %TAG directives
+ *                                          list.
+ * @param[in]       implicit                If the document start indicator is
+ *                                          implicit.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_event *
+yaml_document_start_event_create(struct yaml_version_directive *version,
+				 struct yaml_tag_directive *start,
+				 struct yaml_tag_directive *end,
+				 int implicit,
+				 gfp_t gfp);
+
+/**
+ * Create the DOCUMENT-END event.
+ *
+ * The @a implicit argument is considered as a stylistic parameter and may be
+ * ignored by the emitter.
+ *
+ * @param[in]       implicit    If the document end indicator is implicit.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_event *
+yaml_document_end_event_create(int implicit, gfp_t gfp);
+
+/**
+ * Create an ALIAS event.
+ *
+ * @param[in]       anchor      The anchor value.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_event *
+yaml_alias_event_create(const char *anchor, gfp_t gfp);
+
+/**
+ * Create a SCALAR event.
+ *
+ * The @a style argument may be ignored by the emitter.
+ *
+ * Either the @a tag attribute or one of the @a plain_implicit and
+ * @a quoted_implicit flags must be set.
+ *
+ * @param[in]       anchor          The scalar anchor or @c NULL.
+ * @param[in]       tag             The scalar tag or @c NULL.
+ * @param[in]       value           The scalar value.
+ * @param[in]       length          The length of the scalar value.
+ * @param[in]       plain_implicit  If the tag may be omitted for the plain
+ *                                  style.
+ * @param[in]       quoted_implicit If the tag may be omitted for any
+ *                                  non-plain style.
+ * @param[in]       style           The scalar style.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_event *
+yaml_scalar_event_create(const char *anchor, const char *tag,
+			 const char *value, ssize_t length, bool steal,
+			 int plain_implicit, int quoted_implicit,
+			 enum yaml_scalar_style style,
+			 gfp_t gfp);
+
+/**
+ * Create a SEQUENCE-START event.
+ *
+ * The @a style argument may be ignored by the emitter.
+ *
+ * Either the @a tag attribute or the @a implicit flag must be set.
+ *
+ * @param[in]       anchor      The sequence anchor or @c NULL.
+ * @param[in]       tag         The sequence tag or @c NULL.
+ * @param[in]       implicit    If the tag may be omitted.
+ * @param[in]       style       The sequence style.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_event *
+yaml_sequence_start_event_create(const char *anchor,
+				 const char *tag,
+				 int implicit,
+				 enum yaml_sequence_style style,
+				 gfp_t gfp);
+
+/**
+ * Create a SEQUENCE-END event.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_event *
+yaml_sequence_end_event_create(gfp_t gfp);
+
+/**
+ * Create a MAPPING-START event.
+ *
+ * The @a style argument may be ignored by the emitter.
+ *
+ * Either the @a tag attribute or the @a implicit flag must be set.
+ *
+ * @param[in]       anchor      The mapping anchor or @c NULL.
+ * @param[in]       tag         The mapping tag or @c NULL.
+ * @param[in]       implicit    If the tag may be omitted.
+ * @param[in]       style       The mapping style.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_event *
+yaml_mapping_start_event_create(const char *anchor,
+				const char *tag,
+				int implicit,
+				enum yaml_mapping_style style,
+				gfp_t gfp);
+
+/**
+ * Create a MAPPING-END event.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_event *
+yaml_mapping_end_event_create(gfp_t gfp);
+
+/**
+ * Free any memory allocated for an event object.
+ *
+ * @param[in,out]   event   An event object.
+ */
+
+void yaml_event_delete(struct yaml_event *event);
+
+#endif /* __LINUX_YAML_EVENTS_H__ */
diff --git a/include/linux/yaml.h b/include/linux/yaml.h
new file mode 100644
index 000000000000..a262f4a793e9
--- /dev/null
+++ b/include/linux/yaml.h
@@ -0,0 +1,188 @@ 
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright (c) 2006-2016 Kirill Simonov
+ * Copyright (c) 2017-2019 Ingy döt Net
+ * Copyright (c) 2020 Intel Corporation
+ */
+
+#ifndef __LINUX_YAML_H__
+#define __LINUX_YAML_H__
+
+#include <linux/types.h>
+#include <linux/list.h>
+
+struct seq_file;
+
+struct yaml_emitter;
+struct yaml_event;
+
+/** Scalar styles. */
+enum yaml_scalar_style {
+	YAML_ANY_SCALAR_STYLE = 0, /** Let the emitter choose the style. */
+
+	YAML_PLAIN_SCALAR_STYLE,
+
+	YAML_SINGLE_QUOTED_SCALAR_STYLE,
+	YAML_DOUBLE_QUOTED_SCALAR_STYLE,
+
+	YAML_LITERAL_SCALAR_STYLE,
+	YAML_FOLDED_SCALAR_STYLE,
+};
+
+/** Sequence styles. */
+enum yaml_sequence_style {
+	YAML_ANY_SEQUENCE_STYLE = 0, /** Let the emitter choose the style. */
+
+	YAML_BLOCK_SEQUENCE_STYLE,
+	YAML_FLOW_SEQUENCE_STYLE,
+};
+
+/** Mapping styles. */
+enum yaml_mapping_style {
+	YAML_ANY_MAPPING_STYLE = 0, /** Let the emitter choose the style. */
+
+	YAML_BLOCK_MAPPING_STYLE,
+	YAML_FLOW_MAPPING_STYLE
+};
+
+typedef int (*yaml_write_t)(void *data, const u8 *buffer, size_t size);
+
+/**
+ * Initialize an emitter.
+ *
+ * This function creates a new emitter object.  An application is responsible
+ * for destroying the object using the yaml_emitter_delete() function.
+ *
+ * @param[out]      emitter     An empty parser object.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+struct yaml_emitter *yaml_emitter_create(gfp_t gfp);
+
+/**
+ * Destroy an emitter.
+ *
+ * @param[in,out]   emitter     An emitter object.
+ */
+void yaml_emitter_destroy(struct yaml_emitter *emitter);
+
+/**
+ * Set a string output.
+ *
+ * The emitter will write the output characters to the @a output buffer of the
+ * size @a size.  The emitter will set @a size_written to the number of written
+ * bytes.  If the buffer is smaller than required, the emitter produces the
+ * YAML_WRITE_ERROR error.
+ *
+ * @param[in,out]   emitter         An emitter object.
+ * @param[in]       output          An output buffer.
+ * @param[in]       size            The buffer size.
+ * @param[in]       size_written    The pointer to save the number of written
+ *                                  bytes.
+ */
+void yaml_emitter_set_string(struct yaml_emitter *emitter,
+			     u8 *output, size_t size,
+			     size_t *size_written);
+
+void yaml_emitter_set_seq_file(struct yaml_emitter *emitter,
+			       struct seq_file *file);
+
+void yaml_emitter_set_output(struct yaml_emitter *emitter,
+			     yaml_write_t handler,
+			     void *data);
+
+/**
+ * Set if the output should be in the "canonical" format as in the YAML
+ * specification.
+ *
+ * @param[in,out]   emitter     An emitter object.
+ * @param[in]       canonical   If the output is canonical.
+ */
+void yaml_emitter_set_canonical(struct yaml_emitter *emitter, bool canonical);
+
+/**
+ * Set the indentation increment.
+ *
+ * @param[in,out]   emitter     An emitter object.
+ * @param[in]       indent      The indentation increment (1 < . < 10).
+ */
+void yaml_emitter_set_indent(struct yaml_emitter *emitter, int indent);
+
+/**
+ * Set the preferred line width. @c -1 means unlimited.
+ *
+ * @param[in,out]   emitter     An emitter object.
+ * @param[in]       width       The preferred line width.
+ */
+void yaml_emitter_set_width(struct yaml_emitter *emitter, int width);
+
+/**
+ * Set if unescaped non-ASCII characters are allowed.
+ *
+ * @param[in,out]   emitter     An emitter object.
+ * @param[in]       unicode     If unescaped Unicode characters are allowed.
+ */
+void yaml_emitter_set_unicode(struct yaml_emitter *emitter, bool unicode);
+
+/**
+ * Emit an event.
+ *
+ * The event object may be generated using the yaml_parser_parse() function.
+ * The emitter takes the responsibility for the event object and destroys its
+ * content after it is emitted. The event object is destroyed even if the
+ * function fails.
+ *
+ * @param[in,out]   emitter     An emitter object.
+ * @param[in,out]   event       An event object.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+void yaml_emitter_emit(struct yaml_emitter *emitter, struct yaml_event *event);
+
+/**
+ * Flush the accumulated characters to the output.
+ *
+ * @param[in,out]   emitter     An emitter object.
+ *
+ * @returns @c 1 if the function succeeded, @c 0 on error.
+ */
+int yaml_emitter_flush(struct yaml_emitter *emitter, const char **problem);
+
+gfp_t yaml_emitter_set_gfp(struct yaml_emitter *emitter, gfp_t gfp);
+
+struct yaml_emitter *yaml_open(yaml_write_t handler, void *data);
+struct yaml_emitter *yaml_open_file(struct seq_file *m);
+struct yaml_emitter *yaml_open_string(char *buf, size_t len, size_t *written);
+
+void __yaml_mapping_start(struct yaml_emitter *emitter,
+			  enum yaml_mapping_style style);
+static inline void yaml_mapping_start(struct yaml_emitter *emitter)
+{
+	__yaml_mapping_start(emitter, YAML_ANY_MAPPING_STYLE);
+}
+void yaml_mapping_end(struct yaml_emitter *emitter);
+
+void __yaml_sequence_start(struct yaml_emitter *emitter,
+			   enum yaml_sequence_style style);
+static inline void yaml_sequence_start(struct yaml_emitter *emitter)
+{
+	__yaml_sequence_start(emitter, YAML_ANY_SEQUENCE_STYLE);
+}
+void yaml_sequence_end(struct yaml_emitter *emitter);
+
+__printf(2, 3)
+void yaml_alias_printf(struct yaml_emitter *emitter, const char *fmt, ...);
+
+__printf(2, 3)
+void yaml_scalar_printf(struct yaml_emitter *emitter, const char *fmt, ...);
+
+__printf(3, 4)
+void yaml_pair_printf(struct yaml_emitter *emitter,
+		      const char *name, const char *fmt, ...);
+
+void yaml_ascii85_encode(struct yaml_emitter *emitter,
+			 const void *data, size_t len, bool compressed);
+
+int yaml_close(struct yaml_emitter *emitter, const char **problem);
+
+#endif /* __LINUX_YAML_H__ */
diff --git a/lib/Kconfig b/lib/Kconfig
index 0cf875fd627c..da15068d60b5 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -648,6 +648,17 @@  config OBJAGG
 config STRING_SELFTEST
 	tristate "Test string functions"
 
+config YAML
+	tristate
+
+config YAML_EMITTER
+	tristate
+	select YAML
+
+config TEST_YAML
+	tristate "Small test module for libyaml"
+	depends on YAML
+
 endmenu
 
 config GENERIC_IOREMAP
diff --git a/lib/Makefile b/lib/Makefile
index 5d64890d6b6a..6805e56d2db8 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -241,6 +241,8 @@  obj-$(CONFIG_ASN1) += asn1_decoder.o
 
 obj-$(CONFIG_FONT_SUPPORT) += fonts/
 
+obj-$(CONFIG_YAML) += yaml/
+
 hostprogs	:= gen_crc32table
 hostprogs	+= gen_crc64table
 clean-files	:= crc32table.h
diff --git a/lib/yaml/Makefile b/lib/yaml/Makefile
new file mode 100644
index 000000000000..f5e8c1795914
--- /dev/null
+++ b/lib/yaml/Makefile
@@ -0,0 +1,8 @@ 
+# SPDX-License-Identifier: GPL-2.0
+
+yaml-objs = yaml-events.o
+yaml-objs-$(CONFIG_YAML_EMITTER) += yaml-emitter.o yaml-simple.o
+yaml-objs += $(yaml-objs-m)
+
+obj-$(CONFIG_YAML) += yaml.o
+obj-$(CONFIG_TEST_YAML) += test-yaml.o
diff --git a/lib/yaml/test-yaml.c b/lib/yaml/test-yaml.c
new file mode 100644
index 000000000000..5c3c7a928859
--- /dev/null
+++ b/lib/yaml/test-yaml.c
@@ -0,0 +1,123 @@ 
+// SPDX-License-Identifier: GPL
+/*
+ * Copyright (c) 2020 Intel Corporation
+ */
+#include <linux/debugfs.h>
+#include <linux/module.h>
+#include <linux/yaml.h>
+#include <linux/yaml-events.h>
+
+struct dentry *root;
+
+#if IS_ENABLED(CONFIG_YAML_EMITTER)
+static int y_events_show(struct seq_file *m, void *data)
+{
+	struct yaml_emitter *emitter;
+	struct yaml_event *event;
+	const char *problem;
+
+	emitter = yaml_emitter_create(GFP_KERNEL);
+	if (!emitter)
+		return -ENOMEM;
+
+	yaml_emitter_set_seq_file(emitter, m);
+
+	event = yaml_stream_start_event_create(GFP_KERNEL);
+	yaml_emitter_emit(emitter, event);
+
+	event = yaml_document_start_event_create(NULL, NULL, NULL, true,
+						 GFP_KERNEL);
+	yaml_emitter_emit(emitter, event);
+
+	event = yaml_mapping_start_event_create(NULL, NULL, true,
+						YAML_FLOW_MAPPING_STYLE,
+						GFP_KERNEL);
+	yaml_emitter_emit(emitter, event);
+
+	event = yaml_scalar_event_create(NULL, NULL,
+					 "General", -1, false,
+					 true, true, YAML_PLAIN_SCALAR_STYLE,
+					 GFP_KERNEL);
+	yaml_emitter_emit(emitter, event);
+
+	event = yaml_scalar_event_create(NULL, NULL,
+					 "Kenobi", -1, false,
+					 true, true, YAML_PLAIN_SCALAR_STYLE,
+					 GFP_KERNEL);
+	yaml_emitter_emit(emitter, event);
+
+	event = yaml_mapping_end_event_create(GFP_KERNEL);
+	yaml_emitter_emit(emitter, event);
+
+	event = yaml_document_end_event_create(true, GFP_KERNEL);
+	yaml_emitter_emit(emitter, event);
+
+	event = yaml_stream_end_event_create(GFP_KERNEL);
+	yaml_emitter_emit(emitter, event);
+
+	if (yaml_emitter_flush(emitter, &problem))
+		pr_err("test-yaml events failed: %s\n", problem);
+
+	yaml_emitter_destroy(emitter);
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(y_events);
+
+static int y_simple_show(struct seq_file *m, void *data)
+{
+	struct yaml_emitter *yaml;
+	const char *problem;
+
+	yaml = yaml_open_file(m);
+	if (!yaml)
+		return -ENOMEM;
+
+	yaml_mapping_start(yaml); {
+		yaml_scalar_printf(yaml, "map");
+		yaml_mapping_start(yaml); {
+			yaml_scalar_printf(yaml, "Hello");
+			yaml_mapping_start(yaml); {
+				yaml_pair_printf(yaml,
+						 "General", "%s", "Kenobi");
+			} yaml_mapping_end(yaml);
+		} yaml_mapping_end(yaml);
+
+		yaml_scalar_printf(yaml, "sequence");
+		yaml_sequence_start(yaml); {
+			yaml_scalar_printf(yaml, "1");
+			yaml_scalar_printf(yaml, "2");
+			yaml_scalar_printf(yaml, "3");
+			yaml_ascii85_encode(yaml, "4", 2, false);
+		} yaml_sequence_end(yaml);
+	} yaml_mapping_end(yaml);
+
+	if (yaml_close(yaml, &problem))
+		pr_err("test-yaml simple failed: %s\n", problem);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(y_simple);
+#endif
+
+static int __init test_yaml_init(void)
+{
+	root = debugfs_create_dir("libyaml", NULL);
+	if (!root)
+		return -ENOMEM;
+
+#if IS_ENABLED(CONFIG_YAML_EMITTER)
+	debugfs_create_file("emit-events", 0444, root, NULL, &y_events_fops);
+	debugfs_create_file("emit-simple", 0444, root, NULL, &y_simple_fops);
+#endif
+
+	return 0;
+}
+
+static void __exit test_yaml_exit(void)
+{
+	debugfs_remove(root);
+}
+
+module_init(test_yaml_init);
+module_exit(test_yaml_exit);
+MODULE_LICENSE("GPL");
diff --git a/lib/yaml/yaml-emitter.c b/lib/yaml/yaml-emitter.c
new file mode 100644
index 000000000000..a58116045624
--- /dev/null
+++ b/lib/yaml/yaml-emitter.c
@@ -0,0 +1,2539 @@ 
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (c) 2006-2016 Kirill Simonov
+ * Copyright (c) 2017-2019 Ingy döt Net
+ * Copyright (c) 2020 Intel Corporation
+ */
+#include <linux/ctype.h>
+#include <linux/mm.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/yaml.h>
+#include <linux/yaml-events.h>
+
+#include "yaml-emitter.h"
+
+#define INITIAL_STACK_SIZE  16
+
+struct yaml_string {
+	const char *start;
+	const char *end;
+	const char *pos;
+};
+
+#define STRING_INIT(str, len) { (str), (str) + (len), (str) }
+
+#define YAML_STRING(name, str) \
+	struct yaml_string name = STRING_INIT(str, strlen(str))
+
+#define CHECK_AT(s, octet, offset)					\
+	((s).pos[offset] == (char)(octet))
+#define CHECK(s, octet) (CHECK_AT((s), (octet), 0))
+
+#define IS_ALPHA_AT(s, offset)						\
+	(isalnum((s).pos[offset]) ||					\
+	 (s).pos[offset] == '_' ||					\
+	 (s).pos[offset] == '-')
+#define IS_ALPHA(s)	IS_ALPHA_AT((s), 0)
+
+#define IS_ASCII_AT(s, offset) ((s).pos[offset] <= '\x7F')
+#define IS_ASCII(s)    IS_ASCII_AT((s), 0)
+
+#define IS_PRINTABLE_AT(s, offset) isprint((s).pos[offset])
+#define IS_PRINTABLE(s)    IS_PRINTABLE_AT((s), 0)
+
+#define IS_Z_AT(s, offset)    CHECK_AT((s), '\0', (offset))
+#define IS_Z(s)    IS_Z_AT((s), 0)
+
+#define IS_BOM_AT(s, offset)						\
+	(CHECK_AT((s), '\xEF', (offset) + 0) &&				\
+	 CHECK_AT((s), '\xBB', (offset) + 1) &&				\
+	 CHECK_AT((s), '\xBF', (offset) + 2))  /* BOM (#xFEFF) */
+#define IS_BOM(s)  IS_BOM_AT(s, 0)
+
+#define IS_SPACE_AT(s, offset)  CHECK_AT((s), ' ', (offset))
+#define IS_SPACE(s)	IS_SPACE_AT((s), 0)
+
+#define IS_TAB_AT(s, offset)    CHECK_AT((s), '\t', (offset))
+#define IS_TAB(s)	IS_TAB_AT((s), 0)
+
+#define IS_BLANK_AT(s, offset)						\
+	(IS_SPACE_AT((s), (offset)) || IS_TAB_AT((s), (offset)))
+#define IS_BLANK(s)	IS_BLANK_AT((s), 0)
+
+#define IS_BREAK_AT(s, offset)						\
+	(CHECK_AT((s), '\r', (offset)) || /* CR (#xD)*/			\
+	 CHECK_AT((s), '\n', (offset)) || /* LF (#xA) */		\
+	 (CHECK_AT((s), '\xC2', (offset)) &&				\
+	  CHECK_AT((s), '\x85', (offset) + 1)) || /* NEL (#x85) */	\
+	 (CHECK_AT((s), '\xE2', (offset)) &&				\
+	  CHECK_AT((s), '\x80', (offset) + 1) &&			\
+	  CHECK_AT((s), '\xA8', (offset) + 2)) ||   /* LS (#x2028) */	\
+	 (CHECK_AT((s), '\xE2', (offset)) &&				\
+	  CHECK_AT((s), '\x80', (offset) + 1) &&			\
+	    CHECK_AT((s), '\xA9', (offset) + 2)))  /* PS (#x2029) */
+#define IS_BREAK(s)	IS_BREAK_AT((s), 0)
+
+#define IS_CRLF_AT(s, offset)						\
+	(CHECK_AT((s), '\r', (offset)) && CHECK_AT((s), '\n', (offset) + 1))
+#define IS_CRLF(s)	IS_CRLF_AT((s), 0)
+
+#define IS_BREAKZ_AT(s, offset)						\
+	(IS_BREAK_AT((s), (offset)) || IS_Z_AT((s), (offset)))
+#define IS_BREAKZ(s)	IS_BREAKZ_AT((s), 0)
+
+#define IS_SPACEZ_AT(s, offset)						\
+	(IS_SPACE_AT((s), (offset)) || IS_BREAKZ_AT((s), (offset)))
+#define IS_SPACEZ(s)	IS_SPACEZ_AT((s), 0)
+
+#define IS_BLANKZ_AT(s, offset)						\
+	(IS_BLANK_AT((s), (offset)) || IS_BREAKZ_AT((s), (offset)))
+#define IS_BLANKZ(s)	IS_BLANKZ_AT((s), 0)
+
+static inline int __utf8_width(const struct yaml_string *s, int idx)
+{
+	return ((s->pos[idx] & 0x80) == 0x00 ? 1 :
+		(s->pos[idx] & 0xE0) == 0xC0 ? 2 :
+		(s->pos[idx] & 0xF0) == 0xE0 ? 3 :
+		(s->pos[idx] & 0xF8) == 0xF0 ? 4 :
+		0);
+}
+
+static inline int utf8_width(const struct yaml_string *s)
+{
+	return __utf8_width(s, 0);
+}
+
+#define ADVANCE(s)	((s).pos += utf8_width((&s)))
+
+static bool yaml_stack_extend(void **start, void **top, void **end);
+
+#define EMPTY(context, stack) ((stack).start == (stack).top)
+
+#define PUSH(context, stack, value)					\
+	(((stack).top != (stack).end ||					\
+	  !yaml_stack_extend((void **)&(stack).start,			\
+			     (void **)&(stack).top, (void **)&(stack).end)) ? \
+	 (*((stack).top++) = value, 0) :				\
+	 ((context)->errno = -ENOMEM))
+
+#define POP(context, stack) (*(--(stack).top))
+
+static int
+__set_error(struct yaml_emitter *emitter, const char *problem, int err)
+{
+	WARN_ON(emitter->errno);
+
+	emitter->errno = err;
+	emitter->problem = problem;
+
+	return err;
+}
+
+static int set_error(struct yaml_emitter *emitter, const char *problem)
+{
+	return __set_error(emitter, problem, EINVAL);
+}
+
+static int flush(struct yaml_emitter *emitter)
+{
+	int err;
+
+	if (unlikely(emitter->errno))
+		return emitter->errno;
+
+	emitter->buf.last = emitter->buf.pos;
+	emitter->buf.pos = emitter->buf.start;
+
+	if (emitter->buf.start == emitter->buf.last)
+		return 0;
+
+	err = emitter->write_handler(emitter->write_data,
+				     emitter->buf.start,
+				     emitter->buf.last - emitter->buf.start);
+	if (unlikely(err < 0))
+		return __set_error(emitter, "write error", err);
+
+	emitter->buf.last = emitter->buf.start;
+	emitter->buf.pos = emitter->buf.start;
+	return 0;
+}
+
+static int maybe_flush(struct yaml_emitter *emitter, int len)
+{
+	if (emitter->buf.pos + len < emitter->buf.end)
+		return 0;
+
+	return flush(emitter);
+}
+
+static int put(struct yaml_emitter *emitter, const char value)
+{
+	int err;
+
+	err = maybe_flush(emitter, 5);
+	if (err)
+		return err;
+
+	*emitter->buf.pos++ = value;
+	emitter->column++;
+	return 0;
+}
+
+static int put_break(struct yaml_emitter *emitter)
+{
+	int err;
+
+	err =  put(emitter, '\n');
+	if (err)
+		return err;
+
+	emitter->column = 0;
+	return 0;
+}
+
+static int put_char(struct yaml_emitter *emitter, struct yaml_string *str)
+{
+	const int width = utf8_width(str);
+	int err;
+
+	err = maybe_flush(emitter, width);
+	if (err)
+		return err;
+
+	memcpy(emitter->buf.pos, str->pos, width);
+	emitter->buf.pos += width;
+	str->pos += width;
+
+	emitter->column++;
+	return 0;
+}
+
+static int put_char_break(struct yaml_emitter *emitter, struct yaml_string *str)
+{
+	const int width = utf8_width(str);
+	int err;
+
+	err = maybe_flush(emitter, width);
+	if (err)
+		return err;
+
+	memcpy(emitter->buf.pos, str->pos, width);
+	emitter->buf.pos += width;
+	str->pos += width;
+
+	emitter->column = 0;
+	return 0;
+}
+
+static struct yaml_event *next_event(const struct yaml_emitter *emitter)
+{
+	return list_first_entry(&emitter->events, struct yaml_event, link);
+}
+
+static bool need_more_events(struct yaml_emitter *emitter)
+{
+	struct yaml_event *event;
+	int acc;
+
+	if (list_empty(&emitter->events))
+		return true;
+
+	acc = 0;
+	switch (next_event(emitter)->type) {
+	case YAML_DOCUMENT_START_EVENT:
+		acc = 1;
+		break;
+	case YAML_SEQUENCE_START_EVENT:
+		acc = 2;
+		break;
+	case YAML_MAPPING_START_EVENT:
+		acc = 3;
+		break;
+
+	default:
+		return false;
+	}
+
+	if (emitter->num_events > acc)
+		return false;
+
+	acc = 0;
+	list_for_each_entry(event, &emitter->events, link) {
+		switch (event->type) {
+		case YAML_STREAM_START_EVENT:
+		case YAML_DOCUMENT_START_EVENT:
+		case YAML_SEQUENCE_START_EVENT:
+		case YAML_MAPPING_START_EVENT:
+			acc++;
+			break;
+
+		case YAML_STREAM_END_EVENT:
+		case YAML_DOCUMENT_END_EVENT:
+		case YAML_SEQUENCE_END_EVENT:
+		case YAML_MAPPING_END_EVENT:
+			acc--;
+			break;
+
+		default:
+			break;
+		}
+
+		if (!acc)
+			return false;
+	}
+
+	return true;
+}
+
+static int
+analyze_version_directive(struct yaml_emitter *emitter,
+			  struct yaml_version_directive version)
+{
+	if (version.major != 1 || version.minor != 1)
+		return set_error(emitter, "incompatible %YAML directive");
+
+	return 0;
+}
+
+static int
+analyze_tag_directive(struct yaml_emitter *emitter,
+		      struct yaml_tag_directive tag)
+{
+	YAML_STRING(handle, tag.handle);
+	YAML_STRING(prefix, tag.prefix);
+
+	if (handle.start == handle.end)
+		return set_error(emitter, "tag handle must not be empty");
+
+	if (handle.start[0] != '!')
+		return set_error(emitter, "tag handle must start with '!'");
+
+	if (handle.end[-1] != '!')
+		return set_error(emitter, "tag handle must end with '!'");
+
+	handle.pos++;
+
+	while (handle.pos < handle.end - 1) {
+		if (!IS_ALPHA(handle))
+			return set_error(emitter,
+					 "tag handle must contain alphanumerical characters only");
+
+		ADVANCE(handle);
+	}
+
+	if (prefix.start == prefix.end)
+		return set_error(emitter, "tag prefix must not be empty");
+
+	return 0;
+}
+
+static int
+analyze_anchor(struct yaml_emitter *emitter, const char *anchor, int alias)
+{
+	YAML_STRING(string, anchor);
+
+	if (string.start == string.end) {
+		return set_error(emitter, alias ?
+				 "alias value must not be empty" :
+				 "anchor value must not be empty");
+	}
+
+	while (string.pos != string.end) {
+		if (!IS_ALPHA(string)) {
+			return set_error(emitter, alias ?
+					 "alias value must contain alphanumerical characters only" :
+					 "anchor value must contain alphanumerical characters only");
+		}
+		ADVANCE(string);
+	}
+
+	emitter->anchor_data.anchor = string.start;
+	emitter->anchor_data.anchor_length = string.end - string.start;
+	emitter->anchor_data.alias = alias;
+	return 0;
+}
+
+static int analyze_tag(struct yaml_emitter *emitter, const char *str)
+{
+	YAML_STRING(string, str);
+	struct yaml_tag_directive *tag;
+
+	if (string.start == string.end)
+		return set_error(emitter, "tag value must not be empty");
+
+	for (tag = emitter->tags.start; tag != emitter->tags.top; tag++) {
+		size_t prefix_length = strlen(tag->prefix);
+
+		if (prefix_length < string.end - string.start &&
+		    strncmp(tag->prefix, string.start, prefix_length) == 0) {
+			emitter->tag_data.handle = tag->handle;
+			emitter->tag_data.handle_length = strlen(tag->handle);
+			emitter->tag_data.suffix = string.start + prefix_length;
+			emitter->tag_data.suffix_length =
+				(string.end - string.start) - prefix_length;
+			return 0;
+		}
+	}
+
+	emitter->tag_data.suffix = string.start;
+	emitter->tag_data.suffix_length = string.end - string.start;
+	return 0;
+}
+
+static int
+analyze_scalar(struct yaml_emitter *emitter, const char *value, size_t length)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+
+	int block_indicators = 0;
+	int flow_indicators = 0;
+	int line_breaks = 0;
+	int special_characters = 0;
+
+	int leading_space = 0;
+	int leading_break = 0;
+	int trailing_space = 0;
+	int trailing_break = 0;
+	int break_space = 0;
+	int space_break = 0;
+
+	int preceded_by_whitespace = 0;
+	int followed_by_whitespace = 0;
+	int previous_space = 0;
+	int previous_break = 0;
+
+	emitter->scalar_data.value = value;
+	emitter->scalar_data.length = length;
+
+	if (string.start == string.end) {
+		emitter->scalar_data.multiline = 0;
+		emitter->scalar_data.flow_plain_allowed = 0;
+		emitter->scalar_data.block_plain_allowed = 1;
+		emitter->scalar_data.single_quoted_allowed = 1;
+		emitter->scalar_data.block_allowed = 0;
+		return 0;
+	}
+
+	if ((CHECK_AT(string, '-', 0) &&
+	     CHECK_AT(string, '-', 1) &&
+	     CHECK_AT(string, '-', 2)) ||
+	    (CHECK_AT(string, '.', 0) &&
+	     CHECK_AT(string, '.', 1) &&
+	     CHECK_AT(string, '.', 2))) {
+		block_indicators = 1;
+		flow_indicators = 1;
+	}
+
+	preceded_by_whitespace = 1;
+	followed_by_whitespace = IS_BLANKZ_AT(string, utf8_width(&string));
+
+	while (string.pos != string.end) {
+		if (string.start == string.pos) {
+			if (CHECK(string, '#') || CHECK(string, ',') ||
+			    CHECK(string, '[') || CHECK(string, ']') ||
+			    CHECK(string, '{') || CHECK(string, '}') ||
+			    CHECK(string, '&') || CHECK(string, '*') ||
+			    CHECK(string, '!') || CHECK(string, '|') ||
+			    CHECK(string, '>') || CHECK(string, '\'') ||
+			    CHECK(string, '"') || CHECK(string, '%') ||
+			    CHECK(string, '@') || CHECK(string, '`')) {
+				flow_indicators = 1;
+				block_indicators = 1;
+			}
+
+			if (CHECK(string, '?') || CHECK(string, ':')) {
+				flow_indicators = 1;
+				if (followed_by_whitespace)
+					block_indicators = 1;
+			}
+
+			if (CHECK(string, '-') && followed_by_whitespace) {
+				flow_indicators = 1;
+				block_indicators = 1;
+			}
+		} else {
+			if (CHECK(string, ',') || CHECK(string, '?') ||
+			    CHECK(string, '[') || CHECK(string, ']') ||
+			    CHECK(string, '{') || CHECK(string, '}'))
+				flow_indicators = 1;
+
+			if (CHECK(string, ':')) {
+				flow_indicators = 1;
+				if (followed_by_whitespace)
+					block_indicators = 1;
+			}
+
+			if (CHECK(string, '#') && preceded_by_whitespace) {
+				flow_indicators = 1;
+				block_indicators = 1;
+			}
+		}
+
+		if (!IS_PRINTABLE(string) ||
+		    (!IS_ASCII(string) && !emitter->unicode)) {
+			special_characters = 1;
+		}
+
+		if (IS_BREAK(string))
+			line_breaks = 1;
+
+		if (IS_SPACE(string)) {
+			if (string.start == string.pos)
+				leading_space = 1;
+
+			if (string.pos + utf8_width(&string) == string.end)
+				trailing_space = 1;
+
+			if (previous_break)
+				break_space = 1;
+
+			previous_space = 1;
+			previous_break = 0;
+		} else if (IS_BREAK(string)) {
+			if (string.start == string.pos)
+				leading_break = 1;
+
+			if (string.pos + utf8_width(&string) == string.end)
+				trailing_break = 1;
+
+			if (previous_space)
+				space_break = 1;
+
+			previous_space = 0;
+			previous_break = 1;
+		} else {
+			previous_space = 0;
+			previous_break = 0;
+		}
+
+		preceded_by_whitespace = IS_BLANKZ(string);
+		ADVANCE(string);
+		if (string.pos != string.end)
+			followed_by_whitespace =
+				IS_BLANKZ_AT(string, utf8_width(&string));
+	}
+
+	emitter->scalar_data.multiline = line_breaks;
+
+	emitter->scalar_data.flow_plain_allowed = 1;
+	emitter->scalar_data.block_plain_allowed = 1;
+	emitter->scalar_data.single_quoted_allowed = 1;
+	emitter->scalar_data.block_allowed = 1;
+
+	if (leading_space ||
+	    leading_break ||
+	    trailing_space ||
+	    trailing_break) {
+		emitter->scalar_data.flow_plain_allowed = 0;
+		emitter->scalar_data.block_plain_allowed = 0;
+	}
+
+	if (trailing_space)
+		emitter->scalar_data.block_allowed = 0;
+
+	if (break_space) {
+		emitter->scalar_data.flow_plain_allowed = 0;
+		emitter->scalar_data.block_plain_allowed = 0;
+		emitter->scalar_data.single_quoted_allowed = 0;
+	}
+
+	if (space_break || special_characters) {
+		emitter->scalar_data.flow_plain_allowed = 0;
+		emitter->scalar_data.block_plain_allowed = 0;
+		emitter->scalar_data.single_quoted_allowed = 0;
+		emitter->scalar_data.block_allowed = 0;
+	}
+
+	if (line_breaks) {
+		emitter->scalar_data.flow_plain_allowed = 0;
+		emitter->scalar_data.block_plain_allowed = 0;
+	}
+
+	if (flow_indicators)
+		emitter->scalar_data.flow_plain_allowed = 0;
+
+	if (block_indicators)
+		emitter->scalar_data.block_plain_allowed = 0;
+
+	return 0;
+}
+
+static int
+analyze_event(struct yaml_emitter *emitter, const struct yaml_event *event)
+{
+	int err;
+
+	emitter->anchor_data.anchor = NULL;
+	emitter->anchor_data.anchor_length = 0;
+	emitter->tag_data.handle = NULL;
+	emitter->tag_data.handle_length = 0;
+	emitter->tag_data.suffix = NULL;
+	emitter->tag_data.suffix_length = 0;
+	emitter->scalar_data.value = NULL;
+	emitter->scalar_data.length = 0;
+
+	switch (event->type) {
+	case YAML_ALIAS_EVENT:
+		return analyze_anchor(emitter, event->alias.anchor, 1);
+
+	case YAML_SCALAR_EVENT:
+		if (event->scalar.anchor) {
+			err = analyze_anchor(emitter, event->scalar.anchor, 0);
+			if (err)
+				return err;
+		}
+
+		if (event->scalar.tag &&
+		    (emitter->canonical ||
+		     (!event->scalar.plain_implicit &&
+		      !event->scalar.quoted_implicit))) {
+			err = analyze_tag(emitter, event->scalar.tag);
+			if (err)
+				return err;
+		}
+
+		return analyze_scalar(emitter,
+				      event->scalar.value,
+				      event->scalar.length);
+
+	case YAML_SEQUENCE_START_EVENT:
+		if (event->sequence_start.anchor) {
+			err = analyze_anchor(emitter,
+					     event->sequence_start.anchor, 0);
+			if (err)
+				return err;
+		}
+
+		if (event->sequence_start.tag &&
+		    (emitter->canonical || !event->sequence_start.implicit)) {
+			err = analyze_tag(emitter,
+					  event->sequence_start.tag);
+			if (err)
+				return err;
+		}
+
+		return 0;
+
+	case YAML_MAPPING_START_EVENT:
+		if (event->mapping_start.anchor) {
+			err = analyze_anchor(emitter,
+					     event->mapping_start.anchor, 0);
+			if (err)
+				return err;
+		}
+
+		if (event->mapping_start.tag &&
+		    (emitter->canonical || !event->mapping_start.implicit)) {
+			err = analyze_tag(emitter, event->mapping_start.tag);
+			if (err)
+				return err;
+		}
+
+		return 0;
+
+	default:
+		return 0;
+	}
+}
+
+static bool check_empty_document(const struct yaml_emitter *emitter)
+{
+	return false;
+}
+
+static bool check_empty_sequence(const struct yaml_emitter *emitter)
+{
+	const struct yaml_event *event;
+
+	if (emitter->num_events < 2)
+		return false;
+
+	event = next_event(emitter);
+	return (event->type == YAML_SEQUENCE_START_EVENT &&
+		list_next_entry(event, link)->type == YAML_SEQUENCE_END_EVENT);
+}
+
+static bool check_empty_mapping(const struct yaml_emitter *emitter)
+{
+	const struct yaml_event *event;
+
+	if (emitter->num_events < 2)
+		return false;
+
+	event = next_event(emitter);
+	return (event->type == YAML_MAPPING_START_EVENT &&
+		list_next_entry(event, link)->type == YAML_MAPPING_END_EVENT);
+}
+
+static bool check_simple_key(const struct yaml_emitter *emitter)
+{
+	const struct yaml_event *event = next_event(emitter);
+	size_t length = 0;
+
+	switch (event->type) {
+	case YAML_ALIAS_EVENT:
+		length += emitter->anchor_data.anchor_length;
+		break;
+
+	case YAML_SCALAR_EVENT:
+		if (emitter->scalar_data.multiline)
+			return false;
+
+		length +=
+			emitter->anchor_data.anchor_length +
+			emitter->tag_data.handle_length +
+			emitter->tag_data.suffix_length +
+			emitter->scalar_data.length;
+		break;
+
+	case YAML_SEQUENCE_START_EVENT:
+		if (!check_empty_sequence(emitter))
+			return false;
+
+		length +=
+			emitter->anchor_data.anchor_length +
+			emitter->tag_data.handle_length +
+			emitter->tag_data.suffix_length;
+		break;
+
+	case YAML_MAPPING_START_EVENT:
+		if (!check_empty_mapping(emitter))
+			return false;
+
+		length +=
+			emitter->anchor_data.anchor_length +
+			emitter->tag_data.handle_length +
+			emitter->tag_data.suffix_length;
+		break;
+
+	default:
+		return false;
+	}
+
+	return length <= 128;
+}
+
+static int increase_indent(struct yaml_emitter *emitter,
+			   bool flow, bool indentless)
+{
+	int err;
+
+	err = PUSH(emitter, emitter->indents, emitter->indent);
+	if (err)
+		return err;
+
+	if (emitter->indent < 0)
+		emitter->indent = flow ? emitter->best_indent : 0;
+	else if (!indentless)
+		emitter->indent += emitter->best_indent;
+
+	return 0;
+}
+
+static int write_indent(struct yaml_emitter *emitter)
+{
+	int indent = (emitter->indent >= 0) ? emitter->indent : 0;
+	int err;
+
+	if (!emitter->indention ||
+	    emitter->column > indent ||
+	    (emitter->column == indent && !emitter->whitespace)) {
+		err = put_break(emitter);
+		if (err)
+			return err;
+	}
+
+	while (emitter->column < indent) {
+		err = put(emitter, ' ');
+		if (err)
+			return err;
+	}
+
+	emitter->whitespace = 1;
+	emitter->indention = 1;
+	return 0;
+}
+
+static int write_indicator(struct yaml_emitter *emitter,
+			   const char *indicator, int need_whitespace,
+			   int is_whitespace, int is_indention)
+{
+	YAML_STRING(string, indicator);
+	int err;
+
+	if (need_whitespace && !emitter->whitespace) {
+		err = put(emitter, ' ');
+		if (err)
+			return err;
+	}
+
+	while (string.pos != string.end) {
+		err = put_char(emitter, &string);
+		if (err)
+			return err;
+	}
+
+	emitter->whitespace = is_whitespace;
+	emitter->indention = (emitter->indention && is_indention);
+	emitter->open_ended = 0;
+	return 0;
+}
+
+static int write_anchor(struct yaml_emitter *emitter,
+			const char *value, size_t length)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+	int err;
+
+	while (string.pos != string.end) {
+		err = put_char(emitter, &string);
+		if (err)
+			return err;
+	}
+
+	emitter->whitespace = 0;
+	emitter->indention = 0;
+	return 0;
+}
+
+static int write_tag_handle(struct yaml_emitter *emitter,
+			    const char *value, size_t length)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+	int err;
+
+	if (!emitter->whitespace) {
+		err = put(emitter, ' ');
+		if (err)
+			return err;
+	}
+
+	while (string.pos != string.end) {
+		err = put_char(emitter, &string);
+		if (err)
+			return err;
+	}
+
+	emitter->whitespace = 0;
+	emitter->indention = 0;
+	return 0;
+}
+
+static int write_tag_content(struct yaml_emitter *emitter,
+			     const char *value, size_t length,
+			     int need_whitespace)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+	int err;
+
+	if (need_whitespace && !emitter->whitespace) {
+		err = put(emitter, ' ');
+		if (err)
+			return err;
+	}
+
+	while (string.pos != string.end) {
+		if (IS_ALPHA(string) ||
+		    CHECK(string, ';') ||
+		    CHECK(string, '/') ||
+		    CHECK(string, '?') ||
+		    CHECK(string, ':') ||
+		    CHECK(string, '@') ||
+		    CHECK(string, '&') ||
+		    CHECK(string, '=') ||
+		    CHECK(string, '+') ||
+		    CHECK(string, '$') ||
+		    CHECK(string, ',') ||
+		    CHECK(string, '_') ||
+		    CHECK(string, '.') ||
+		    CHECK(string, '~') ||
+		    CHECK(string, '*') ||
+		    CHECK(string, '\'') ||
+		    CHECK(string, '(') ||
+		    CHECK(string, ')') ||
+		    CHECK(string, '[') ||
+		    CHECK(string, ']')) {
+			err = put_char(emitter, &string);
+			if (err)
+				return err;
+		} else {
+			int width = utf8_width(&string);
+
+			while (width--) {
+				unsigned int value = *string.pos++;
+				int c;
+
+				err = put(emitter, '%');
+				if (err)
+					return err;
+
+				c = value >> 4;
+				c += c < 10 ? '0' : 'A' - 10;
+				err = put(emitter, c);
+				if (err)
+					return err;
+
+				c = value & 0x0f;
+				c += c < 10 ? '0' : 'A' - 10;
+				err = put(emitter, c);
+				if (err)
+					return err;
+			}
+		}
+	}
+
+	emitter->whitespace = 0;
+	emitter->indention = 0;
+	return 0;
+}
+
+static int write_tag(struct yaml_emitter *emitter)
+{
+	int err;
+
+	if (!emitter->tag_data.handle && !emitter->tag_data.suffix)
+		return 0;
+
+	if (emitter->tag_data.handle) {
+		err = write_tag_handle(emitter,
+				       emitter->tag_data.handle,
+				       emitter->tag_data.handle_length);
+		if (err)
+			return err;
+
+		if (emitter->tag_data.suffix) {
+			err = write_tag_content(emitter,
+						emitter->tag_data.suffix,
+						emitter->tag_data.suffix_length,
+						0);
+			if (err)
+				return err;
+		}
+	} else {
+		err = write_indicator(emitter, "!<", 1, 0, 0);
+		if (err)
+			return err;
+
+		err = write_tag_content(emitter,
+					emitter->tag_data.suffix,
+					emitter->tag_data.suffix_length,
+					0);
+		if (err)
+			return err;
+
+		err = write_indicator(emitter, ">", 0, 0, 0);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static int write_plain_scalar(struct yaml_emitter *emitter,
+			      const char *value,
+			      size_t length,
+			      int allow_breaks)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+	int spaces = 0;
+	int breaks = 0;
+	int err;
+
+	if (!emitter->whitespace) {
+		err = put(emitter, ' ');
+		if (err)
+			return err;
+	}
+
+	while (string.pos != string.end) {
+		if (IS_SPACE(string)) {
+			if (allow_breaks && !spaces &&
+			    emitter->column > emitter->best_width &&
+			    !IS_SPACE_AT(string, 1)) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+				ADVANCE(string);
+			} else {
+				err = put_char(emitter, &string);
+				if (err)
+					return err;
+			}
+			spaces = 1;
+		} else if (IS_BREAK(string)) {
+			if (!breaks && CHECK(string, '\n')) {
+				err = put_break(emitter);
+				if (err)
+					return err;
+			}
+
+			err = put_char_break(emitter, &string);
+			if (err)
+				return err;
+
+			emitter->indention = 1;
+			breaks = 1;
+		} else {
+			if (breaks) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+			}
+
+			err = put_char(emitter, &string);
+			if (err)
+				return err;
+
+			emitter->indention = 0;
+			spaces = 0;
+			breaks = 0;
+		}
+	}
+
+	emitter->whitespace = 0;
+	emitter->indention = 0;
+	if (emitter->root_context)
+		emitter->open_ended = 1;
+	return 0;
+}
+
+static int
+write_single_quoted_scalar(struct yaml_emitter *emitter,
+			   const char *value,
+			   size_t length,
+			   int allow_breaks)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+	int spaces = 0;
+	int breaks = 0;
+	int err;
+
+	err = write_indicator(emitter, "'", 1, 0, 0);
+	if (err)
+		return err;
+
+	while (string.pos != string.end) {
+		if (IS_SPACE(string)) {
+			if (allow_breaks && !spaces &&
+			    emitter->column > emitter->best_width &&
+			    string.pos != string.start &&
+			    string.pos != string.end - 1 &&
+			    !IS_SPACE_AT(string, 1)) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+				ADVANCE(string);
+			} else {
+				err = put_char(emitter, &string);
+				if (err)
+					return err;
+			}
+			spaces = 1;
+		} else if (IS_BREAK(string)) {
+			if (!breaks && CHECK(string, '\n')) {
+				err = put_break(emitter);
+				if (err)
+					return err;
+			}
+
+			err = put_char_breaks(emitter, &string);
+			if (err)
+				return err;
+
+			emitter->indention = 1;
+			breaks = 1;
+		} else {
+			if (breaks) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+			}
+
+			if (CHECK(string, '\'')) {
+				err = put(emitter, '\'');
+				if (err)
+					return err;
+			}
+
+			err = put_char(emitter, &string);
+			if (err)
+				return err;
+
+			emitter->indention = 0;
+			spaces = 0;
+			breaks = 0;
+		}
+	}
+
+	if (breaks) {
+		err = write_indent(emitter);
+		if (err)
+			return err;
+	}
+
+	err = write_indicator(emitter, "'", 0, 0, 0);
+	if (err)
+		return err;
+
+	emitter->whitespace = 0;
+	emitter->indention = 0;
+	return 0;
+}
+
+static int
+write_double_quoted_scalar(struct yaml_emitter *emitter,
+			   const char *value, size_t length, int allow_breaks)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+	int spaces = 0;
+	int err;
+
+	err = write_indicator(emitter, "\"", 1, 0, 0);
+	if (err)
+		return err;
+
+	while (string.pos != string.end) {
+		if (!IS_PRINTABLE(string) ||
+		    (!emitter->unicode && !IS_ASCII(string)) ||
+		    IS_BOM(string) ||
+		    IS_BREAK(string) ||
+		    CHECK(string, '"') ||
+		    CHECK(string, '\\')) {
+			u8 octet = string.pos[0];
+			unsigned int width;
+			unsigned int value;
+			int k;
+
+			width = (octet & 0x80) == 0x00 ? 1 :
+				(octet & 0xE0) == 0xC0 ? 2 :
+				(octet & 0xF0) == 0xE0 ? 3 :
+				(octet & 0xF8) == 0xF0 ? 4 : 0;
+			value = (octet & 0x80) == 0x00 ? octet & 0x7F :
+				(octet & 0xE0) == 0xC0 ? octet & 0x1F :
+				(octet & 0xF0) == 0xE0 ? octet & 0x0F :
+				(octet & 0xF8) == 0xF0 ? octet & 0x07 : 0;
+			for (k = 1; k < (int)width; k++) {
+				octet = string.pos[k];
+				value = (value << 6) + (octet & 0x3F);
+			}
+			string.pos += width;
+
+			err = put(emitter, '\\');
+			if (err)
+				return err;
+
+			switch (value) {
+			case 0x00:
+				err = put(emitter, '0');
+				if (err)
+					return err;
+				break;
+
+			case 0x07:
+				err = put(emitter, 'a');
+				if (err)
+					return err;
+				break;
+
+			case 0x08:
+				err = put(emitter, 'b');
+				if (err)
+					return err;
+				break;
+
+			case 0x09:
+				err = put(emitter, 't');
+				if (err)
+					return err;
+				break;
+
+			case 0x0A:
+				err = put(emitter, 'n');
+				if (err)
+					return err;
+				break;
+
+			case 0x0B:
+				err = put(emitter, 'v');
+				if (err)
+					return err;
+				break;
+
+			case 0x0C:
+				err = put(emitter, 'f');
+				if (err)
+					return err;
+				break;
+
+			case 0x0D:
+				err = put(emitter, 'r');
+				if (err)
+					return err;
+				break;
+
+			case 0x1B:
+				err = put(emitter, 'e');
+				if (err)
+					return err;
+				break;
+
+			case 0x22:
+				err = put(emitter, '\"');
+				if (err)
+					return err;
+				break;
+
+			case 0x5C:
+				err = put(emitter, '\\');
+				if (err)
+					return err;
+				break;
+
+			case 0x85:
+				err = put(emitter, 'N');
+				if (err)
+					return err;
+				break;
+
+			case 0xA0:
+				err = put(emitter, '_');
+				if (err)
+					return err;
+				break;
+
+			case 0x2028:
+				err = put(emitter, 'L');
+				if (err)
+					return err;
+				break;
+
+			case 0x2029:
+				err = put(emitter, 'P');
+				if (err)
+					return err;
+				break;
+
+			default:
+				if (value <= 0xFF) {
+					err = put(emitter, 'x');
+					if (err)
+						return err;
+					width = 2;
+				} else if (value <= 0xFFFF) {
+					err = put(emitter, 'u');
+					if (err)
+						return err;
+					width = 4;
+				} else {
+					err = put(emitter, 'U');
+					if (err)
+						return err;
+					width = 8;
+				}
+
+				for (k = (width - 1) * 4; k >= 0; k -= 4) {
+					int digit;
+
+					digit = (value >> k) & 0x0F;
+					digit += digit < 10 ? '0' : 'A' - 10;
+					err = put(emitter, digit);
+					if (err)
+						return err;
+				}
+			}
+			spaces = 0;
+		} else if (IS_SPACE(string)) {
+			if (allow_breaks &&
+			    !spaces &&
+			    emitter->column > emitter->best_width &&
+			    string.pos != string.start &&
+			    string.pos != string.end - 1) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+
+				if (IS_SPACE_AT(string, 1)) {
+					err = put(emitter, '\\');
+					if (err)
+						return err;
+				}
+				ADVANCE(string);
+			} else {
+				err = put_char(emitter, &string);
+				if (err)
+					return err;
+			}
+			spaces = 1;
+		} else {
+			err = put_char(emitter, &string);
+			if (err)
+				return err;
+			spaces = 0;
+		}
+	}
+
+	err = write_indicator(emitter, "\"", 0, 0, 0);
+	if (err)
+		return err;
+
+	emitter->whitespace = 0;
+	emitter->indention = 0;
+	return 0;
+}
+
+static int
+write_block_scalar_hints(struct yaml_emitter *emitter,
+			 struct yaml_string string)
+{
+	char indent_hint[2];
+	const char *chomp_hint = NULL;
+	int err;
+
+	if (IS_SPACE(string) || IS_BREAK(string)) {
+		indent_hint[0] = '0' + (char)emitter->best_indent;
+		indent_hint[1] = '\0';
+		err = write_indicator(emitter, indent_hint, 0, 0, 0);
+		if (err)
+			return err;
+	}
+
+	emitter->open_ended = 0;
+
+	string.pos = string.end;
+	if (string.start == string.pos) {
+		chomp_hint = "-";
+	} else {
+		do {
+			string.pos--;
+		} while ((*string.pos & 0xC0) == 0x80);
+
+		if (!IS_BREAK(string)) {
+			chomp_hint = "-";
+		} else if (string.start == string.pos) {
+			chomp_hint = "+";
+			emitter->open_ended = 1;
+		} else {
+			do {
+				string.pos--;
+			} while ((*string.pos & 0xC0) == 0x80);
+
+			if (IS_BREAK(string)) {
+				chomp_hint = "+";
+				emitter->open_ended = 1;
+			}
+		}
+	}
+
+	if (chomp_hint) {
+		err = write_indicator(emitter, chomp_hint, 0, 0, 0);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static int
+write_literal_scalar(struct yaml_emitter *emitter,
+		     const char *value, size_t length)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+	int breaks = 1;
+	int err;
+
+	err = write_indicator(emitter, "|", 1, 0, 0);
+	if (err)
+		return err;
+
+	err = write_block_scalar_hints(emitter, string);
+	if (err)
+		return err;
+
+	err = put_break(emitter);
+	if (err)
+		return err;
+
+	emitter->indention = 1;
+	emitter->whitespace = 1;
+
+	while (string.pos != string.end) {
+		if (IS_BREAK(string)) {
+			err = put_char_break(emitter, &string);
+			if (err)
+				return err;
+
+			emitter->indention = 1;
+			breaks = 1;
+		} else {
+			if (breaks) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+			}
+
+			err = put_char(emitter, &string);
+			if (err)
+				return err;
+
+			emitter->indention = 0;
+			breaks = 0;
+		}
+	}
+
+	return 0;
+}
+
+static int
+write_folded_scalar(struct yaml_emitter *emitter,
+		    const char *value, size_t length)
+{
+	struct yaml_string string = STRING_INIT(value, length);
+	int breaks = 1;
+	int leading_spaces = 1;
+	int err;
+
+	err = write_indicator(emitter, ">", 1, 0, 0);
+	if (err)
+		return err;
+
+	err = write_block_scalar_hints(emitter, string);
+	if (err)
+		return err;
+
+	err = put_break(emitter);
+	if (err)
+		return err;
+
+	emitter->indention = 1;
+	emitter->whitespace = 1;
+
+	while (string.pos != string.end) {
+		if (IS_BREAK(string)) {
+			if (!breaks && !leading_spaces && CHECK(string, '\n')) {
+				int k;
+
+				k = 0;
+				while (IS_BREAK_AT(string, k))
+					k += __utf8_width(&string, k);
+
+				if (!IS_BLANKZ_AT(string, k)) {
+					err = put_break(emitter);
+					if (err)
+						return err;
+				}
+			}
+
+			err = put_char_break(emitter, &string);
+			if (err)
+				return err;
+
+			emitter->indention = 1;
+			breaks = 1;
+		} else {
+			if (breaks) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+
+				leading_spaces = IS_BLANK(string);
+			}
+
+			if (!breaks &&
+			    IS_SPACE(string) &&
+			    !IS_SPACE_AT(string, 1) &&
+			    emitter->column > emitter->best_width) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+
+				ADVANCE(string);
+			} else {
+				err = put_char(emitter, &string);
+				if (err)
+					return err;
+			}
+			emitter->indention = 0;
+			breaks = 0;
+		}
+	}
+
+	return err;
+}
+
+static int
+write_scalar(struct yaml_emitter *emitter)
+{
+	switch (emitter->scalar_data.style) {
+	case YAML_PLAIN_SCALAR_STYLE:
+		return write_plain_scalar(emitter,
+					  emitter->scalar_data.value,
+					  emitter->scalar_data.length,
+					  !emitter->simple_key_context);
+
+	case YAML_SINGLE_QUOTED_SCALAR_STYLE:
+		return write_single_quoted_scalar(emitter,
+						  emitter->scalar_data.value,
+						  emitter->scalar_data.length,
+						  !emitter->simple_key_context);
+
+	case YAML_DOUBLE_QUOTED_SCALAR_STYLE:
+		return write_double_quoted_scalar(emitter,
+						  emitter->scalar_data.value,
+						  emitter->scalar_data.length,
+						  !emitter->simple_key_context);
+
+	case YAML_LITERAL_SCALAR_STYLE:
+		return write_literal_scalar(emitter,
+					    emitter->scalar_data.value,
+					    emitter->scalar_data.length);
+
+	case YAML_FOLDED_SCALAR_STYLE:
+		return write_folded_scalar(emitter,
+					   emitter->scalar_data.value,
+					   emitter->scalar_data.length);
+
+	default:
+		BUG();
+		return 0;
+	}
+}
+
+static int emit_anchor(struct yaml_emitter *emitter)
+{
+	int err;
+
+	if (!emitter->anchor_data.anchor)
+		return 0;
+
+	err = write_indicator(emitter,
+			      emitter->anchor_data.alias ? "*" : "&",
+			      1, 0, 0);
+	if (err)
+		return err;
+
+	return write_anchor(emitter,
+			    emitter->anchor_data.anchor,
+			    emitter->anchor_data.anchor_length);
+}
+
+static int emit_stream_start(struct yaml_emitter *emitter,
+			     const struct yaml_event *event)
+{
+	if (event->type == YAML_STREAM_START_EVENT) {
+		if (emitter->best_indent < 2 || emitter->best_indent > 9)
+			emitter->best_indent  = 2;
+
+		if (emitter->best_width >= 0 &&
+		    emitter->best_width <= emitter->best_indent * 2)
+			emitter->best_width = 80;
+
+		if (emitter->best_width < 0)
+			emitter->best_width = INT_MAX;
+
+		emitter->indent = -1;
+
+		emitter->column = 0;
+		emitter->whitespace = 1;
+		emitter->indention = 1;
+
+		emitter->state = EMIT_FIRST_DOCUMENT_START_STATE;
+		return 0;
+	}
+
+	return set_error(emitter, "expected STREAM-START");
+}
+
+static int
+append_tag_directive(struct yaml_emitter *emitter,
+		     struct yaml_tag_directive value,
+		     int allow_duplicates)
+{
+	struct yaml_tag_directive *tag;
+	struct yaml_tag_directive copy = {};
+	int err;
+
+	for (tag = emitter->tags.start; tag != emitter->tags.top; tag++) {
+		if (strcmp(value.handle, tag->handle) == 0) {
+			if (allow_duplicates)
+				return 0;
+
+			return set_error(emitter, "duplicate %TAG directive");
+		}
+	}
+
+	copy.handle = kstrdup(value.handle, GFP_KERNEL);
+	copy.prefix = kstrdup(value.prefix, GFP_KERNEL);
+	if (!copy.handle || !copy.prefix) {
+		emitter->errno = -ENOMEM;
+		err = -ENOMEM;
+		goto error;
+	}
+
+	err = PUSH(emitter, emitter->tags, copy);
+	if (err)
+		goto error;
+
+	return 0;
+
+error:
+	kfree(copy.handle);
+	kfree(copy.prefix);
+	return err;
+}
+
+static int
+emit_document_start(struct yaml_emitter *emitter,
+		    const struct yaml_event *event,
+		    int first)
+{
+	int err;
+
+	if (event->type == YAML_DOCUMENT_START_EVENT) {
+		struct yaml_tag_directive default_tag_directives[] = {
+			{ "!", "!" },
+			{ "!!", "tag:yaml.org,2002:" },
+			{ NULL, NULL }
+		};
+		struct yaml_tag_directive *tag;
+		int implicit;
+
+		if (event->document_start.version) {
+			err = analyze_version_directive(emitter,
+							*event->document_start.version);
+			if (err)
+				return err;
+		}
+
+		for (tag = event->document_start.tags.start;
+		     tag != event->document_start.tags.end;
+		     tag++) {
+			err = analyze_tag_directive(emitter, *tag);
+			if (err)
+				return err;
+
+			err = append_tag_directive(emitter, *tag, 0);
+			if (err)
+				return err;
+		}
+
+		for (tag = default_tag_directives;
+		     tag->handle; tag++) {
+			err = append_tag_directive(emitter, *tag, 1);
+			if (err)
+				return err;
+		}
+
+		implicit = event->document_start.implicit;
+		if (!first || emitter->canonical)
+			implicit = 0;
+
+		if ((event->document_start.version ||
+		     event->document_start.tags.start
+		     != event->document_start.tags.end) &&
+		    emitter->open_ended) {
+			err = write_indicator(emitter, "...", 1, 0, 0);
+			if (err)
+				return err;
+
+			err = write_indent(emitter);
+			if (err)
+				return err;
+		}
+
+		if (event->document_start.version) {
+			implicit = 0;
+			err = write_indicator(emitter, "%YAML", 1, 0, 0);
+			if (err)
+				return err;
+
+			err = write_indicator(emitter, "1.1", 1, 0, 0);
+			if (err)
+				return err;
+
+			err = write_indent(emitter);
+			if (err)
+				return err;
+		}
+
+		if (event->document_start.tags.start !=
+		    event->document_start.tags.end) {
+			implicit = 0;
+			for (tag = event->document_start.tags.start;
+			     tag != event->document_start.tags.end;
+			     tag++) {
+				err = write_indicator(emitter, "%TAG", 1, 0, 0);
+				if (err)
+					return err;
+
+				err = write_tag_handle(emitter,
+						       tag->handle,
+						       strlen(tag->handle));
+				if (err)
+					return err;
+
+				err = write_tag_content(emitter,
+							tag->prefix,
+							strlen(tag->prefix),
+							1);
+				if (err)
+					return err;
+
+				err = write_indent(emitter);
+				if (err)
+					return err;
+			}
+		}
+
+		if (check_empty_document(emitter))
+			implicit = 0;
+
+		if (!implicit) {
+			err = write_indent(emitter);
+			if (err)
+				return err;
+
+			err = write_indicator(emitter, "---", 1, 0, 0);
+			if (err)
+				return err;
+
+			if (emitter->canonical) {
+				err = write_indent(emitter);
+				if (err)
+					return err;
+			}
+		}
+
+		emitter->state = EMIT_DOCUMENT_CONTENT_STATE;
+		return 0;
+	}
+
+	if (event->type == YAML_STREAM_END_EVENT) {
+		err = flush(emitter);
+		if (err)
+			return err;
+
+		emitter->state = EMIT_END_STATE;
+		return 0;
+	}
+
+	return set_error(emitter, "expected DOCUMENT-START or STREAM-END");
+}
+
+static int
+emit_document_end(struct yaml_emitter *emitter, const struct yaml_event *event)
+{
+	int err;
+
+	if (event->type == YAML_DOCUMENT_END_EVENT) {
+		err = write_indent(emitter);
+		if (err)
+			return err;
+
+		if (!event->document_end.implicit) {
+			err = write_indicator(emitter, "...", 1, 0, 0);
+			if (err)
+				return err;
+
+			err = write_indent(emitter);
+			if (err)
+				return err;
+		}
+
+		err = flush(emitter);
+		if (err)
+			return err;
+
+		emitter->state = EMIT_DOCUMENT_START_STATE;
+
+		while (!EMPTY(emitter, emitter->tags)) {
+			struct yaml_tag_directive tag =
+				POP(emitter, emitter->tags);
+
+			kfree(tag.handle);
+			kfree(tag.prefix);
+		}
+
+		return 0;
+	}
+
+	return set_error(emitter, "expected DOCUMENT-END");
+}
+
+static int
+emit_alias(struct yaml_emitter *emitter, const struct yaml_event *event)
+{
+	int err;
+
+	err = emit_anchor(emitter);
+	if (err)
+		return err;
+
+	emitter->state = POP(emitter, emitter->states);
+	return 0;
+}
+
+static int
+select_scalar_style(struct yaml_emitter *emitter, const struct yaml_event *event)
+{
+	enum yaml_scalar_style style = event->scalar.style;
+	bool no_tag = !emitter->tag_data.handle && !emitter->tag_data.suffix;
+
+	if (no_tag &&
+	    !event->scalar.plain_implicit &&
+	    !event->scalar.quoted_implicit)
+		return set_error(emitter,
+				 "neither tag nor implicit flags are specified");
+
+	if (style == YAML_ANY_SCALAR_STYLE)
+		style = YAML_PLAIN_SCALAR_STYLE;
+
+	if (emitter->canonical)
+		style = YAML_DOUBLE_QUOTED_SCALAR_STYLE;
+
+	if (emitter->simple_key_context && emitter->scalar_data.multiline)
+		style = YAML_DOUBLE_QUOTED_SCALAR_STYLE;
+
+	if (style == YAML_PLAIN_SCALAR_STYLE) {
+		if ((emitter->flow_level && !emitter->scalar_data.flow_plain_allowed) ||
+		    (!emitter->flow_level && !emitter->scalar_data.block_plain_allowed))
+			style = YAML_SINGLE_QUOTED_SCALAR_STYLE;
+
+		if (!emitter->scalar_data.length &&
+		    (emitter->flow_level || emitter->simple_key_context))
+			style = YAML_SINGLE_QUOTED_SCALAR_STYLE;
+
+		if (no_tag && !event->scalar.plain_implicit)
+			style = YAML_SINGLE_QUOTED_SCALAR_STYLE;
+	}
+
+	if (style == YAML_SINGLE_QUOTED_SCALAR_STYLE) {
+		if (!emitter->scalar_data.single_quoted_allowed)
+			style = YAML_DOUBLE_QUOTED_SCALAR_STYLE;
+	}
+
+	if (style == YAML_LITERAL_SCALAR_STYLE ||
+	    style == YAML_FOLDED_SCALAR_STYLE) {
+		if (!emitter->scalar_data.block_allowed ||
+		    emitter->flow_level ||
+		    emitter->simple_key_context)
+			style = YAML_DOUBLE_QUOTED_SCALAR_STYLE;
+	}
+
+	if (no_tag && !event->scalar.quoted_implicit &&
+	    style != YAML_PLAIN_SCALAR_STYLE) {
+		emitter->tag_data.handle = "!";
+		emitter->tag_data.handle_length = 1;
+	}
+
+	emitter->scalar_data.style = style;
+	return 0;
+}
+
+static int
+emit_scalar(struct yaml_emitter *emitter, const struct yaml_event *event)
+{
+	int err;
+
+	err = select_scalar_style(emitter, event);
+	if (err)
+		return err;
+
+	err = emit_anchor(emitter);
+	if (err)
+		return err;
+
+	err = write_tag(emitter);
+	if (err)
+		return err;
+
+	err = increase_indent(emitter, 1, 0);
+	if (err)
+		return err;
+
+	err = write_scalar(emitter);
+	if (err)
+		return err;
+
+	emitter->indent = POP(emitter, emitter->indents);
+	emitter->state = POP(emitter, emitter->states);
+	return 0;
+}
+
+static int
+emit_sequence_start(struct yaml_emitter *emitter, const struct yaml_event *event)
+{
+	int err;
+
+	err = emit_anchor(emitter);
+	if (err)
+		return err;
+
+	err = write_tag(emitter);
+	if (err)
+		return err;
+
+	if (emitter->flow_level || emitter->canonical ||
+	    event->sequence_start.style == YAML_FLOW_SEQUENCE_STYLE ||
+	    check_empty_sequence(emitter)) {
+		emitter->state = EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE;
+	} else {
+		emitter->state = EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE;
+	}
+
+	return 0;
+}
+
+static int
+emit_mapping_start(struct yaml_emitter *emitter, const struct yaml_event *event)
+{
+	int err;
+
+	err = emit_anchor(emitter);
+	if (err)
+		return err;
+
+	err = write_tag(emitter);
+	if (err)
+		return err;
+
+	if (emitter->flow_level ||
+	    emitter->canonical ||
+	    event->mapping_start.style == YAML_FLOW_MAPPING_STYLE ||
+	    check_empty_mapping(emitter)) {
+		emitter->state = EMIT_FLOW_MAPPING_FIRST_KEY_STATE;
+	} else {
+		emitter->state = EMIT_BLOCK_MAPPING_FIRST_KEY_STATE;
+	}
+
+	return 0;
+}
+
+static int
+emit_node(struct yaml_emitter *emitter, const struct yaml_event *event,
+	  int root, int sequence, int mapping, int simple_key)
+{
+	emitter->root_context = root;
+	emitter->sequence_context = sequence;
+	emitter->mapping_context = mapping;
+	emitter->simple_key_context = simple_key;
+
+	switch (event->type) {
+	case YAML_ALIAS_EVENT:
+		return emit_alias(emitter, event);
+
+	case YAML_SCALAR_EVENT:
+		return emit_scalar(emitter, event);
+
+	case YAML_SEQUENCE_START_EVENT:
+		return emit_sequence_start(emitter, event);
+
+	case YAML_MAPPING_START_EVENT:
+		return emit_mapping_start(emitter, event);
+
+	default:
+		return set_error(emitter,
+				 "expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS");
+	}
+
+	return 0;
+}
+
+static int
+emit_document_content(struct yaml_emitter *emitter, const struct yaml_event *event)
+{
+	int err;
+
+	err = PUSH(emitter, emitter->states, EMIT_DOCUMENT_END_STATE);
+	if (err)
+		return err;
+
+	return emit_node(emitter, event, 1, 0, 0, 0);
+}
+
+static int
+emit_flow_sequence_item(struct yaml_emitter *emitter,
+			const struct yaml_event *event,
+			bool first)
+{
+	int err;
+
+	if (first) {
+		err = write_indicator(emitter, "[", 1, 1, 0);
+		if (err)
+			return err;
+
+		err = increase_indent(emitter, 1, 0);
+		if (err)
+			return err;
+
+		emitter->flow_level++;
+	}
+
+	if (event->type == YAML_SEQUENCE_END_EVENT) {
+		emitter->flow_level--;
+		emitter->indent = POP(emitter, emitter->indents);
+
+		if (emitter->canonical && !first) {
+			err = write_indicator(emitter, ",", 0, 0, 0);
+			if (err)
+				return err;
+
+			err = write_indent(emitter);
+			if (err)
+				return err;
+		}
+
+		err = write_indicator(emitter, "]", 0, 0, 0);
+		if (err)
+			return err;
+
+		emitter->state = POP(emitter, emitter->states);
+		return 0;
+	}
+
+	if (!first) {
+		err = write_indicator(emitter, ",", 0, 0, 0);
+		if (err)
+			return err;
+	}
+
+	if (emitter->canonical || emitter->column > emitter->best_width) {
+		err = write_indent(emitter);
+		if (err)
+			return err;
+	}
+
+	err = PUSH(emitter, emitter->states, EMIT_FLOW_SEQUENCE_ITEM_STATE);
+	if (err)
+		return err;
+
+	return emit_node(emitter, event, 0, 1, 0, 0);
+}
+
+static int
+emit_flow_mapping_key(struct yaml_emitter *emitter,
+		      const struct yaml_event *event, int first)
+{
+	int err;
+
+	if (first) {
+		err = write_indicator(emitter, "{", 1, 1, 0);
+		if (err)
+			return err;
+
+		err = increase_indent(emitter, 1, 0);
+		if (err)
+			return err;
+
+		emitter->flow_level++;
+	}
+
+	if (event->type == YAML_MAPPING_END_EVENT) {
+		emitter->flow_level--;
+		emitter->indent = POP(emitter, emitter->indents);
+
+		if (emitter->canonical && !first) {
+			err = write_indicator(emitter, ",", 0, 0, 0);
+			if (err)
+				return err;
+
+			err = write_indent(emitter);
+			if (err)
+				return err;
+		}
+
+		err = write_indicator(emitter, "}", 0, 0, 0);
+		if (err)
+			return err;
+
+		emitter->state = POP(emitter, emitter->states);
+		return 0;
+	}
+
+	if (!first) {
+		err = write_indicator(emitter, ",", 0, 0, 0);
+		if (err)
+			return err;
+	}
+
+	if (emitter->canonical || emitter->column > emitter->best_width) {
+		err = write_indent(emitter);
+		if (err)
+			return err;
+	}
+
+	if (!emitter->canonical && check_simple_key(emitter)) {
+		err = PUSH(emitter, emitter->states,
+			   EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE);
+		if (err)
+			return err;
+
+		return emit_node(emitter, event, 0, 0, 1, 1);
+	}
+
+	err = write_indicator(emitter, "?", 1, 0, 0);
+	if (err)
+		return err;
+
+	err = PUSH(emitter, emitter->states, EMIT_FLOW_MAPPING_VALUE_STATE);
+	if (err)
+		return err;
+
+	return emit_node(emitter, event, 0, 0, 1, 0);
+}
+
+static int
+emit_flow_mapping_value(struct yaml_emitter *emitter,
+			const struct yaml_event *event, int simple)
+{
+	int err;
+
+	if (simple) {
+		err = write_indicator(emitter, ":", 0, 0, 0);
+		if (err)
+			return err;
+	} else {
+		if (emitter->canonical ||
+		    emitter->column > emitter->best_width) {
+			err = write_indent(emitter);
+			if (err)
+				return err;
+		}
+
+		err = write_indicator(emitter, ":", 1, 0, 0);
+		if (err)
+			return err;
+	}
+
+	err = PUSH(emitter, emitter->states, EMIT_FLOW_MAPPING_KEY_STATE);
+	if (err)
+		return err;
+
+	return emit_node(emitter, event, 0, 0, 1, 0);
+}
+
+static int
+emit_block_sequence_item(struct yaml_emitter *emitter,
+			 const struct yaml_event *event, int first)
+{
+	int err;
+
+	if (first) {
+		err = increase_indent(emitter, 0,
+				      emitter->mapping_context &&
+				      !emitter->indention);
+		if (err)
+			return err;
+	}
+
+	if (event->type == YAML_SEQUENCE_END_EVENT) {
+		emitter->indent = POP(emitter, emitter->indents);
+		emitter->state = POP(emitter, emitter->states);
+		return 0;
+	}
+
+	err = write_indent(emitter);
+	if (err)
+		return err;
+
+	err = write_indicator(emitter, "-", 1, 0, 1);
+	if (err)
+		return err;
+
+	err = PUSH(emitter, emitter->states, EMIT_BLOCK_SEQUENCE_ITEM_STATE);
+	if (err)
+		return err;
+
+	return emit_node(emitter, event, 0, 1, 0, 0);
+}
+
+static int
+emit_block_mapping_key(struct yaml_emitter *emitter,
+		       const struct yaml_event *event, int first)
+{
+	int err;
+
+	if (first) {
+		err = increase_indent(emitter, 0, 0);
+		if (err)
+			return err;
+	}
+
+	if (event->type == YAML_MAPPING_END_EVENT) {
+		emitter->indent = POP(emitter, emitter->indents);
+		emitter->state = POP(emitter, emitter->states);
+		return 0;
+	}
+
+	err = write_indent(emitter);
+	if (err)
+		return err;
+
+	if (check_simple_key(emitter)) {
+		err = PUSH(emitter, emitter->states,
+			   EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE);
+		if (err)
+			return err;
+
+		return emit_node(emitter, event, 0, 0, 1, 1);
+	} else {
+		err = write_indicator(emitter, "?", 1, 0, 1);
+		if (err)
+			return err;
+
+		err = PUSH(emitter, emitter->states,
+			   EMIT_BLOCK_MAPPING_VALUE_STATE);
+		if (err)
+			return err;
+
+		return emit_node(emitter, event, 0, 0, 1, 0);
+	}
+}
+
+static int
+emit_block_mapping_value(struct yaml_emitter *emitter,
+			 const struct yaml_event *event, int simple)
+{
+	int err;
+
+	if (simple) {
+		err = write_indicator(emitter, ":", 0, 0, 0);
+		if (err)
+			return err;
+	} else {
+		err = write_indent(emitter);
+		if (err)
+			return err;
+
+		err = write_indicator(emitter, ":", 1, 0, 1);
+		if (err)
+			return err;
+	}
+
+	err = PUSH(emitter, emitter->states, EMIT_BLOCK_MAPPING_KEY_STATE);
+	if (err)
+		return err;
+
+	return emit_node(emitter, event, 0, 0, 1, 0);
+}
+
+static int
+emitter_state_machine(struct yaml_emitter *emitter,
+		      const struct yaml_event *event)
+{
+	switch (emitter->state) {
+	case EMIT_STREAM_START_STATE:
+		return emit_stream_start(emitter, event);
+
+	case EMIT_FIRST_DOCUMENT_START_STATE:
+		return emit_document_start(emitter, event, true);
+
+	case EMIT_DOCUMENT_START_STATE:
+		return emit_document_start(emitter, event, false);
+
+	case EMIT_DOCUMENT_CONTENT_STATE:
+		return emit_document_content(emitter, event);
+
+	case EMIT_DOCUMENT_END_STATE:
+		return emit_document_end(emitter, event);
+
+	case EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE:
+		return emit_flow_sequence_item(emitter, event, true);
+
+	case EMIT_FLOW_SEQUENCE_ITEM_STATE:
+		return emit_flow_sequence_item(emitter, event, false);
+
+	case EMIT_FLOW_MAPPING_FIRST_KEY_STATE:
+		return emit_flow_mapping_key(emitter, event, true);
+
+	case EMIT_FLOW_MAPPING_KEY_STATE:
+		return emit_flow_mapping_key(emitter, event, false);
+
+	case EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE:
+		return emit_flow_mapping_value(emitter, event, true);
+
+	case EMIT_FLOW_MAPPING_VALUE_STATE:
+		return emit_flow_mapping_value(emitter, event, false);
+
+	case EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE:
+		return emit_block_sequence_item(emitter, event, true);
+
+	case EMIT_BLOCK_SEQUENCE_ITEM_STATE:
+		return emit_block_sequence_item(emitter, event, false);
+
+	case EMIT_BLOCK_MAPPING_FIRST_KEY_STATE:
+		return emit_block_mapping_key(emitter, event, true);
+
+	case EMIT_BLOCK_MAPPING_KEY_STATE:
+		return emit_block_mapping_key(emitter, event, false);
+
+	case EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE:
+		return emit_block_mapping_value(emitter, event, true);
+
+	case EMIT_BLOCK_MAPPING_VALUE_STATE:
+		return emit_block_mapping_value(emitter, event, false);
+
+	case EMIT_END_STATE:
+		return set_error(emitter, "expected nothing after STREAM-END");
+
+	default:
+		BUG();
+	}
+
+	return 0;
+}
+
+void yaml_emitter_emit(struct yaml_emitter *emitter, struct yaml_event *event)
+{
+	if (!event || !emitter || emitter->errno) {
+		yaml_event_delete(event);
+		return;
+	}
+
+	list_add_tail(&event->link, &emitter->events);
+	emitter->num_events++;
+
+	while (!need_more_events(emitter)) {
+		event = next_event(emitter);
+
+		if (analyze_event(emitter, event))
+			return;
+
+		if (emitter_state_machine(emitter, event))
+			return;
+
+		emitter->num_events--;
+		list_del(&event->link);
+		yaml_event_delete(event);
+	}
+}
+EXPORT_SYMBOL(yaml_emitter_emit);
+
+int yaml_emitter_flush(struct yaml_emitter *emitter, const char **problem)
+{
+	int err;
+
+	if (!emitter) {
+		if (problem)
+			*problem = "allocation error";
+		return -ENOMEM;
+	}
+
+	if (emitter->errno)
+		err = emitter->errno;
+	else
+		err = flush(emitter);
+	if (!err)
+		return 0;
+
+	if (problem)
+		*problem = emitter->problem;
+	return err;
+}
+EXPORT_SYMBOL(yaml_emitter_flush);
+
+static bool yaml_stack_extend(void **start, void **top, void **end)
+{
+	void *new_start;
+
+	if (*end - *start >= INT_MAX / 2)
+		return false;
+
+	new_start = krealloc(*start, (*end - *start) * 2, GFP_KERNEL);
+	if (!new_start)
+		return false;
+
+	*top = new_start + (*top - *start);
+	*end = new_start + (*end - *start) * 2;
+	*start = new_start;
+
+	return true;
+}
+
+#define STACK_INIT(context, stack)                                     \
+	(((stack).start = kmalloc(INITIAL_STACK_SIZE * sizeof(*(stack). start),\
+				  gfp)) ?			\
+	((stack).top = (stack).start,					\
+	(stack).end = (stack).start + INITIAL_STACK_SIZE,		\
+	1) : 0)
+#define STACK_DEL(context, stack) kfree((stack).start)
+
+struct yaml_emitter *yaml_emitter_create(gfp_t gfp)
+{
+	struct yaml_emitter *emitter;
+
+	emitter = kzalloc(sizeof(*emitter), gfp);
+	if (!emitter)
+		return NULL;
+
+	emitter->gfp = gfp;
+	INIT_LIST_HEAD(&emitter->events);
+
+	emitter->buf.start = kmalloc(PAGE_SIZE, gfp);
+	if (!emitter->buf.start)
+		goto error;
+
+	emitter->buf.pos = emitter->buf.start;
+	emitter->buf.last = emitter->buf.pos;
+	emitter->buf.end = emitter->buf.start + PAGE_SIZE;
+
+	if (!STACK_INIT(emitter, emitter->states))
+		goto error;
+
+	if (!STACK_INIT(emitter, emitter->indents))
+		goto error;
+
+	if (!STACK_INIT(emitter, emitter->tags))
+		goto error;
+
+	return emitter;
+
+error:
+	yaml_emitter_destroy(emitter);
+	return NULL;
+}
+EXPORT_SYMBOL(yaml_emitter_create);
+
+gfp_t yaml_emitter_set_gfp(struct yaml_emitter *emitter, gfp_t gfp)
+{
+	gfp_t old = 0;
+
+	if (emitter) {
+		old = emitter->gfp;
+		emitter->gfp = gfp;
+	}
+
+	return old;
+}
+EXPORT_SYMBOL(yaml_emitter_set_gfp);
+
+static void delete_events(struct yaml_emitter *emitter)
+{
+	struct yaml_event *event, *next;
+
+	list_for_each_entry_safe(event, next, &emitter->events, link)
+		yaml_event_delete(event);
+}
+
+void yaml_emitter_destroy(struct yaml_emitter *emitter)
+{
+	if (!emitter)
+		return;
+
+	delete_events(emitter);
+
+	kfree(emitter->buf.start);
+	STACK_DEL(emitter, emitter->states);
+	STACK_DEL(emitter, emitter->indents);
+	while (!EMPTY(empty, emitter->tags)) {
+		struct yaml_tag_directive tag =
+			POP(emitter, emitter->tags);
+
+		kfree(tag.handle);
+		kfree(tag.prefix);
+	}
+	STACK_DEL(emitter, emitter->tags);
+	kfree(emitter->anchors);
+
+	kfree(emitter);
+}
+EXPORT_SYMBOL(yaml_emitter_destroy);
+
+static int string_write_handler(void *data, const u8 *buffer, size_t size)
+{
+	struct yaml_emitter *emitter = data;
+	typeof(emitter->output.string) *out = &emitter->output.string;
+
+	if (out->size - *out->size_written < size)
+		size = out->size - *out->size_written;
+
+	memcpy(out->buffer + *out->size_written, buffer, size);
+	*out->size_written += size;
+	return 0;
+}
+
+void yaml_emitter_set_string(struct yaml_emitter *emitter,
+			     u8 *output,
+			     size_t size,
+			     size_t *size_written)
+{
+	if (!emitter)
+		return;
+
+	emitter->write_handler = string_write_handler;
+	emitter->write_data = emitter;
+
+	emitter->output.string.buffer = output;
+	emitter->output.string.size = size;
+	emitter->output.string.size_written = size_written;
+	*size_written = 0;
+}
+EXPORT_SYMBOL(yaml_emitter_set_string);
+
+static int seq_file_write_handler(void *data, const u8 *buffer, size_t size)
+{
+	/* Ignore the overflow error; seq will restart with a larger buffer */
+	seq_write(data, buffer, size);
+	return 0;
+}
+
+void yaml_emitter_set_seq_file(struct yaml_emitter *emitter,
+			       struct seq_file *file)
+{
+	if (!emitter)
+		return;
+
+	emitter->write_handler = seq_file_write_handler;
+	emitter->write_data = file;
+}
+EXPORT_SYMBOL(yaml_emitter_set_seq_file);
+
+void yaml_emitter_set_width(struct yaml_emitter *emitter, int width)
+{
+	if (!emitter)
+		return;
+
+	emitter->best_width = (width >= 0) ? width : -1;
+}
+EXPORT_SYMBOL(yaml_emitter_set_width);
+
+void yaml_emitter_set_unicode(struct yaml_emitter *emitter, bool unicode)
+{
+	if (!emitter)
+		return;
+
+	emitter->unicode = unicode;
+}
+EXPORT_SYMBOL(yaml_emitter_set_unicode);
+
+void yaml_emitter_set_indent(struct yaml_emitter *emitter, int indent)
+{
+	if (!emitter)
+		return;
+
+	emitter->best_indent = (1 < indent && indent < 10) ? indent : 2;
+}
+EXPORT_SYMBOL(yaml_emitter_set_indent);
+
+void yaml_emitter_set_canonical(struct yaml_emitter *emitter, bool canonical)
+{
+	if (!emitter)
+		return;
+
+	emitter->canonical = canonical;
+}
+EXPORT_SYMBOL(yaml_emitter_set_canonical);
+
+void yaml_emitter_set_output(struct yaml_emitter *emitter,
+			     yaml_write_t handler,
+			     void *data)
+{
+	if (!emitter)
+		return;
+
+	emitter->write_handler = handler;
+	emitter->write_data = data;
+}
+EXPORT_SYMBOL(yaml_emitter_set_output);
diff --git a/lib/yaml/yaml-emitter.h b/lib/yaml/yaml-emitter.h
new file mode 100644
index 000000000000..c4ed590f73cf
--- /dev/null
+++ b/lib/yaml/yaml-emitter.h
@@ -0,0 +1,140 @@ 
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (c) 2006-2016 Kirill Simonov
+ * Copyright (c) 2017-2019 Ingy döt Net
+ * Copyright (c) 2020 Intel Corporation
+ */
+
+#ifndef YAML_EMITTER_H
+#define YAML_EMITTER_H
+
+#include <linux/gfp.h>
+#include <linux/list.h>
+#include <linux/yaml.h>
+
+enum yaml_emitter_state {
+	EMIT_STREAM_START_STATE,
+	EMIT_FIRST_DOCUMENT_START_STATE,
+	EMIT_DOCUMENT_START_STATE,
+	EMIT_DOCUMENT_CONTENT_STATE,
+	EMIT_DOCUMENT_END_STATE,
+	EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE,
+	EMIT_FLOW_SEQUENCE_ITEM_STATE,
+	EMIT_FLOW_MAPPING_FIRST_KEY_STATE,
+	EMIT_FLOW_MAPPING_KEY_STATE,
+	EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE,
+	EMIT_FLOW_MAPPING_VALUE_STATE,
+	EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE,
+	EMIT_BLOCK_SEQUENCE_ITEM_STATE,
+	EMIT_BLOCK_MAPPING_FIRST_KEY_STATE,
+	EMIT_BLOCK_MAPPING_KEY_STATE,
+	EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE,
+	EMIT_BLOCK_MAPPING_VALUE_STATE,
+	EMIT_END_STATE
+};
+
+struct yaml_anchors {
+	int references;
+	int anchor;
+	int serialized;
+};
+
+struct yaml_emitter {
+	int errno;
+	const char *problem;
+	gfp_t gfp;
+
+	yaml_write_t write_handler;
+	void *write_data;
+
+	union {
+		struct {
+			u8 *buffer;
+			size_t size;
+			size_t *size_written;
+		} string;
+	} output;
+
+	struct {
+		char *start;
+		char *end;
+		char *pos;
+		char *last;
+	} buf;
+
+	int canonical;
+	int best_indent;
+	int best_width;
+	int unicode;
+
+	struct {
+		enum yaml_emitter_state *start;
+		enum yaml_emitter_state *end;
+		enum yaml_emitter_state *top;
+	} states;
+
+	enum yaml_emitter_state state;
+
+	struct list_head events;
+	int num_events;
+
+	struct {
+		int *start;
+		int *end;
+		int *top;
+	} indents;
+
+	struct {
+		struct yaml_tag_directive *start;
+		struct yaml_tag_directive *end;
+		struct yaml_tag_directive *top;
+	} tags;
+
+	int indent;
+	int flow_level;
+
+	int root_context;
+	int sequence_context;
+	int mapping_context;
+	int simple_key_context;
+
+	int column;
+	int whitespace;
+	int indention;
+	int open_ended;
+
+	/** Anchor analysis. */
+	struct {
+		const char *anchor;
+		size_t anchor_length;
+		int alias;
+	} anchor_data;
+
+	/** Tag analysis. */
+	struct {
+		const char *handle;
+		size_t handle_length;
+		const char *suffix;
+		size_t suffix_length;
+	} tag_data;
+
+	/** Scalar analysis. */
+	struct {
+		const char *value;
+		size_t length;
+		int multiline;
+		int flow_plain_allowed;
+		int block_plain_allowed;
+		int single_quoted_allowed;
+		int block_allowed;
+		enum yaml_scalar_style style;
+	} scalar_data;
+
+	int opened;
+	int closed;
+
+	struct yaml_anchors *anchors;
+	int last_anchor_id;
+};
+
+#endif /* YAML_EMITTER_H */
diff --git a/lib/yaml/yaml-events.c b/lib/yaml/yaml-events.c
new file mode 100644
index 000000000000..adc7ed887d6a
--- /dev/null
+++ b/lib/yaml/yaml-events.c
@@ -0,0 +1,424 @@ 
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (c) 2006-2016 Kirill Simonov
+ * Copyright (c) 2017-2019 Ingy döt Net
+ * Copyright (c) 2020 Intel Corporation
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/yaml.h>
+#include <linux/yaml-events.h>
+
+static bool check_utf8(const char *pos, size_t length)
+{
+	const char *end = pos + length;
+
+	while (pos < end) {
+		u8 octet = pos[0];
+		unsigned int width;
+		unsigned int value;
+		size_t k;
+
+		width = (octet & 0x80) == 0x00 ? 1 :
+			(octet & 0xE0) == 0xC0 ? 2 :
+			(octet & 0xF0) == 0xE0 ? 3 :
+			(octet & 0xF8) == 0xF0 ? 4 : 0;
+		value = (octet & 0x80) == 0x00 ? octet & 0x7F :
+			(octet & 0xE0) == 0xC0 ? octet & 0x1F :
+			(octet & 0xF0) == 0xE0 ? octet & 0x0F :
+			(octet & 0xF8) == 0xF0 ? octet & 0x07 : 0;
+		if (!width)
+			return false;
+
+		if (pos + width > end)
+			return false;
+
+		for (k = 1; k < width; k++) {
+			octet = pos[k];
+			if ((octet & 0xC0) != 0x80)
+				return false;
+
+			value = (value << 6) + (octet & 0x3F);
+		}
+
+		if (!(width == 1 ||
+		      (width == 2 && value >= 0x80) ||
+		      (width == 3 && value >= 0x800) ||
+		      (width == 4 && value >= 0x10000)))
+			return false;
+
+		pos += width;
+	}
+
+	return true;
+}
+
+static struct yaml_event *yaml_event_create(int type, gfp_t gfp)
+{
+	struct yaml_event *ev;
+
+	ev = kzalloc(sizeof(*ev), gfp);
+	if (ev)
+		ev->type = type;
+
+	return ev;
+}
+
+static void yaml_event_free(struct yaml_event *event)
+{
+	kfree(event);
+}
+
+struct yaml_event *yaml_stream_start_event_create(gfp_t gfp)
+{
+	return yaml_event_create(YAML_STREAM_START_EVENT, gfp);
+}
+EXPORT_SYMBOL(yaml_stream_start_event_create);
+
+struct yaml_event *yaml_stream_end_event_create(gfp_t gfp)
+{
+	return yaml_event_create(YAML_STREAM_END_EVENT, gfp);
+}
+EXPORT_SYMBOL(yaml_stream_end_event_create);
+
+struct yaml_event *
+yaml_document_start_event_create(struct yaml_version_directive *version,
+				 struct yaml_tag_directive *start,
+				 struct yaml_tag_directive *end,
+				 int implicit,
+				 gfp_t gfp)
+{
+	struct yaml_version_directive *version_copy = NULL;
+	struct {
+		struct yaml_tag_directive *start;
+		struct yaml_tag_directive *end;
+		struct yaml_tag_directive *top;
+	} copy = {};
+	struct yaml_tag_directive value = {};
+	struct yaml_event *event;
+
+	if (!((start && end) || start == end))
+		return NULL;
+
+	event = yaml_event_create(YAML_DOCUMENT_START_EVENT, gfp);
+	if (!event)
+		return NULL;
+
+	if (version) {
+		version_copy = kmemdup(version, sizeof(*version), gfp);
+		if (!version_copy)
+			goto error;
+	}
+
+#if 0
+	if (start != end) {
+		struct yaml_tag_directive *tag;
+		struct {
+			enum yaml_error error;
+		} context;
+
+		if (!STACK_INIT(&context, copy, struct yaml_tag_directive*))
+			goto error;
+
+		for (tag = start; tag != end; tag++) {
+			if (!check_utf8(tag->handle, strlen(tag->handle)))
+				goto error;
+
+			if (!check_utf8(tag->prefix, strlen(tag->prefix)))
+				goto error;
+
+			value.handle = kstrdup(tag->handle, gfp);
+			value.prefix = kstrdup(tag->prefix, gfp);
+			if (!value.handle || !value.prefix)
+				goto error;
+
+			if (!PUSH(&context, copy, value))
+				goto error;
+
+			value.handle = NULL;
+			value.prefix = NULL;
+		}
+	}
+#endif
+
+	event->type = YAML_DOCUMENT_START_EVENT;
+	event->document_start.version = version_copy;
+	event->document_start.tags.start = copy.start;
+	event->document_start.tags.end = copy.top;
+	event->document_start.implicit = implicit;
+
+	return event;
+
+error:
+	kfree(version_copy);
+#if 0
+	while (!STACK_EMPTY(context, copy)) {
+		struct yaml_tag_directive value = POP(context, copy);
+
+		kfree(value.handle);
+		kfree(value.prefix);
+	}
+	STACK_DEL(context, copy);
+#endif
+	kfree(value.handle);
+	kfree(value.prefix);
+	return NULL;
+}
+EXPORT_SYMBOL(yaml_document_start_event_create);
+
+struct yaml_event *
+yaml_document_end_event_create(int implicit, gfp_t gfp)
+{
+	struct yaml_event *event;
+
+	event = yaml_event_create(YAML_DOCUMENT_END_EVENT, gfp);
+	if (event)
+		event->document_end.implicit = implicit;
+
+	return event;
+}
+EXPORT_SYMBOL(yaml_document_end_event_create);
+
+struct yaml_event *
+yaml_alias_event_create(const char *anchor, gfp_t gfp)
+{
+	struct yaml_event *event;
+
+	if (!anchor || !check_utf8(anchor, strlen(anchor)))
+		return NULL;
+
+	event = yaml_event_create(YAML_ALIAS_EVENT, gfp);
+	if (!event)
+		return NULL;
+
+	event->alias.anchor = kstrdup(anchor, gfp);
+	if (!event->alias.anchor) {
+		yaml_event_free(event);
+		return NULL;
+	}
+
+	return event;
+}
+EXPORT_SYMBOL(yaml_alias_event_create);
+
+struct yaml_event *
+yaml_scalar_event_create(const char *anchor, const char *tag,
+			 const char *value, ssize_t length, bool steal,
+			 int plain_implicit, int quoted_implicit,
+			 enum yaml_scalar_style style,
+			 gfp_t gfp)
+{
+	struct yaml_event *event;
+	char *anchor_copy = NULL;
+	char *tag_copy = NULL;
+
+	if (!value)
+		return NULL;
+
+	if (anchor) {
+		if (!check_utf8(anchor, strlen(anchor)))
+			goto error;
+
+		anchor_copy = kstrdup(anchor, gfp);
+		if (!anchor_copy)
+			goto error;
+	}
+
+	if (tag) {
+		if (!check_utf8(tag, strlen(tag)))
+			goto error;
+
+		tag_copy = kstrdup(tag, gfp);
+		if (!tag_copy)
+			goto error;
+	}
+
+	if (length < 0)
+		length = strlen(value);
+
+	if (!check_utf8(value, length))
+		goto error;
+
+	event = yaml_event_create(YAML_SCALAR_EVENT, gfp);
+	if (!event)
+		goto error;
+
+	if (steal) {
+		event->scalar.value = (char *)value;
+	} else {
+		event->scalar.value = kmemdup(value, length, gfp);
+		if (!event->scalar.value) {
+			yaml_event_free(event);
+			goto error;
+		}
+	}
+	event->scalar.length = length;
+
+	event->scalar.anchor = anchor_copy;
+	event->scalar.tag = tag_copy;
+	event->scalar.plain_implicit = plain_implicit;
+	event->scalar.quoted_implicit = quoted_implicit;
+	event->scalar.style = style;
+	return event;
+
+error:
+	kfree(anchor_copy);
+	kfree(tag_copy);
+	return NULL;
+}
+EXPORT_SYMBOL(yaml_scalar_event_create);
+
+struct yaml_event *
+yaml_sequence_start_event_create(const char *anchor,
+				 const char *tag,
+				 int implicit,
+				 enum yaml_sequence_style style,
+				 gfp_t gfp)
+{
+	char *anchor_copy = NULL;
+	char *tag_copy = NULL;
+	struct yaml_event *event;
+
+	if (anchor) {
+		if (!check_utf8(anchor, strlen(anchor)))
+			goto error;
+
+		anchor_copy = kstrdup(anchor, gfp);
+		if (!anchor_copy)
+			goto error;
+	}
+
+	if (tag) {
+		if (!check_utf8(tag, strlen(tag)))
+			goto error;
+
+		tag_copy = kstrdup(tag, gfp);
+		if (!tag_copy)
+			goto error;
+	}
+
+	event = yaml_event_create(YAML_SEQUENCE_START_EVENT, gfp);
+	if (!event)
+		goto error;
+
+	event->sequence_start.anchor = anchor_copy;
+	event->sequence_start.tag = tag_copy;
+	event->sequence_start.implicit = implicit;
+	event->sequence_start.style = style;
+	return event;
+
+error:
+	kfree(anchor_copy);
+	kfree(tag_copy);
+	return NULL;
+}
+EXPORT_SYMBOL(yaml_sequence_start_event_create);
+
+struct yaml_event *yaml_sequence_end_event_create(gfp_t gfp)
+{
+	return yaml_event_create(YAML_SEQUENCE_END_EVENT, gfp);
+}
+EXPORT_SYMBOL(yaml_sequence_end_event_create);
+
+struct yaml_event *
+yaml_mapping_start_event_create(const char *anchor,
+				const char *tag,
+				int implicit,
+				enum yaml_mapping_style style,
+				gfp_t gfp)
+{
+	char *anchor_copy = NULL;
+	char *tag_copy = NULL;
+	struct yaml_event *event;
+
+	if (anchor) {
+		if (!check_utf8(anchor, strlen(anchor)))
+			goto error;
+
+		anchor_copy = kstrdup(anchor, gfp);
+		if (!anchor_copy)
+			goto error;
+	}
+
+	if (tag) {
+		if (!check_utf8(tag, strlen(tag)))
+			goto error;
+
+		tag_copy = kstrdup(tag, gfp);
+		if (!tag_copy)
+			goto error;
+	}
+
+	event = yaml_event_create(YAML_MAPPING_START_EVENT, gfp);
+	if (!event)
+		goto error;
+
+	event->mapping_start.anchor = anchor_copy;
+	event->mapping_start.tag = tag_copy;
+	event->mapping_start.implicit = implicit;
+	event->mapping_start.style = style;
+	return event;
+
+error:
+	kfree(anchor_copy);
+	kfree(tag_copy);
+	return NULL;
+}
+EXPORT_SYMBOL(yaml_mapping_start_event_create);
+
+struct yaml_event *yaml_mapping_end_event_create(gfp_t gfp)
+{
+	return yaml_event_create(YAML_MAPPING_END_EVENT, gfp);
+}
+EXPORT_SYMBOL(yaml_mapping_end_event_create);
+
+void yaml_event_delete(struct yaml_event *event)
+{
+	struct yaml_tag_directive *tag;
+
+	if (!event)
+		return;
+
+	switch (event->type) {
+	case YAML_DOCUMENT_START_EVENT:
+		kfree(event->document_start.version);
+		for (tag = event->document_start.tags.start;
+		     tag != event->document_start.tags.end;
+		     tag++) {
+			kfree(tag->handle);
+			kfree(tag->prefix);
+		}
+		kfree(event->document_start.tags.start);
+		break;
+
+	case YAML_ALIAS_EVENT:
+		kfree(event->alias.anchor);
+		break;
+
+	case YAML_SCALAR_EVENT:
+		kfree(event->scalar.anchor);
+		kfree(event->scalar.tag);
+		kfree(event->scalar.value);
+		break;
+
+	case YAML_SEQUENCE_START_EVENT:
+		kfree(event->sequence_start.anchor);
+		kfree(event->sequence_start.tag);
+		break;
+
+	case YAML_MAPPING_START_EVENT:
+		kfree(event->mapping_start.anchor);
+		kfree(event->mapping_start.tag);
+		break;
+
+	default:
+		break;
+	}
+
+	yaml_event_free(event);
+}
+EXPORT_SYMBOL(yaml_event_delete);
+
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/lib/yaml/yaml-simple.c b/lib/yaml/yaml-simple.c
new file mode 100644
index 000000000000..ed8ea7637b59
--- /dev/null
+++ b/lib/yaml/yaml-simple.c
@@ -0,0 +1,227 @@ 
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (c) 2020 Intel Corporation
+ */
+
+#include <linux/ascii85.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/yaml.h>
+#include <linux/yaml-events.h>
+
+#include "yaml-emitter.h"
+
+struct yaml_emitter *__yaml_open(struct yaml_emitter *emitter)
+{
+	struct yaml_event *event;
+
+	event = yaml_stream_start_event_create(emitter->gfp);
+	yaml_emitter_emit(emitter, event);
+
+	event = yaml_document_start_event_create(NULL, NULL, NULL, true,
+						 emitter->gfp);
+	yaml_emitter_emit(emitter, event);
+
+	return emitter;
+}
+
+struct yaml_emitter *yaml_open(yaml_write_t handler, void *data)
+{
+	struct yaml_emitter *emitter;
+
+	emitter = yaml_emitter_create(GFP_KERNEL);
+	if (!emitter)
+		return NULL;
+
+	yaml_emitter_set_output(emitter, handler, data);
+	return __yaml_open(emitter);
+}
+EXPORT_SYMBOL(yaml_open);
+
+struct yaml_emitter *yaml_open_file(struct seq_file *m)
+{
+	struct yaml_emitter *emitter;
+
+	emitter = yaml_emitter_create(GFP_KERNEL);
+	if (!emitter)
+		return NULL;
+
+	yaml_emitter_set_seq_file(emitter, m);
+	return __yaml_open(emitter);
+}
+EXPORT_SYMBOL(yaml_open_file);
+
+struct yaml_emitter *yaml_open_string(char *buf, size_t len, size_t *out)
+{
+	struct yaml_emitter *emitter;
+
+	emitter = yaml_emitter_create(GFP_KERNEL);
+	if (!emitter)
+		return NULL;
+
+	yaml_emitter_set_string(emitter, buf, len, out);
+	return __yaml_open(emitter);
+}
+EXPORT_SYMBOL(yaml_open_string);
+
+void __yaml_mapping_start(struct yaml_emitter *emitter,
+			  enum yaml_mapping_style style)
+{
+	struct yaml_event *event;
+
+	event = yaml_mapping_start_event_create(NULL, NULL, true, style,
+						emitter->gfp);
+	yaml_emitter_emit(emitter, event);
+}
+EXPORT_SYMBOL(__yaml_mapping_start);
+
+void yaml_mapping_end(struct yaml_emitter *emitter)
+{
+	yaml_emitter_emit(emitter, yaml_mapping_end_event_create(emitter->gfp));
+}
+EXPORT_SYMBOL(yaml_mapping_end);
+
+void __yaml_sequence_start(struct yaml_emitter *emitter,
+			   enum yaml_sequence_style style)
+{
+	struct yaml_event *event;
+
+	event = yaml_sequence_start_event_create(NULL, NULL, true, style,
+						 emitter->gfp);
+	yaml_emitter_emit(emitter, event);
+}
+EXPORT_SYMBOL(__yaml_sequence_start);
+
+void yaml_sequence_end(struct yaml_emitter *emitter)
+{
+	yaml_emitter_emit(emitter,
+			  yaml_sequence_end_event_create(emitter->gfp));
+}
+EXPORT_SYMBOL(yaml_sequence_end);
+
+void yaml_alias_printf(struct yaml_emitter *emitter,
+		       const char *fmt, ...)
+{
+	va_list ap;
+	char *str;
+
+	va_start(ap, fmt);
+	str = kvasprintf(emitter->gfp, fmt, ap);
+	va_end(ap);
+
+	yaml_emitter_emit(emitter, yaml_alias_event_create(str, emitter->gfp));
+
+	kfree(str);
+}
+EXPORT_SYMBOL(yaml_alias_printf);
+
+void yaml_scalar_printf(struct yaml_emitter *emitter,
+			const char *fmt, ...)
+{
+	struct yaml_event *event;
+	va_list ap;
+	char *str;
+
+	va_start(ap, fmt);
+	str = kvasprintf(emitter->gfp, fmt, ap);
+	va_end(ap);
+
+	event = yaml_scalar_event_create(NULL, NULL, str, -1, true,
+					 true, true, 0, emitter->gfp);
+	yaml_emitter_emit(emitter, event);
+}
+EXPORT_SYMBOL(yaml_scalar_printf);
+
+void yaml_pair_printf(struct yaml_emitter *emitter,
+		      const char *name, const char *fmt, ...)
+{
+	struct yaml_event *event;
+	va_list ap;
+	char *str;
+
+	event = yaml_scalar_event_create(NULL, NULL, name, -1, false,
+					 true, true, 0, emitter->gfp);
+	yaml_emitter_emit(emitter, event);
+
+	va_start(ap, fmt);
+	str = kvasprintf(emitter->gfp, fmt, ap);
+	va_end(ap);
+
+	event = yaml_scalar_event_create(NULL, NULL, str, -1, true,
+					 true, true, 0, emitter->gfp);
+	yaml_emitter_emit(emitter, event);
+}
+EXPORT_SYMBOL(yaml_pair_printf);
+
+void yaml_ascii85_encode(struct yaml_emitter *emitter,
+			 const void *data, size_t len, bool compressed)
+{
+	struct yaml_event *event;
+	const u32 *src = data;
+	char *buf, *dst;
+	int i;
+
+	if (!data || !len)
+		return;
+
+	buf = kmalloc(ascii85_encode_len(len) * 5, emitter->gfp);
+	if (!buf)
+		return;
+
+	dst = buf;
+	while (len > 4) {
+		u32 in = *src++;
+
+		if (in == 0) {
+			*dst++ = 'z';
+		} else {
+			for (i = 5; i--; ) {
+				dst[i] = '!' + in % 85;
+				in /= 85;
+			}
+			dst += 5;
+		}
+		len -= 4;
+	}
+
+	if (len) {
+		u32 in = 0;
+
+		memcpy(&in, src, len);
+
+		if (in == 0) {
+			*dst++ = 'z';
+		} else {
+			for (i = 5; i--; ) {
+				dst[i] = '!' + in % 85;
+				in /= 85;
+			}
+			dst += 5;
+		}
+	}
+
+	event = yaml_scalar_event_create(NULL,
+					 compressed ? "ascii85.gz" : "ascii85",
+					 buf, dst - buf, true,
+					 false, false,
+					 YAML_FOLDED_SCALAR_STYLE,
+					 emitter->gfp);
+	yaml_emitter_emit(emitter, event);
+}
+EXPORT_SYMBOL(yaml_ascii85_encode);
+
+int yaml_close(struct yaml_emitter *emitter, const char **problem)
+{
+	int err;
+
+	yaml_emitter_emit(emitter,
+			  yaml_document_end_event_create(true, GFP_KERNEL));
+	yaml_emitter_emit(emitter,
+			  yaml_stream_end_event_create(GFP_KERNEL));
+
+	err = yaml_emitter_flush(emitter, problem);
+	yaml_emitter_destroy(emitter);
+
+	return err;
+}
+EXPORT_SYMBOL(yaml_close);