/* * 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 -o -n <# of interrupt vectors> * -b * All parameters are required. * * The is assumed to be a binary file containing the intList * section from the Zephyr Kernel ELF image. * * 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 #include #include #include #include #include #include #include #include #include #include "version.h" /* Define __packed for the idtEntry structure defined in idtEnt.h */ #define __packed __attribute__((__packed__)) /* This comes from the shared directory */ #include #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 -n \n", filenames[EXECFILE]); if (len == SHORT_USAGE) { return; } fprintf(stderr, "\n" " options\n" "\n" " -b \n\n" " [Mandatory] The interrupt vector bitmap output file.\n\n" " -i \n\n" " [Mandatory] The input file in binary format.\n\n" " -o \n\n" " [Mandatory] The IDT output file.\n\n" " -m \n\n" " [Mandatory] The IRQ to interrupt vector output file\n\n" " -n \n\n" " [Mandatory] Number of vectors\n\n" " -l \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"); }