x86: iamcu: Add support for the IAMCU calling convention.

Add support for compilers conforming to the IAMCU calling convention
as documented by
https://github.com/hjl-tools/x86-psABI/wiki/iamcu-psABI-0.7.pdf


Change-Id: I6fd9d5bede0538b2049772e3850a5940c5dd911e
Signed-off-by: Dirk Brandewie <dirk.j.brandewie@intel.com>
This commit is contained in:
Dirk Brandewie 2015-12-14 09:56:38 -08:00 committed by Anas Nashif
commit d8a1c8ef17
16 changed files with 595 additions and 3 deletions

View file

@ -22,6 +22,18 @@ menu "x86 architecture"
config ARCH
default "x86"
config X86_IAMCU
bool
default n
prompt "IAMCU calling convention"
help
The IAMCU calling convention changes the X86 C calling convention to
pass some arguments via registers allowing for code size and performance
improvements. Great care needs to be taken if you have assembly code
that will be called from C or C code called from assembly code, the
assembly code will need to be updated to conform to the new calling
convention. If in doubt say N
config ARCH_DEFCONFIG
string
default "arch/x86/defconfig"

View file

@ -11,7 +11,11 @@ endif
# character starts a comment
KBUILD_AFLAGS += -Wa,--divide
obj-y = i386_sysV_abi/
ifndef CONFIG_X86_IAMCU
obj-y += i386_sysV_abi/
else
obj-y += iamcu_abi/
endif
obj-y += gdt.o fatal.o cpuhalt.o \
msr.o dynamic.o intconnect.o \

View file

@ -0,0 +1,6 @@
ccflags-y += -I$(srctree)/kernel/nanokernel/include
ccflags-y += -I$(srctree)/kernel/microkernel/include
obj-y += swap.o intstub.o thread.o \
iamcu.o

View file

@ -0,0 +1,132 @@
/*
* Copyright (c) 2015 Intel Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define _ASMLANGUAGE
#include <toolchain.h>
#include <sections.h>
#include <nano_private.h>
#include <arch/x86/asm.h>
#include <offsets.h> /* nanokernel structure offset definitions */
#include <arch/cpu.h> /* _NANO_ERR_SPURIOUS_INT */
GTEXT(_thread_entry_wrapper)
GTEXT(_thread_entry)
GTEXT(_irq_sw_handler)
/*
* @brief Wrapper for _thread_entry
*
* The routine pops parameters for the _thread_entry from stack frame, prepared
* by the _new_thread() routine.
*
* @return N/A
*/
SECTION_FUNC(TEXT, _thread_entry_wrapper)
popl %eax
popl %edx
popl %ecx
jmp _thread_entry
#if CONFIG_IRQ_OFFLOAD
SECTION_FUNC(TEXT, _irq_sw_handler)
pushl %eax
pushl %edx
pushl %ecx
call _irq_do_offload
pop %ecx
pop %edx
pop %eax
iret
#endif
#if ALL_DYN_IRQ_STUBS > 0
BRANCH_LABEL(_DynIntStubCommon)
pushl %eax
pushl %ecx
movl _common_dynamic_irq_handler, %eax
call _execute_handler
/* Clean up and call IRET */
pop %ecx
pop %eax
pop %edx
iret
/* Create all the dynamic IRQ stubs
*
* NOTE: Please update DYN_STUB_SIZE in include/arch/x86/arch.h if you change
* how large the generated stubs are, otherwise _get_dynamic_stub() will
* be unable to correctly determine the offset
*/
/*
* Create nice labels for all the stubs so we can see where we
* are in a debugger
*/
.altmacro
.macro __INT_STUB_NUM id
BRANCH_LABEL(_DynIntStub\id)
.endm
.macro INT_STUB_NUM id
__INT_STUB_NUM %id
.endm
GTEXT(_DynIntStubsBegin)
SECTION_FUNC(TEXT, _DynIntStubsBegin)
stub_num = 0
.rept ((ALL_DYN_IRQ_STUBS + DYN_STUB_PER_BLOCK - 1) / DYN_STUB_PER_BLOCK)
block_counter = 0
.rept DYN_STUB_PER_BLOCK
.if stub_num < ALL_DYN_IRQ_STUBS
INT_STUB_NUM stub_num
pushl %edx
/*
* 2-byte push imm8. Consumed by
* common_dynamic_handler(), see intconnect.c
*/
movl $stub_num, %edx
/*
* Check to make sure this isn't the last stub in
* a block, in which case we just fall through
*/
.if (block_counter <> (DYN_STUB_PER_BLOCK - 1) && \
(stub_num <> ALL_DYN_IRQ_STUBS - 1))
/* This should always be a 2-byte jmp rel8 */
jmp 1f
.endif
stub_num = stub_num + 1
block_counter = block_counter + 1
.endif
.endr
/*
* This must a 5-bvte jump rel32, which is why _DynStubCommon
* is before the actual stubs
*/
1: jmp _DynIntStubCommon
.endr
#endif /* ALL_DYN_IRQ_STUBS */

