x86: add thread support

Thread support is added for the x86 architecture. Both
the local apic and the tsc udelay() functions have a
call to thread_yield_microseconds() so as to provide an
opportunity to run pending threads.

Change-Id: Ie39b9eb565eb189676c06645bdf2a8720fe0636a
Signed-off-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-on: http://review.coreboot.org/3207
Tested-by: build bot (Jenkins)
Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
This commit is contained in:
Aaron Durbin 2013-05-06 12:22:23 -05:00 committed by Ronald G. Minnich
parent 4409a5eef6
commit 38c326d041
9 changed files with 140 additions and 1 deletions

View File

@ -326,7 +326,7 @@ config TIMER_QUEUE
config COOP_MULTITASKING config COOP_MULTITASKING
def_bool n def_bool n
depends on TIMER_QUEUE depends on TIMER_QUEUE && ARCH_X86
help help
Cooperative multitasking allows callbacks to be multiplexed on the Cooperative multitasking allows callbacks to be multiplexed on the
main thread of ramstage. With this enabled it allows for multiple main thread of ramstage. With this enabled it allows for multiple

View File

@ -9,6 +9,8 @@ ramstage-y += memset.c
ramstage-y += memcpy.c ramstage-y += memcpy.c
ramstage-y += ebda.c ramstage-y += ebda.c
ramstage-y += rom_media.c ramstage-y += rom_media.c
ramstage-$(CONFIG_COOP_MULTITASKING) += thread.c
ramstage-$(CONFIG_COOP_MULTITASKING) += thread_switch.S
romstage-$(CONFIG_EARLY_CONSOLE) += romstage_console.c romstage-$(CONFIG_EARLY_CONSOLE) += romstage_console.c
romstage-y += cbfs_and_run.c romstage-y += cbfs_and_run.c

View File

@ -10,6 +10,11 @@
_stack: _stack:
.space CONFIG_MAX_CPUS*CONFIG_STACK_SIZE .space CONFIG_MAX_CPUS*CONFIG_STACK_SIZE
_estack: _estack:
#if CONFIG_COOP_MULTITASKING
.global thread_stacks
thread_stacks:
.space CONFIG_STACK_SIZE*CONFIG_NUM_THREADS
#endif
.section ".textfirst", "ax", @progbits .section ".textfirst", "ax", @progbits
.code32 .code32
@ -45,6 +50,10 @@ _start:
/* set new stack */ /* set new stack */
movl $_estack, %esp movl $_estack, %esp
#if CONFIG_COOP_MULTITASKING
/* Push the thread pointer. */
pushl $0
#endif
/* Push the cpu index and struct cpu */ /* Push the cpu index and struct cpu */
pushl $0 pushl $0
pushl $0 pushl $0

58
src/arch/x86/lib/thread.c Normal file
View File

@ -0,0 +1,58 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2013 Google, Inc.
*
* 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 <thread.h>
/* The stack frame looks like the following after a pushad instruction. */
struct pushad_regs {
uint32_t edi; /* Offset 0x00 */
uint32_t esi; /* Offset 0x04 */
uint32_t ebp; /* Offset 0x08 */
uint32_t esp; /* Offset 0x0c */
uint32_t ebx; /* Offset 0x10 */
uint32_t edx; /* Offset 0x14 */
uint32_t ecx; /* Offset 0x18 */
uint32_t eax; /* Offset 0x1c */
};
static inline uintptr_t push_stack(uintptr_t cur_stack, uintptr_t value)
{
uintptr_t *addr;
cur_stack -= sizeof(value);
addr = (uintptr_t *)cur_stack;
*addr = value;
return cur_stack;
}
void arch_prepare_thread(struct thread *t,
void asmlinkage (*thread_entry)(void *), void *arg)
{
uintptr_t stack = t->stack_current;
/* Imitate thread_entry(t) with return address of 0. thread_entry()
* is assumed to never return. */
stack = push_stack(stack, (uintptr_t)arg);
stack = push_stack(stack, (uintptr_t)0);
stack = push_stack(stack, (uintptr_t)thread_entry);
/* Make room for the registers. Ignore intial values. */
stack -= sizeof(struct pushad_regs);
t->stack_current = stack;
}

View File

