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 */
Hi, Holger,
On 26.01.2009 18:57 Uhr, Holger Hesselbarth wrote:
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.
Can you explain a little bit how this is going to be different from what we do in coreboot v2 today?
At the moment a mainboard specifies one or more "CPU sockets" in the Config.lb file. Each socket "knows" which CPUs fit in there, and pulls in the configuration/initialization code for those CPUs. During run-time the correct code for your cpu is chosen via the cpuid. That code - enables cpu cache - sets up the CPU's local APIC - loads CPU microcode - puts CPU to full speed via SpeedStep at al - enables errata fixes - sets up MTRRs according to your CPU, RAM and UMA graphics memory - starts secondary cores - and potentially does other things, like enable certain C4 settings, virtualization, cpu temperature sensors, ... (Especially this part could use some brush-up)
It's not recommendable to make the MTRR user configurable, as they have to be set up according to the RAM in the machine. There's not much (or nothing?) to optimize by doing it manually.
Best regards, Stefan
Hi Stefan (and all others, too),
At the moment a mainboard specifies one or more "CPU sockets" in the Config.lb file. Each socket "knows" which CPUs fit in there, and pulls
currently socket 370 is a synonym for all slot 1 and socket 370 processors which is quite okay since they are - at least in terms of configuration - the same. but currently socket 370 (and slot 2) is also a synonym for intel model_6xx which is not quite correct. VIA cpus could run on those, too. so socket 370 should include at least VIA's init routines, too. on the other hand: the init routines for most (all?) intel processors can be made generic so that they support the range from Pentium 1 on socket 7 to Core2 on LGA775. at least the range from Pentium II to Pentium III including all Celeron models can be initialized with one quite simple routine. this would include all Xeon models, too. the same accounts for VIA processors: the init routines for the onboard models are quite the same for the socket 370 models because they have similar/same cores. we could have manufacturer wide init routines. or at least divide the manufacturer routines into groups. slot1/s370 should be one and contain routines for Intel (Celeron,Pentium II, Pentium III) and VIA (Cyrix III, C3) processors. the intel routines could be reused in the slot 2 group (containing all Xeons really) and in future groups like LGA775.
in the configuration/initialization code for those CPUs. During run-time the correct code for your cpu is chosen via the cpuid. That code
- enables cpu cache
currently only the L1 cache is switched on. the L2 cache is left unconfigured and disabled at least for all socket 370 / slot 2 processors. there is a way to determine the size of the L2 cache on intel processors by reading the information the cpuid command supplies. it makes a generic L2 cache init routine quite easy. an intel document on L2 cache initialization could come in handy. it would be better than following the resourced v1 inititalization code.
- sets up the CPU's local APIC
- loads CPU microcode
currently only two microcode files are included. we could either include all microcodes supplied by intel resulting in a huge increase of codesize or dissect the file supplied by intel and only include updates that could affect cpus possibly be used on the socket. or we could ignore microcode updates completely as they are loaded by the OS (windows by default, linux by additional tool) anyway. the file by intel currently contains microcode updates for all their processors from the pentium II upto their latest processor. they can be divided into groups of cores (by cpu id) and sockets (by platform id).
It's not recommendable to make the MTRR user configurable, as they have to be set up according to the RAM in the machine. There's not much (or nothing?) to optimize by doing it manually.
sorry i didn't make myself clear. i was not talking about the MTRR but about the L2 cache latency. it can be made user configurable on intel Pentium II / III processors. abit's bios supports this feature, too. however there's no great impact on the performance. on the pentium iii the serial number can be enabled/disabled. and on VIA/Cyrix/Centaur cpus there are some performance features that could be user configurable. on those cpus one could even alter the processor name string but i'm not sure we should support things like that.