cpu/x86/mtrr: add helper for setting multiple MTRRs

Introduce concept of var_mtrr_context object for tracking and
assigning MTRR values. The algorithm is lifted from postcar_loader
code, but it's generalized for different type of users: setting
MSRs explicitly or deferring to a particular caller's desired
actions.

BUG=b:155426691,b:155322763

Change-Id: Ic03b4b617196f04071093bbba4cf28d23fa482d8
Signed-off-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/41849
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Furquan Shaikh <furquan@google.com>
This commit is contained in:
Aaron Durbin 2020-05-28 21:21:49 -06:00
parent a21eae0441
commit fa5eded303
2 changed files with 98 additions and 0 deletions

View File

@ -65,3 +65,70 @@ void clear_all_var_mtrr(void)
wrmsr(MTRR_PHYS_BASE(i), mtrr); wrmsr(MTRR_PHYS_BASE(i), mtrr);
} }
} }
void var_mtrr_context_init(struct var_mtrr_context *ctx, void *arg)
{
ctx->upper_mask = (1U << (cpu_phys_address_size() - 32)) - 1;
ctx->max_var_mtrrs = get_var_mtrr_count();
ctx->used_var_mtrrs = 0;
ctx->arg = arg;
}
int var_mtrr_set_with_cb(struct var_mtrr_context *ctx, uintptr_t addr, size_t size,
int type, void (*callback)(const struct var_mtrr_context *ctx,
uintptr_t base_addr, size_t size,
msr_t base, msr_t mask))
{
/* Utilize additional MTRRs if the specified size is greater than the
base address alignment. */
while (size != 0) {
uint32_t addr_lsb;
uint32_t size_msb;
uint32_t mtrr_size;
msr_t base;
msr_t mask;
if (ctx->used_var_mtrrs >= ctx->max_var_mtrrs) {
printk(BIOS_ERR, "No more variable MTRRs: %d\n",
ctx->max_var_mtrrs);
return -1;
}
addr_lsb = fls(addr);
size_msb = fms(size);
/* All MTRR entries need to have their base aligned to the mask
size. The maximum size is calculated by a function of the
min base bit set and maximum size bit set. */
if (addr_lsb > size_msb)
mtrr_size = 1 << size_msb;
else
mtrr_size = 1 << addr_lsb;
base.hi = (uint64_t)addr >> 32;
base.lo = addr | type;
mask.hi = ctx->upper_mask;
mask.lo = ~(mtrr_size - 1) | MTRR_PHYS_MASK_VALID;
callback(ctx, addr, mtrr_size, base, mask);
ctx->used_var_mtrrs++;
size -= mtrr_size;
addr += mtrr_size;
}
return 0;
}
static void set_mtrr(const struct var_mtrr_context *ctx, uintptr_t base_addr, size_t size,
msr_t base, msr_t mask)
{
int i = var_mtrr_context_current_mtrr(ctx);
wrmsr(MTRR_PHYS_BASE(i), base);
wrmsr(MTRR_PHYS_MASK(i), mask);
}
int var_mtrr_set(struct var_mtrr_context *ctx, uintptr_t addr, size_t size, int type)
{
return var_mtrr_set_with_cb(ctx, addr, size, type, set_mtrr);
}

View File

@ -110,6 +110,37 @@ void clear_all_var_mtrr(void);
asmlinkage void display_mtrrs(void); asmlinkage void display_mtrrs(void);
/* Variable MTRR structure to help track and set MTRRs prior to ramstage. This
and the following APIs can be used to set up more complex MTRR solutions
instead of open coding get_free_var_mtrr() and set_var_mtrr() or for determining
a future solution, such as postcar_loader. */
struct var_mtrr_context {
uint32_t upper_mask;
int max_var_mtrrs;
int used_var_mtrrs;
void *arg; /* optional callback parameter */
};
/* Returns 0-relative MTRR from context. Use MTRR_PHYS_BASE|MASK macros for calculating
MSR address value. */
static inline int var_mtrr_context_current_mtrr(const struct var_mtrr_context *ctx)
{
return ctx->used_var_mtrrs;
}
/* Initialize var_mtrr_context object. Assumes all variable MTRRs are not yet used. */
void var_mtrr_context_init(struct var_mtrr_context *ctx, void *arg);
/* Allocate a variable mtrr base and mask, calling the provided callback for each MTRR
MSR base-mask pair needed to accommodate the address and size request.
Returns < 0 on error and 0 on success. */
int var_mtrr_set_with_cb(struct var_mtrr_context *ctx,
uintptr_t addr, size_t size, int type,
void (*callback)(const struct var_mtrr_context *ctx,
uintptr_t base_addr, size_t size,
msr_t base, msr_t mask));
/* Same as var_mtrr_set_with_cb() but just write the MSRs directly. */
int var_mtrr_set(struct var_mtrr_context *ctx, uintptr_t addr, size_t size, int type);
/* /*
* Set the MTRRs using the data on the stack from setup_stack_and_mtrrs. * Set the MTRRs using the data on the stack from setup_stack_and_mtrrs.
* Return a new top_of_stack value which removes the setup_stack_and_mtrrs data. * Return a new top_of_stack value which removes the setup_stack_and_mtrrs data.