diff --git a/docs/misc/builtin-ucode.txt b/docs/misc/builtin-ucode.txt
new file mode 100644
index 0000000000..43bb60d3eb
--- /dev/null
+++ b/docs/misc/builtin-ucode.txt
@@ -0,0 +1,60 @@
+-------------------------------------------------
+Builtin Microcode Support for x86 (AMD and INTEL)
+-------------------------------------------------
+Author:
+  Eslam Elnikety <elnikety@amazon.com>
+Initial version:
+  Dec 2019
+-------------------------------------------------
+
+About:
+------
+* This documentation describes preparing the builtin microcode blobs to use as
+  builtin microcode update within the Xen image itself.
+
+* Support for builtin microcode is limited to x86.
+
+* Builtin support is available via the configurations BUILTIN_UCODE and
+  BUILTIN_UCODE_DIR. The first enables the support (default is off), and the
+  latter directs the build system to where it can find the microcode directory
+  (default is /lib/firmware).
+
+Microcode Directory:
+--------------------
+This directory holds the microcode blobs to be built in the Xen image. There
+are two subdirectories: amd-ucode and intel-ucode for AMD and INTEL,
+respectively.
+
+INTEL microcode blobs typically follow the naming format FF-MM-SS for
+{F}amily-{M}odel-{S}tepping. Alternatively, GenuineIntel.bin bundles a bunch
+of FF-MM-SS blobs into a single binary and the one matching the host CPU gets
+picked when performing the microcode update. For AMD, the canonical name is
+AuthenticAMD.bin. Similarly, such binary can bundle a bunch of microcode blobs
+for different families.
+
+The builtin microcode is generated by concatenating the microcode blobs under
+intel-ucode into GenuineIntel.bin, and those under amd-ucode into
+AuthenticAMD.bin. Those are then copied into the Xen image itself.
+
+Here is an example microcode directory structure, following the convention [1]:
+
+/lib/firmware
+|-- amd-ucode
+...
+|   |-- microcode_amd_fam15h.bin
+...
+|-- intel-ucode
+...
+|   |-- 06-3a-09
+...
+
+Alternatively, the subdirectories can directly contain GenuineIntel.bin and
+AuthenticAMD.bin (since both are concatenation of the individual microcode
+blobs and the end result is the same).
+
+An empty or non-existant subdirectory (amd-ucode and/or intel-ucode) excludes
+the respective AMD or INTEL microcode from being built in.
+
+Reference(s):
+-------------
+[1] https://www.kernel.org/doc/Documentation/x86/microcode.txt
diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc
index 891d2d439f..ba25db95da 100644
--- a/docs/misc/xen-command-line.pandoc
+++ b/docs/misc/xen-command-line.pandoc
@@ -2113,7 +2113,7 @@ logic applies:
    active by default.
 
 ### ucode (x86)
-> `= List of [ <integer> | scan=<bool>, nmi=<bool> ]`
+> `= List of [ <integer> | scan=<bool> | builtin=<bool>, nmi=<bool> ]`
 
 Specify how and where to find CPU microcode update blob.
 
@@ -2128,6 +2128,9 @@ when used with xen.efi (there the concept of modules doesn't exist, and
 the blob gets specified via the `ucode=<filename>` config file/section
 entry; see [EFI configuration file description](efi.html)).
 
+'builtin' instructs the hypervisor to use the builtin microcode update. This
+option is available only if option BUILTIN_UCODE is enabled.
+
 'scan' instructs the hypervisor to scan the multiboot images for an cpio
 image that contains microcode. Depending on the platform the blob with the
 microcode in the cpio name space must be:
diff --git a/xen/arch/x86/Kconfig b/xen/arch/x86/Kconfig
index 02bb05f42e..14c5992d86 100644
--- a/xen/arch/x86/Kconfig
+++ b/xen/arch/x86/Kconfig
@@ -218,6 +218,26 @@ config MEM_SHARING
 	bool "Xen memory sharing support" if EXPERT = "y"
 	depends on HVM
 
