604 lines
15 KiB
C
604 lines
15 KiB
C
/*
|
|
* Copyright (c) 2012-2014 Wind River Systems, Inc.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* @brief Generate static IDT and a bitmap of allocated interrupt vectors.
|
|
* Creates a static IA-32 Interrupt Descriptor Table (IDT).
|
|
*
|
|
* This program expects to be invoked as follows:
|
|
* gen_idt -i <input file> -o <IDT output file> -n <# of interrupt vectors>
|
|
* -b <allocated vectors bitmap file>
|
|
* All parameters are required.
|
|
*
|
|
* The <input file> is assumed to be a binary file containing the intList
|
|
* section from the Zephyr Kernel ELF image.
|
|
*
|
|
* <number of interrupt vectors> is the same as CONFIG_IDT_NUM_VECTORS.
|
|
*
|
|
* It is expected that this program is invoked from within the build system
|
|
* during the link stage.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <stdint.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include "version.h"
|
|
|
|
/* Define __packed for the idtEntry structure defined in idtEnt.h */
|
|
#define __packed __attribute__((__packed__))
|
|
|
|
/* This comes from the shared directory */
|
|
#include <segmentation.h>
|
|
|
|
#if !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__WIN32__)
|
|
#define O_BINARY 0
|
|
#endif
|
|
|
|
#define MAX_NUM_VECTORS 256
|
|
#define MAX_PRIORITIES 14
|
|
#define MAX_VECTORS_PER_PRIORITY 16
|
|
#define MAX_IRQS 256
|
|
|
|
#define UNSPECIFIED_INT_VECTOR ((unsigned int) -1)
|
|
#define UNSPECIFIED_PRIORITY ((unsigned int) -1)
|
|
#define UNSPECIFIED_IRQ ((unsigned int) -1)
|
|
|
|
#define KERNEL_CODE_SEG_SELECTOR 0x0008
|
|
|
|
static void get_exec_name(char *pathname);
|
|
static void usage(int len);
|
|
static void get_options(int argc, char *argv[]);
|
|
static void open_files(void);
|
|
static void read_input_file(void);
|
|
static void close_files(void);
|
|
static void validate_input_file(void);
|
|
static void generate_idt(void);
|
|
static void generate_interrupt_vector_bitmap(void);
|
|
static void clean_exit(int exit_code);
|
|
static void debug(const char *format, ...);
|
|
|
|
struct genidt_header_s {
|
|
uint32_t spurious_addr;
|
|
uint32_t spurious_no_error_addr;
|
|
unsigned int num_entries;
|
|
};
|
|
|
|
struct genidt_entry_s {
|
|
uint32_t isr;
|
|
unsigned int irq;
|
|
unsigned int priority;
|
|
unsigned int vector_id;
|
|
unsigned int dpl;
|
|
};
|
|
|
|
static struct genidt_header_s genidt_header;
|
|
static struct genidt_entry_s supplied_entry[MAX_NUM_VECTORS];
|
|
static struct genidt_entry_s generated_entry[MAX_NUM_VECTORS];
|
|
|
|
enum {
|
|
IFILE = 0, /* input file */
|
|
OFILE, /* output file */
|
|
MFILE, /* irq to interrupt vector mapping file */
|
|
NUSERFILES, /* number of user-provided file names */
|
|
EXECFILE = NUSERFILES, /* for name of executable */
|
|
NFILES /* total number of file names */
|
|
};
|
|
enum { SHORT_USAGE, LONG_USAGE };
|
|
|
|
static int fds[NUSERFILES] = {-1, -1};
|
|
static char *filenames[NFILES];
|
|
static unsigned int num_vectors = (unsigned int)-1;
|
|
static struct version version = {KERNEL_VERSION, 1, 1, 6};
|
|
static unsigned int num_irq_lines = -1;
|
|
static int verbose;
|
|
|
|
static void debug(const char *format, ...)
|
|
{
|
|
va_list vargs;
|
|
|
|
if (!verbose)
|
|
return;
|
|
|
|
va_start(vargs, format);
|
|
vfprintf(stderr, format, vargs);
|
|
va_end(vargs);
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
get_exec_name(argv[0]);
|
|
get_options(argc, argv); /* may exit */
|
|
open_files(); /* may exit */
|
|
read_input_file(); /* may exit */
|
|
validate_input_file(); /* may exit */
|
|
generate_interrupt_vector_bitmap(); /* may exit */
|
|
generate_idt(); /* may exit */
|
|
close_files();
|
|
return 0;
|
|
}
|
|
|
|
static void get_options(int argc, char *argv[])
|
|
{
|
|
char *endptr;
|
|
int ii, opt;
|
|
|
|
while ((opt = getopt(argc, argv, "hi:o:m:n:vl:d")) != -1) {
|
|
switch (opt) {
|
|
case 'i':
|
|
filenames[IFILE] = optarg;
|
|
break;
|
|
case 'o':
|
|
filenames[OFILE] = optarg;
|
|
break;
|
|
case 'm':
|
|
filenames[MFILE] = optarg;
|
|
break;
|
|
case 'h':
|
|
usage(LONG_USAGE);
|
|
exit(0);
|
|
case 'l':
|
|
num_irq_lines = (unsigned int) strtoul(optarg, &endptr, 10);
|
|
if ((*optarg == '\0') || (*endptr != '\0')) {
|
|
usage(SHORT_USAGE);
|
|
exit(-1);
|
|
}
|
|
break;
|
|
case 'n':
|
|
num_vectors = (unsigned int) strtoul(optarg, &endptr, 10);
|
|
if ((*optarg == '\0') || (*endptr != '\0')) {
|
|
usage(SHORT_USAGE);
|
|
exit(-1);
|
|
}
|
|
break;
|
|
case 'v':
|
|
show_version(filenames[EXECFILE], &version);
|
|
exit(0);
|
|
case 'd':
|
|
verbose = 1;
|
|
break;
|
|
default:
|
|
usage(SHORT_USAGE);
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
if (num_vectors > MAX_NUM_VECTORS) {
|
|
usage(SHORT_USAGE);
|
|
exit(-1);
|
|
}
|
|
|
|
if (num_irq_lines > MAX_IRQS) {
|
|
usage(SHORT_USAGE);
|
|
exit(-1);
|
|
}
|
|
|
|
for (ii = IFILE; ii < NUSERFILES; ii++) {
|
|
if (!filenames[ii]) {
|
|
usage(SHORT_USAGE);
|
|
exit(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void get_exec_name(char *pathname)
|
|
{
|
|
int end = strlen(pathname) - 1;
|
|
|
|
while (end != -1) {
|
|
#if defined(WINDOWS) /* Might have both slashes in path */
|
|
if ((pathname[end] == '/') || (pathname[end] == '\\'))
|
|
#else
|
|
if (pathname[end] == '/')
|
|
#endif
|
|
{
|
|
if ((0 == end) || (pathname[end - 1] != '\\')) {
|
|
++end;
|
|
break;
|
|
}
|
|
}
|
|
--end;
|
|
}
|
|
filenames[EXECFILE] = &pathname[end];
|
|
}
|
|
|
|
static void open_files(void)
|
|
{
|
|
int ii;
|
|
|
|
fds[IFILE] = open(filenames[IFILE], O_RDONLY | O_BINARY);
|
|
fds[OFILE] = open(filenames[OFILE], O_WRONLY | O_BINARY |
|
|
O_TRUNC | O_CREAT,
|
|
S_IWUSR | S_IRUSR);
|
|
fds[MFILE] = open(filenames[MFILE], O_WRONLY | O_BINARY | O_CREAT |
|
|
O_TRUNC | O_BINARY,
|
|
S_IWUSR | S_IRUSR);
|
|
|
|
for (ii = 0; ii < NUSERFILES; ii++) {
|
|
int invalid = fds[ii] == -1;
|
|
|
|
if (invalid) {
|
|
char *invalid = filenames[ii];
|
|
|
|
fprintf(stderr, "invalid file %s\n", invalid);
|
|
for (--ii; ii >= 0; ii--) {
|
|
close(fds[ii]);
|
|
}
|
|
exit(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void show_entry(struct genidt_entry_s *entry)
|
|
{
|
|
debug("Vector %3d: ISR 0x%08x IRQ %3d PRI %2d DPL %2x\n",
|
|
entry->vector_id, entry->isr, entry->irq, entry->priority,
|
|
entry->dpl);
|
|
}
|
|
|
|
static void read_input_file(void)
|
|
{
|
|
ssize_t bytes_read;
|
|
size_t bytes_to_read;
|
|
|
|
/* Read the header information. */
|
|
bytes_read = read(fds[IFILE], &genidt_header, sizeof(genidt_header));
|
|
if (bytes_read != sizeof(genidt_header)) {
|
|
goto read_error;
|
|
}
|
|
|
|
debug("Spurious interrupt handlers found at 0x%x and 0x%x.\n",
|
|
genidt_header.spurious_addr, genidt_header.spurious_no_error_addr);
|
|
debug("There are %d ISR(s).\n", genidt_header.num_entries);
|
|
|
|
|
|
if (genidt_header.num_entries > num_vectors) {
|
|
fprintf(stderr,
|
|
"Too many ISRs found. Got %u. Expected no more than %u.\n"
|
|
"Malformed input file?\n",
|
|
genidt_header.num_entries, num_vectors);
|
|
clean_exit(-1);
|
|
}
|
|
|
|
bytes_to_read = sizeof(struct genidt_entry_s) * genidt_header.num_entries;
|
|
bytes_read = read(fds[IFILE], supplied_entry, bytes_to_read);
|
|
if (bytes_read != bytes_to_read) {
|
|
goto read_error;
|
|
}
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < genidt_header.num_entries; i++) {
|
|
show_entry(&supplied_entry[i]);
|
|
}
|
|
return;
|
|
|
|
read_error:
|
|
fprintf(stderr, "Error occurred while reading input file. Aborting...\n");
|
|
clean_exit(-1);
|
|
}
|
|
|
|
static void validate_dpl(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < genidt_header.num_entries; i++) {
|
|
if (supplied_entry[i].dpl != 0) {
|
|
fprintf(stderr,
|
|
"Invalid DPL bits specified. Must be zero.\n");
|
|
show_entry(&supplied_entry[i]);
|
|
clean_exit(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void validate_vector_id(void)
|
|
{
|
|
int i;
|
|
int vectors[MAX_NUM_VECTORS] = {0};
|
|
|
|
for (i = 0; i < genidt_header.num_entries; i++) {
|
|
if (supplied_entry[i].vector_id == UNSPECIFIED_INT_VECTOR) {
|
|
/*
|
|
* Vector is to be allocated. No further validation to be
|
|
* done at the moment.
|
|
*/
|
|
continue;
|
|
}
|
|
|
|
if (supplied_entry[i].vector_id >= num_vectors) {
|
|
fprintf(stderr,
|
|
"Vector ID exceeds specified # of vectors (%d).\n",
|
|
num_vectors);
|
|
show_entry(&supplied_entry[i]);
|
|
clean_exit(-1);
|
|
}
|
|
|
|
if (vectors[supplied_entry[i].vector_id] != 0) {
|
|
fprintf(stderr, "Duplicate vector ID found.\n");
|
|
show_entry(&supplied_entry[i]);
|
|
clean_exit(-1);
|
|
}
|
|
vectors[supplied_entry[i].vector_id]++;
|
|
}
|
|
}
|
|
|
|
static void validate_priority(void)
|
|
{
|
|
int i;
|
|
|
|
/* Validate the priority. */
|
|
for (i = 0; i < genidt_header.num_entries; i++) {
|
|
if (supplied_entry[i].priority == UNSPECIFIED_PRIORITY) {
|
|
if (supplied_entry[i].vector_id == UNSPECIFIED_INT_VECTOR) {
|
|
fprintf(stderr,
|
|
"Either priority or vector ID must be specified.\n");
|
|
show_entry(&supplied_entry[i]);
|
|
clean_exit(-1);
|
|
}
|
|
} else if (supplied_entry[i].priority >= MAX_PRIORITIES) {
|
|
fprintf(stderr, "Priority must not exceed %d.\n",
|
|
MAX_PRIORITIES - 1);
|
|
show_entry(&supplied_entry[i]);
|
|
clean_exit(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void validate_irq(void)
|
|
{
|
|
int i;
|
|
int num_irqs[MAX_IRQS] = {0};
|
|
|
|
/* Validate the IRQ number */
|
|
for (i = 0; i < genidt_header.num_entries; i++) {
|
|
if (supplied_entry[i].irq == UNSPECIFIED_IRQ) {
|
|
if (supplied_entry[i].vector_id == UNSPECIFIED_INT_VECTOR) {
|
|
fprintf(stderr,
|
|
"Either IRQ or vector ID must be specified.\n");
|
|
show_entry(&supplied_entry[i]);
|
|
clean_exit(-1);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (supplied_entry[i].irq >= num_irq_lines) {
|
|
/*
|
|
* If code to support the PIC is re-introduced, then this
|
|
* check will need to be updated.
|
|
*/
|
|
fprintf(stderr, "IRQ must be between 0 and %d inclusive.\n",
|
|
num_irq_lines - 1);
|
|
show_entry(&supplied_entry[i]);
|
|
clean_exit(-1);
|
|
}
|
|
num_irqs[supplied_entry[i].irq]++;
|
|
}
|
|
|
|
for (i = 0; i < num_irq_lines; i++) {
|
|
if (num_irqs[i] > 1) {
|
|
fprintf(stderr, "Multiple requests (%d) for IRQ %d detected.\n",
|
|
num_irqs[i], i);
|
|
clean_exit(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void validate_input_file(void)
|
|
{
|
|
validate_dpl(); /* exits on error */
|
|
validate_irq(); /* exits on error */
|
|
validate_vector_id(); /* exits on error */
|
|
validate_priority(); /* exits on error */
|
|
}
|
|
|
|
static void generate_idt(void)
|
|
{
|
|
unsigned int i;
|
|
unsigned int vector_id;
|
|
|
|
/*
|
|
* Initialize the generated entries with default
|
|
* spurious interrupt handlers.
|
|
*/
|
|
|
|
for (i = 0; i < num_vectors; i++) {
|
|
if ((((1 << i) & _EXC_ERROR_CODE_FAULTS)) && (i < 32)) {
|
|
generated_entry[i].isr = genidt_header.spurious_addr;
|
|
} else {
|
|
generated_entry[i].isr = genidt_header.spurious_no_error_addr;
|
|
}
|
|
/* Initialize the [irq] and [priority] fields to aid in debugging. */
|
|
generated_entry[i].irq = UNSPECIFIED_IRQ;
|
|
generated_entry[i].priority = UNSPECIFIED_PRIORITY;
|
|
generated_entry[i].vector_id = i;
|
|
generated_entry[i].dpl = 0;
|
|
}
|
|
|
|
/*
|
|
* Overwrite the generated entries as appropriate with the
|
|
* validated supplied entries.
|
|
*/
|
|
|
|
for (i = 0; i < genidt_header.num_entries; i++) {
|
|
vector_id = supplied_entry[i].vector_id;
|
|
generated_entry[vector_id] = supplied_entry[i];
|
|
}
|
|
|
|
/*
|
|
* We now have the address of all ISR stub/functions captured in
|
|
* generated_entry[]. Now construct the actual IDT.
|
|
*/
|
|
|
|
for (i = 0; i < num_vectors; i++) {
|
|
struct segment_descriptor idt_entry;
|
|
ssize_t bytes_written;
|
|
|
|
_init_irq_gate(&idt_entry, KERNEL_CODE_SEG_SELECTOR,
|
|
generated_entry[i].isr, generated_entry[i].dpl);
|
|
|
|
bytes_written = write(fds[OFILE], &idt_entry, sizeof(idt_entry));
|
|
if (bytes_written != sizeof(idt_entry)) {
|
|
fprintf(stderr, "Failed to write IDT entry %u.\n", num_vectors);
|
|
clean_exit(-1);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static int find_first_set_lsb(unsigned int value)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
if ((value & (1 << i)) != 0) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static void generate_interrupt_vector_bitmap(void)
|
|
{
|
|
int i;
|
|
unsigned int num_elements = (num_vectors + 31) / 32;
|
|
unsigned int interrupt_vector_bitmap[num_elements];
|
|
uint8_t map_irq_to_vector_id[MAX_IRQS] = {0};
|
|
unsigned int value;
|
|
unsigned int index;
|
|
unsigned int mask_index;
|
|
int bit;
|
|
size_t bytes_to_write;
|
|
ssize_t bytes_written;
|
|
static unsigned int mask[2] = {0x0000ffff, 0xffff0000};
|
|
|
|
/* Initially mark each interrupt vector as available */
|
|
for (i = 0; i < num_elements; i++) {
|
|
interrupt_vector_bitmap[i] = 0xffffffff;
|
|
}
|
|
|
|
/* Ensure that any leftover entries are marked as allocated. */
|
|
for (i = num_vectors; i < num_elements * 32; i++) {
|
|
index = i / 32;
|
|
bit = i & 0x1f;
|
|
|
|
interrupt_vector_bitmap[index] &= ~(1 << bit);
|
|
}
|
|
|
|
/*
|
|
* Vector allocation is done in two steps.
|
|
* 1. Loop through each supplied entry and if an explicit vector was
|
|
* specified, mark it as allocated.
|
|
* 2. Loop through each supplied entry and allocate the vector if
|
|
* it is required.
|
|
* This approach guarantees that explicitly specified interrupt vectors
|
|
* will get their requested slots.
|
|
*/
|
|
|
|
for (i = 0; i < genidt_header.num_entries; i++) {
|
|
if (supplied_entry[i].vector_id == UNSPECIFIED_INT_VECTOR) {
|
|
/* This vector will be allocated in the next for-loop. */
|
|
continue;
|
|
}
|
|
|
|
index = supplied_entry[i].vector_id / 32;
|
|
bit = supplied_entry[i].vector_id & 31;
|
|
|
|
interrupt_vector_bitmap[index] &= ~(1 << bit);
|
|
}
|
|
|
|
for (i = 0; i < genidt_header.num_entries; i++) {
|
|
if (supplied_entry[i].vector_id != UNSPECIFIED_INT_VECTOR) {
|
|
/* This vector has already been processed. */
|
|
continue;
|
|
}
|
|
|
|
/* We can assume priority has been explicitly set as
|
|
* validate_priority() enforces that you can't use both
|
|
* UNSPECIFIED_INT_VECTOR and UNSPECIFIED_INT_PRIORITY
|
|
*/
|
|
index = (supplied_entry[i].priority + 2) / 2;
|
|
mask_index = (supplied_entry[i].priority + 2) & 1;
|
|
value = interrupt_vector_bitmap[index] & mask[mask_index];
|
|
bit = find_first_set_lsb(value);
|
|
if (bit < 0) {
|
|
fprintf(stderr,
|
|
"No remaining vectors for priority %d are available.\n",
|
|
supplied_entry[i].priority);
|
|
clean_exit(-1);
|
|
}
|
|
|
|
interrupt_vector_bitmap[index] &= ~(1 << bit);
|
|
map_irq_to_vector_id[supplied_entry[i].irq] = (index * 32) + bit;
|
|
supplied_entry[i].vector_id = (index * 32) + bit;
|
|
debug("Assigned IRQ %3d (priority %2d) vector %3d\n",
|
|
supplied_entry[i].irq, supplied_entry[i].priority,
|
|
supplied_entry[i].vector_id);
|
|
}
|
|
|
|
bytes_to_write = num_irq_lines;
|
|
bytes_written = write(fds[MFILE], map_irq_to_vector_id, bytes_to_write);
|
|
if (bytes_written != bytes_to_write) {
|
|
fprintf(stderr, "Failed to write all data to '%s'.\n",
|
|
filenames[MFILE]);
|
|
clean_exit(-1);
|
|
}
|
|
}
|
|
|
|
static void close_files(void)
|
|
{
|
|
int ii;
|
|
|
|
for (ii = 0; ii < NUSERFILES; ii++) {
|
|
close(fds[ii]);
|
|
}
|
|
}
|
|
|
|
static void clean_exit(int exit_code)
|
|
{
|
|
close_files();
|
|
exit(exit_code);
|
|
}
|
|
|
|
static void usage(int len)
|
|
{
|
|
fprintf(stderr, "\n%s -i <input file> -n <n>\n", filenames[EXECFILE]);
|
|
|
|
if (len == SHORT_USAGE) {
|
|
return;
|
|
}
|
|
|
|
fprintf(stderr,
|
|
"\n"
|
|
" options\n"
|
|
"\n"
|
|
" -b <allocated interrupt vector bitmap output file>\n\n"
|
|
" [Mandatory] The interrupt vector bitmap output file.\n\n"
|
|
" -i <binary file>\n\n"
|
|
" [Mandatory] The input file in binary format.\n\n"
|
|
" -o <IDT output file>\n\n"
|
|
" [Mandatory] The IDT output file.\n\n"
|
|
" -m <IRQ to interrupt vector map file>\n\n"
|
|
" [Mandatory] The IRQ to interrupt vector output file\n\n"
|
|
" -n <n>\n\n"
|
|
" [Mandatory] Number of vectors\n\n"
|
|
" -l <n>\n\n"
|
|
" [Mandatory] Number of IRQ lines\n\n"
|
|
" -v Display version.\n\n"
|
|
" -h Display this help.\n\n"
|
|
" -d Display extra debugging output.\n\n"
|
|
"\nReturns -1 on error, 0 on success\n\n");
|
|
}
|