Skip to content

Commit 4ab4102

Browse files
ChangSeokBaebp3tk0v
authored andcommitted
x86/microcode/intel: Support mailbox transfer
The functions for sending microcode data and retrieving the next offset were previously placeholders, as they need to handle a specific mailbox format. While the kernel supports similar mailboxes, none of them are compatible with this one. Attempts to share code led to unnecessary complexity, so add a dedicated implementation instead. [ bp: Sort the include properly. ] Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com> Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de> Reviewed-by: Tony Luck <tony.luck@intel.com> Tested-by: Anselm Busse <abusse@amazon.de> Link: https://lore.kernel.org/20250320234104.8288-1-chang.seok.bae@intel.com
1 parent afc3b50 commit 4ab4102

1 file changed

Lines changed: 166 additions & 6 deletions

File tree

  • arch/x86/kernel/cpu/microcode

arch/x86/kernel/cpu/microcode/intel.c

Lines changed: 166 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#define pr_fmt(fmt) "microcode: " fmt
1414
#include <linux/earlycpio.h>
1515
#include <linux/firmware.h>
16+
#include <linux/pci_ids.h>
1617
#include <linux/uaccess.h>
1718
#include <linux/initrd.h>
1819
#include <linux/kernel.h>
@@ -41,8 +42,31 @@ static const char ucode_path[] = "kernel/x86/microcode/GenuineIntel.bin";
4142

4243
#define MBOX_CONTROL_OFFSET 0x0
4344
#define MBOX_STATUS_OFFSET 0x4
45+
#define MBOX_WRDATA_OFFSET 0x8
46+
#define MBOX_RDDATA_OFFSET 0xc
4447

4548
#define MASK_MBOX_CTRL_ABORT BIT(0)
49+
#define MASK_MBOX_CTRL_GO BIT(31)
50+
51+
#define MASK_MBOX_STATUS_ERROR BIT(2)
52+
#define MASK_MBOX_STATUS_READY BIT(31)
53+
54+
#define MASK_MBOX_RESP_SUCCESS BIT(0)
55+
#define MASK_MBOX_RESP_PROGRESS BIT(1)
56+
#define MASK_MBOX_RESP_ERROR BIT(2)
57+
58+
#define MBOX_CMD_LOAD 0x3
59+
#define MBOX_OBJ_STAGING 0xb
60+
#define MBOX_HEADER(size) ((PCI_VENDOR_ID_INTEL) | \
61+
(MBOX_OBJ_STAGING << 16) | \
62+
((u64)((size) / sizeof(u32)) << 32))
63+
64+
/* The size of each mailbox header */
65+
#define MBOX_HEADER_SIZE sizeof(u64)
66+
/* The size of staging hardware response */
67+
#define MBOX_RESPONSE_SIZE sizeof(u64)
68+
69+
#define MBOX_XACTION_TIMEOUT_MS (10 * MSEC_PER_SEC)
4670

4771
/* Current microcode patch used in early patching on the APs. */
4872
static struct microcode_intel *ucode_patch_va __read_mostly;
@@ -327,6 +351,49 @@ static __init struct microcode_intel *scan_microcode(void *data, size_t size,
327351
return size ? NULL : patch;
328352
}
329353

354+
static inline u32 read_mbox_dword(void __iomem *mmio_base)
355+
{
356+
u32 dword = readl(mmio_base + MBOX_RDDATA_OFFSET);
357+
358+
/* Acknowledge read completion to the staging hardware */
359+
writel(0, mmio_base + MBOX_RDDATA_OFFSET);
360+
return dword;
361+
}
362+
363+
static inline void write_mbox_dword(void __iomem *mmio_base, u32 dword)
364+
{
365+
writel(dword, mmio_base + MBOX_WRDATA_OFFSET);
366+
}
367+
368+
static inline u64 read_mbox_header(void __iomem *mmio_base)
369+
{
370+
u32 high, low;
371+
372+
low = read_mbox_dword(mmio_base);
373+
high = read_mbox_dword(mmio_base);
374+
375+
return ((u64)high << 32) | low;
376+
}
377+
378+
static inline void write_mbox_header(void __iomem *mmio_base, u64 value)
379+
{
380+
write_mbox_dword(mmio_base, value);
381+
write_mbox_dword(mmio_base, value >> 32);
382+
}
383+
384+
static void write_mbox_data(void __iomem *mmio_base, u32 *chunk, unsigned int chunk_bytes)
385+
{
386+
int i;
387+
388+
/*
389+
* The MMIO space is mapped as Uncached (UC). Each write arrives
390+
* at the device as an individual transaction in program order.
391+
* The device can then reassemble the sequence accordingly.
392+
*/
393+
for (i = 0; i < chunk_bytes / sizeof(u32); i++)
394+
write_mbox_dword(mmio_base, chunk[i]);
395+
}
396+
330397
/*
331398
* Prepare for a new microcode transfer: reset hardware and record the
332399
* image size.
@@ -377,24 +444,83 @@ static bool can_send_next_chunk(struct staging_state *ss, int *err)
377444
return true;
378445
}
379446

447+
/*
448+
* The hardware indicates completion by returning a sentinel end offset.
449+
*/
450+
static inline bool is_end_offset(u32 offset)
451+
{
452+
return offset == UINT_MAX;
453+
}
454+
380455
/*
381456
* Determine whether staging is complete: either the hardware signaled
382457
* the end offset, or no more transactions are permitted (retry limit
383458
* reached).
384459
*/
385460
static inline bool staging_is_complete(struct staging_state *ss, int *err)
386461
{
387-
return (ss->offset == UINT_MAX) || !can_send_next_chunk(ss, err);
462+
return is_end_offset(ss->offset) || !can_send_next_chunk(ss, err);
463+
}
464+
465+
/*
466+
* Wait for the hardware to complete a transaction.
467+
* Return 0 on success, or an error code on failure.
468+
*/
469+
static int wait_for_transaction(struct staging_state *ss)
470+
{
471+
u32 timeout, status;
472+
473+
/* Allow time for hardware to complete the operation: */
474+
for (timeout = 0; timeout < MBOX_XACTION_TIMEOUT_MS; timeout++) {
475+
msleep(1);
476+
477+
status = readl(ss->mmio_base + MBOX_STATUS_OFFSET);
478+
/* Break out early if the hardware is ready: */
479+
if (status & MASK_MBOX_STATUS_READY)
480+
break;
481+
}
482+
483+
/* Check for explicit error response */
484+
if (status & MASK_MBOX_STATUS_ERROR)
485+
return -EIO;
486+
487+
/*
488+
* Hardware has neither responded to the action nor signaled any
489+
* error. Treat this as a timeout.
490+
*/
491+
if (!(status & MASK_MBOX_STATUS_READY))
492+
return -ETIMEDOUT;
493+
494+
return 0;
388495
}
389496