+config BUILTIN_UCODE
+	def_bool n
+	prompt "Support for Builtin Microcode"
+	---help---
+	  Include the CPU microcode update in the Xen image itself. With this
+	  support, Xen can update the CPU microcode upon boot using the builtin
+	  microcode, with no need for an additional microcode boot modules.
+
+	  If unsure, say N.
+
+config BUILTIN_UCODE_DIR
+	string
+	default "/lib/firmware"
+	depends on BUILTIN_UCODE
+	---help---
+	  The directory containing the microcode blobs.
+
+	  See docs/misc/builtin-ucode.txt for how such directory should be
+	  structured to hold AMD and INTEL microcode.
+
 endmenu
 
 source "common/Kconfig"
diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile
index 7da5a2631e..8ac93a15a7 100644
--- a/xen/arch/x86/Makefile
+++ b/xen/arch/x86/Makefile
@@ -7,6 +7,7 @@ subdir-y += mm
 subdir-$(CONFIG_XENOPROF) += oprofile
 subdir-$(CONFIG_PV) += pv
 subdir-y += x86_64
+subdir-$(CONFIG_BUILTIN_UCODE) += microcode
 
 alternative-y := alternative.init.o
 alternative-$(CONFIG_LIVEPATCH) :=
diff --git a/xen/arch/x86/microcode.c b/xen/arch/x86/microcode.c
index 6ced293d88..7afbe44286 100644
--- a/xen/arch/x86/microcode.c
+++ b/xen/arch/x86/microcode.c
@@ -97,6 +97,14 @@ static struct ucode_mod_blob __initdata ucode_blob;
  */
 static bool_t __initdata ucode_scan;
 
+#ifdef CONFIG_BUILTIN_UCODE
+/* builtin is the default when BUILTIN_UCODE is set */
+static bool_t __initdata ucode_builtin = 1;
+
+extern const char __builtin_intel_ucode_start[], __builtin_intel_ucode_end[];
+extern const char __builtin_amd_ucode_start[], __builtin_amd_ucode_end[];
+#endif
+
 /* By default, ucode loading is done in NMI handler */
 static bool ucode_in_nmi = true;
 
