uboot/arch/x86/cpu/i386/cpu.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2008-2011
   4 * Graeme Russ, <graeme.russ@gmail.com>
   5 *
   6 * (C) Copyright 2002
   7 * Daniel Engström, Omicron Ceti AB, <daniel@omicron.se>
   8 *
   9 * (C) Copyright 2002
  10 * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
  11 * Marius Groeger <mgroeger@sysgo.de>
  12 *
  13 * (C) Copyright 2002
  14 * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
  15 * Alex Zuepke <azu@sysgo.de>
  16 *
  17 * Part of this file is adapted from coreboot
  18 * src/arch/x86/lib/cpu.c
  19 */
  20
  21#include <common.h>
  22#include <cpu_func.h>
  23#include <malloc.h>
  24#include <asm/control_regs.h>
  25#include <asm/cpu.h>
  26#include <asm/mp.h>
  27#include <asm/msr.h>
  28#include <asm/mtrr.h>
  29#include <asm/processor-flags.h>
  30
  31DECLARE_GLOBAL_DATA_PTR;
  32
  33/*
  34 * Constructor for a conventional segment GDT (or LDT) entry
  35 * This is a macro so it can be used in initialisers
  36 */
  37#define GDT_ENTRY(flags, base, limit)                   \
  38        ((((base)  & 0xff000000ULL) << (56-24)) |       \
  39         (((flags) & 0x0000f0ffULL) << 40) |            \
  40         (((limit) & 0x000f0000ULL) << (48-16)) |       \
  41         (((base)  & 0x00ffffffULL) << 16) |            \
  42         (((limit) & 0x0000ffffULL)))
  43
  44struct gdt_ptr {
  45        u16 len;
  46        u32 ptr;
  47} __packed;
  48
  49struct cpu_device_id {
  50        unsigned vendor;
  51        unsigned device;
  52};
  53
  54struct cpuinfo_x86 {
  55        uint8_t x86;            /* CPU family */
  56        uint8_t x86_vendor;     /* CPU vendor */
  57        uint8_t x86_model;
  58        uint8_t x86_mask;
  59};
  60
  61/*
  62 * List of cpu vendor strings along with their normalized
  63 * id values.
  64 */
  65static const struct {
  66        int vendor;
  67        const char *name;
  68} x86_vendors[] = {
  69        { X86_VENDOR_INTEL,     "GenuineIntel", },
  70        { X86_VENDOR_CYRIX,     "CyrixInstead", },
  71        { X86_VENDOR_AMD,       "AuthenticAMD", },
  72        { X86_VENDOR_UMC,       "UMC UMC UMC ", },
  73        { X86_VENDOR_NEXGEN,    "NexGenDriven", },
  74        { X86_VENDOR_CENTAUR,   "CentaurHauls", },
  75        { X86_VENDOR_RISE,      "RiseRiseRise", },
  76        { X86_VENDOR_TRANSMETA, "GenuineTMx86", },
  77        { X86_VENDOR_TRANSMETA, "TransmetaCPU", },
  78        { X86_VENDOR_NSC,       "Geode by NSC", },
  79        { X86_VENDOR_SIS,       "SiS SiS SiS ", },
  80};
  81
  82static void load_ds(u32 segment)
  83{
  84        asm volatile("movl %0, %%ds" : : "r" (segment * X86_GDT_ENTRY_SIZE));
  85}
  86
  87static void load_es(u32 segment)
  88{
  89        asm volatile("movl %0, %%es" : : "r" (segment * X86_GDT_ENTRY_SIZE));
  90}
  91
  92static void load_fs(u32 segment)
  93{
  94        asm volatile("movl %0, %%fs" : : "r" (segment * X86_GDT_ENTRY_SIZE));
  95}
  96
  97static void load_gs(u32 segment)
  98{
  99        asm volatile("movl %0, %%gs" : : "r" (segment * X86_GDT_ENTRY_SIZE));
 100}
 101
 102static void load_ss(u32 segment)
 103{
 104        asm volatile("movl %0, %%ss" : : "r" (segment * X86_GDT_ENTRY_SIZE));
 105}
 106
 107static void load_gdt(const u64 *boot_gdt, u16 num_entries)
 108{
 109        struct gdt_ptr gdt;
 110
 111        gdt.len = (num_entries * X86_GDT_ENTRY_SIZE) - 1;
 112        gdt.ptr = (ulong)boot_gdt;
 113
 114        asm volatile("lgdtl %0\n" : : "m" (gdt));
 115}
 116
 117void arch_setup_gd(gd_t *new_gd)
 118{
 119        u64 *gdt_addr;
 120
 121        gdt_addr = new_gd->arch.gdt;
 122
 123        /*
 124         * CS: code, read/execute, 4 GB, base 0
 125         *
 126         * Some OS (like VxWorks) requires GDT entry 1 to be the 32-bit CS
 127         */
 128        gdt_addr[X86_GDT_ENTRY_UNUSED] = GDT_ENTRY(0xc09b, 0, 0xfffff);
 129        gdt_addr[X86_GDT_ENTRY_32BIT_CS] = GDT_ENTRY(0xc09b, 0, 0xfffff);
 130
 131        /* DS: data, read/write, 4 GB, base 0 */
 132        gdt_addr[X86_GDT_ENTRY_32BIT_DS] = GDT_ENTRY(0xc093, 0, 0xfffff);
 133
 134        /* FS: data, read/write, 4 GB, base (Global Data Pointer) */
 135        new_gd->arch.gd_addr = new_gd;
 136        gdt_addr[X86_GDT_ENTRY_32BIT_FS] = GDT_ENTRY(0xc093,
 137                     (ulong)&new_gd->arch.gd_addr, 0xfffff);
 138
 139        /* 16-bit CS: code, read/execute, 64 kB, base 0 */
 140        gdt_addr[X86_GDT_ENTRY_16BIT_CS] = GDT_ENTRY(0x009b, 0, 0x0ffff);
 141
 142        /* 16-bit DS: data, read/write, 64 kB, base 0 */
 143        gdt_addr[X86_GDT_ENTRY_16BIT_DS] = GDT_ENTRY(0x0093, 0, 0x0ffff);
 144
 145        gdt_addr[X86_GDT_ENTRY_16BIT_FLAT_CS] = GDT_ENTRY(0x809b, 0, 0xfffff);
 146        gdt_addr[X86_GDT_ENTRY_16BIT_FLAT_DS] = GDT_ENTRY(0x8093, 0, 0xfffff);
 147
 148        load_gdt(gdt_addr, X86_GDT_NUM_ENTRIES);
 149        load_ds(X86_GDT_ENTRY_32BIT_DS);
 150        load_es(X86_GDT_ENTRY_32BIT_DS);
 151        load_gs(X86_GDT_ENTRY_32BIT_DS);
 152        load_ss(X86_GDT_ENTRY_32BIT_DS);
 153        load_fs(X86_GDT_ENTRY_32BIT_FS);
 154}
 155
 156#ifdef CONFIG_HAVE_FSP
 157/*
 158 * Setup FSP execution environment GDT
 159 *
 160 * Per Intel FSP external architecture specification, before calling any FSP
 161 * APIs, we need make sure the system is in flat 32-bit mode and both the code
 162 * and data selectors should have full 4GB access range. Here we reuse the one
 163 * we used in arch/x86/cpu/start16.S, and reload the segement registers.
 164 */
 165void setup_fsp_gdt(void)
 166{
 167        load_gdt((const u64 *)(gdt_rom + CONFIG_RESET_SEG_START), 4);
 168        load_ds(X86_GDT_ENTRY_32BIT_DS);
 169        load_ss(X86_GDT_ENTRY_32BIT_DS);
 170        load_es(X86_GDT_ENTRY_32BIT_DS);
 171        load_fs(X86_GDT_ENTRY_32BIT_DS);
 172        load_gs(X86_GDT_ENTRY_32BIT_DS);
 173}
 174#endif
 175
 176/*
 177 * Cyrix CPUs without cpuid or with cpuid not yet enabled can be detected
 178 * by the fact that they preserve the flags across the division of 5/2.
 179 * PII and PPro exhibit this behavior too, but they have cpuid available.
 180 */
 181
 182/*
 183 * Perform the Cyrix 5/2 test. A Cyrix won't change
 184 * the flags, while other 486 chips will.
 185 */
 186static inline int test_cyrix_52div(void)
 187{
 188        unsigned int test;
 189
 190        __asm__ __volatile__(
 191             "sahf\n\t"         /* clear flags (%eax = 0x0005) */
 192             "div %b2\n\t"      /* divide 5 by 2 */
 193             "lahf"             /* store flags into %ah */
 194             : "=a" (test)
 195             : "0" (5), "q" (2)
 196             : "cc");
 197
 198        /* AH is 0x02 on Cyrix after the divide.. */
 199        return (unsigned char) (test >> 8) == 0x02;
 200}
 201
 202/*
 203 *      Detect a NexGen CPU running without BIOS hypercode new enough
 204 *      to have CPUID. (Thanks to Herbert Oppmann)
 205 */
 206static int deep_magic_nexgen_probe(void)
 207{
 208        int ret;
 209
 210        __asm__ __volatile__ (
 211                "       movw    $0x5555, %%ax\n"
 212                "       xorw    %%dx,%%dx\n"
 213                "       movw    $2, %%cx\n"
 214                "       divw    %%cx\n"
 215                "       movl    $0, %%eax\n"
 216                "       jnz     1f\n"
 217                "       movl    $1, %%eax\n"
 218                "1:\n"
 219                : "=a" (ret) : : "cx", "dx");
 220        return  ret;
 221}
 222
 223static bool has_cpuid(void)
 224{
 225        return flag_is_changeable_p(X86_EFLAGS_ID);
 226}
 227
 228static bool has_mtrr(void)
 229{
 230        return cpuid_edx(0x00000001) & (1 << 12) ? true : false;
 231}
 232
 233static int build_vendor_name(char *vendor_name)
 234{
 235        struct cpuid_result result;
 236        result = cpuid(0x00000000);
 237        unsigned int *name_as_ints = (unsigned int *)vendor_name;
 238
 239        name_as_ints[0] = result.ebx;
 240        name_as_ints[1] = result.edx;
 241        name_as_ints[2] = result.ecx;
 242
 243        return result.eax;
 244}
 245
 246static void identify_cpu(struct cpu_device_id *cpu)
 247{
 248        char vendor_name[16];
 249        int i;
 250
 251        vendor_name[0] = '\0'; /* Unset */
 252        cpu->device = 0; /* fix gcc 4.4.4 warning */
 253
 254        /* Find the id and vendor_name */
 255        if (!has_cpuid()) {
 256                /* Its a 486 if we can modify the AC flag */
 257                if (flag_is_changeable_p(X86_EFLAGS_AC))
 258                        cpu->device = 0x00000400; /* 486 */
 259                else
 260                        cpu->device = 0x00000300; /* 386 */
 261                if ((cpu->device == 0x00000400) && test_cyrix_52div()) {
 262                        memcpy(vendor_name, "CyrixInstead", 13);
 263                        /* If we ever care we can enable cpuid here */
 264                }
 265                /* Detect NexGen with old hypercode */
 266                else if (deep_magic_nexgen_probe())
 267                        memcpy(vendor_name, "NexGenDriven", 13);
 268        }
 269        if (has_cpuid()) {
 270                int  cpuid_level;
 271
 272                cpuid_level = build_vendor_name(vendor_name);
 273                vendor_name[12] = '\0';
 274
 275                /* Intel-defined flags: level 0x00000001 */
 276                if (cpuid_level >= 0x00000001) {
 277                        cpu->device = cpuid_eax(0x00000001);
 278                } else {
 279                        /* Have CPUID level 0 only unheard of */
 280                        cpu->device = 0x00000400;
 281                }
 282        }
 283        cpu->vendor = X86_VENDOR_UNKNOWN;
 284        for (i = 0; i < ARRAY_SIZE(x86_vendors); i++) {
 285                if (memcmp(vendor_name, x86_vendors[i].name, 12) == 0) {
 286                        cpu->vendor = x86_vendors[i].vendor;
 287                        break;
 288                }
 289        }
 290}
 291
 292static inline void get_fms(struct cpuinfo_x86 *c, uint32_t tfms)
 293{
 294        c->x86 = (tfms >> 8) & 0xf;
 295        c->x86_model = (tfms >> 4) & 0xf;
 296        c->x86_mask = tfms & 0xf;
 297        if (c->x86 == 0xf)
 298                c->x86 += (tfms >> 20) & 0xff;
 299        if (c->x86 >= 0x6)
 300                c->x86_model += ((tfms >> 16) & 0xF) << 4;
 301}
 302
 303u32 cpu_get_family_model(void)
 304{
 305        return gd->arch.x86_device & 0x0fff0ff0;
 306}
 307
 308u32 cpu_get_stepping(void)
 309{
 310        return gd->arch.x86_mask;
 311}
 312
 313/* initialise FPU, reset EM, set MP and NE */
 314static void setup_cpu_features(void)
 315{
 316        const u32 em_rst = ~X86_CR0_EM;
 317        const u32 mp_ne_set = X86_CR0_MP | X86_CR0_NE;
 318
 319        asm ("fninit\n" \
 320        "movl %%cr0, %%eax\n" \
 321        "andl %0, %%eax\n" \
 322        "orl  %1, %%eax\n" \
 323        "movl %%eax, %%cr0\n" \
 324        : : "i" (em_rst), "i" (mp_ne_set) : "eax");
 325}
 326
 327static void setup_identity(void)
 328{
 329        /* identify CPU via cpuid and store the decoded info into gd->arch */
 330        if (has_cpuid()) {
 331                struct cpu_device_id cpu;
 332                struct cpuinfo_x86 c;
 333
 334                identify_cpu(&cpu);
 335                get_fms(&c, cpu.device);
 336                gd->arch.x86 = c.x86;
 337                gd->arch.x86_vendor = cpu.vendor;
 338                gd->arch.x86_model = c.x86_model;
 339                gd->arch.x86_mask = c.x86_mask;
 340                gd->arch.x86_device = cpu.device;
 341
 342                gd->arch.has_mtrr = has_mtrr();
 343        }
 344}
 345
 346/* Don't allow PCI region 3 to use memory in the 2-4GB memory hole */
 347static void setup_pci_ram_top(void)
 348{
 349        gd->pci_ram_top = 0x80000000U;
 350}
 351
 352static void setup_mtrr(void)
 353{
 354        u64 mtrr_cap;
 355
 356        /* Configure fixed range MTRRs for some legacy regions */
 357        if (!gd->arch.has_mtrr)
 358                return;
 359
 360        mtrr_cap = native_read_msr(MTRR_CAP_MSR);
 361        if (mtrr_cap & MTRR_CAP_FIX) {
 362                /* Mark the VGA RAM area as uncacheable */
 363                native_write_msr(MTRR_FIX_16K_A0000_MSR,
 364                                 MTRR_FIX_TYPE(MTRR_TYPE_UNCACHEABLE),
 365                                 MTRR_FIX_TYPE(MTRR_TYPE_UNCACHEABLE));
 366
 367                /*
 368                 * Mark the PCI ROM area as cacheable to improve ROM
 369                 * execution performance.
 370                 */
 371                native_write_msr(MTRR_FIX_4K_C0000_MSR,
 372                                 MTRR_FIX_TYPE(MTRR_TYPE_WRBACK),
 373                                 MTRR_FIX_TYPE(MTRR_TYPE_WRBACK));
 374                native_write_msr(MTRR_FIX_4K_C8000_MSR,
 375                                 MTRR_FIX_TYPE(MTRR_TYPE_WRBACK),
 376                                 MTRR_FIX_TYPE(MTRR_TYPE_WRBACK));
 377                native_write_msr(MTRR_FIX_4K_D0000_MSR,
 378                                 MTRR_FIX_TYPE(MTRR_TYPE_WRBACK),
 379                                 MTRR_FIX_TYPE(MTRR_TYPE_WRBACK));
 380                native_write_msr(MTRR_FIX_4K_D8000_MSR,
 381                                 MTRR_FIX_TYPE(MTRR_TYPE_WRBACK),
 382                                 MTRR_FIX_TYPE(MTRR_TYPE_WRBACK));
 383
 384                /* Enable the fixed range MTRRs */
 385                msr_setbits_64(MTRR_DEF_TYPE_MSR, MTRR_DEF_TYPE_FIX_EN);
 386        }
 387}
 388
 389int x86_cpu_init_tpl(void)
 390{
 391        setup_cpu_features();
 392        setup_identity();
 393
 394        return 0;
 395}
 396
 397int x86_cpu_init_f(void)
 398{
 399        if (ll_boot_init())
 400                setup_cpu_features();
 401        setup_identity();
 402        setup_mtrr();
 403        setup_pci_ram_top();
 404
 405        /* Set up the i8254 timer if required */
 406        if (IS_ENABLED(CONFIG_I8254_TIMER))
 407                i8254_init();
 408
 409        return 0;
 410}
 411
 412int x86_cpu_reinit_f(void)
 413{
 414        setup_identity();
 415        setup_pci_ram_top();
 416
 417        return 0;
 418}
 419
 420void x86_enable_caches(void)
 421{
 422        unsigned long cr0;
 423
 424        cr0 = read_cr0();
 425        cr0 &= ~(X86_CR0_NW | X86_CR0_CD);
 426        write_cr0(cr0);
 427        wbinvd();
 428}
 429void enable_caches(void) __attribute__((weak, alias("x86_enable_caches")));
 430
 431void x86_disable_caches(void)
 432{
 433        unsigned long cr0;
 434
 435        cr0 = read_cr0();
 436        cr0 |= X86_CR0_NW | X86_CR0_CD;
 437        wbinvd();
 438        write_cr0(cr0);
 439        wbinvd();
 440}
 441void disable_caches(void) __attribute__((weak, alias("x86_disable_caches")));
 442
 443int dcache_status(void)
 444{
 445        return !(read_cr0() & X86_CR0_CD);
 446}
 447
 448void cpu_enable_paging_pae(ulong cr3)
 449{
 450        __asm__ __volatile__(
 451                /* Load the page table address */
 452                "movl   %0, %%cr3\n"
 453                /* Enable pae */
 454                "movl   %%cr4, %%eax\n"
 455                "orl    $0x00000020, %%eax\n"
 456                "movl   %%eax, %%cr4\n"
 457                /* Enable paging */
 458                "movl   %%cr0, %%eax\n"
 459                "orl    $0x80000000, %%eax\n"
 460                "movl   %%eax, %%cr0\n"
 461                :
 462                : "r" (cr3)
 463                : "eax");
 464}
 465
 466void cpu_disable_paging_pae(void)
 467{
 468        /* Turn off paging */
 469        __asm__ __volatile__ (
 470                /* Disable paging */
 471                "movl   %%cr0, %%eax\n"
 472                "andl   $0x7fffffff, %%eax\n"
 473                "movl   %%eax, %%cr0\n"
 474                /* Disable pae */
 475                "movl   %%cr4, %%eax\n"
 476                "andl   $0xffffffdf, %%eax\n"
 477                "movl   %%eax, %%cr4\n"
 478                :
 479                :
 480                : "eax");
 481}
 482
 483static bool can_detect_long_mode(void)
 484{
 485        return cpuid_eax(0x80000000) > 0x80000000UL;
 486}
 487
 488static bool has_long_mode(void)
 489{
 490        return cpuid_edx(0x80000001) & (1 << 29) ? true : false;
 491}
 492
 493int cpu_has_64bit(void)
 494{
 495        return has_cpuid() && can_detect_long_mode() &&
 496                has_long_mode();
 497}
 498
 499#define PAGETABLE_BASE          0x80000
 500#define PAGETABLE_SIZE          (6 * 4096)
 501
 502/**
 503 * build_pagetable() - build a flat 4GiB page table structure for 64-bti mode
 504 *
 505 * @pgtable: Pointer to a 24iKB block of memory
 506 */
 507static void build_pagetable(uint32_t *pgtable)
 508{
 509        uint i;
 510
 511        memset(pgtable, '\0', PAGETABLE_SIZE);
 512
 513        /* Level 4 needs a single entry */
 514        pgtable[0] = (ulong)&pgtable[1024] + 7;
 515
 516        /* Level 3 has one 64-bit entry for each GiB of memory */
 517        for (i = 0; i < 4; i++)
 518                pgtable[1024 + i * 2] = (ulong)&pgtable[2048] + 0x1000 * i + 7;
 519
 520        /* Level 2 has 2048 64-bit entries, each repesenting 2MiB */
 521        for (i = 0; i < 2048; i++)
 522                pgtable[2048 + i * 2] = 0x183 + (i << 21UL);
 523}
 524
 525int cpu_jump_to_64bit(ulong setup_base, ulong target)
 526{
 527        uint32_t *pgtable;
 528
 529        pgtable = memalign(4096, PAGETABLE_SIZE);
 530        if (!pgtable)
 531                return -ENOMEM;
 532
 533        build_pagetable(pgtable);
 534        cpu_call64((ulong)pgtable, setup_base, target);
 535        free(pgtable);
 536
 537        return -EFAULT;
 538}
 539
 540/*
 541 * Jump from SPL to U-Boot
 542 *
 543 * This function is work-in-progress with many issues to resolve.
 544 *
 545 * It works by setting up several regions:
 546 *   ptr      - a place to put the code that jumps into 64-bit mode
 547 *   gdt      - a place to put the global descriptor table
 548 *   pgtable  - a place to put the page tables
 549 *
 550 * The cpu_call64() code is copied from ROM and then manually patched so that
 551 * it has the correct GDT address in RAM. U-Boot is copied from ROM into
 552 * its pre-relocation address. Then we jump to the cpu_call64() code in RAM,
 553 * which changes to 64-bit mode and starts U-Boot.
 554 */
 555int cpu_jump_to_64bit_uboot(ulong target)
 556{
 557        typedef void (*func_t)(ulong pgtable, ulong setup_base, ulong target);
 558        uint32_t *pgtable;
 559        func_t func;
 560        char *ptr;
 561
 562        pgtable = (uint32_t *)PAGETABLE_BASE;
 563
 564        build_pagetable(pgtable);
 565
 566        extern long call64_stub_size;
 567        ptr = malloc(call64_stub_size);
 568        if (!ptr) {
 569                printf("Failed to allocate the cpu_call64 stub\n");
 570                return -ENOMEM;
 571        }
 572        memcpy(ptr, cpu_call64, call64_stub_size);
 573
 574        func = (func_t)ptr;
 575
 576        /*
 577         * Copy U-Boot from ROM
 578         * TODO(sjg@chromium.org): Figure out a way to get the text base
 579         * correctly here, and in the device-tree binman definition.
 580         *
 581         * Also consider using FIT so we get the correct image length and
 582         * parameters.
 583         */
 584        memcpy((char *)target, (char *)0xfff00000, 0x100000);
 585
 586        /* Jump to U-Boot */
 587        func((ulong)pgtable, 0, (ulong)target);
 588
 589        return -EFAULT;
 590}
 591
 592#ifdef CONFIG_SMP
 593static int enable_smis(struct udevice *cpu, void *unused)
 594{
 595        return 0;
 596}
 597
 598static struct mp_flight_record mp_steps[] = {
 599        MP_FR_BLOCK_APS(mp_init_cpu, NULL, mp_init_cpu, NULL),
 600        /* Wait for APs to finish initialization before proceeding */
 601        MP_FR_BLOCK_APS(NULL, NULL, enable_smis, NULL),
 602};
 603
 604int x86_mp_init(void)
 605{
 606        struct mp_params mp_params;
 607
 608        mp_params.parallel_microcode_load = 0,
 609        mp_params.flight_plan = &mp_steps[0];
 610        mp_params.num_records = ARRAY_SIZE(mp_steps);
 611        mp_params.microcode_pointer = 0;
 612
 613        if (mp_init(&mp_params)) {
 614                printf("Warning: MP init failure\n");
 615                return -EIO;
 616        }
 617
 618        return 0;
 619}
 620#endif
 621