/* SPDX-License-Identifier: GPL-2.0-only */

#include <cpu/x86/mtrr.h>
#include <cpu/x86/msr.h>

/*
 * Configure the MTRRs to cache the BIOS ACM. No general-purpose
 * registers are preserved. Inputs are taken from SSE registers:
 *
 *    %xmm0: BIOS ACM base
 *    %xmm1: BIOS ACM size
 *
 * These two SSE registers are not preserved, but the others are.
 */
.macro SET_UP_MTRRS_FOR_BIOS_ACM

	/* Determine CPU_ADDR_BITS and load PHYSMASK high word to %edx. */
	movl	$0x80000008, %eax
	cpuid
	movb	%al, %cl
	sub	$32, %cl
	movl	$1, %edx
	shl	%cl, %edx
	subl	$1, %edx
	movl	%edx, %edi /* %edi contains the MTRR_HIGH_MASK */

	/* Get the number of variable MTRRs */
	movl	$(MTRR_CAP_MSR), %ecx
	rdmsr
	andl	$(0xff), %eax

	/* Initialize ECX */
	movl	$(MTRR_PHYS_BASE(0)), %ecx

	jmp	cond_allocate_var_mtrrs

body_allocate_var_mtrrs:

	/* Program MTRR base */
	xorl	%edx, %edx
	movd	%xmm0, %eax
	orl	$(MTRR_TYPE_WRBACK), %eax
	wrmsr
	incl	%ecx				/* Move index to MTRR_PHYS_MASK */

	/* Temporarily transfer MSR index to EDX so that CL can be used */
	movl	%ecx, %edx

	/* Determine next size to cache */
	bsr	%ebx, %ecx
	movl	$(1), %ebx
	shl	%cl, %ebx			/* Can only use CL here */

	/* Restore ECX */
	movl	%edx, %ecx

	/* Update saved base address */
	addl	%ebx, %eax
	movd	%eax, %xmm0

	/* Update saved remaining size */
	movd	%xmm1, %eax
	subl	%ebx, %eax
	movd	%eax, %xmm1

	/* Program MTRR mask */
	movl	%edi, %edx
	xorl	%eax, %eax
	subl	%ebx, %eax			/* %eax = 4GIB - size to cache */
	orl	$(MTRR_PHYS_MASK_VALID), %eax
	wrmsr
	incl	%ecx				/* Move index to next MTRR_PHYS_BASE */

cond_allocate_var_mtrrs:

	/* Check if we still need to cache something */
	movd	%xmm1, %ebx
	andl	%ebx, %ebx

	jnz	body_allocate_var_mtrrs

.endm