View file

@ -0,0 +1,115 @@
/*
* Copyright (c) 2015 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <nano_private.h>
#include <drivers/loapic.h>
extern void _sys_power_save_idle_exit(int32_t ticks);
#ifdef CONFIG_NESTED_INTERRUPTS
static inline void enable_interrupts(void)
{
__asm__ volatile("sti");
};
#else
static inline void enable_interrupts(void){};
#endif
static inline void disable_interrupts(void)
{
__asm__ volatile("sti");
};
typedef void (*int_handler_t) (int context);
void _execute_handler(int_handler_t function, int context)
{
_int_latency_start();
if (!_nanokernel.nested) {
/* move to the interrupt stack and push current stack
* pointer onto interrupt stack
*/
__asm__ volatile ("movl %%esp, %%edx \n\t"
"movl %0, %%esp \n\t"
"pushl %%edx \n\t"
:
:"m" (_nanokernel.common_isp)
:"%edx"
);
}
_nanokernel.nested++;
#ifdef CONFIG_ADVANCED_POWER_MANAGEMENT
if (_nanokernel.idle) {
_sys_power_save_idle_exit(_nanokernel.idle);
_nanokernel.idle = 0;
}
#endif
_int_latency_stop();
enable_interrupts();
(*function)(context);
_loapic_eoi();
disable_interrupts();
_nanokernel.nested--;
/* Are we returning to a task or fiber context? If so we need
* to do some work based on the context that was interrupted
*/
if (!_nanokernel.nested) {
/* switch to kernel stack */
__asm__ volatile ("popl %esp");
/* if the interrupted context was a task we need to
* swap back to the interrupted context
*/
if ((_nanokernel.current->flags & PREEMPTIBLE) &&
_nanokernel.fiber) {
/* move flags into arg0 we can't use local
* variables here since the stack may have
* changed.
*/
__asm__ volatile ("pushfl \n\t"
"popl %eax \n\t"
"call _Swap");
}
}
}
void _SpuriousIntHandler(void)
{
__asm__ volatile("cld"); /* clear direction flag */
/*
* The task's regular stack is being used, but push the value of ESP
* anyway so that _ExcExit can "recover the stack pointer"
* without determining whether the exception occurred while CPL=3
*/
__asm__ volatile ("pushl %esp");
again:
_NanoFatalErrorHandler(_NANO_ERR_SPURIOUS_INT, &_default_esf);
/* The handler should no return but if it does call it again */
goto again;
}
void _SpuriousIntNoErrCodeHandler(void)
{
__asm__ volatile ("pushl %eax");
_SpuriousIntHandler();
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2015 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <toolchain.h>
#include <sections.h>
#include <nano_private.h>
unsigned int _Swap(unsigned int eflags)
{
struct tcs *next;
int rv;
/* Save the current context onto the stack */
__asm__ volatile("pushl %eax\n\t" /* push eflags _Swap argumet*/
"pushl %edi\n\t"
"pushl %esi\n\t"
"pushl %ebx\n\t"
"pushl %ebp\n\t"
"pushl %ebx\n\t"); /* eax slot for fiber return */
/* save the stack pointer into the current context structure */
__asm__ volatile("mov %%esp, %0"
:"=m" (_nanokernel.current->coopReg.esp));
/* find the next context to run */
if (_nanokernel.fiber) {
next = _nanokernel.fiber;
_nanokernel.fiber = (struct tcs *)_nanokernel.fiber->link;
} else {
next = _nanokernel.task;
}
_nanokernel.current = next;
/* recover the stack pointer for the incoming context */
__asm__ volatile("mov %0, %%esp"
:
:"m" (next->coopReg.esp));
/* restore the context */
__asm__ volatile("popl %eax\n\t"
"popl %ebp\n\t"
"popl %ebx\n\t"
"popl %esi\n\t"
"popl %edi\n\t"
"popfl \n\t"
);
/* The correct return value is already in eax but this makes
* the compiler happy
*/
__asm__ volatile("mov %%eax, %0"
:"=m" (rv)
);
return rv;
}

View file

@ -0,0 +1,141 @@
/*
* Copyright (c) 2015 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef CONFIG_MICROKERNEL
#include <microkernel.h>
#include <micro_private_types.h>
#endif /* CONFIG_MICROKERNEL */
#ifdef CONFIG_INIT_STACKS
#include <string.h>
#endif /* CONFIG_INIT_STACKS */
#include <toolchain.h>
#include <sections.h>
#include <nano_private.h>
#include <wait_q.h>
/* the one and only nanokernel control structure */
tNANO _nanokernel = {0};
/* forward declaration to asm function to adjust setup the arguments
* to _thread_entry()
*/
void _thread_entry_wrapper(_thread_entry_t, _thread_arg_t,
_thread_arg_t, _thread_arg_t);
/**
*
* @brief Create a new kernel execution context
*
* This function initializes a thread control structure (TCS) for a
* new kernel execution context. A fake stack frame is created as if
* the context had been "swapped out" via _Swap()
*
* @param stack_memory pointer to the context stack area
* @param stack_size size of contexts stack area
* @param thread_func new contexts entry function
* @param parameter1 first entry function parameter
* @param parameter2 second entry function parameter
* @param parameter3 third entry function parameter
* @param priority Priority of the new context
* @param options Additional options for the context
*
* @return none
*
* \NOMANUAL
*/
void _new_thread(char *stack_memory, unsigned stack_size,
_thread_entry_t thread_func, void *parameter1,
void *parameter2, void *parameter3, int priority,
unsigned options)
{
unsigned long *thread_context;
struct tcs *tcs = (struct tcs *)stack_memory;
ARG_UNUSED(options);
#ifdef CONFIG_INIT_STACKS
memset(stack_memory, 0xaa, stack_size);
#endif
tcs->link = (struct tcs *)NULL; /* thread not inserted into list yet */
tcs->prio = priority;
if (priority == -1) {
tcs->flags = PREEMPTIBLE | TASK;
} else {
tcs->flags = FIBER;
}
#ifdef CONFIG_THREAD_CUSTOM_DATA
/* Initialize custom data field (value is opaque to kernel) */
tcs->custom_data = NULL;
#endif
/* carve the thread entry struct from the "base" of the stack */
thread_context =
(unsigned long *)STACK_ROUND_DOWN(stack_memory + stack_size);
/*
* Create an initial context on the stack expected by the _Swap()
* primitive.
* Given that both task and fibers execute at privilege 0, the
* setup for both threads are equivalent.
*/
/* push arguments required by _thread_entry() */
*--thread_context = (unsigned long)parameter3;
*--thread_context = (unsigned long)parameter2;
*--thread_context = (unsigned long)parameter1;
*--thread_context = (unsigned long)thread_func;
/* push initial EFLAGS; only modify IF and IOPL bits */
*--thread_context = (unsigned long)_thread_entry_wrapper;
*--thread_context = (EflagsGet() & ~EFLAGS_MASK) | EFLAGS_INITIAL;
*--thread_context = 0;
*--thread_context = 0;
*--thread_context = 0;
--thread_context;
*thread_context = (unsigned long)(thread_context + 4);
*--thread_context = 0;
tcs->coopReg.esp = (unsigned long)thread_context;
#if defined(CONFIG_THREAD_MONITOR)
{
unsigned int imask;
/*
* Add the newly initialized thread to head of the
* list of threads. This singly linked list of threads
* maintains ALL the threads in the system: both tasks
* and fibers regardless of whether they are runnable.
*/
imask = irq_lock();
tcs->next_thread = _nanokernel.threads;
_nanokernel.threads = tcs;
irq_unlock(imask);
}
#endif /* CONFIG_THREAD_MONITOR */
_nano_timeout_tcs_init(tcs);
}

View file

@ -14,3 +14,5 @@ CONFIG_IPM_CONSOLE_RECEIVER=y
CONFIG_ARC_INIT=y
CONFIG_PINMUX=y
CONFIG_PINMUX_NUM_PINS=58
CONFIG_X86_IAMCU=y
CONFIG_TOOLCHAIN_VARIANT="iamcu"

View file

@ -11,3 +11,5 @@ CONFIG_SERIAL_HAS_DRIVER=y
CONFIG_PRINTK=y
CONFIG_ISR_STACK_SIZE=256
CONFIG_MAIN_STACK_SIZE=512
CONFIG_X86_IAMCU=y
CONFIG_TOOLCHAIN_VARIANT="iamcu"

View file

@ -12,3 +12,5 @@ CONFIG_IPM_QUARK_SE=y
CONFIG_IPM_QUARK_SE_MASTER=y
CONFIG_IPM_CONSOLE_RECEIVER=y
CONFIG_ARC_INIT=y
CONFIG_X86_IAMCU=y
CONFIG_TOOLCHAIN_VARIANT="iamcu"

View file

@ -51,6 +51,8 @@
#define ALL_DYN_STUBS (ALL_DYN_EXC_STUBS + ALL_DYN_IRQ_STUBS)
/*
* Synchronize these DYN_STUB_* macros with the generated assembly for
* _DynIntStubsBegin in intstub.S / _DynExcStubsBegin in excstub.S
@ -58,7 +60,11 @@
*/
/* Size of each dynamic interrupt/exception stub in bytes */
#ifdef CONFIG_IAMCU
#define DYN_STUB_SIZE 8
#else
#define DYN_STUB_SIZE 9
#endif
/*
* Offset from the beginning of a stub to the byte containing the argument

View file

@ -57,6 +57,7 @@ extern "C" {
#ifdef _ASMLANGUAGE
loapic_eoi = (CONFIG_LOAPIC_BASE_ADDRESS + LOAPIC_EOI)
#if !defined(CONFIG_X86_IAMCU)
.macro loapic_mkstub device isr context
GTEXT(_\()\device\()_\()\isr\()_stub)
@ -66,6 +67,25 @@ SECTION_FUNC(TEXT, _\()\device\()_\()\isr\()_stub)
call \isr /* Call actual interrupt handler */
jmp _IntExitWithEoi /* Inform kernel interrupt is done */
.endm
#else
.macro loapic_mkstub device isr context
GTEXT(_\()\device\()_\()\isr\()_stub)
SECTION_FUNC(TEXT, _\()\device\()_\()\isr\()_stub)
pushl %eax
pushl %edx
pushl %ecx
movl $\isr, %eax
movl $\context, %edx
call _execute_handler
pop %ecx
pop %edx
pop %eax
iret
.endm
#endif /* CONFIG_X86_IAMCU */
#else /* _ASMLANGUAGE */
#include <device.h>

View file

@ -23,6 +23,7 @@
#error test_asm_inline_gcc.h goes only with x86 GCC
#endif
#if !defined(CONFIG_X86_IAMCU)
static inline void isr_dummy(void)
{
extern void _IntEnt(void);
@ -39,5 +40,27 @@ static inline void isr_dummy(void)
: :
);
}
#else
typedef void (*int_handler_t) (int context);
static inline void isr_dummy(void)
{
extern void _execute_handler(int_handler_t function, int context);
__asm__ volatile (
"pushl %%eax \n\t"
"pushl %%edx \n\t"
"pushl %%ecx \n\t"
"mov dummyIsr, %%eax \n\t"
"movl $0, %%edx \n\t"
"call _execute_handler \n\t"
"pop %%ecx \n\t"
"pop %%edx \n\t"
"pop %%eax \n\t"
"iret \n\t"
: :
);
}
#endif
#endif /* _TEST_ASM_INLINE_GCC_H */

