diff --git a/lib/x86/processor.h b/lib/x86/processor.h
index 3d58ef7..c6b1db6 100644
--- a/lib/x86/processor.h
+++ b/lib/x86/processor.h
@@ -105,6 +105,8 @@
 #define X86_CR4_CET		BIT(X86_CR4_CET_BIT)
 #define X86_CR4_PKS_BIT		(24)
 #define X86_CR4_PKS		BIT(X86_CR4_PKS_BIT)
+#define X86_CR4_LAM_SUP_BIT	(28)
+#define X86_CR4_LAM_SUP	BIT(X86_CR4_LAM_SUP_BIT)
 
 #define X86_EFLAGS_CF_BIT	(0)
 #define X86_EFLAGS_CF		BIT(X86_EFLAGS_CF_BIT)
@@ -248,6 +250,7 @@ static inline bool is_intel(void)
 #define	X86_FEATURE_SPEC_CTRL		(CPUID(0x7, 0, EDX, 26))
 #define	X86_FEATURE_ARCH_CAPABILITIES	(CPUID(0x7, 0, EDX, 29))
 #define	X86_FEATURE_PKS			(CPUID(0x7, 0, ECX, 31))
+#define	X86_FEATURE_LAM			(CPUID(0x7, 1, EAX, 26))
 
 /*
  * Extended Leafs, a.k.a. AMD defined
diff --git a/x86/Makefile.x86_64 b/x86/Makefile.x86_64
index f483dea..af626cc 100644
--- a/x86/Makefile.x86_64
+++ b/x86/Makefile.x86_64
@@ -34,6 +34,7 @@ tests += $(TEST_DIR)/rdpru.$(exe)
 tests += $(TEST_DIR)/pks.$(exe)
 tests += $(TEST_DIR)/pmu_lbr.$(exe)
 tests += $(TEST_DIR)/pmu_pebs.$(exe)
+tests += $(TEST_DIR)/lam_sup.$(exe)
 
 ifeq ($(CONFIG_EFI),y)
 tests += $(TEST_DIR)/amd_sev.$(exe)
diff --git a/x86/lam_sup.c b/x86/lam_sup.c
new file mode 100644
index 0000000..67d5b5e
--- /dev/null
+++ b/x86/lam_sup.c
@@ -0,0 +1,170 @@
+/*
+ * Intel LAM_SUP unit test
+ *
+ * Copyright (C) 2023 Intel
+ *
+ * Author: Robert Hoo <robert.hu@linux.intel.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2 or
+ * later.
+ */
+
+#include "libcflat.h"
+#include "processor.h"
+#include "desc.h"
+#include "vmalloc.h"
+#include "alloc_page.h"
+#include "vm.h"
+#include "asm/io.h"
+#include "ioram.h"
+
+#define LAM57_BITS 6
+#define LAM48_BITS 15
+#define LAM57_MASK	GENMASK_ULL(62, 57)
+#define LAM48_MASK	GENMASK_ULL(62, 48)
+
+static int gp_count;
+static jmp_buf jbuf;
+
+static int get_lam_bits(void)
+{
+	if (this_cpu_has(X86_FEATURE_LA57) && read_cr4() & X86_CR4_LA57)
+		return LAM57_BITS;
+	else
+		return LAM48_BITS;
+}
+
+/* According to LAM mode, set metadata in high bits */
+static u64 set_metadata(u64 src, unsigned long lam)
+{
+	u64 metadata;
+
+	switch (lam) {
+	case LAM57_BITS: /* Set metadata in bits 62:57 */
+		metadata = (rdtsc() & ((1UL << LAM57_BITS) - 1)) << 57;
+		metadata |= (src & ~(LAM57_MASK));
+		break;
+	case LAM48_BITS: /* Set metadata in bits 62:48 */
+		metadata = (rdtsc() & ((1UL << LAM48_BITS) - 1)) << 48;
+		metadata |= (src & ~(LAM48_MASK));
+		break;
+	default:
+		metadata = src;
+		break;
+	}
+
+	return metadata;
+}
+
+static void handle_gp(struct ex_regs *regs)
+{
+	report_info("#GP caught, error_code = %ld\n", regs->error_code);
+	gp_count++;
+	longjmp(jbuf, 1);
+}
+
+/* Refer to emulator.c */
+static void test_mov(void *mem)
+{
+	unsigned long t1, t2;
+
+	// test mov reg, r/m and mov r/m, reg
+	t1 = 0x123456789abcdefull & -1ul;
+	asm volatile("mov %[t1], (%[mem])\n\t"
+		     "mov (%[mem]), %[t2]"
+		     : [t2]"=r"(t2)
+		     : [t1]"r"(t1), [mem]"r"(mem)
+		     : "memory");
+}
+
+
+int main(int ac, char **av)
+{
+	unsigned long cr4;
+	volatile bool lam_enumerated;
+	int vector, expect_vector;
+	u64 *ptr;
+	int lam_bits;
+	void *vaddr, *mem;
+	phys_addr_t paddr;
+	handler old;
+
+	lam_enumerated = this_cpu_has(X86_FEATURE_LAM);
+	if (!lam_enumerated)
+		report_info("This CPU doesn't support LAM feature\n");
+	else
+		report_info("This CPU supports LAM feature\n");
+
+	expect_vector = lam_enumerated ? 0 : GP_VECTOR;
+
+	/* Set CR4.LAM_SUP */
+	cr4 = read_cr4();
+	vector = write_cr4_safe(cr4 | X86_CR4_LAM_SUP);
+
+	if (lam_enumerated)
+		report(vector == expect_vector && (cr4 | X86_CR4_LAM_SUP) == read_cr4(),
+		       "Set CR4.LAM_SUP");
+	else
+		report(vector == expect_vector, "Set CR4.LAM_SUP");
+
+	/* Clear CR4.LAM_SUP */
+	cr4 = read_cr4();
+	vector = write_cr4_safe(cr4 & ~X86_CR4_LAM_SUP);
+	expect_vector = 0;
+	report(vector == expect_vector && (cr4 & ~X86_CR4_LAM_SUP) == read_cr4(),
+	       "Clear CR4.LAM_SUP");
+
+	/* Re-set CR4.LAM_SUP for next tests */
+	cr4 = read_cr4();
+	vector = write_cr4_safe(cr4 | X86_CR4_LAM_SUP);
+	expect_vector = lam_enumerated ? 0 : GP_VECTOR;
+	if (lam_enumerated)
+		report(vector == expect_vector && (cr4 | X86_CR4_LAM_SUP) == read_cr4(),
+			"Re-enable CR4.LAM_SUP");
+	else
+		report(vector == expect_vector, "Re-enable CR4.LAM_SUP");
+
+	/* Try access Supervisor mode address with meta data */
+	setup_vm();
+	vaddr = alloc_vpage();
+	paddr = virt_to_phys(alloc_page());
+
+	install_page(current_page_table(), paddr, vaddr);
+	old = handle_exception(GP_VECTOR, handle_gp);
+
+	strcpy((char *)vaddr, "LAM SUP Test origin string.");
+
+	lam_bits = get_lam_bits();
+	ptr = (u64 *)set_metadata((u64)vaddr, lam_bits);
+
+	if (setjmp(jbuf) == 0)
+		strcpy((char *)ptr, "LAM SUP Test NEW string.");
+
+	if (lam_enumerated && (read_cr4() & X86_CR4_LAM_SUP))
+		report(gp_count == 0, "strcpy with tagged addr succeed");
+	else
+		report(gp_count > 0, "strcpy with tagged addr cause #GP");
+
+	/* emulator coverage. referred to emulator.c */
+	gp_count = 0;
+	mem = alloc_vpage();
+	install_page((void *)read_cr3(), IORAM_BASE_PHYS, mem);
+
+	ptr = (u64 *)set_metadata((u64)mem, lam_bits);
+	if (setjmp(jbuf) == 0)
+		test_mov(ptr);
+
+	if (lam_enumerated && (read_cr4() & X86_CR4_LAM_SUP))
+		report(gp_count == 0, "MMIO cpy test for emulator succeed");
+	else
+		report(gp_count > 0, "MMIO cpy test for emulator cause #GP");
+
+	/*
+	 * Restore old #GP handler, though mostly likely effectively
+	 * unnecessary, for symmetry and conservativeness.
+	 */
+	handle_exception(GP_VECTOR, old);
+
+	return report_summary();
+}
+
diff --git a/x86/unittests.cfg b/x86/unittests.cfg
index f324e32..08a9b20 100644
--- a/x86/unittests.cfg
+++ b/x86/unittests.cfg
@@ -478,3 +478,13 @@ file = cet.flat
 arch = x86_64
 smp = 2
 extra_params = -enable-kvm -m 2048 -cpu host
+
+[intel-lam]
+file = lam_sup.flat
+arch = x86_64
+extra_params = -enable-kvm -cpu host
+
+[intel-no-lam]
+file = lam_sup.flat
+arch = x86_64
+extra_params = -enable-kvm -cpu host,-lam
