i'm back after quite some time of silence but i've never lost track of the coreboot development though.
the time has come to contribute some real code. i decided to start with a generic cpu detection routine that at a later point should be able to detect all cpus from the original Pentium upwards. my next step is to write configuration functions for all models depending on the manufacturer. the first goal is to make L2 cache available for all Slot1/Socket370 processors. i already took a look at the L2 init code from coreboot v1 and i guess i got it. some official intel document on L2 cache init would help though. the benefit of a generic would be that exchanging the cpu wouldn't rise the need to compile a new bios first. even if changing from one manufacturer to another. the drawback of a generic init routine would be that all init routines would be included in the final bios. so the routines should be kept as short as possible. to make some things user-configurable i propose a new section in the options file: cpu settings. mttr configuration could be called from the configuration routines or included in them. any thoughts on that? i also propose to enforce maximum performance settings on VIA/Cyrix, Centaur and older AMD processors as they have feature control registers which where meant to disable some advanced functions to maintain Intel Pentium compatibility. talking about Cyrix: i'll use the famous 5/2 division to identify the 6x86 with switched off cpuid and enable it by writing its internal configuration registers.
i attached the files with the detection routines and the modified header files. the cpu_data structure in the cpu.h is losely based on the sources of x86info by Dave Jones. i also took the check_cpuid from his code. the copyright headers are not set in any of the files yet, but i didn't commit anything anyway ;) i didn't try to compile it, i just wanted to make sure i'm heading in the right direction.
/* * (C) 2001 Dave Jones. * 2009 adapted for coreboot by Holger Hesselbarth * * Licensed under the terms of the GNU GPL License version 2. */
static int flag_is_changeable_p(unsigned long flag) { unsigned long f1, f2; __asm__ volatile("pushf\n\t" "pushf\n\t" "pop %0\n\t" "mov %0,%1\n\t" "xor %2,%0\n\t" "push %0\n\t" "popf\n\t" "pushf\n\t" "pop %0\n\t" "popf\n\t" : "=&r" (f1), "=&r" (f2) : "ir" (flag)); return ((f1^f2) & flag) != 0; }
int check_cpuid(void) { return flag_is_changeable_p(0x200000); }
/* * (C) 2009 Holger Hesselbarth */
#include <cpu/cpu.h> #include <identify_cpu.h>
static void identify_cpu(struct cpu_data *cpu) { /* check if cpuid is supported */ if((check_cpuid()) == 0) { // TODO : check if this is a Cyrix cpu with cpuid disabled return; }
/* gather basic cpu information */ get_basic_cpu_information(cpu);
/* gather extended cpu information */ get_extended_cpu_information(cpu);
return; };
static void get_basic_cpu_information(struct cpu_data *cpu) { unsigned int maxbi, vendor;
/* get basic cpu information */ cpuid(CPU_BASIC_INF, &maxbi, &vendor, NULL, NULL); cpu->maxbi = (maxbi & 0xFFFF ); /* high-order word is non-zero on some Cyrix cpus */
if(cpu->maxbi < 1) { return; }
/* get family, model, stepping and feature flags */ cpuid((CPU_BASIC_INF + 1), &eax, &ebx, &ecx, &edx); cpu->stepping = (eax & 0x0F); cpu->model = ((eax >> 4) & 0x0F); cpu->family = ((eax >> 8) & 0x0F); cpu->ecx_bi_flags = ecx; cpu->edx_bi_flags = edx; /* get more basic cpu information based on vendor */ switch(vendor) { case 0x756e6547 : /* uneG */ cpu->vendor = X86_VENDOR_INTEL; cpu->type = ((eax >> 12) & 0x03);
if(cpu->family == 0xF) { cpu->emodel = ((eax >> 16) & 0x0F); cpu->efamily = ((eax >> 20) & 0xFF); } else if(cpu->family == 0x6) { cpu->emodel = ((eax >> 16) & 0x0F); }
cpu->brand = (ebx & 0xFF); cpu->clflush = ((ebx >> 8) & 0xFF); cpu->apicid = ((ebx >> 24) & 0xFF); break; case 0x68747541 : /* thuA */ cpu->vendor = X86_VENDOR_AMD; // TODO : check for extended family & model break; case 0x69727943 : /* iryC */ cpu->vendor = X86_VENDOR_CYRIX; break; case 0x746e6543 : /* tneC */ cpu->vendor = X86_VENDOR_CENTAUR; break; // TODO : add more cpu makers default : cpu->vendor = X86_VENDOR_UNKNOWN; break; }
return; };
static void get_extended_cpu_information(struct cpu_data *cpu) { /* get extended cpu information * the IDT WinChip C6 needs a special treatment */ if((cpu->vendor == X86_VENDOR_CENTAUR) && (cpu->model == 4)) { cpuid(CPU_C6_EXT_INF, &(cpu->maxei), NULL, NULL, NULL); } else { cpuid(CPU_EXT_INF, &(cpu->maxei), NULL, NULL, NULL); }
return; };
#ifndef CPU_X86_IDENTIFY_CPU_H #define CPU_X86_IDENTIFY_CPU_H
extern int check_cpuid(void);
#define CPU_BASIC_INF 0x00000000 #define CPU_EXT_INF 0x80000000 #define CPU_C6_EXT_INF 0xC0000000
#endif /* CPU_Xf86_IDENTIFY_CPU_H */
#ifndef CPU_CPU_H #define CPU_CPU_H
#include <arch/cpu.h>
#define CPU_NAME_LEN 80
struct cpu_data { struct cpudata *next; unsigned char number; unsigned char vendor;
unsigned char family; unsigned char model; unsigned char type; unsigned char stepping; unsigned char efamily; unsigned char emodel;
unsigned char platform_id; unsigned char maxbi; unsigned int maxei;
unsigned int ecx_bi_flags; unsigned int edx_bi_flags;
unsigned int cachesize_L1_I; unsigned int cachesize_L1_D; unsigned int cachesize_L2; unsigned int cachesize_L3; unsigned int cachesize_trace;
char name[CPU_NAME_LEN];
unsigned int nr_cores; unsigned int nr_logical; /* Intel specific bits */ unsigned char brand; unsigned char clflush; unsigned char apicid; /* Intel Pentium III serial number (PSN) */ unsigned int serialno[2]; };
struct device; struct bus;
void cpu_initialize(void); void initialize_cpus(struct bus *cpu_bus);
#define __cpu_driver __attribute__ ((used,__section__(".rodata.cpu_driver"))) /** start of compile time generated pci driver array */ extern struct cpu_driver cpu_drivers[]; /** end of compile time generated pci driver array */ extern struct cpu_driver ecpu_drivers[];
#endif /* CPU_CPU_H */