diff options
author | Stefan Reinauer <stepan@coreboot.org> | 2010-12-11 20:33:41 +0000 |
---|---|---|
committer | Stefan Reinauer <stepan@openbios.org> | 2010-12-11 20:33:41 +0000 |
commit | 8677a23d5b053d550f70246de9c7dc8fd4e2fbf9 (patch) | |
tree | d9a7c6042de85d623739e2679ba90c66aad2797f /src/arch/x86/lib | |
parent | 198cb96387c457affa01696405ffaa4531e8e361 (diff) |
After this has been brought up many times before, rename src/arch/i386 to
src/arch/x86.
Signed-off-by: Stefan Reinauer <stepan@coreboot.org>
Acked-by: Patrick Georgi <patrick@georgi-clan.de>
git-svn-id: svn://svn.coreboot.org/coreboot/trunk@6161 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
Diffstat (limited to 'src/arch/x86/lib')
-rw-r--r-- | src/arch/x86/lib/Makefile.inc | 13 | ||||
-rw-r--r-- | src/arch/x86/lib/c_start.S | 317 | ||||
-rw-r--r-- | src/arch/x86/lib/cbfs_and_run.c | 55 | ||||
-rw-r--r-- | src/arch/x86/lib/cpu.c | 268 | ||||
-rw-r--r-- | src/arch/x86/lib/exception.c | 489 | ||||
-rw-r--r-- | src/arch/x86/lib/id.inc | 15 | ||||
-rw-r--r-- | src/arch/x86/lib/id.lds | 6 | ||||
-rw-r--r-- | src/arch/x86/lib/ioapic.c | 139 | ||||
-rw-r--r-- | src/arch/x86/lib/pci_ops_auto.c | 100 | ||||
-rw-r--r-- | src/arch/x86/lib/pci_ops_conf1.c | 63 | ||||
-rw-r--r-- | src/arch/x86/lib/pci_ops_conf2.c | 76 | ||||
-rw-r--r-- | src/arch/x86/lib/pci_ops_mmconf.c | 64 | ||||
-rw-r--r-- | src/arch/x86/lib/printk_init.c | 57 | ||||
-rw-r--r-- | src/arch/x86/lib/stages.c | 28 | ||||
-rw-r--r-- | src/arch/x86/lib/walkcbfs.S | 126 |
15 files changed, 1816 insertions, 0 deletions
diff --git a/src/arch/x86/lib/Makefile.inc b/src/arch/x86/lib/Makefile.inc new file mode 100644 index 0000000000..43ac4693f7 --- /dev/null +++ b/src/arch/x86/lib/Makefile.inc @@ -0,0 +1,13 @@ +ramstage-y += c_start.S +ramstage-y += cpu.c +ramstage-y += pci_ops_conf1.c +ramstage-y += pci_ops_conf2.c +ramstage-y += pci_ops_mmconf.c +ramstage-y += pci_ops_auto.c +ramstage-y += exception.c +ramstage-$(CONFIG_IOAPIC) += ioapic.c + +romstage-y += printk_init.c +romstage-y += cbfs_and_run.c + +$(obj)/arch/x86/lib/console.ramstage.o :: $(obj)/build.h diff --git a/src/arch/x86/lib/c_start.S b/src/arch/x86/lib/c_start.S new file mode 100644 index 0000000000..94ce4a70c3 --- /dev/null +++ b/src/arch/x86/lib/c_start.S @@ -0,0 +1,317 @@ +#include <cpu/x86/post_code.h> + + .section ".text" + .code32 + .globl _start +_start: + cli + lgdt %cs:gdtaddr + ljmp $0x10, $1f +1: movl $0x18, %eax + movl %eax, %ds + movl %eax, %es + movl %eax, %ss + movl %eax, %fs + movl %eax, %gs + + post_code(0x13) /* post 13 */ + + /** clear stack */ + cld + leal _stack, %edi + movl $_estack, %ecx + subl %edi, %ecx + shrl $2, %ecx /* it is 32 bit aligned, right? */ + xorl %eax, %eax + rep + stosl + + /** clear bss */ + leal _bss, %edi + movl $_ebss, %ecx + subl %edi, %ecx + jz .Lnobss + shrl $2, %ecx /* it is 32 bit aligned, right? */ + xorl %eax, %eax + rep + stosl +.Lnobss: + + /* set new stack */ + movl $_estack, %esp + + /* Push the cpu index and struct cpu */ + pushl $0 + pushl $0 + + /* push the boot_complete flag */ + pushl %ebp + + /* Save the stack location */ + movl %esp, %ebp + + /* Initialize the Interrupt Descriptor table */ + leal _idt, %edi + leal vec0, %ebx + movl $(0x10 << 16), %eax /* cs selector */ + +1: movw %bx, %ax + movl %ebx, %edx + movw $0x8E00, %dx /* Interrupt gate - dpl=0, present */ + movl %eax, 0(%edi) + movl %edx, 4(%edi) + addl $6, %ebx + addl $8, %edi + cmpl $_idt_end, %edi + jne 1b + + /* Load the Interrupt descriptor table */ + lidt idtarg + + /* + * Now we are finished. Memory is up, data is copied and + * bss is cleared. Now we call the main routine and + * let it do the rest. + */ + post_code(0xfe) /* post fe */ + + /* Restore the stack location */ + movl %ebp, %esp + + /* The boot_complete flag has already been pushed */ + call hardwaremain + /* NOTREACHED */ +.Lhlt: + post_code(0xee) /* post ee */ + hlt + jmp .Lhlt + +vec0: + pushl $0 /* error code */ + pushl $0 /* vector */ + jmp int_hand +vec1: + pushl $0 /* error code */ + pushl $1 /* vector */ + jmp int_hand + +vec2: + pushl $0 /* error code */ + pushl $2 /* vector */ + jmp int_hand + +vec3: + pushl $0 /* error code */ + pushl $3 /* vector */ + jmp int_hand + +vec4: + pushl $0 /* error code */ + pushl $4 /* vector */ + jmp int_hand + +vec5: + pushl $0 /* error code */ + pushl $5 /* vector */ + jmp int_hand + +vec6: + pushl $0 /* error code */ + pushl $6 /* vector */ + jmp int_hand + +vec7: + pushl $0 /* error code */ + pushl $7 /* vector */ + jmp int_hand + +vec8: + /* error code */ + pushl $8 /* vector */ + jmp int_hand + .word 0x9090 + +vec9: + pushl $0 /* error code */ + pushl $9 /* vector */ + jmp int_hand + +vec10: + /* error code */ + pushl $10 /* vector */ + jmp int_hand + .word 0x9090 + +vec11: + /* error code */ + pushl $11 /* vector */ + jmp int_hand + .word 0x9090 + +vec12: + /* error code */ + pushl $12 /* vector */ + jmp int_hand + .word 0x9090 + +vec13: + /* error code */ + pushl $13 /* vector */ + jmp int_hand + .word 0x9090 + +vec14: + /* error code */ + pushl $14 /* vector */ + jmp int_hand + .word 0x9090 + +vec15: + pushl $0 /* error code */ + pushl $15 /* vector */ + jmp int_hand + +vec16: + pushl $0 /* error code */ + pushl $16 /* vector */ + jmp int_hand + +vec17: + /* error code */ + pushl $17 /* vector */ + jmp int_hand + .word 0x9090 + +vec18: + pushl $0 /* error code */ + pushl $18 /* vector */ + jmp int_hand + +vec19: + pushl $0 /* error code */ + pushl $19 /* vector */ + jmp int_hand + +int_hand: + /* At this point on the stack there is: + * 0(%esp) vector + * 4(%esp) error code + * 8(%esp) eip + * 12(%esp) cs + * 16(%esp) eflags + */ + pushl %edi + pushl %esi + pushl %ebp + /* Original stack pointer */ + leal 32(%esp), %ebp + pushl %ebp + pushl %ebx + pushl %edx + pushl %ecx + pushl %eax + + pushl %esp /* Pointer to structure on the stack */ + call x86_exception + pop %eax /* Drop the pointer */ + + popl %eax + popl %ecx + popl %edx + popl %ebx + popl %ebp /* Ignore saved %esp value */ + popl %ebp + popl %esi + popl %edi + + addl $8, %esp /* pop of the vector and error code */ + + iret + +#if CONFIG_GDB_STUB == 1 + + .globl gdb_stub_breakpoint +gdb_stub_breakpoint: + popl %eax /* Return address */ + pushfl + pushl %cs + pushl %eax /* Return address */ + pushl $0 /* No error code */ + pushl $32 /* vector 32 is user defined */ + jmp int_hand + +#endif + + .globl gdt, gdt_end, gdt_limit, idtarg + +gdt_limit = gdt_end - gdt - 1 /* compute the table limit */ +gdtaddr: + .word gdt_limit + .long gdt /* we know the offset */ + + .data + + /* This is the gdt for GCC part of coreboot. + * It is different from the gdt in ROMCC/ASM part of coreboot + * which is defined in entry32.inc + * + * When the machine is initially started, we use a very simple + * gdt from rom (that in entry32.inc) which only contains those + * entries we need for protected mode. + * + * When we're executing code from RAM, we want to do more complex + * stuff, like initializing PCI option roms in real mode, or doing + * a resume from a suspend to ram. + */ +gdt: + /* selgdt 0, unused */ + .word 0x0000, 0x0000 /* dummy */ + .byte 0x00, 0x00, 0x00, 0x00 + + /* selgdt 8, unused */ + .word 0x0000, 0x0000 /* dummy */ + .byte 0x00, 0x00, 0x00, 0x00 + + /* selgdt 0x10, flat code segment */ + .word 0xffff, 0x0000 + .byte 0x00, 0x9b, 0xcf, 0x00 /* G=1 and 0x0f, So we get 4Gbytes for limit */ + + /* selgdt 0x18, flat data segment */ + .word 0xffff, 0x0000 + .byte 0x00, 0x93, 0xcf, 0x00 + + /* selgdt 0x20, unused */ + .word 0x0000, 0x0000 /* dummy */ + .byte 0x00, 0x00, 0x00, 0x00 + + /* The next two entries are used for executing VGA option ROMs */ + + /* selgdt 0x28 16 bit 64k code at 0x00000000 */ + .word 0xffff, 0x0000 + .byte 0, 0x9a, 0, 0 + + /* selgdt 0x30 16 bit 64k data at 0x00000000 */ + .word 0xffff, 0x0000 + .byte 0, 0x92, 0, 0 + + /* The next two entries are used for ACPI S3 RESUME */ + + /* selgdt 0x38, flat data segment 16 bit */ + .word 0x0000, 0x0000 /* dummy */ + .byte 0x00, 0x93, 0x8f, 0x00 /* G=1 and 0x0f, So we get 4Gbytes for limit */ + + /* selgdt 0x40, flat code segment 16 bit */ + .word 0xffff, 0x0000 + .byte 0x00, 0x9b, 0x8f, 0x00 /* G=1 and 0x0f, So we get 4Gbytes for limit */ +gdt_end: + +idtarg: + .word _idt_end - _idt - 1 /* limit */ + .long _idt + .word 0 +_idt: + .fill 20, 8, 0 # idt is uninitialized +_idt_end: + + .previous +.code32 diff --git a/src/arch/x86/lib/cbfs_and_run.c b/src/arch/x86/lib/cbfs_and_run.c new file mode 100644 index 0000000000..5e3d8fe922 --- /dev/null +++ b/src/arch/x86/lib/cbfs_and_run.c @@ -0,0 +1,55 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2008-2009 coresystems GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <console/console.h> +#include <cbfs.h> +#include <arch/stages.h> + +void cbfs_and_run_core(const char *filename, unsigned ebp) +{ + u8 *dst; + + print_debug("Loading image.\n"); + dst = cbfs_load_stage(filename); + if ((void *)dst == (void *) -1) + die("FATAL: Essential component is missing.\n"); + + print_debug("Jumping to image.\n"); + __asm__ volatile ( + "movl %%eax, %%ebp\n" + "jmp *%%edi\n" + :: "a"(ebp), "D"(dst) + ); +} + +void __attribute__((regparm(0))) copy_and_run(unsigned cpu_reset) +{ + // FIXME fix input parameters instead normalizing them here. + if (cpu_reset == 1) cpu_reset = -1; + else cpu_reset = 0; + + cbfs_and_run_core(CONFIG_CBFS_PREFIX "/coreboot_ram", cpu_reset); +} + +#if CONFIG_AP_CODE_IN_CAR == 1 +void __attribute__((regparm(0))) copy_and_run_ap_code_in_car(unsigned ret_addr) +{ + cbfs_and_run_core(CONFIG_CBFS_PREFIX "/coreboot_ap", ret_addr); +} +#endif diff --git a/src/arch/x86/lib/cpu.c b/src/arch/x86/lib/cpu.c new file mode 100644 index 0000000000..3732ae296e --- /dev/null +++ b/src/arch/x86/lib/cpu.c @@ -0,0 +1,268 @@ +#include <console/console.h> +#include <cpu/cpu.h> +#include <arch/io.h> +#include <string.h> +#include <cpu/x86/mtrr.h> +#include <cpu/x86/msr.h> +#include <cpu/x86/lapic.h> +#include <arch/cpu.h> +#include <device/path.h> +#include <device/device.h> +#include <smp/spinlock.h> + +/* Standard macro to see if a specific flag is changeable */ +static inline int flag_is_changeable_p(uint32_t flag) +{ + uint32_t f1, f2; + + asm( + "pushfl\n\t" + "pushfl\n\t" + "popl %0\n\t" + "movl %0,%1\n\t" + "xorl %2,%0\n\t" + "pushl %0\n\t" + "popfl\n\t" + "pushfl\n\t" + "popl %0\n\t" + "popfl\n\t" + : "=&r" (f1), "=&r" (f2) + : "ir" (flag)); + return ((f1^f2) & flag) != 0; +} + + +/* Probe for the CPUID instruction */ +static int have_cpuid_p(void) +{ + return flag_is_changeable_p(X86_EFLAGS_ID); +} + +/* + * Cyrix CPUs without cpuid or with cpuid not yet enabled can be detected + * by the fact that they preserve the flags across the division of 5/2. + * PII and PPro exhibit this behavior too, but they have cpuid available. + */ + +/* + * Perform the Cyrix 5/2 test. A Cyrix won't change + * the flags, while other 486 chips will. + */ +static inline int test_cyrix_52div(void) +{ + unsigned int test; + + __asm__ __volatile__( + "sahf\n\t" /* clear flags (%eax = 0x0005) */ + "div %b2\n\t" /* divide 5 by 2 */ + "lahf" /* store flags into %ah */ + : "=a" (test) + : "0" (5), "q" (2) + : "cc"); + + /* AH is 0x02 on Cyrix after the divide.. */ + return (unsigned char) (test >> 8) == 0x02; +} + +/* + * Detect a NexGen CPU running without BIOS hypercode new enough + * to have CPUID. (Thanks to Herbert Oppmann) + */ + +static int deep_magic_nexgen_probe(void) +{ + int ret; + + __asm__ __volatile__ ( + " movw $0x5555, %%ax\n" + " xorw %%dx,%%dx\n" + " movw $2, %%cx\n" + " divw %%cx\n" + " movl $0, %%eax\n" + " jnz 1f\n" + " movl $1, %%eax\n" + "1:\n" + : "=a" (ret) : : "cx", "dx" ); + return ret; +} + +/* List of cpu vendor strings along with their normalized + * id values. + */ +static struct { + int vendor; + const char *name; +} x86_vendors[] = { + { X86_VENDOR_INTEL, "GenuineIntel", }, + { X86_VENDOR_CYRIX, "CyrixInstead", }, + { X86_VENDOR_AMD, "AuthenticAMD", }, + { X86_VENDOR_UMC, "UMC UMC UMC ", }, + { X86_VENDOR_NEXGEN, "NexGenDriven", }, + { X86_VENDOR_CENTAUR, "CentaurHauls", }, + { X86_VENDOR_RISE, "RiseRiseRise", }, + { X86_VENDOR_TRANSMETA, "GenuineTMx86", }, + { X86_VENDOR_TRANSMETA, "TransmetaCPU", }, + { X86_VENDOR_NSC, "Geode by NSC", }, + { X86_VENDOR_SIS, "SiS SiS SiS ", }, +}; + +static const char *x86_vendor_name[] = { + [X86_VENDOR_INTEL] = "Intel", + [X86_VENDOR_CYRIX] = "Cyrix", + [X86_VENDOR_AMD] = "AMD", + [X86_VENDOR_UMC] = "UMC", + [X86_VENDOR_NEXGEN] = "NexGen", + [X86_VENDOR_CENTAUR] = "Centaur", + [X86_VENDOR_RISE] = "Rise", + [X86_VENDOR_TRANSMETA] = "Transmeta", + [X86_VENDOR_NSC] = "NSC", + [X86_VENDOR_SIS] = "SiS", +}; + +static const char *cpu_vendor_name(int vendor) +{ + const char *name; + name = "<invalid cpu vendor>"; + if ((vendor < (ARRAY_SIZE(x86_vendor_name))) && + (x86_vendor_name[vendor] != 0)) + { + name = x86_vendor_name[vendor]; + } + return name; +} + +static void identify_cpu(struct device *cpu) +{ + char vendor_name[16]; + int i; + + vendor_name[0] = '\0'; /* Unset */ + + /* Find the id and vendor_name */ + if (!have_cpuid_p()) { + /* Its a 486 if we can modify the AC flag */ + if (flag_is_changeable_p(X86_EFLAGS_AC)) { + cpu->device = 0x00000400; /* 486 */ + } else { + cpu->device = 0x00000300; /* 386 */ + } + if ((cpu->device == 0x00000400) && test_cyrix_52div()) { + memcpy(vendor_name, "CyrixInstead", 13); + /* If we ever care we can enable cpuid here */ + } + /* Detect NexGen with old hypercode */ + else if (deep_magic_nexgen_probe()) { + memcpy(vendor_name, "NexGenDriven", 13); + } + } + if (have_cpuid_p()) { + int cpuid_level; + struct cpuid_result result; + result = cpuid(0x00000000); + cpuid_level = result.eax; + vendor_name[ 0] = (result.ebx >> 0) & 0xff; + vendor_name[ 1] = (result.ebx >> 8) & 0xff; + vendor_name[ 2] = (result.ebx >> 16) & 0xff; + vendor_name[ 3] = (result.ebx >> 24) & 0xff; + vendor_name[ 4] = (result.edx >> 0) & 0xff; + vendor_name[ 5] = (result.edx >> 8) & 0xff; + vendor_name[ 6] = (result.edx >> 16) & 0xff; + vendor_name[ 7] = (result.edx >> 24) & 0xff; + vendor_name[ 8] = (result.ecx >> 0) & 0xff; + vendor_name[ 9] = (result.ecx >> 8) & 0xff; + vendor_name[10] = (result.ecx >> 16) & 0xff; + vendor_name[11] = (result.ecx >> 24) & 0xff; + vendor_name[12] = '\0'; + + /* Intel-defined flags: level 0x00000001 */ + if (cpuid_level >= 0x00000001) { + cpu->device = cpuid_eax(0x00000001); + } + else { + /* Have CPUID level 0 only unheard of */ + cpu->device = 0x00000400; + } + } + cpu->vendor = X86_VENDOR_UNKNOWN; + for(i = 0; i < ARRAY_SIZE(x86_vendors); i++) { + if (memcmp(vendor_name, x86_vendors[i].name, 12) == 0) { + cpu->vendor = x86_vendors[i].vendor; + break; + } + } +} + +static void set_cpu_ops(struct device *cpu) +{ + struct cpu_driver *driver; + cpu->ops = 0; + for (driver = cpu_drivers; driver < ecpu_drivers; driver++) { + struct cpu_device_id *id; + for(id = driver->id_table; id->vendor != X86_VENDOR_INVALID; id++) { + if ((cpu->vendor == id->vendor) && + (cpu->device == id->device)) + { + goto found; + } + } + } + return; + found: + cpu->ops = driver->ops; +} + +void cpu_initialize(void) +{ + /* Because we busy wait at the printk spinlock. + * It is important to keep the number of printed messages + * from secondary cpus to a minimum, when debugging is + * disabled. + */ + struct device *cpu; + struct cpu_info *info; + struct cpuinfo_x86 c; + + info = cpu_info(); + + printk(BIOS_INFO, "Initializing CPU #%ld\n", info->index); + + cpu = info->cpu; + if (!cpu) { + die("CPU: missing cpu device structure"); + } + + /* Find what type of cpu we are dealing with */ + identify_cpu(cpu); + printk(BIOS_DEBUG, "CPU: vendor %s device %x\n", + cpu_vendor_name(cpu->vendor), cpu->device); + + get_fms(&c, cpu->device); + + printk(BIOS_DEBUG, "CPU: family %02x, model %02x, stepping %02x\n", + c.x86, c.x86_model, c.x86_mask); + + /* Lookup the cpu's operations */ + set_cpu_ops(cpu); + + if(!cpu->ops) { + /* mask out the stepping and try again */ + cpu->device -= c.x86_mask; + set_cpu_ops(cpu); + cpu->device += c.x86_mask; + if(!cpu->ops) die("Unknown cpu"); + printk(BIOS_DEBUG, "Using generic cpu ops (good)\n"); + } + + + /* Initialize the cpu */ + if (cpu->ops && cpu->ops->init) { + cpu->enabled = 1; + cpu->initialized = 1; + cpu->ops->init(cpu); + } + + printk(BIOS_INFO, "CPU #%ld initialized\n", info->index); + + return; +} + diff --git a/src/arch/x86/lib/exception.c b/src/arch/x86/lib/exception.c new file mode 100644 index 0000000000..20917b6f40 --- /dev/null +++ b/src/arch/x86/lib/exception.c @@ -0,0 +1,489 @@ +#include <console/console.h> +#include <string.h> + +#if defined(CONFIG_GDB_STUB) && CONFIG_GDB_STUB == 1 + +/* BUFMAX defines the maximum number of characters in inbound/outbound buffers. + * At least NUM_REGBYTES*2 are needed for register packets + */ +#define BUFMAX 400 +enum regnames { + EAX = 0, ECX, EDX, EBX, ESP, EBP, ESI, EDI, + PC /* also known as eip */, + PS /* also known as eflags */, + CS, SS, DS, ES, FS, GS, + NUM_REGS /* Number of registers. */ +}; + +static uint32_t gdb_stub_registers[NUM_REGS]; + +#define GDB_SIG0 0 /* Signal 0 */ +#define GDB_SIGHUP 1 /* Hangup */ +#define GDB_SIGINT 2 /* Interrupt */ +#define GDB_SIGQUIT 3 /* Quit */ +#define GDB_SIGILL 4 /* Illegal instruction */ +#define GDB_SIGTRAP 5 /* Trace/breakpoint trap */ +#define GDB_SIGABRT 6 /* Aborted */ +#define GDB_SIGEMT 7 /* Emulation trap */ +#define GDB_SIGFPE 8 /* Arithmetic exception */ +#define GDB_SIGKILL 9 /* Killed */ +#define GDB_SIGBUS 10 /* Bus error */ +#define GDB_SIGSEGV 11 /* Segmentation fault */ +#define GDB_SIGSYS 12 /* Bad system call */ +#define GDB_SIGPIPE 13 /* Broken pipe */ +#define GDB_SIGALRM 14 /* Alarm clock */ +#define GDB_SIGTERM 15 /* Terminated */ +#define GDB_SIGURG 16 /* Urgent I/O condition */ +#define GDB_SIGSTOP 17 /* Stopped (signal) */ +#define GDB_SIGTSTP 18 /* Stopped (user) */ +#define GDB_SIGCONT 19 /* Continued */ +#define GDB_SIGCHLD 20 /* Child status changed */ +#define GDB_SIGTTIN 21 /* Stopped (tty input) */ +#define GDB_SIGTTOU 22 /* Stopped (tty output) */ +#define GDB_SIGIO 23 /* I/O possible */ +#define GDB_SIGXCPU 24 /* CPU time limit exceeded */ +#define GDB_SIGXFSZ 25 /* File size limit exceeded */ +#define GDB_SIGVTALRM 26 /* Virtual timer expired */ +#define GDB_SIGPROF 27 /* Profiling timer expired */ +#define GDB_SIGWINCH 28 /* Window size changed */ +#define GDB_SIGLOST 29 /* Resource lost */ +#define GDB_SIGUSR1 30 /* User defined signal 1 */ +#define GDB_SUGUSR2 31 /* User defined signal 2 */ +#define GDB_SIGPWR 32 /* Power fail/restart */ +#define GDB_SIGPOLL 33 /* Pollable event occurred */ +#define GDB_SIGWIND 34 /* SIGWIND */ +#define GDB_SIGPHONE 35 /* SIGPHONE */ +#define GDB_SIGWAITING 36 /* Process's LWPs are blocked */ +#define GDB_SIGLWP 37 /* Signal LWP */ +#define GDB_SIGDANGER 38 /* Swap space dangerously low */ +#define GDB_SIGGRANT 39 /* Monitor mode granted */ +#define GDB_SIGRETRACT 40 /* Need to relinquish monitor mode */ +#define GDB_SIGMSG 41 /* Monitor mode data available */ +#define GDB_SIGSOUND 42 /* Sound completed */ +#define GDB_SIGSAK 43 /* Secure attention */ +#define GDB_SIGPRIO 44 /* SIGPRIO */ + +#define GDB_SIG33 45 /* Real-time event 33 */ +#define GDB_SIG34 46 /* Real-time event 34 */ +#define GDB_SIG35 47 /* Real-time event 35 */ +#define GDB_SIG36 48 /* Real-time event 36 */ +#define GDB_SIG37 49 /* Real-time event 37 */ +#define GDB_SIG38 50 /* Real-time event 38 */ +#define GDB_SIG39 51 /* Real-time event 39 */ +#define GDB_SIG40 52 /* Real-time event 40 */ +#define GDB_SIG41 53 /* Real-time event 41 */ +#define GDB_SIG42 54 /* Real-time event 42 */ +#define GDB_SIG43 55 /* Real-time event 43 */ +#define GDB_SIG44 56 /* Real-time event 44 */ +#define GDB_SIG45 57 /* Real-time event 45 */ +#define GDB_SIG46 58 /* Real-time event 46 */ +#define GDB_SIG47 59 /* Real-time event 47 */ +#define GDB_SIG48 60 /* Real-time event 48 */ +#define GDB_SIG49 61 /* Real-time event 49 */ +#define GDB_SIG50 62 /* Real-time event 50 */ +#define GDB_SIG51 63 /* Real-time event 51 */ +#define GDB_SIG52 64 /* Real-time event 52 */ +#define GDB_SIG53 65 /* Real-time event 53 */ +#define GDB_SIG54 66 /* Real-time event 54 */ +#define GDB_SIG55 67 /* Real-time event 55 */ +#define GDB_SIG56 68 /* Real-time event 56 */ +#define GDB_SIG57 69 /* Real-time event 57 */ +#define GDB_SIG58 70 /* Real-time event 58 */ +#define GDB_SIG59 71 /* Real-time event 59 */ +#define GDB_SIG60 72 /* Real-time event 60 */ +#define GDB_SIG61 73 /* Real-time event 61 */ +#define GDB_SIG62 74 /* Real-time event 62 */ +#define GDB_SIG63 75 /* Real-time event 63 */ +#define GDB_SIGCANCEL 76 /* LWP internal signal */ +#define GDB_SIG32 77 /* Real-time event 32 */ +#define GDB_SIG64 78 /* Real-time event 64 */ +#define GDB_SIG65 79 /* Real-time event 65 */ +#define GDB_SIG66 80 /* Real-time event 66 */ +#define GDB_SIG67 81 /* Real-time event 67 */ +#define GDB_SIG68 82 /* Real-time event 68 */ +#define GDB_SIG69 83 /* Real-time event 69 */ +#define GDB_SIG70 84 /* Real-time event 70 */ +#define GDB_SIG71 85 /* Real-time event 71 */ +#define GDB_SIG72 86 /* Real-time event 72 */ +#define GDB_SIG73 87 /* Real-time event 73 */ +#define GDB_SIG74 88 /* Real-time event 74 */ +#define GDB_SIG75 89 /* Real-time event 75 */ +#define GDB_SIG76 90 /* Real-time event 76 */ +#define GDB_SIG77 91 /* Real-time event 77 */ +#define GDB_SIG78 92 /* Real-time event 78 */ +#define GDB_SIG79 93 /* Real-time event 79 */ +#define GDB_SIG80 94 /* Real-time event 80 */ +#define GDB_SIG81 95 /* Real-time event 81 */ +#define GDB_SIG82 96 /* Real-time event 82 */ +#define GDB_SIG83 97 /* Real-time event 83 */ +#define GDB_SIG84 98 /* Real-time event 84 */ +#define GDB_SIG85 99 /* Real-time event 85 */ +#define GDB_SIG86 100 /* Real-time event 86 */ +#define GDB_SIG87 101 /* Real-time event 87 */ +#define GDB_SIG88 102 /* Real-time event 88 */ +#define GDB_SIG89 103 /* Real-time event 89 */ +#define GDB_SIG90 104 /* Real-time event 90 */ +#define GDB_SIG91 105 /* Real-time event 91 */ +#define GDB_SIG92 106 /* Real-time event 92 */ +#define GDB_SIG93 107 /* Real-time event 93 */ +#define GDB_SIG94 108 /* Real-time event 94 */ +#define GDB_SIG95 109 /* Real-time event 95 */ +#define GDB_SIG96 110 /* Real-time event 96 */ +#define GDB_SIG97 111 /* Real-time event 97 */ +#define GDB_SIG98 112 /* Real-time event 98 */ +#define GDB_SIG99 113 /* Real-time event 99 */ +#define GDB_SIG100 114 /* Real-time event 100 */ +#define GDB_SIG101 115 /* Real-time event 101 */ +#define GDB_SIG102 116 /* Real-time event 102 */ +#define GDB_SIG103 117 /* Real-time event 103 */ +#define GDB_SIG104 118 /* Real-time event 104 */ +#define GDB_SIG105 119 /* Real-time event 105 */ +#define GDB_SIG106 120 /* Real-time event 106 */ +#define GDB_SIG107 121 /* Real-time event 107 */ +#define GDB_SIG108 122 /* Real-time event 108 */ +#define GDB_SIG109 123 /* Real-time event 109 */ +#define GDB_SIG110 124 /* Real-time event 110 */ +#define GDB_SIG111 125 /* Real-time event 111 */ +#define GDB_SIG112 126 /* Real-time event 112 */ +#define GDB_SIG113 127 /* Real-time event 113 */ +#define GDB_SIG114 128 /* Real-time event 114 */ +#define GDB_SIG115 129 /* Real-time event 115 */ +#define GDB_SIG116 130 /* Real-time event 116 */ +#define GDB_SIG117 131 /* Real-time event 117 */ +#define GDB_SIG118 132 /* Real-time event 118 */ +#define GDB_SIG119 133 /* Real-time event 119 */ +#define GDB_SIG120 134 /* Real-time event 120 */ +#define GDB_SIG121 135 /* Real-time event 121 */ +#define GDB_SIG122 136 /* Real-time event 122 */ +#define GDB_SIG123 137 /* Real-time event 123 */ +#define GDB_SIG124 138 /* Real-time event 124 */ +#define GDB_SIG125 139 /* Real-time event 125 */ +#define GDB_SIG126 140 /* Real-time event 126 */ +#define GDB_SIG127 141 /* Real-time event 127 */ +#define GDB_SIGINFO 142 /* Information request */ +#define GDB_UNKNOWN 143 /* Unknown signal */ +#define GDB_DEFAULT 144 /* error: default signal */ +/* Mach exceptions */ +#define GDB_EXC_BAD_ACCESS 145 /* Could not access memory */ +#define GDB_EXC_BAD_INSTRCTION 146 /* Illegal instruction/operand */ +#define GDB_EXC_ARITHMETIC 147 /* Arithmetic exception */ +#define GDB_EXC_EMULATION 148 /* Emulation instruction */ +#define GDB_EXC_SOFTWARE 149 /* Software generated exception */ +#define GDB_EXC_BREAKPOINT 150 /* Breakpoint */ + + + +static unsigned char exception_to_signal[] = +{ + [0] = GDB_SIGFPE, /* divide by zero */ + [1] = GDB_SIGTRAP, /* debug exception */ + [2] = GDB_SIGSEGV, /* NMI Interrupt */ + [3] = GDB_SIGTRAP, /* Breakpoint */ + [4] = GDB_SIGSEGV, /* into instruction (overflow) */ + [5] = GDB_SIGSEGV, /* bound instruction */ + [6] = GDB_SIGILL, /* Invalid opcode */ + [7] = GDB_SIGSEGV, /* coprocessor not available */ + [8] = GDB_SIGSEGV, /* double fault */ + [9] = GDB_SIGFPE, /* coprocessor segment overrun */ + [10] = GDB_SIGSEGV, /* Invalid TSS */ + [11] = GDB_SIGBUS, /* Segment not present */ + [12] = GDB_SIGBUS, /* stack exception */ + [13] = GDB_SIGSEGV, /* general protection */ + [14] = GDB_SIGSEGV, /* page fault */ + [15] = GDB_UNKNOWN, /* reserved */ + [16] = GDB_SIGEMT, /* coprocessor error */ + [17] = GDB_SIGBUS, /* alignment check */ + [18] = GDB_SIGSEGV, /* machine check */ + [19] = GDB_SIGFPE, /* simd floating point exception */ + [20] = GDB_UNKNOWN, + [21] = GDB_UNKNOWN, + [22] = GDB_UNKNOWN, + [23] = GDB_UNKNOWN, + [24] = GDB_UNKNOWN, + [25] = GDB_UNKNOWN, + [26] = GDB_UNKNOWN, + [27] = GDB_UNKNOWN, + [28] = GDB_UNKNOWN, + [29] = GDB_UNKNOWN, + [30] = GDB_UNKNOWN, + [31] = GDB_UNKNOWN, + [32] = GDB_SIGINT, /* User interrupt */ +}; + +static const char hexchars[] = "0123456789abcdef"; +static char in_buffer[BUFMAX]; +static char out_buffer[BUFMAX]; + + +static inline void stub_putc(int ch) +{ + console_tx_byte(ch); +} + +static inline int stub_getc(void) +{ + return console_rx_byte(); +} + +static int hex(char ch) +{ + if ((ch >= 'a') && (ch <= 'f')) + return (ch - 'a' + 10); + if ((ch >= '0') && (ch <= '9')) + return (ch - '0'); + if ((ch >= 'A') && (ch <= 'F')) + return (ch - 'A' + 10); + return (-1); +} + +/* + * While we find hexadecimal digits, build an int. + * Fals is returned if nothing is parsed true otherwise. + */ +static int parse_ulong(char **ptr, unsigned long *value) +{ + int digit; + char *start; + + start = *ptr; + *value = 0; + + while((digit = hex(**ptr)) >= 0) { + *value = ((*value) << 4) | digit; + (*ptr)++; + } + return start != *ptr; +} + +/* convert the memory pointed to by mem into hex, placing result in buf */ +/* return a pointer to the last char put in buf (null) */ +static void copy_to_hex(char *buf, void *addr, unsigned long count) +{ + unsigned char ch; + char *mem = addr; + + while(count--) { + ch = *mem++; + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch & 0x0f]; + } + *buf = 0; + return; +} + + +/* convert the hex array pointed to by buf into binary to be placed in mem */ +/* return a pointer to the character AFTER the last byte written */ +static void copy_from_hex(void *addr, char *buf, unsigned long count) +{ + unsigned char ch; + char *mem = addr; + + while(count--) { + ch = hex (*buf++) << 4; + ch = ch + hex (*buf++); + *mem++ = ch; + } +} + + +/* scan for the sequence $<data>#<checksum> */ + +static int get_packet(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int count; + char ch; + + /* Wishlit implement a timeout in get_packet */ + do { + /* wait around for the start character, ignore all other characters */ + while ((ch = (stub_getc() & 0x7f)) != '$'); + checksum = 0; + xmitcsum = -1; + + count = 0; + + /* now, read until a # or end of buffer is found */ + while (count < BUFMAX) { + ch = stub_getc() & 0x7f; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex(stub_getc() & 0x7f) << 4; + xmitcsum += hex(stub_getc() & 0x7f); + + if (checksum != xmitcsum) { + stub_putc('-'); /* failed checksum */ + } + else { + stub_putc('+'); /* successful transfer */ + } + } + } while(checksum != xmitcsum); + return 1; +} + +/* send the packet in buffer.*/ +static void put_packet(char *buffer) +{ + unsigned char checksum; + int count; + char ch; + + /* $<packet info>#<checksum>. */ + do { + stub_putc('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count])) { + stub_putc(ch); + checksum += ch; + count += 1; + } + + stub_putc('#'); + stub_putc(hexchars[checksum >> 4]); + stub_putc(hexchars[checksum % 16]); + + } while ((stub_getc() & 0x7f) != '+'); + +} +#endif /* CONFIG_GDB_STUB */ + +#include <arch/registers.h> + +void x86_exception(struct eregs *info); + +void x86_exception(struct eregs *info) +{ +#if CONFIG_GDB_STUB == 1 + int signo; + memcpy(gdb_stub_registers, info, 8*sizeof(uint32_t)); + gdb_stub_registers[PC] = info->eip; + gdb_stub_registers[CS] = info->cs; + gdb_stub_registers[PS] = info->eflags; + signo = GDB_UNKNOWN; + if (info->vector < ARRAY_SIZE(exception_to_signal)) { + signo = exception_to_signal[info->vector]; + } + + /* reply to the host that an exception has occured */ + out_buffer[0] = 'S'; + out_buffer[1] = hexchars[(signo>>4) & 0xf]; + out_buffer[2] = hexchars[signo & 0xf]; + out_buffer[3] = '\0'; + put_packet(out_buffer); + + while(1) { + unsigned long addr, length; + char *ptr; + out_buffer[0] = '\0'; + out_buffer[1] = '\0'; + if (!get_packet(in_buffer)) { + break; + } + switch(in_buffer[0]) { + case '?': /* last signal */ + out_buffer[0] = 'S'; + out_buffer[1] = hexchars[(signo >> 4) & 0xf]; + out_buffer[2] = hexchars[signo & 0xf]; + out_buffer[3] = '\0'; + break; + case 'g': /* return the value of the cpu registers */ + copy_to_hex(out_buffer, &gdb_stub_registers, sizeof(gdb_stub_registers)); + break; + case 'G': /* set the value of the CPU registers - return OK */ + copy_from_hex(&gdb_stub_registers, in_buffer + 1, sizeof(gdb_stub_registers)); + memcpy(info, gdb_stub_registers, 8*sizeof(uint32_t)); + info->eip = gdb_stub_registers[PC]; + info->cs = gdb_stub_registers[CS]; + info->eflags = gdb_stub_registers[PS]; + memcpy(out_buffer, "OK",3); + break; + case 'm': + /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + ptr = &in_buffer[1]; + if ( parse_ulong(&ptr, &addr) && + (*ptr++ == ',') && + parse_ulong(&ptr, &length)) { + copy_to_hex(out_buffer, (void *)addr, length); + } else { + memcpy(out_buffer, "E01", 4); + } + break; + case 'M': + /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ + ptr = &in_buffer[1]; + if ( parse_ulong(&ptr, &addr) && + (*(ptr++) == ',') && + parse_ulong(&ptr, &length) && + (*(ptr++) == ':')) { + copy_from_hex((void *)addr, ptr, length); + memcpy(out_buffer, "OK", 3); + } + else { + memcpy(out_buffer, "E02", 4); + } + break; + case 's': + case 'c': + /* cAA..AA Continue at address AA..AA(optional) */ + /* sAA..AA Step one instruction from AA..AA(optional) */ + ptr = &in_buffer[1]; + if (parse_ulong(&ptr, &addr)) { + info->eip = addr; + } + + /* Clear the trace bit */ + info->eflags &= ~(1 << 8); + /* Set the trace bit if we are single stepping */ + if (in_buffer[0] == 's') { + info->eflags |= (1 << 8); + } + return; + break; + case 'D': + memcpy(out_buffer, "OK", 3); + break; + case 'k': /* kill request? */ + break; + case 'q': /* query */ + break; + case 'z': /* z0AAAA,LLLL remove memory breakpoint */ + /* z1AAAA,LLLL remove hardware breakpoint */ + /* z2AAAA,LLLL remove write watchpoint */ + /* z3AAAA,LLLL remove read watchpoint */ + /* z4AAAA,LLLL remove access watchpoint */ + case 'Z': /* Z0AAAA,LLLL insert memory breakpoint */ + /* Z1AAAA,LLLL insert hardware breakpoint */ + /* Z2AAAA,LLLL insert write watchpoint */ + /* Z3AAAA,LLLL insert read watchpoint */ + /* Z4AAAA,LLLL insert access watchpoint */ + break; + default: + break; + } + put_packet(out_buffer); + } +#else /* !CONFIG_GDB_STUB */ + printk(BIOS_EMERG, + "Unexpected Exception: %d @ %02x:%08x - Halting\n" + "Code: %d eflags: %08x\n" + "eax: %08x ebx: %08x ecx: %08x edx: %08x\n" + "edi: %08x esi: %08x ebp: %08x esp: %08x\n", + info->vector, info->cs, info->eip, + info->error_code, info->eflags, + info->eax, info->ebx, info->ecx, info->edx, + info->edi, info->esi, info->ebp, info->esp); + die(""); +#endif +} diff --git a/src/arch/x86/lib/id.inc b/src/arch/x86/lib/id.inc new file mode 100644 index 0000000000..443dbad38a --- /dev/null +++ b/src/arch/x86/lib/id.inc @@ -0,0 +1,15 @@ + .section ".id", "a", @progbits + + .globl __id_start +__id_start: +vendor: + .asciz CONFIG_MAINBOARD_VENDOR +part: + .asciz CONFIG_MAINBOARD_PART_NUMBER +.long __id_end + CONFIG_ID_SECTION_OFFSET - vendor /* Reverse offset to the vendor id */ +.long __id_end + CONFIG_ID_SECTION_OFFSET - part /* Reverse offset to the part number */ +.long CONFIG_ROM_SIZE /* Size of this romimage */ + .globl __id_end + +__id_end: +.previous diff --git a/src/arch/x86/lib/id.lds b/src/arch/x86/lib/id.lds new file mode 100644 index 0000000000..d646270daf --- /dev/null +++ b/src/arch/x86/lib/id.lds @@ -0,0 +1,6 @@ +SECTIONS { + . = (CONFIG_ROMBASE + CONFIG_ROM_IMAGE_SIZE - CONFIG_ID_SECTION_OFFSET) - (__id_end - __id_start); + .id (.): { + *(.id) + } +} diff --git a/src/arch/x86/lib/ioapic.c b/src/arch/x86/lib/ioapic.c new file mode 100644 index 0000000000..e39fe8fd0a --- /dev/null +++ b/src/arch/x86/lib/ioapic.c @@ -0,0 +1,139 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2010 coresystems GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <arch/io.h> +#include <arch/ioapic.h> +#include <console/console.h> +#include <cpu/x86/lapic.h> + +static u32 io_apic_read(u32 ioapic_base, u32 reg) +{ + write32(ioapic_base, reg); + return read32(ioapic_base + 0x10); +} + +static void io_apic_write(u32 ioapic_base, u32 reg, u32 value) +{ + write32(ioapic_base, reg); + write32(ioapic_base + 0x10, value); +} + +void clear_ioapic(u32 ioapic_base) +{ + u32 low, high; + u32 i, ioapic_interrupts; + + printk(BIOS_DEBUG, "IOAPIC: Clearing IOAPIC at 0x%08x\n", ioapic_base); + + /* Read the available number of interrupts. */ + ioapic_interrupts = (io_apic_read(ioapic_base, 0x01) >> 16) & 0xff; + if (!ioapic_interrupts || ioapic_interrupts == 0xff) + ioapic_interrupts = 24; + printk(BIOS_DEBUG, "IOAPIC: %d interrupts\n", ioapic_interrupts); + + low = DISABLED; + high = NONE; + + for (i = 0; i < ioapic_interrupts; i++) { + io_apic_write(ioapic_base, i * 2 + 0x10, low); + io_apic_write(ioapic_base, i * 2 + 0x11, high); + + printk(BIOS_SPEW, "IOAPIC: reg 0x%08x value 0x%08x 0x%08x\n", + i, high, low); + } + + if (io_apic_read(ioapic_base, 0x10) == 0xffffffff) { + printk(BIOS_WARNING, "IOAPIC not responding.\n"); + return; + } +} + +void setup_ioapic(u32 ioapic_base, u8 ioapic_id) +{ + u32 bsp_lapicid = lapicid(); + u32 low, high; + u32 i, ioapic_interrupts; + + printk(BIOS_DEBUG, "IOAPIC: Initializing IOAPIC at 0x%08x\n", + ioapic_base); + printk(BIOS_DEBUG, "IOAPIC: Bootstrap Processor Local APIC = 0x%02x\n", + bsp_lapicid); + + if (ioapic_id) { + printk(BIOS_DEBUG, "IOAPIC: ID = 0x%02x\n", ioapic_id); + /* Set IOAPIC ID if it has been specified. */ + io_apic_write(ioapic_base, 0x00, + (io_apic_read(ioapic_base, 0x00) & 0xfff0ffff) | + (ioapic_id << 24)); + } + + /* Read the available number of interrupts. */ + ioapic_interrupts = (io_apic_read(ioapic_base, 0x01) >> 16) & 0xff; + if (!ioapic_interrupts || ioapic_interrupts == 0xff) + ioapic_interrupts = 24; + printk(BIOS_DEBUG, "IOAPIC: %d interrupts\n", ioapic_interrupts); + +// XXX this decision should probably be made elsewhere, and +// it's the C3, not the EPIA this depends on. +#if defined(CONFIG_EPIA_VT8237R_INIT) && CONFIG_EPIA_VT8237R_INIT +#define IOAPIC_INTERRUPTS_ON_APIC_SERIAL_BUS +#else +#define IOAPIC_INTERRUPTS_ON_FSB +#endif + +#ifdef IOAPIC_INTERRUPTS_ON_FSB + /* + * For the Pentium 4 and above APICs deliver their interrupts + * on the front side bus, enable that. + */ + printk(BIOS_DEBUG, "IOAPIC: Enabling interrupts on FSB\n"); + io_apic_write(ioapic_base, 0x03, + io_apic_read(ioapic_base, 0x03) | (1 << 0)); +#endif +#ifdef IOAPIC_INTERRUPTS_ON_APIC_SERIAL_BUS + printk(BIOS_DEBUG, "IOAPIC: Enabling interrupts on APIC serial bus\n"); + io_apic_write(ioapic_base, 0x03, 0); +#endif + + /* Enable Virtual Wire Mode. */ + low = ENABLED | TRIGGER_EDGE | POLARITY_HIGH | PHYSICAL_DEST | ExtINT; + high = bsp_lapicid << (56 - 32); + + io_apic_write(ioapic_base, 0x10, low); + io_apic_write(ioapic_base, 0x11, high); + + if (io_apic_read(ioapic_base, 0x10) == 0xffffffff) { + printk(BIOS_WARNING, "IOAPIC not responding.\n"); + return; + } + + printk(BIOS_SPEW, "IOAPIC: reg 0x%08x value 0x%08x 0x%08x\n", + 0, high, low); + + low = DISABLED; + high = NONE; + + for (i = 1; i < ioapic_interrupts; i++) { + io_apic_write(ioapic_base, i * 2 + 0x10, low); + io_apic_write(ioapic_base, i * 2 + 0x11, high); + + printk(BIOS_SPEW, "IOAPIC: reg 0x%08x value 0x%08x 0x%08x\n", + i, high, low); + } +} diff --git a/src/arch/x86/lib/pci_ops_auto.c b/src/arch/x86/lib/pci_ops_auto.c new file mode 100644 index 0000000000..92eedd30fb --- /dev/null +++ b/src/arch/x86/lib/pci_ops_auto.c @@ -0,0 +1,100 @@ +#include <stddef.h> +#include <console/console.h> +#include <arch/io.h> +#include <arch/pciconf.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <device/pci_ops.h> + +/* + * Before we decide to use direct hardware access mechanisms, we try to do some + * trivial checks to ensure it at least _seems_ to be working -- we just test + * whether bus 00 contains a host bridge (this is similar to checking + * techniques used in XFree86, but ours should be more reliable since we + * attempt to make use of direct access hints provided by the PCI BIOS). + * + * This should be close to trivial, but it isn't, because there are buggy + * chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID. + */ +static int pci_sanity_check(const struct pci_bus_operations *o) +{ + uint16_t class, vendor; + unsigned bus; + int devfn; + struct bus pbus; /* Dummy device */ +#define PCI_CLASS_BRIDGE_HOST 0x0600 +#define PCI_CLASS_DISPLAY_VGA 0x0300 +#define PCI_VENDOR_ID_COMPAQ 0x0e11 +#define PCI_VENDOR_ID_INTEL 0x8086 +#define PCI_VENDOR_ID_MOTOROLA 0x1057 + + for (bus = 0, devfn = 0; devfn < 0x100; devfn++) { + class = o->read16(&pbus, bus, devfn, PCI_CLASS_DEVICE); + vendor = o->read16(&pbus, bus, devfn, PCI_VENDOR_ID); + if (((class == PCI_CLASS_BRIDGE_HOST) || (class == PCI_CLASS_DISPLAY_VGA)) || + ((vendor == PCI_VENDOR_ID_INTEL) || (vendor == PCI_VENDOR_ID_COMPAQ) || + (vendor == PCI_VENDOR_ID_MOTOROLA))) { + return 1; + } + } + printk(BIOS_ERR, "PCI: Sanity check failed\n"); + return 0; +} + +struct pci_bus_operations *pci_bus_fallback_ops = NULL; + +static const struct pci_bus_operations *pci_check_direct(void) +{ + unsigned int tmp; + + /* + * Check if configuration type 1 works. + */ + { + outb(0x01, 0xCFB); + tmp = inl(0xCF8); + outl(0x80000000, 0xCF8); + if ((inl(0xCF8) == 0x80000000) && + pci_sanity_check(&pci_cf8_conf1)) + { + outl(tmp, 0xCF8); + printk(BIOS_DEBUG, "PCI: Using configuration type 1\n"); + return &pci_cf8_conf1; + } + outl(tmp, 0xCF8); + } + + /* + * Check if configuration type 2 works. + */ + { + outb(0x00, 0xCFB); + outb(0x00, 0xCF8); + outb(0x00, 0xCFA); + if ((inb(0xCF8) == 0x00 && inb(0xCFA) == 0x00) && + pci_sanity_check(&pci_cf8_conf2)) + { + printk(BIOS_DEBUG, "PCI: Using configuration type 2\n"); + return &pci_cf8_conf2; + } + } + + die("pci_check_direct failed\n"); + return NULL; +} + +const struct pci_bus_operations *pci_remember_direct(void) +{ + if (!pci_bus_fallback_ops) + pci_bus_fallback_ops = (struct pci_bus_operations *)pci_check_direct(); + return pci_bus_fallback_ops; +} + +/** Set the method to be used for PCI, type I or type II + */ +void pci_set_method(device_t dev) +{ + printk(BIOS_INFO, "Finding PCI configuration type.\n"); + dev->ops->ops_pci_bus = pci_remember_direct(); + post_code(0x5f); +} diff --git a/src/arch/x86/lib/pci_ops_conf1.c b/src/arch/x86/lib/pci_ops_conf1.c new file mode 100644 index 0000000000..36db54c21b --- /dev/null +++ b/src/arch/x86/lib/pci_ops_conf1.c @@ -0,0 +1,63 @@ +#include <console/console.h> +#include <arch/io.h> +#include <arch/pciconf.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <device/pci_ops.h> +/* + * Functions for accessing PCI configuration space with type 1 accesses + */ + +#if CONFIG_PCI_IO_CFG_EXT == 0 +#define CONFIG_CMD(bus,devfn, where) (0x80000000 | (bus << 16) | (devfn << 8) | (where & ~3)) +#else +#define CONFIG_CMD(bus,devfn, where) (0x80000000 | (bus << 16) | (devfn << 8) | ((where & 0xff) & ~3) | ((where & 0xf00)<<16) ) +#endif + +static uint8_t pci_conf1_read_config8(struct bus *pbus, int bus, int devfn, int where) +{ + outl(CONFIG_CMD(bus, devfn, where), 0xCF8); + return inb(0xCFC + (where & 3)); +} + +static uint16_t pci_conf1_read_config16(struct bus *pbus, int bus, int devfn, int where) +{ + outl(CONFIG_CMD(bus, devfn, where), 0xCF8); + return inw(0xCFC + (where & 2)); +} + +static uint32_t pci_conf1_read_config32(struct bus *pbus, int bus, int devfn, int where) +{ + outl(CONFIG_CMD(bus, devfn, where), 0xCF8); + return inl(0xCFC); +} + +static void pci_conf1_write_config8(struct bus *pbus, int bus, int devfn, int where, uint8_t value) +{ + outl(CONFIG_CMD(bus, devfn, where), 0xCF8); + outb(value, 0xCFC + (where & 3)); +} + +static void pci_conf1_write_config16(struct bus *pbus, int bus, int devfn, int where, uint16_t value) +{ + outl(CONFIG_CMD(bus, devfn, where), 0xCF8); + outw(value, 0xCFC + (where & 2)); +} + +static void pci_conf1_write_config32(struct bus *pbus, int bus, int devfn, int where, uint32_t value) +{ + outl(CONFIG_CMD(bus, devfn, where), 0xCF8); + outl(value, 0xCFC); +} + +#undef CONFIG_CMD + +const struct pci_bus_operations pci_cf8_conf1 = +{ + .read8 = pci_conf1_read_config8, + .read16 = pci_conf1_read_config16, + .read32 = pci_conf1_read_config32, + .write8 = pci_conf1_write_config8, + .write16 = pci_conf1_write_config16, + .write32 = pci_conf1_write_config32, +}; diff --git a/src/arch/x86/lib/pci_ops_conf2.c b/src/arch/x86/lib/pci_ops_conf2.c new file mode 100644 index 0000000000..839f5b44c7 --- /dev/null +++ b/src/arch/x86/lib/pci_ops_conf2.c @@ -0,0 +1,76 @@ +#include <console/console.h> +#include <arch/io.h> +#include <arch/pciconf.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <device/pci_ops.h> +/* + * Functions for accessing PCI configuration space with type 2 accesses + */ + +#define IOADDR(devfn, where) ((0xC000 | ((devfn & 0x78) << 5)) + where) +#define FUNC(devfn) (((devfn & 7) << 1) | 0xf0) +#define SET(bus,devfn) outb(FUNC(devfn), 0xCF8); outb(bus, 0xCFA); + +static uint8_t pci_conf2_read_config8(struct bus *pbus, int bus, int devfn, int where) +{ + uint8_t value; + SET(bus, devfn); + value = inb(IOADDR(devfn, where)); + outb(0, 0xCF8); + return value; +} + +static uint16_t pci_conf2_read_config16(struct bus *pbus, int bus, int devfn, int where) +{ + uint16_t value; + SET(bus, devfn); + value = inw(IOADDR(devfn, where)); + outb(0, 0xCF8); + return value; +} + +static uint32_t pci_conf2_read_config32(struct bus *pbus, int bus, int devfn, int where) +{ + uint32_t value; + SET(bus, devfn); + value = inl(IOADDR(devfn, where)); + outb(0, 0xCF8); + return value; +} + +static void pci_conf2_write_config8(struct bus *pbus, int bus, int devfn, int where, uint8_t value) +{ + SET(bus, devfn); + outb(value, IOADDR(devfn, where)); + outb(0, 0xCF8); +} + +static void pci_conf2_write_config16(struct bus *pbus, int bus, int devfn, int where, uint16_t value) +{ + SET(bus, devfn); + outw(value, IOADDR(devfn, where)); + outb(0, 0xCF8); +} + +static void pci_conf2_write_config32(struct bus *pbus, int bus, int devfn, int where, uint32_t value) +{ + SET(bus, devfn); + outl(value, IOADDR(devfn, where)); + outb(0, 0xCF8); +} + +#undef SET +#undef IOADDR +#undef FUNC + +const struct pci_bus_operations pci_cf8_conf2 = +{ + .read8 = pci_conf2_read_config8, + .read16 = pci_conf2_read_config16, + .read32 = pci_conf2_read_config32, + .write8 = pci_conf2_write_config8, + .write16 = pci_conf2_write_config16, + .write32 = pci_conf2_write_config32, +}; + diff --git a/src/arch/x86/lib/pci_ops_mmconf.c b/src/arch/x86/lib/pci_ops_mmconf.c new file mode 100644 index 0000000000..7d8fb329d0 --- /dev/null +++ b/src/arch/x86/lib/pci_ops_mmconf.c @@ -0,0 +1,64 @@ +#if CONFIG_MMCONF_SUPPORT + +#include <console/console.h> +#include <arch/io.h> +#include <arch/pciconf.h> +#include <device/pci.h> +#include <device/pci_ids.h> +#include <device/pci_ops.h> + + +/* + * Functions for accessing PCI configuration space with mmconf accesses + */ + +#define PCI_MMIO_ADDR(SEGBUS, DEVFN, WHERE) ( \ + CONFIG_MMCONF_BASE_ADDRESS | \ + (((SEGBUS) & 0xFFF) << 20) | \ + (((DEVFN) & 0xFF) << 12) | \ + ((WHERE) & 0xFFF)) + +#include <arch/mmio_conf.h> + +static uint8_t pci_mmconf_read_config8(struct bus *pbus, int bus, int devfn, int where) +{ + return (read8x(PCI_MMIO_ADDR(bus, devfn, where))); +} + +static uint16_t pci_mmconf_read_config16(struct bus *pbus, int bus, int devfn, int where) +{ + return (read16x(PCI_MMIO_ADDR(bus, devfn, where) & ~1)); +} + +static uint32_t pci_mmconf_read_config32(struct bus *pbus, int bus, int devfn, int where) +{ + return (read32x(PCI_MMIO_ADDR(bus, devfn, where) & ~3)); +} + +static void pci_mmconf_write_config8(struct bus *pbus, int bus, int devfn, int where, uint8_t value) +{ + write8x(PCI_MMIO_ADDR(bus, devfn, where), value); +} + +static void pci_mmconf_write_config16(struct bus *pbus, int bus, int devfn, int where, uint16_t value) +{ + write16x(PCI_MMIO_ADDR(bus, devfn, where) & ~1, value); +} + +static void pci_mmconf_write_config32(struct bus *pbus, int bus, int devfn, int where, uint32_t value) +{ + write32x(PCI_MMIO_ADDR(bus, devfn, where) & ~3, value); +} + + +const struct pci_bus_operations pci_ops_mmconf = +{ + .read8 = pci_mmconf_read_config8, + .read16 = pci_mmconf_read_config16, + .read32 = pci_mmconf_read_config32, + .write8 = pci_mmconf_write_config8, + .write16 = pci_mmconf_write_config16, + .write32 = pci_mmconf_write_config32, +}; + +#endif diff --git a/src/arch/x86/lib/printk_init.c b/src/arch/x86/lib/printk_init.c new file mode 100644 index 0000000000..2f7f751832 --- /dev/null +++ b/src/arch/x86/lib/printk_init.c @@ -0,0 +1,57 @@ +/* + * This file is part of the coreboot project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of + * the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <console/console.h> +#include <console/vtxprintf.h> +#include <uart8250.h> + +#if CONFIG_CONSOLE_NE2K +#include <console/ne2k.h> +#endif + +static void console_tx_byte(unsigned char byte) +{ +#if CONFIG_CONSOLE_NE2K +#ifdef __PRE_RAM__ + ne2k_append_data(&byte, 1, CONFIG_CONSOLE_NE2K_IO_PORT); +#endif +#endif + if (byte == '\n') + uart8250_tx_byte(CONFIG_TTYS0_BASE, '\r'); + + uart8250_tx_byte(CONFIG_TTYS0_BASE, byte); +} + +int do_printk(int msg_level, const char *fmt, ...) +{ + va_list args; + int i; + + if (msg_level > console_loglevel) { + return 0; + } + + va_start(args, fmt); + i = vtxprintf(console_tx_byte, fmt, args); + va_end(args); +#if CONFIG_CONSOLE_NE2K + ne2k_transmit(CONFIG_CONSOLE_NE2K_IO_PORT); +#endif + return i; +} diff --git a/src/arch/x86/lib/stages.c b/src/arch/x86/lib/stages.c new file mode 100644 index 0000000000..a6a232a04a --- /dev/null +++ b/src/arch/x86/lib/stages.c @@ -0,0 +1,28 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2010 coresystems GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +static void skip_romstage(void) +{ + asm volatile ( + "/* set the boot_complete flag */\n" + "movl $0xffffffff, %%ebp\n" + "jmp __main\n" + ); +} + diff --git a/src/arch/x86/lib/walkcbfs.S b/src/arch/x86/lib/walkcbfs.S new file mode 100644 index 0000000000..395c46e20c --- /dev/null +++ b/src/arch/x86/lib/walkcbfs.S @@ -0,0 +1,126 @@ +#define CBFS_HEADER_PTR 0xfffffffc + +#define CBFS_HEADER_MAGIC 0 +#define CBFS_HEADER_VERSION (CBFS_HEADER_MAGIC + 4) +#define CBFS_HEADER_ROMSIZE (CBFS_HEADER_VERSION + 4) +#define CBFS_HEADER_BOOTBLOCKSIZE (CBFS_HEADER_ROMSIZE + 4) +#define CBFS_HEADER_ALIGN (CBFS_HEADER_BOOTBLOCKSIZE + 4) +#define CBFS_HEADER_OFFSET (CBFS_HEADER_ALIGN + 4) + +#define CBFS_FILE_MAGIC 0 +#define CBFS_FILE_LEN (CBFS_FILE_MAGIC + 8) +#define CBFS_FILE_TYPE (CBFS_FILE_LEN + 4) +#define CBFS_FILE_CHECKSUM (CBFS_FILE_TYPE + 4) +#define CBFS_FILE_OFFSET (CBFS_FILE_CHECKSUM + 4) + +#define CBFS_FILE_STRUCTSIZE (CBFS_FILE_OFFSET + 4) + +#define CBFS_STAGE_COMPRESSION 0 +#define CBFS_STAGE_ENTRY (CBFS_STAGE_COMPRESSION + 4) +#define CBFS_STAGE_LOAD (CBFS_STAGE_ENTRY + 8) +#define CBFS_STAGE_LEN (CBFS_STAGE_LOAD + 8) +#define CBFS_STAGE_MEMLEN (CBFS_STAGE_LEN + 4) + +/* + input %esi: filename + input %esp: return address (not pointer to return address!) + output %eax: entry point + clobbers %ebx, %ecx, %edi +*/ +walkcbfs: + cld + + mov CBFS_HEADER_PTR, %eax + mov CBFS_HEADER_ROMSIZE(%eax), %ecx + bswap %ecx + mov $0, %ebx + sub %ecx, %ebx + mov CBFS_HEADER_OFFSET(%eax), %ecx + bswap %ecx + add %ecx, %ebx + + /* determine filename length */ + mov $0, %eax +1: + cmpb $0, (%eax,%esi) + jz 2f + add $1, %eax + jmp 1b +2: + add $1, %eax +walker: + mov 0(%ebx), %edi + cmp %edi, filemagic + jne searchfile + mov 4(%ebx), %edi + cmp %edi, filemagic+4 + jne searchfile + + mov %ebx, %edi + add $CBFS_FILE_STRUCTSIZE, %edi /* edi = address of first byte after struct cbfs_file */ + mov %eax, %ecx + repe cmpsb + # zero flag set if strings are equal + jnz tryharder + + # we found it! + mov CBFS_FILE_OFFSET(%ebx), %eax + bswap %eax + add %ebx, %eax + add $CBFS_STAGE_ENTRY, %eax /* eax = ((cbfs_stage* (cbfs_file* ebx)->offset)->entry) */ + mov 0(%eax), %eax + jmp *%esp + +tryharder: + sub %ebx, %edi + sub $CBFS_FILE_STRUCTSIZE, %edi /* edi = # of walked bytes */ + sub %edi, %esi /* esi = start of filename */ + + /* ebx = ecx = (current+offset+len+ALIGN-1) & ~(ALIGN-1) */ + mov CBFS_FILE_OFFSET(%ebx), %ecx + bswap %ecx + add %ebx, %ecx + mov CBFS_FILE_LEN(%ebx), %edi + bswap %edi + add %edi, %ecx + mov CBFS_HEADER_PTR, %edi + mov CBFS_HEADER_ALIGN(%edi), %edi + bswap %edi + sub $1, %edi + add %edi, %ecx + not %edi + and %edi, %ecx + + /* if oldaddr >= addr, leave */ + cmp %ebx, %ecx + jbe out + + mov %ecx, %ebx + +check_for_exit: + /* look if we should exit: did we pass into the bootblock already? */ + mov CBFS_HEADER_PTR, %ecx + mov CBFS_HEADER_BOOTBLOCKSIZE(%ecx), %ecx + bswap %ecx + not %ecx + add $1, %ecx + + cmp %ecx, %ebx + /* if bootblockstart >= addr (==we're still in the data area) , jump back */ + jbe walker + +out: + mov $0, %eax + jmp *%esp + + +searchfile: + /* if filemagic isn't found, move forward cbfs_header->align bytes */ + mov CBFS_HEADER_PTR, %edi + mov CBFS_HEADER_ALIGN(%edi), %edi + bswap %edi + add %edi, %ebx + jmp check_for_exit + +filemagic: + .ascii "LARCHIVE" |