diff options
-rw-r--r-- | src/arch/x86/postcar_loader.c | 59 |
1 files changed, 37 insertions, 22 deletions
diff --git a/src/arch/x86/postcar_loader.c b/src/arch/x86/postcar_loader.c index b5d8db0e1b..d9719ff729 100644 --- a/src/arch/x86/postcar_loader.c +++ b/src/arch/x86/postcar_loader.c @@ -59,29 +59,44 @@ int postcar_frame_init(struct postcar_frame *pcf, size_t stack_size) void postcar_frame_add_mtrr(struct postcar_frame *pcf, uintptr_t addr, size_t size, int type) { - size_t align; - - if (pcf->num_var_mttrs >= pcf->max_var_mttrs) { - printk(BIOS_ERR, "No more variable MTRRs: %d\n", - pcf->max_var_mttrs); - return; - } - - /* Determine address alignment by lowest bit set in address. */ - align = addr & (addr ^ (addr - 1)); - - if (align < size) { - printk(BIOS_ERR, "Address (%lx) alignment (%zx) < size (%zx)\n", - addr, align, size); - size = align; + /* + * 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; + + if (pcf->num_var_mttrs >= pcf->max_var_mttrs) { + printk(BIOS_ERR, "No more variable MTRRs: %d\n", + pcf->max_var_mttrs); + return; + } + + 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; + + printk(BIOS_DEBUG, "MTRR Range: Start=%lx End=%lx (Size %x)\n", + addr, addr + mtrr_size, mtrr_size); + + stack_push(pcf, pcf->upper_mask); + stack_push(pcf, ~(mtrr_size - 1) | MTRR_PHYS_MASK_VALID); + stack_push(pcf, 0); + stack_push(pcf, addr | type); + pcf->num_var_mttrs++; + + size -= mtrr_size; + addr += mtrr_size; } - - /* Push MTRR mask then base -- upper 32-bits then lower 32-bits. */ - stack_push(pcf, pcf->upper_mask); - stack_push(pcf, ~(size - 1) | MTRR_PHYS_MASK_VALID); - stack_push(pcf, 0); - stack_push(pcf, addr | type); - pcf->num_var_mttrs++; } void *postcar_commit_mtrrs(struct postcar_frame *pcf) |