diff options
-rw-r--r-- | i386/Makefrag.am | 2 | ||||
-rw-r--r-- | i386/i386/apic.c | 221 | ||||
-rw-r--r-- | i386/i386/apic.h | 139 |
3 files changed, 362 insertions, 0 deletions
diff --git a/i386/Makefrag.am b/i386/Makefrag.am index b2b4f77f..035fd34d 100644 --- a/i386/Makefrag.am +++ b/i386/Makefrag.am @@ -152,6 +152,8 @@ EXTRA_DIST += \ if PLATFORM_at libkernel_a_SOURCES += \ + i386/i386/apic.h \ + i386/i386/apic.c \ i386/i386/hardclock.c \ i386/i386/hardclock.h \ i386/i386/io_map.c \ diff --git a/i386/i386/apic.c b/i386/i386/apic.c new file mode 100644 index 00000000..f0b4a153 --- /dev/null +++ b/i386/i386/apic.c @@ -0,0 +1,221 @@ +/* apic.c - APIC controller management for Mach. + Copyright (C) 2020 Free Software Foundation, Inc. + Written by Almudena Garcia Jurado-Centurion + + This file is part of GNU Mach. + + GNU Mach is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + GNU Mach is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include <i386/apic.h> +#include <string.h> +#include <vm/vm_kern.h> +#include <kern/printf.h> +#include <kern/kalloc.h> + + +volatile ApicLocalUnit* lapic = NULL; + +ApicInfo apic_data; + +/* + * apic_data_init: initialize the apic_data structures to preliminary values. + * Reserve memory to the lapic list dynamic vector. + * Returns 0 if success, -1 if error. + */ +int +apic_data_init(void) +{ + apic_data.cpu_lapic_list = NULL; + apic_data.ncpus = 0; + apic_data.nioapics = 0; + apic_data.nirqoverride = 0; + + /* Reserve the vector memory for the maximum number of processors. */ + apic_data.cpu_lapic_list = (uint16_t*) kalloc(NCPUS*sizeof(uint16_t)); + + /* If the memory reserve fails, return -1 to advice about the error. */ + if (apic_data.cpu_lapic_list == NULL) + return -1; + + return 0; +} + +/* + * apic_lapic_init: initialize lapic pointer to the memory common address. + * Receives as input a pointer to the virtual memory address, previously mapped in a page. + */ +void +apic_lapic_init(ApicLocalUnit* lapic_ptr) +{ + lapic = lapic_ptr; +} + +/* + * apic_add_cpu: add a new lapic/cpu entry to the cpu_lapic list. + * Receives as input the lapic's APIC ID. + */ +void +apic_add_cpu(uint16_t apic_id) +{ + apic_data.cpu_lapic_list[apic_data.ncpus] = apic_id; + apic_data.ncpus++; +} + +/* + * apic_add_ioapic: add a new ioapic entry to the ioapic list. + * Receives as input an ioapic_data structure, filled with the IOAPIC entry's data. + */ +void +apic_add_ioapic(IoApicData ioapic) +{ + apic_data.ioapic_list[apic_data.nioapics] = ioapic; + apic_data.nioapics++; +} + +/* + * apic_add_irq_override: add a new IRQ to the irq_override list. + * Receives as input an irq_override_data structure, filled with the IRQ entry's data. + */ +void +apic_add_irq_override(IrqOverrideData irq_over) +{ + apic_data.irq_override_list[apic_data.nirqoverride] = irq_over; + apic_data.nirqoverride++; +} + +/* + * apic_get_cpu_apic_id: returns the apic_id of a cpu. + * Receives as input the kernel ID of a CPU. + */ +uint16_t +apic_get_cpu_apic_id(int kernel_id) +{ + if (kernel_id >= NCPUS) + return -1; + + return apic_data.cpu_lapic_list[kernel_id]; +} + +/* apic_get_lapic: returns a reference to the common memory address for Local APIC. */ +volatile ApicLocalUnit* +apic_get_lapic(void) +{ + return lapic; +} + +/* + * apic_get_ioapic: returns the IOAPIC identified by its kernel ID. + * Receives as input the IOAPIC's Kernel ID. + * Returns a ioapic_data structure with the IOAPIC's data. + */ +struct IoApicData +apic_get_ioapic(int kernel_id) +{ + IoApicData io_apic = {}; + + if (kernel_id < MAX_IOAPICS) + return apic_data.ioapic_list[kernel_id]; + return io_apic; +} + +/* apic_get_numcpus: returns the current number of cpus. */ +uint8_t +apic_get_numcpus(void) +{ + return apic_data.ncpus; +} + +/* apic_get_num_ioapics: returns the current number of ioapics. */ +uint8_t +apic_get_num_ioapics(void) +{ + return apic_data.nioapics; +} + +/* + * apic_get_current_cpu: returns the apic_id of current cpu. + */ +uint16_t +apic_get_current_cpu(void) +{ + uint16_t apic_id; + + if(lapic == NULL) + apic_id = 0; + else + apic_id = lapic->apic_id.r; + + return apic_id; +} + + +/* + * apic_refit_cpulist: adjust the size of cpu_lapic array to fit the real number of cpus + * instead the maximum number. + * + * Returns 0 if success, -1 if error. + */ +int apic_refit_cpulist(void) +{ + uint16_t* old_list = apic_data.cpu_lapic_list; + uint16_t* new_list = NULL; + + if (old_list == NULL) + return -1; + + new_list = (uint16_t*) kalloc(apic_data.ncpus*sizeof(uint16_t)); + + if (new_list == NULL) + return -1; + + for (int i = 0; i < apic_data.ncpus; i++) + new_list[i] = old_list[i]; + + apic_data.cpu_lapic_list = new_list; + kfree((vm_offset_t) old_list, NCPUS*sizeof(uint16_t)); + + return 0; +} + +/* + * apic_print_info: shows the list of Local APIC and IOAPIC. + * Shows each CPU and IOAPIC, with Its Kernel ID and APIC ID. + */ +void apic_print_info(void) +{ + int i; + int ncpus, nioapics; + + ncpus = apic_get_numcpus(); + nioapics = apic_get_num_ioapics(); + + uint16_t lapic_id; + uint16_t ioapic_id; + + IoApicData ioapic; + + printf("CPUS:\n"); + for (i = 0; i < ncpus; i++) { + lapic_id = apic_get_cpu_apic_id(i); + printf(" CPU %d - APIC ID %x\n", i, lapic_id); + } + + printf("IOAPICS:\n"); + for (i = 0; i < nioapics; i++) { + ioapic = apic_get_ioapic(i); + ioapic_id = ioapic.apic_id; + printf(" IOAPIC %d - APIC ID %x\n", i, ioapic_id); + } +} diff --git a/i386/i386/apic.h b/i386/i386/apic.h new file mode 100644 index 00000000..e2d2c508 --- /dev/null +++ b/i386/i386/apic.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + * Author: Bryan Ford, University of Utah CSL + */ +#ifndef _IMPS_APIC_ +#define _IMPS_APIC_ + +#ifndef __ASSEMBLER__ + +#include <stdint.h> + +typedef struct ApicReg { + unsigned r; /* the actual register */ + unsigned p[3]; /* pad to the next 128-bit boundary */ +} ApicReg; + +typedef struct ApicIoUnit { + ApicReg select; + ApicReg window; +} ApicIoUnit; + + +typedef struct ApicLocalUnit { + ApicReg reserved0; /* 0x000 */ + ApicReg reserved1; /* 0x010 */ + ApicReg apic_id; /* 0x020 */ + ApicReg version; /* 0x030 */ + ApicReg reserved4; /* 0x040 */ + ApicReg reserved5; /* 0x050 */ + ApicReg reserved6; /* 0x060 */ + ApicReg reserved7; /* 0x070 */ + ApicReg task_pri; /* 0x080 */ + ApicReg arbitration_pri; /* 0x090 */ + ApicReg processor_pri; /* 0x0a0 */ + ApicReg eoi; /* 0x0b0 */ + ApicReg remote; /* 0x0c0 */ + ApicReg logical_dest; /* 0x0d0 */ + ApicReg dest_format; /* 0x0e0 */ + ApicReg spurious_vector; /* 0x0f0 */ + ApicReg isr[8]; /* 0x100 */ + ApicReg tmr[8]; /* 0x180 */ + ApicReg irr[8]; /* 0x200 */ + ApicReg error_status; /* 0x280 */ + ApicReg reserved28[6]; /* 0x290 */ + ApicReg lvt_cmci; /* 0x2f0 */ + ApicReg icr_low; /* 0x300 */ + ApicReg icr_high; /* 0x310 */ + ApicReg lvt_timer; /* 0x320 */ + ApicReg lvt_thermal; /* 0x330 */ + ApicReg lvt_performance_monitor; /* 0x340 */ + ApicReg lvt_lint0; /* 0x350 */ + ApicReg lvt_lint1; /* 0x360 */ + ApicReg lvt_error; /* 0x370 */ + ApicReg init_count; /* 0x380 */ + ApicReg cur_count; /* 0x390 */ + ApicReg reserved3a; /* 0x3a0 */ + ApicReg reserved3b; /* 0x3b0 */ + ApicReg reserved3c; /* 0x3c0 */ + ApicReg reserved3d; /* 0x3d0 */ + ApicReg divider_config; /* 0x3e0 */ + ApicReg reserved3f; /* 0x3f0 */ +} ApicLocalUnit; + +typedef struct IoApicData { + uint8_t apic_id; + uint32_t addr; + uint32_t base; +} IoApicData; + +#define APIC_IRQ_OVERRIDE_ACTIVE_LOW 2 +#define APIC_IRQ_OVERRIDE_LEVEL_TRIGGERED 8 + +typedef struct IrqOverrideData { + uint8_t bus; + uint8_t irq; + uint32_t gsi; + uint16_t flags; +} IrqOverrideData; + +#define MAX_IOAPICS 16 +#define MAX_IRQ_OVERRIDE 24 + +typedef struct ApicInfo { + uint8_t ncpus; + uint8_t nioapics; + int nirqoverride; + uint16_t* cpu_lapic_list; + struct IoApicData ioapic_list[MAX_IOAPICS]; + struct IrqOverrideData irq_override_list[MAX_IRQ_OVERRIDE]; +} ApicInfo; + +int apic_data_init(void); +void apic_add_cpu(uint16_t apic_id); +void apic_lapic_init(ApicLocalUnit* lapic_ptr); +void apic_add_ioapic(struct IoApicData); +void apic_add_irq_override(struct IrqOverrideData irq_over); +uint16_t apic_get_cpu_apic_id(int kernel_id); +volatile ApicLocalUnit* apic_get_lapic(void); +struct IoApicData apic_get_ioapic(int kernel_id); +uint8_t apic_get_numcpus(void); +uint8_t apic_get_num_ioapics(void); +uint16_t apic_get_current_cpu(void); +void apic_print_info(void); +int apic_refit_cpulist(void); + +#endif + +#define APIC_IO_UNIT_ID 0x00 +#define APIC_IO_VERSION 0x01 +#define APIC_IO_REDIR_LOW(int_pin) (0x10+(int_pin)*2) +#define APIC_IO_REDIR_HIGH(int_pin) (0x11+(int_pin)*2) + +/* Set or clear a bit in a 255-bit APIC mask register. + These registers are spread through eight 32-bit registers. */ +#define APIC_SET_MASK_BIT(reg, bit) \ + ((reg)[(bit) >> 5].r |= 1 << ((bit) & 0x1f)) +#define APIC_CLEAR_MASK_BIT(reg, bit) \ + ((reg)[(bit) >> 5].r &= ~(1 << ((bit) & 0x1f))) + +#endif /*_IMPS_APIC_*/ + |