diff --git a/zircon/kernel/arch/x86/BUILD.gn b/zircon/kernel/arch/x86/BUILD.gn
index 5378932bb28235cca63e3d8266cbd44b814141c4..855a184a0c44df2df3f638b6c1b2b64cbd8411f9 100644
--- a/zircon/kernel/arch/x86/BUILD.gn
+++ b/zircon/kernel/arch/x86/BUILD.gn
@@ -91,6 +91,7 @@ library("x86") {
     "mmu.cpp",
     "mmu_mem_types.cpp",
     "mp.cpp",
+    "msr.cpp",
     "ops.S",
     "perf_mon.cpp",
     "proc_trace.cpp",
diff --git a/zircon/kernel/arch/x86/include/arch/x86.h b/zircon/kernel/arch/x86/include/arch/x86.h
index 4b6f554953102541b150d537733cf525a8069984..2bb61c0ae0c70eebfcbccd996cc2e5c8aebcd3c4 100644
--- a/zircon/kernel/arch/x86/include/arch/x86.h
+++ b/zircon/kernel/arch/x86/include/arch/x86.h
@@ -10,6 +10,7 @@
 #pragma once
 
 #include <cpuid.h>
+#include <kernel/cpu.h>
 #include <stdbool.h>
 #include <stdlib.h>
 #include <sys/types.h>
@@ -326,6 +327,9 @@ static inline uint32_t read_msr32(uint32_t msr_id) {
 
 zx_status_t read_msr_safe(uint32_t msr_id, uint64_t* val);
 
+// Read msr |msr_id| on CPU |cpu| and return the 64-bit value.
+uint64_t read_msr_on_cpu(cpu_num_t cpu, uint32_t msr_id);
+
 static inline void write_msr(uint32_t msr_id, uint64_t msr_write_val) {
     __asm__ __volatile__(
         "wrmsr \n\t"
diff --git a/zircon/kernel/arch/x86/msr.cpp b/zircon/kernel/arch/x86/msr.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1ef338a2734db6024c7698e4e8c30f3240f4d1bd
--- /dev/null
+++ b/zircon/kernel/arch/x86/msr.cpp
@@ -0,0 +1,55 @@
+// Copyright 2019 The Fuchsia Authors
+//
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT
+
+#include <assert.h>
+#include <debug.h>
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <trace.h>
+#include <zircon/compiler.h>
+
+#include <arch/mp.h>
+#include <arch/ops.h>
+#include <arch/x86.h>
+#include <arch/x86/apic.h>
+#include <arch/x86/descriptor.h>
+#include <arch/x86/feature.h>
+#include <arch/x86/interrupts.h>
+#include <arch/x86/mp.h>
+#include <arch/x86/tsc.h>
+#include <dev/hw_rng.h>
+#include <dev/interrupt.h>
+#include <kernel/event.h>
+#include <kernel/timer.h>
+#include <platform.h>
+#include <zircon/types.h>
+
+
+struct read_msr_context {
+    uint32_t msr;
+    uint64_t val;
+};
+
+static void read_msr_on_cpu_task(void* raw_context) {
+    auto* const context = reinterpret_cast<struct read_msr_context*>(raw_context);
+    context->val = read_msr(context->msr);
+}
+
+uint64_t read_msr_on_cpu(cpu_num_t cpu, uint32_t msr_id) {
+    struct read_msr_context context = {};
+    cpu_mask_t mask = {};
+
+    if (!mp_is_cpu_online(cpu)) {
+        return -1;
+    }
+
+    context.msr = msr_id;
+    mask |= cpu_num_to_mask(cpu);
+    mp_sync_exec(MP_IPI_TARGET_MASK, mask, read_msr_on_cpu_task,
+                 reinterpret_cast<void*>(&context));
+    return context.val;
+}