390497
/*
391498
* Transmit a chunk of the microcode image to the hardware.
392499
* Return 0 on success, or an error code on failure.
393500
*/
394-
static int send_data_chunk(struct staging_state *ss, void *ucode_ptr __maybe_unused)
501+
static int send_data_chunk(struct staging_state *ss, void *ucode_ptr)
395502
{
396-
pr_debug_once("Staging mailbox loading code needs to be implemented.\n");
397-
return -EPROTONOSUPPORT;
503+
u32 *src_chunk = ucode_ptr + ss->offset;
504+
u16 mbox_size;
505+
506+
/*
507+
* Write a 'request' mailbox object in this order:
508+
* 1. Mailbox header includes total size
509+
* 2. Command header specifies the load operation
510+
* 3. Data section contains a microcode chunk
511+
*
512+
* Thus, the mailbox size is two headers plus the chunk size.
513+
*/
514+
mbox_size = MBOX_HEADER_SIZE * 2 + ss->chunk_size;
515+
write_mbox_header(ss->mmio_base, MBOX_HEADER(mbox_size));
516+
write_mbox_header(ss->mmio_base, MBOX_CMD_LOAD);
517+
write_mbox_data(ss->mmio_base, src_chunk, ss->chunk_size);
518+
ss->bytes_sent += ss->chunk_size;
519+
520+
/* Notify the hardware that the mailbox is ready for processing. */
521+
writel(MASK_MBOX_CTRL_GO, ss->mmio_base + MBOX_CONTROL_OFFSET);
522+
523+
return wait_for_transaction(ss);
398524
}
399525

400526
/*
@@ -403,8 +529,42 @@ static int send_data_chunk(struct staging_state *ss, void *ucode_ptr __maybe_unu
403529
*/
404530
static int fetch_next_offset(struct staging_state *ss)
405531
{
406-
pr_debug_once("Staging mailbox response handling code needs to be implemented.\n");
407-
return -EPROTONOSUPPORT;
532+
const u64 expected_header = MBOX_HEADER(MBOX_HEADER_SIZE + MBOX_RESPONSE_SIZE);
533+
u32 offset, status;
534+
u64 header;
535+
536+
/*
537+
* The 'response' mailbox returns three fields, in order:
538+
* 1. Header
539+
* 2. Next offset in the microcode image
540+
* 3. Status flags
541+
*/
542+
header = read_mbox_header(ss->mmio_base);
543+
offset = read_mbox_dword(ss->mmio_base);
544+
status = read_mbox_dword(ss->mmio_base);
545+
546+
/* All valid responses must start with the expected header. */
547+
if (header != expected_header) {
548+
pr_err_once("staging: invalid response header (0x%llx)\n", header);
549+
return -EBADR;
550+
}
551+
552+
/*
553+
* Verify the offset: If not at the end marker, it must not
554+
* exceed the microcode image length.
555+
*/
556+
if (!is_end_offset(offset) && offset > ss->ucode_len) {
557+
pr_err_once("staging: invalid offset (%u) past the image end (%u)\n",
558+
offset, ss->ucode_len);
559+
return -EINVAL;
560+
}
561+
562+
/* Hardware may report errors explicitly in the status field */
563+
if (status & MASK_MBOX_RESP_ERROR)
564+
return -EPROTO;
565+
566+
ss->offset = offset;
567+
return 0;
408568
}
409569

410570
/*

0 commit comments

Comments
 (0)