View file

@ -49,14 +49,26 @@ SYS_NANO_CPU_EXC_CONNECT(exc_divide_error_handler,IV_DIVIDE_ERROR)
GTEXT(nanoIntStub)
#if !defined(CONFIG_X86_IAMCU)
SECTION_FUNC(TEXT, nanoIntStub)
call _IntEnt
pushl $0
call isr_handler
addl $4, %esp
jmp _IntExit
#else
SECTION_FUNC(TEXT, nanoIntStub)
pushl %eax
pushl %edx
pushl %ecx
movl $isr_handler, %eax
movl $0, %edx
call _execute_handler
pop %ecx
pop %edx
pop %eax
iret
#endif
#else
#error Arch not supported

View file

@ -23,6 +23,7 @@
#error test_asm_inline_gcc.h goes only with x86 GCC
#endif
#if !defined(CONFIG_X86_IAMCU)
static inline void isr_dummy(void)
{
extern void _IntEnt(void);
@ -39,5 +40,27 @@ static inline void isr_dummy(void)
: :
);
}
#else
typedef void (*int_handler_t) (int context);
static inline void isr_dummy(void)
{
extern void _execute_handler(int_handler_t function, int context);
__asm__ volatile (
"pushl %%eax \n\t"
"pushl %%edx \n\t"
"pushl %%ecx \n\t"
"mov dummyIsr, %%eax \n\t"
"movl $0, %%edx \n\t"
"call _execute_handler \n\t"
"pop %%ecx \n\t"
"pop %%edx \n\t"
"pop %%eax \n\t"
"iret \n\t"
: :
);
}
#endif
#endif /* _TEST_ASM_INLINE_GCC_H */

View file

@ -23,6 +23,7 @@
#error test_asm_inline_gcc.h goes only with x86 GCC
#endif
#if !defined(CONFIG_X86_IAMCU)
static inline void isr_dummy(void)
{
extern void _IntEnt(void);
@ -40,4 +41,27 @@ static inline void isr_dummy(void)
);
}
#else
typedef void (*int_handler_t) (int context);
static inline void isr_dummy(void)
{
extern void _execute_handler(int_handler_t function, int context);
__asm__ volatile (
"pushl %%eax \n\t"
"pushl %%edx \n\t"
"pushl %%ecx \n\t"
"mov dummyIsr, %%eax \n\t"
"movl $0, %%edx \n\t"
"call _execute_handler \n\t"
"pop %%ecx \n\t"
"pop %%edx \n\t"
"pop %%eax \n\t"
"iret \n\t"
: :
);
}
#endif
#endif /* _TEST_ASM_INLINE_GCC_H */