| /* |
| * Copyright (c) 2012-2014 Wind River Systems, Inc. |
| * |
| * 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 |
| * @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; |
| } |
| |
| 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"); |
| } |