zephyr/arch/x86/core/dynamic.c

111 lines
3.2 KiB
C

/*
* 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.
*/
/**
* @file common dynamic irq/exception-relation functions for IA-32 arch
*/
#include <nanokernel.h>
#include <arch/cpu.h>
#include <nano_private.h>
#if ALL_DYN_STUBS > 0
/**
* @brief Allocate dynamic interrupt stub
*
* @param sp Pointer to integer tracking stub allocation
* @param limit Max number of stubs to allow
* @return index of the first available element of the STUB array or -1
* if all elements are used
*/
int _stub_alloc(unsigned int *sp, unsigned int limit)
{
int rv, key;
key = irq_lock();
if (*sp == limit) {
rv = -1;
} else {
rv = (*sp)++;
}
irq_unlock(key);
return rv;
}
/**
* @brief Get the memory address of an unused dynamic IRQ or exception stub
*
* We generate at build time a set of dynamic stubs which push
* a stub index onto the stack for use as an argument by
* common handling code. These don't each have individual labels,
* but it's possible to compute an offset to any particular one
*
* @param stub_idx Stub number to fetch the corresponding stub function
* @param base_ptr Memory location in ROM where stubs begin
* @return Pointer to the stub code to install into the IDT
*/
void *_get_dynamic_stub(int stub_idx, void *base_ptr)
{
uint32_t offset;
/*
* Because we want the sizes of the stubs to be consisent and minimized,
* stubs are grouped into blocks, each containing a push and subsequent
* 2-byte jump instruction to the end of the block, which then contains
* a larger jump instruction to common dynamic IRQ handling code
*/
offset = (stub_idx * DYN_STUB_SIZE) + ((stub_idx / DYN_STUB_PER_BLOCK) *
DYN_STUB_JMP_SIZE);
return (void *)((uint32_t)base_ptr + offset);
}
/**
* @brief Map an IRQ/exception vector back to the corresponding stub index
*
* This is used to fetch a reference to a stub when all we have is the IRQ
* vector.
*
* @param IRQ vector as installed in the IDT
* @return stub index
*/
uint8_t _stub_idx_from_vector(int vector)
{
IDT_ENTRY *idt_entry;
uint8_t *stub_addr;
/*
* We need to do a reverse map from the vector number to the stub
* index. Look in the IDT for the provided vector and find the memory
* address of the handler function, which should be one of
* the dynamic stubs
*/
idt_entry = (IDT_ENTRY *)(_idt_base_address + (vector << 3));
stub_addr = (uint8_t *)((uint32_t)idt_entry->offset_low +
((uint32_t)idt_entry->offset_high << 16));
/*
* Return the specific byte in the handler code which contains
* the stub index, the argument to the initial push operation
*/
return stub_addr[DYN_STUB_IDX_OFFSET];
}
#endif /* ALL_DYN_STUBS */