blob: 5e9a57696e0e880ab96885696bf88a02981f2f81 [file] [log] [blame]
// Copyright 2020 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 <lib/arch/asm.h>
#include <lib/arch/x86/cpuid-asm.h>
#ifdef __x86_64__
#define GLOBAL(x) (x)(%rip)
#define LEA_GLOBAL(x, reg) lea (x)(%rip), reg
#define BX %rbx
#define REGSIZE 8
#define LEAVES_START %rsi
#define LEAVES_STOP %rdi
#define DATA_START %r8
#else
#define GLOBAL(x) x
#define LEA_GLOBAL(x, reg) mov $x, reg
#define BX %ebx
#define REGSIZE 4
#define LEAVES_START %esi
#define LEAVES_STOP %edi
#define DATA_START %ebp
#define movslq movl
#endif // __x86_64__
// Stores CPUID values represented by EAX-EDX at a particular address.
.macro store_cpuid addr:vararg
mov %eax, CPUID_EAX + \addr
mov %ebx, CPUID_EBX + \addr
mov %ecx, CPUID_ECX + \addr
mov %edx, CPUID_EDX + \addr
.endm
// This initializes the CpuidIo objects returned by all the arch::BootCpuid
// instantiations linked in.
.function InitializeBootCpuid, global
push BX
.cfi_adjust_cfa_offset REGSIZE
.cfi_rel_offset BX, 0
// We need to make use of another call-saved register in the 32-bit case.
#ifndef __x86_64__
push DATA_START
.cfi_adjust_cfa_offset REGSIZE
.cfi_rel_offset DATA_START, 0
#endif
// The zeroth leaves (basic, hypervisor, and extended) are special because
// they tell us what other leaves exist.
// They are not included in the special section we iterate over below.
xor %eax,%eax
xor %ecx,%ecx
cpuid
store_cpuid GLOBAL(gBootCpuid0)
mov $CPUID_HYP_LEAF0, %eax
xor %ecx,%ecx
cpuid
store_cpuid GLOBAL(gBootCpuidHyp0)
mov $CPUID_EXT_LEAF0, %eax
xor %ecx,%ecx
cpuid
store_cpuid GLOBAL(gBootCpuidExt0)
// CpuidIo objects are uint32_t[4] data objects (C++ thinks they're a fancier
// type, but that's the layout). Each other BootCpuidIo::Get<Leaf, Subleaf>
// instantiation puts an entry into the BootCpuidLeaf special section.
// Each entry holds a leaf, subleaf, and offset from the entry to the datum.
LEA_GLOBAL(__start_BootCpuidLeaf, LEAVES_START)
LEA_GLOBAL(__stop_BootCpuidLeaf, LEAVES_STOP)
jmp .Loopcheck
.Loop:
// Load the leaf and check if it's supported. Whether the leaf is supported
// is dependent on whether it exceeds the maximum supported within the
// three separate ranges of
// [CPUID_EXT_LEAF, UINT32_MAX)
// [CPUID_HYP_LEAF, CPUID_EXT_LEAF)
// [0, CPUID_HYP_LEAF)
// Note that we jump to .Lunsupported_leaf if we happen on any of the zeroth
// leaves, as we do not expect them to be present in the special section and
// further we expect to have already initiliazed them at the top of the
// routine.
mov 0(LEAVES_START), %eax
// Skip to the hypervisor range if the extended range is unsupported.
cmpl $0, GLOBAL(gBootCpuidExt0 + CPUID_EAX)
je .Ltry_hypervisor
cmp GLOBAL(gBootCpuidExt0 + CPUID_EAX), %eax
ja .Lunsupported_leaf // (max extended leaf, UINT32_MAX]
cmp $CPUID_EXT_LEAF0, %eax
ja .Linit // (0th extended leaf, max extended leaf]
.if CPUID_HYP_LEAF0 > CPUID_EXT_LEAF0
.error "unexpected leaf values"
.endif
.Ltry_hypervisor:
// Skip to the basic range if the hypervisor range is unsupported.
cmpl $0, GLOBAL(gBootCpuidHyp0 + CPUID_EAX)
je .Ltry_basic
cmp GLOBAL(gBootCpuidHyp0 + CPUID_EAX), %eax
ja .Lunsupported_leaf // (max hypervisor leaf, 0th extended leaf]
cmp $CPUID_HYP_LEAF0, %eax
ja .Linit // (0th hypervisor leaf, max hypervisor leaf]
.Ltry_basic:
cmp GLOBAL(gBootCpuid0 + CPUID_EAX), %eax
ja .Lunsupported_leaf // (max basic leaf, 0th hypervisor leaf]
.Linit:
// Load the subleaf and ask the hardware.
mov 4(LEAVES_START), %ecx
cpuid
// Store the register values.
.Lstore:
#if defined(__ELF__)
movslq 8(LEAVES_START), DATA_START // Sign-extend from 32 bits.
store_cpuid 0(LEAVES_START, DATA_START)
// Next iteration.
lea (4 * 3)(LEAVES_START), LEAVES_START
#elif defined(_WIN32)
mov 8(LEAVES_START), DATA_START // Here it's an absolute pointer.
store_cpuid 0(DATA_START)
// Next iteration.
lea (8 + REGSIZE)(LEAVES_START), LEAVES_START
#else
#error "what format?"
#endif
.Loopcheck:
cmp LEAVES_START, LEAVES_STOP
jne .Loop
pop BX
.cfi_adjust_cfa_offset -REGSIZE
.cfi_same_value BX
#ifndef __x86_64__
pop DATA_START
.cfi_adjust_cfa_offset -REGSIZE
.cfi_same_value DATA_START
#endif
ret
.Lunsupported_leaf:
xor %eax, %eax
xor %ebx, %ebx
xor %ecx, %ecx
xor %edx, %edx
jmp .Lstore
.end_function
#ifdef _WIN32
// The equivalent of the __start/__stop magic symbols for PE-COFF is
// to declare $A and $Z sections and symbols within.
.section .drectve, "yn"
.ascii " /MERGE:.BootCpuidLeaf=.rdata"
.section .BootCpuidLeaf$A, "dr"
.balign 4
.int 0
.label __start_BootCpuidLeaf
.section .BootCpuidLeaf$Z, "dr"
.balign 4
.label __stop_BootCpuidLeaf
.int 0
#endif // _WIN32