@@ -110,9 +118,9 @@ void __init microcode_set_module(unsigned int idx)
 }
 
 /*
- * The format is '[<integer>|scan=<bool>, nmi=<bool>]'. Both options are
- * optional. If the EFI has forced which of the multiboot payloads is to be
- * used, only nmi=<bool> is parsed.
+ * The format is '[<integer>|scan=<bool>|builtin=<bool>, nmi=<bool>]'. All
+ * options are optional. If the EFI has forced which of the multiboot payloads
+ * is to be used, only nmi=<bool> is parsed.
  */
 static int __init parse_ucode(const char *s)
 {
@@ -130,6 +138,10 @@ static int __init parse_ucode(const char *s)
         {
             if ( (val = parse_boolean("scan", s, ss)) >= 0 )
                 ucode_scan = val;
+#ifdef CONFIG_BUILTIN_UCODE
+	    else if ( (val = parse_boolean("builtin", s, ss)) >= 0 )
+                ucode_builtin = val;
+#endif
             else
             {
                 const char *q;
@@ -237,6 +249,48 @@ void __init microcode_grab_module(
 scan:
     if ( ucode_scan )
         microcode_scan_module(module_map, mbi);
+
+#ifdef CONFIG_BUILTIN_UCODE
+    /*
+     * Do not use the builtin microcode if:
+     * (a) builtin has been explicitly turned off (e.g., ucode=no-builtin)
+     * (b) a microcode module has been specified or a scan is successful
+     */
+    if ( !ucode_builtin || ucode_mod.mod_end || ucode_blob.size )
+        return;
+
+    /* Set ucode_start/_end to the proper blob */
+    if ( boot_cpu_data.x86_vendor == X86_VENDOR_AMD )
+        ucode_blob.size = (size_t)(__builtin_amd_ucode_end
+                                   - __builtin_amd_ucode_start);
+    else if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL )
+        ucode_blob.size = (size_t)(__builtin_intel_ucode_end
+                                   - __builtin_intel_ucode_start);
+    else
+        return;
+
+    if ( !ucode_blob.size )
+    {
+        printk("No builtin ucode! 'ucode=builtin' is nullified.\n");
+        return;
+    }
+    else if ( ucode_blob.size > MAX_EARLY_CPIO_MICROCODE )
+    {
+        printk("Builtin microcode payload too big! (%ld, we can do %d)\n",
+               ucode_blob.size, MAX_EARLY_CPIO_MICROCODE);
+        ucode_blob.size = 0;
+        return;
+    }
+
+    ucode_blob.data = xmalloc_bytes(ucode_blob.size);
+    if ( !ucode_blob.data )
+        return;
+
+    if ( boot_cpu_data.x86_vendor == X86_VENDOR_AMD )
+        memcpy(ucode_blob.data, __builtin_amd_ucode_start, ucode_blob.size);
+    else
+        memcpy(ucode_blob.data, __builtin_intel_ucode_start, ucode_blob.size);
+#endif
 }
 
 const struct microcode_ops *microcode_ops;
diff --git a/xen/arch/x86/microcode/Makefile b/xen/arch/x86/microcode/Makefile
new file mode 100644
index 0000000000..6d585c5482
--- /dev/null
+++ b/xen/arch/x86/microcode/Makefile
@@ -0,0 +1,40 @@
+# Copyright (C) 2019 Amazon.com, Inc. or its affiliates.
+# Author: Eslam Elnikety <elnikety@amazon.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+obj-y += builtin_ucode.o
+
+# Directory holding the microcode updates.
+UCODE_DIR=$(patsubst "%",%,$(CONFIG_BUILTIN_UCODE_DIR))
+amd-blobs := $(wildcard $(UCODE_DIR)/amd-ucode/*)
+intel-blobs := $(wildcard $(UCODE_DIR)/intel-ucode/*)
+
+builtin_ucode.o: Makefile $(amd-blobs) $(intel-blobs)
+	# Create AMD microcode blob if there are AMD updates on the build system
+	if [ ! -z "$(amd-blobs)" ]; then \
+		cat $(amd-blobs) > $@.bin ; \
+		$(OBJCOPY) -I binary -O elf64-x86-64 -B i386:x86-64 --rename-section .data=.builtin_amd_ucode,alloc,load,readonly,data,contents $@.bin $@.amd; \
+		rm -f $@.bin; \
+	fi
+	# Create INTEL microcode blob if there are INTEL updates on the build system
+	if [ ! -z "$(intel-blobs)" ]; then \
+		cat $(intel-blobs) > $@.bin; \
+		$(OBJCOPY) -I binary -O elf64-x86-64 -B i386:x86-64 --rename-section .data=.builtin_intel_ucode,alloc,load,readonly,data,contents $@.bin $@.intel; \
+		rm -f $@.bin; \
+	fi
+	# Create fake builtin_ucode.o if no updates were present. Otherwise, builtin_ucode.o carries the available updates
+	if [ -z "$(amd-blobs)" -a -z "$(intel-blobs)" ]; then \
+		$(CC) $(CFLAGS) -c -x c /dev/null -o $@; \
+	else \
+		$(LD) $(LDFLAGS) -r -o $@ $@.*; \
+		rm -f $@.*; \
+	fi
diff --git a/xen/arch/x86/xen.lds.S b/xen/arch/x86/xen.lds.S
index 111edb5360..7a4c58c246 100644
--- a/xen/arch/x86/xen.lds.S
+++ b/xen/arch/x86/xen.lds.S
@@ -265,6 +265,18 @@ SECTIONS
        *(SORT(.data.vpci.*))
        __end_vpci_array = .;
 #endif
+
+#if defined(CONFIG_BUILTIN_UCODE)
+       . = ALIGN(POINTER_ALIGN);
+       __builtin_amd_ucode_start = .;
+       *(.builtin_amd_ucode)
+       __builtin_amd_ucode_end = .;
+
+       . = ALIGN(POINTER_ALIGN);
+       __builtin_intel_ucode_start = .;
+       *(.builtin_intel_ucode)
+       __builtin_intel_ucode_end = .;
+#endif
   } :text
 
   . = ALIGN(SECTION_ALIGN);