@ -0,0 +1,58 @@
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2013 Google, Inc.
*
* 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
*/
.code32
.text
/*
* stack layout after pushad:
* +------------+
* | save stack | <-- esp + 0x28
* +------------+
* | new stack | <-- esp + 0x24
* +------------+
* | ret addr | <-- esp + 0x20
* +------------+
* | eax | <-- esp + 0x1c
* +------------+
* | ecx | <-- esp + 0x18
* +------------+
* | edx | <-- esp + 0x14
* +------------+
* | ebx | <-- esp + 0x10
* +------------+
* | orig esp | <-- esp + 0x0c
* +------------+
* | ebp | <-- esp + 0x08
* +------------+
* | esi | <-- esp + 0x04
* +------------+
* | edi | <-- esp + 0x00
* +------------+
*/
.globl switch_to_thread
switch_to_thread:
pusha
/* Save the current stack */
movl 0x28(%esp), %ebx
movl %esp, (%ebx)
/* Switch to the new stack. */
movl 0x24(%esp), %eax
movl %eax, %esp
popa
ret

View File

@ -36,6 +36,7 @@
#include <lib.h> #include <lib.h>
#include <smp/atomic.h> #include <smp/atomic.h>
#include <smp/spinlock.h> #include <smp/spinlock.h>
#include <thread.h>
#include "haswell.h" #include "haswell.h"
/* This needs to match the layout in the .module_parametrs section. */ /* This needs to match the layout in the .module_parametrs section. */
@ -163,6 +164,7 @@ static void asmlinkage ap_init(unsigned int cpu, void *microcode_ptr)
info = cpu_info(); info = cpu_info();
info->index = cpu; info->index = cpu;
info->cpu = cpu_devs[cpu]; info->cpu = cpu_devs[cpu];
thread_init_cpu_info_non_bsp(info);
apic_id_table[info->index] = lapicid(); apic_id_table[info->index] = lapicid();
info->cpu->path.apic.apic_id = apic_id_table[info->index]; info->cpu->path.apic.apic_id = apic_id_table[info->index];

View File

@ -21,6 +21,7 @@
#include <stdint.h> #include <stdint.h>
#include <console/console.h> #include <console/console.h>
#include <delay.h> #include <delay.h>
#include <thread.h>
#include <arch/io.h> #include <arch/io.h>
#include <arch/cpu.h> #include <arch/cpu.h>
#include <cpu/x86/car.h> #include <cpu/x86/car.h>
@ -95,6 +96,9 @@ void udelay(u32 usecs)
{ {
u32 start, value, ticks; u32 start, value, ticks;
if (!thread_yield_microseconds(usecs))
return;
if (!timer_fsb || (lapic_read(LAPIC_LVTT) & if (!timer_fsb || (lapic_read(LAPIC_LVTT) &
(LAPIC_LVT_TIMER_PERIODIC | LAPIC_LVT_MASKED)) != (LAPIC_LVT_TIMER_PERIODIC | LAPIC_LVT_MASKED)) !=
(LAPIC_LVT_TIMER_PERIODIC | LAPIC_LVT_MASKED)) (LAPIC_LVT_TIMER_PERIODIC | LAPIC_LVT_MASKED))

View File

@ -32,6 +32,7 @@
#include <smp/spinlock.h> #include <smp/spinlock.h>
#include <cpu/cpu.h> #include <cpu/cpu.h>
#include <cpu/intel/speedstep.h> #include <cpu/intel/speedstep.h>
#include <thread.h>
#if CONFIG_SMP && CONFIG_MAX_CPUS > 1 #if CONFIG_SMP && CONFIG_MAX_CPUS > 1
/* This is a lot more paranoid now, since Linux can NOT handle /* This is a lot more paranoid now, since Linux can NOT handle
@ -292,6 +293,7 @@ int start_cpu(device_t cpu)
info = (struct cpu_info *)stack_end; info = (struct cpu_info *)stack_end;
info->index = index; info->index = index;
info->cpu = cpu; info->cpu = cpu;
thread_init_cpu_info_non_bsp(info);
/* Advertise the new stack and index to start_cpu */ /* Advertise the new stack and index to start_cpu */
secondary_stack = stack_end; secondary_stack = stack_end;

View File

@ -4,6 +4,7 @@
#include <cpu/x86/tsc.h> #include <cpu/x86/tsc.h>
#include <smp/spinlock.h> #include <smp/spinlock.h>
#include <delay.h> #include <delay.h>
#include <thread.h>
#if !defined(__PRE_RAM__) #if !defined(__PRE_RAM__)
@ -176,6 +177,9 @@ void udelay(unsigned us)
unsigned long long current; unsigned long long current;
unsigned long long clocks; unsigned long long clocks;
if (!thread_yield_microseconds(us))
return;
start = rdtscll(); start = rdtscll();
clocks = us; clocks = us;
clocks *= get_clocks_per_usec(); clocks *= get_clocks_per_usec();