diff --git a/Makefrag.am b/Makefrag.am index ea612275..69ac31a1 100644 --- a/Makefrag.am +++ b/Makefrag.am @@ -122,6 +122,18 @@ EXTRA_DIST += \ ipc/notify.defs +# +# SMP implementation (APIC, ACPI, etc) +# + +libkernel_a_SOURCES += \ + i386/i386at/acpi_parse_apic.h \ + i386/i386at/acpi_parse_apic.c \ + i386/i386/apic.h \ + i386/i386/apic.c \ + kern/smp.h \ + kern/smp.c + # # `kernel' implementation (tasks, threads, trivia, etc.). # diff --git a/configfrag.ac b/configfrag.ac index 91d737ef..454d9f23 100644 --- a/configfrag.ac +++ b/configfrag.ac @@ -46,9 +46,12 @@ AC_DEFINE([BOOTSTRAP_SYMBOLS], [0], [BOOTSTRAP_SYMBOLS]) # Multiprocessor support is still broken. AH_TEMPLATE([MULTIPROCESSOR], [set things up for a uniprocessor]) mach_ncpus=1 +AC_DEFINE_UNQUOTED([NCPUS], [$mach_ncpus], [number of CPUs]) + AC_DEFINE_UNQUOTED([NCPUS], [$mach_ncpus], [number of CPUs]) [if [ $mach_ncpus -gt 1 ]; then] AC_DEFINE([MULTIPROCESSOR], [1], [set things up for a multiprocessor]) + AC_DEFINE_UNQUOTED([NCPUS], [255], [number of CPUs]) [fi] # Restartable Atomic Sequences to get a really fast test-n-set. Can't be diff --git a/i386/i386/apic.c b/i386/i386/apic.c new file mode 100644 index 00000000..17ccc722 --- /dev/null +++ b/i386/i386/apic.c @@ -0,0 +1,257 @@ +/* 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 +#include +#include +#include //printf + + +#define MAX_CPUS 256 + +volatile ApicLocalUnit* lapic = NULL; + +struct apic_info 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) +{ + int success = 0; + + apic_data.cpu_lapic_list = NULL; + apic_data.ncpus = 0; + apic_data.nioapics = 0; + apic_data.cpu_lapic_list = (uint16_t*) kalloc(MAX_CPUS*sizeof(uint16_t)); + apic_data.nirqoverride = 0; + + if(apic_data.cpu_lapic_list == NULL) + { + success = -1; + } + + return success; +} + +/* 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) +{ + int numcpus = apic_data.ncpus; + apic_data.cpu_lapic_list[numcpus] = 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(struct ioapic_data ioapic) +{ + int nioapic = apic_data.nioapics; + + apic_data.ioapic_list[nioapic] = 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(struct irq_override_data irq_over) +{ + int nirq = apic_data.nirqoverride; + + apic_data.irq_override_list[nirq] = 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) +{ + uint16_t apic_id; + + if(kernel_id < MAX_CPUS) + { + apic_id = apic_data.cpu_lapic_list[kernel_id]; + } + else + { + apic_id = -1; + } + + return apic_id; +} + +/* apic_get_lapic: returns a reference to the common memory address for Local APIC */ + +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 ioapic_data +apic_get_ioapic(int kernel_id) +{ + struct ioapic_data io_apic; + + if(kernel_id < 16) + { + io_apic = apic_data.ioapic_list[kernel_id]; + } + + return io_apic; +} + +/* apic_get_numcpus: returns the current number of cpus */ + +int +apic_get_numcpus(void) +{ + return apic_data.ncpus; +} + +/* apic_get_num_ioapics: returns the current number of ioapics */ + +int +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 = (uint16_t*) kalloc(apic_data.ncpus*sizeof(uint16_t)); + int i = 0; + int success = 0; + + + if(new_list != NULL && old_list != NULL) + { + for(i = 0; i < apic_data.ncpus; i++) + { + new_list[i] = old_list[i]; + } + + apic_data.cpu_lapic_list = new_list; + kfree(old_list); + } + else + { + success = -1; + } + + return success; +} + +/* 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; + + struct ioapic_data ioapic; + + printf("CPUS\n"); + printf("-------------------------------------------------\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("\nIOAPICS\n"); + printf("-------------------------------------------------\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..05b0181e --- /dev/null +++ b/i386/i386/apic.h @@ -0,0 +1,190 @@ +/* + * 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 + +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 +{ + /* 0x000 */ + ApicReg reserved0; + /* 0x010 */ + ApicReg reserved1; + /* 0x020 */ + ApicReg apic_id; + /* 0x030 */ + ApicReg version; + /* 0x040 */ + ApicReg reserved4; + /* 0x050 */ + ApicReg reserved5; + /* 0x060 */ + ApicReg reserved6; + /* 0x070 */ + ApicReg reserved7; + /* 0x080 */ + ApicReg task_pri; + /* 0x090 */ + ApicReg arbitration_pri; + /* 0x0a0 */ + ApicReg processor_pri; + /* 0x0b0 */ + ApicReg eoi; + /* 0x0c0 */ + ApicReg remote; + /* 0x0d0 */ + ApicReg logical_dest; + /* 0x0e0 */ + ApicReg dest_format; + /* 0x0f0 */ + ApicReg spurious_vector; + /* 0x100 */ + ApicReg isr[8]; + /* 0x180 */ + ApicReg tmr[8]; + /* 0x200 */ + ApicReg irr[8]; + /* 0x280 */ + ApicReg error_status; + /* 0x290 */ + ApicReg reserved28[6]; + /* 0x2f0 */ + ApicReg lvt_cmci; + /* 0x300 */ + ApicReg icr_low; + /* 0x310 */ + ApicReg icr_high; + /* 0x320 */ + ApicReg lvt_timer; + /* 0x330 */ + ApicReg lvt_thermal; + /* 0x340 */ + ApicReg lvt_performance_monitor; + /* 0x350 */ + ApicReg lvt_lint0; + /* 0x360 */ + ApicReg lvt_lint1; + /* 0x370 */ + ApicReg lvt_error; + /* 0x380 */ + ApicReg init_count; + /* 0x390 */ + ApicReg cur_count; + /* 0x3a0 */ + ApicReg reserved3a; + /* 0x3b0 */ + ApicReg reserved3b; + /* 0x3c0 */ + ApicReg reserved3c; + /* 0x3d0 */ + ApicReg reserved3d; + /* 0x3e0 */ + ApicReg divider_config; + /* 0x3f0 */ + ApicReg reserved3f; +} ApicLocalUnit; + +struct ioapic_data +{ + uint8_t apic_id; + uint32_t addr; + uint32_t base; +}; + +#define APIC_IRQ_OVERRIDE_ACTIVE_LOW 2 +#define APIC_IRQ_OVERRIDE_LEVEL_TRIGGERED 8 + +struct irq_override_data +{ + uint8_t bus; + uint8_t irq; + uint32_t gsi; + uint16_t flags; +}; + +struct apic_info +{ + uint16_t ncpus; + uint16_t nioapics; + int nirqoverride; + + uint16_t* cpu_lapic_list; + struct ioapic_data ioapic_list[16]; + struct irq_override_data irq_override_list[24]; +}; + +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 ioapic_data); +void apic_add_irq_override(struct irq_override_data irq_over); +uint16_t apic_get_cpu_apic_id(int kernel_id); +ApicLocalUnit* apic_get_lapic(void); +struct ioapic_data apic_get_ioapic(int kernel_id); +int apic_get_numcpus(void); +int 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) + +/* Address at which the local unit is mapped in kernel virtual memory. + * Must be constant. + */ + +#define APIC_LOCAL_VA lapic + +#define apic_local_unit (*((volatile ApicLocalUnit*)APIC_LOCAL_VA)) + + +/* 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_*/ + diff --git a/i386/i386at/acpi_parse_apic.c b/i386/i386at/acpi_parse_apic.c new file mode 100644 index 00000000..babb4af7 --- /dev/null +++ b/i386/i386at/acpi_parse_apic.c @@ -0,0 +1,623 @@ +/*Copyright 2018 Juan Bosco Garcia + *Copyright 2018 2019 2020 Almudena Garcia Jurado-Centurion + *This file is part of Min_SMP. + *Min_SMP 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 of the License, or + *(at your option) any later version. + *Min_SMP 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 Min_SMP. If not, see . + */ + +#include +#include //memcmp, memcpy... + +#include //lapic, ioapic... +#include //printf +#include //uint16_t, uint32_t... +#include //machine_slot +#include //phystokv +#include +#include + +#define BAD_CHECKSUM -1 +#define NO_RSDP -2 +#define NO_RSDT -2 +#define BAD_SIGNATURE -3 +#define NO_APIC -4 + +struct acpi_apic *apic_madt = NULL; + +static struct acpi_rsdp* acpi_get_rsdp(void); +static int acpi_check_rsdt(struct acpi_rsdt *); +static struct acpi_rsdt* acpi_get_rsdt(struct acpi_rsdp *rsdp, int* acpi_rsdt_n); +static struct acpi_apic* acpi_get_apic(struct acpi_rsdt *rsdt, int acpi_rsdt_n); +static int acpi_apic_setup(struct acpi_apic *apic); +static int acpi_apic_add_ioapic(struct acpi_apic_ioapic *ioapic_entry); +static int acpi_apic_add_lapic(struct acpi_apic_lapic *lapic_entry); +static int acpi_apic_parse_table(struct acpi_apic *apic); + + +/* acpi_apic_init: find the MADT/APIC table in ACPI tables + * and parses It to find Local APIC and IOAPIC structures + * Each Local APIC stores the info and control structores for a cpu + * + * Returns 0 if success, -1 if error + */ + +int +acpi_apic_init(void) +{ + struct acpi_rsdp *rsdp = 0; + struct acpi_rsdt *rsdt = 0; + int acpi_rsdt_n; + int ret_acpi_setup; + + + //Try to get rsdp pointer + rsdp = acpi_get_rsdp(); + if(rsdp == NULL) + { + return NO_RSDP; + } + + printf("rsdp address %x\n", rsdp); + + //Try to get rsdt pointer + rsdt = acpi_get_rsdt(rsdp, &acpi_rsdt_n); + if(rsdt == NULL) + { + return NO_RSDT; + } + + printf("rsdt address %x\n", rsdt); + + apic_madt = acpi_get_apic(rsdt, acpi_rsdt_n); + if(apic_madt == NULL) + { + return NO_APIC; + } + + printf("apic address %x\n", apic_madt); + + acpi_print_info(rsdp, rsdt, acpi_rsdt_n); + + ret_acpi_setup = acpi_apic_setup(apic_madt); + + if(ret_acpi_setup != 0) + { + return ret_acpi_setup; + } + + apic_print_info(); + + printf("apic setup finished\n"); + + return 0; +} + +/* acpi_print_info: shows by screen the ACPI's rsdp and rsdt virtual address + * and the number of entries stored in RSDT table + * + * Receives as input the references of RSDP and RSDT tables, + * and the number of entries stored in RSDT + */ + +void +acpi_print_info(struct acpi_rsdp *rsdp, struct acpi_rsdt *rsdt, int acpi_rsdt_n) +{ + + printf("ACPI:\n"); + printf(" rsdp = %x; rsdp->rsdt_addr = %x\n", rsdp, rsdp->rsdt_addr); + printf(" rsdt = %x; rsdt->length = %x (n = %x)\n", rsdt, rsdt->header.length, + acpi_rsdt_n); +} + +/* acpi_checksum: calculates the checksum of an ACPI table + * Receives as input the virtual address of the table + * + * Returns 0 if success, other value if error + */ +static int +acpi_checksum(void *addr, uint32_t length) +{ + char *bytes = addr; + int checksum = 0; + unsigned int i; + + //Sum all bytes of addr + for(i = 0; i < length; i++) + { + checksum += bytes[i]; + } + + return checksum & 0xff; +} + +/* acpi_check_rsdp: + * check if the RDST "candidate" table is the real RSDT table + * + * Compare the table signature with the ACPI signature for this table + * and check is the checksum is correct + * + * Receives as input the reference of RSDT table + * + * Returns 0 if correct + */ + +static int +acpi_check_rsdp(struct acpi_rsdp *rsdp) +{ + uint32_t checksum; + int is_rsdp; + int ret_value = 0; + + //Check the integrity of RSDP + if(rsdp == NULL) + { + ret_value = NO_RSDP; + } + else + { + //Check is rsdp signature is equals to ACPI RSDP signature + is_rsdp = memcmp(rsdp->signature, ACPI_RSDP_SIG, sizeof(rsdp->signature)); + + if(is_rsdp == 0) + { + //If yes, calculates rdsp checksum and check It + checksum = acpi_checksum(rsdp, sizeof(struct acpi_rsdp)); + + if(checksum != 0) + { + ret_value = BAD_CHECKSUM; + } + } + else + { + ret_value = BAD_SIGNATURE; + } + } + + return ret_value; +} + +/* acpi_search_rsdp: search the rsdp table in a memory range + * + * Receives as input the initial virtual address, and the lenght + * of memory range + * + * Returns the reference to rsdp structure if success, NULL if failure + */ + +static struct acpi_rsdp* +acpi_search_rsdp(void *addr, uint32_t length) +{ + struct acpi_rsdp *rsdp = NULL; + + void *end; + /* check alignment */ + if((uint32_t)addr & (ACPI_RSDP_ALIGN-1)) + return NULL; + + //Search RDSP in memory space between addr and addr+lenght + for(end = addr+length; addr < end; addr += ACPI_RSDP_ALIGN) + { + + //Check if the current memory block store the RDSP + if(acpi_check_rsdp(addr) == 0) + { + //If yes, store RSDP address + rsdp = (struct acpi_rsdp*) addr; + break; + } + } + + return rsdp; +} + +/* acpi_get_rsdp: tries to find the RSDP table, + * searching It in many memory ranges, following ACPI docs + * + * Returns the reference to RDSP structure if success, NULL if failure + */ + +struct acpi_rsdp* +acpi_get_rsdp(void) +{ + struct acpi_rsdp *rsdp = NULL; + uint16_t *start = 0x0; + uint32_t base = 0x0; + + //EDBA start address + start = (uint16_t*) phystokv(0x040e); + base = *start; + + if(base != 0) //Memory check + { + + base <<= 4; //base = base * 16 + + //Search RSDP in first 1024 bytes from EDBA + rsdp = acpi_search_rsdp((void*)base,1024); + } + + if(rsdp == NULL) + { + //If RSDP isn't in EDBA, search in the BIOS read-only memory space between 0E0000h and 0FFFFFh + rsdp = acpi_search_rsdp((void*) 0x0e0000, 0x100000 - 0x0e0000); + } + + return rsdp; +} + +/* acpi_check_rsdt: check if the RSDT initial address is correct + * checking its checksum + * + * Receives as input a reference for the RSDT "candidate" table + * Returns 0 if success + */ + +static int +acpi_check_rsdt(struct acpi_rsdt *rsdt) +{ + uint32_t checksum; + int ret_value = 0; + + if(rsdt == NULL) + { + ret_value = NO_RSDT; + } + else + { + checksum = acpi_checksum(rsdt, rsdt->header.length); + + if (checksum != 0) + { + ret_value = BAD_CHECKSUM; + } + } + + return ret_value; +} + +/* acpi_get_rsdt: Get RSDT table reference from RSDP entries + * + * Receives as input a reference for RSDP table + * and a reference to store the number of entries of RSDT + * + * Returns the reference to RSDT table if success, NULL if error + */ + +static struct acpi_rsdt* +acpi_get_rsdt(struct acpi_rsdp *rsdp, int* acpi_rsdt_n) +{ + phys_addr_t rsdt_phys; + struct acpi_rsdt *rsdt = NULL; + int acpi_check; + int signature_check; + + if(rsdp != NULL) + { + //Get rsdt address from rsdp + rsdt_phys = rsdp->rsdt_addr; + rsdt = (struct acpi_rsdt*) kmem_map_aligned_table(rsdt_phys, sizeof(struct acpi_rsdt), VM_PROT_READ); + + printf("found rsdt in address %x\n", rsdt); + + //Check is rsdt signature is equals to ACPI RSDT signature + signature_check = memcmp(rsdt->header.signature, ACPI_RSDT_SIG, + sizeof(rsdt->header.signature)); + + if(signature_check == 0) + { + //Check if rsdt is correct + acpi_check = acpi_check_rsdt(rsdt); + + if(acpi_check == 0) + { + //Calculated number of elements stored in rsdt + *acpi_rsdt_n = (rsdt->header.length - sizeof(rsdt->header)) + / sizeof(rsdt->entry[0]); + + } + } + + if (signature_check != 0 || acpi_check != 0) + { + rsdt = NULL; + } + } + + return rsdt; +} + +/* acpi_get_apic: get MADT/APIC table from RSDT entries + * + * Receives as input the RSDT initial address, + * and the number of entries of RSDT table + * + * Returns a reference to APIC/MADT table if success, NULL if failure + */ + +static struct acpi_apic* +acpi_get_apic(struct acpi_rsdt *rsdt, int acpi_rsdt_n) +{ + struct acpi_apic *apic = NULL; + struct acpi_dhdr *descr_header; + int check_signature; + + int i; + + if(rsdt != NULL) + { + //Search APIC entries in rsdt array + for(i = 0; i < acpi_rsdt_n; i++) + { + descr_header = (struct acpi_dhdr*) kmem_map_aligned_table(rsdt->entry[i], sizeof(struct acpi_dhdr), VM_PROT_READ); + + //Check if the entry contains an APIC + check_signature = memcmp(descr_header->signature, ACPI_APIC_SIG, sizeof(descr_header->signature)); + + if(check_signature == 0) + { + //If yes, store the entry in apic + apic = (struct acpi_apic*) kmem_map_aligned_table(rsdt->entry[i], sizeof(struct acpi_apic), VM_PROT_READ | VM_PROT_WRITE); + + printf("found apic in address %x\n", apic); + break; + } + } + } + + return apic; +} + +/* acpi_add_lapic: add a new Local APIC to cpu_to_lapic array + * and increase the number of cpus + * + * Receives as input the Local APIC entry in MADT/APIC table + */ + + +static int +acpi_apic_add_lapic(struct acpi_apic_lapic *lapic_entry) +{ + int ret_value = 0; + int lapic_id; + + + if(lapic_entry == NULL) + { + ret_value = -1; + } + else + { + //If cpu flag is correct + if(lapic_entry->flags & 0x1) + { + //Enumerate CPU and add It to cpu/apic vector + lapic_id = lapic_entry->apic_id; + + apic_add_cpu(lapic_id); + + printf("new cpu found with apic id %x\n", lapic_entry->apic_id);; + } + } + + return ret_value; +} + +/* apic_add_ioapic: add a new IOAPIC to IOAPICS array + * and increase the number of IOAPIC + * + * Receives as input the IOAPIC entry in MADT/APIC table + */ + +static int +acpi_apic_add_ioapic(struct acpi_apic_ioapic *ioapic_entry) +{ + int ret_value = 0; + struct ioapic_data io_apic; + + if(ioapic_entry == NULL) + { + ret_value = -1; + } + else + { + /*Insert ioapic in ioapics array*/ + io_apic.apic_id = ioapic_entry->apic_id; + io_apic.addr = ioapic_entry->addr; + io_apic.base = ioapic_entry->base; + + apic_add_ioapic(io_apic); + + printf("new ioapic found with apic id %x\n", io_apic.apic_id); + } + + return ret_value; +} + + +/* apic_add_ioapic: add a new IOAPIC to IOAPICS array + * and increase the number of IOAPIC + * + * Receives as input the IOAPIC entry in MADT/APIC table + */ + +static int +acpi_apic_add_irq_override(struct acpi_apic_irq_override* irq_override) +{ + int ret_value = 0; + struct irq_override_data irq_over; + + + if(irq_override == NULL) + { + ret_value = -1; + } + else + { + /*Insert ioapic in ioapics array*/ + irq_over.bus = irq_override->bus; + irq_over.irq = irq_override->irq; + irq_over.gsi = irq_override->gsi; + irq_over.flags = irq_override->flags; + + apic_add_irq_override(irq_over); + } + + return ret_value; +} + + +/* apic_parse_table: parse the MADT/APIC table + * Read the APIC/MADT table entry to entry, + * registering the Local APIC or IOAPIC entries + */ + +static int +apic_parse_table(struct acpi_apic *apic) +{ + int ret_value = 0; + struct acpi_apic_dhdr *apic_entry = NULL; + uint32_t end = 0; + + if(apic != NULL) + { + apic_entry = apic->entry; + end = (uint32_t) apic + apic->header.length; + + //Search in APIC entry + while((uint32_t)apic_entry < end) + { + struct acpi_apic_lapic *lapic_entry; + struct acpi_apic_ioapic *ioapic_entry; + struct acpi_apic_irq_override *irq_override_entry; + + //Check entry type + switch(apic_entry->type) + { + + //If APIC entry is a CPU lapic + case ACPI_APIC_ENTRY_LAPIC: + + //Store lapic + lapic_entry = (struct acpi_apic_lapic*) apic_entry; + + acpi_apic_add_lapic(lapic_entry); + + break; + + //If APIC entry is an IOAPIC + case ACPI_APIC_ENTRY_IOAPIC: + + //Store ioapic + ioapic_entry = (struct acpi_apic_ioapic*) apic_entry; + + acpi_apic_add_ioapic(ioapic_entry); + + break; + + //TODO: Parse IRQ OVERRIDE entry + case ACPI_APIC_IRQ_OVERRIDE: + irq_override_entry = (struct acpi_apic_irq_override*) apic_entry; + + acpi_apic_add_irq_override(irq_override_entry); + break; + + } + + //Get next APIC entry + apic_entry = (struct acpi_apic_dhdr*)((uint32_t) apic_entry + + apic_entry->length); + } + } + else //apic == NULL + { + ret_value = -1; + } + + return ret_value; +} + + +/* acpi_apic_setup: parses the APIC/MADT table. + * to find the Local APIC and IOAPIC structures + * and the common address for Local APIC + * + * Receives as input a reference for APIC/MADT table + * Returns 0 if success + * + * Fills the cpu_to_lapic and ioapics array, indexed by Kernel ID + * with a relationship between Kernel ID and APIC ID, + * and map the Local APIC common address, to fill the lapic reference + */ + +static int +acpi_apic_setup(struct acpi_apic *apic) +{ + int apic_checksum; + int ret_value = 0; + ApicLocalUnit* lapic; + int ncpus, nioapics; + int init_success = 0; + + + if(apic != NULL) + { + + //Check the checksum of the APIC + apic_checksum = acpi_checksum(apic, apic->header.length); + + if(apic_checksum != 0) + { + ret_value = BAD_CHECKSUM; + } + else + { + init_success = apic_data_init(); + if(init_success == 0) + { + + printf("lapic found in address %x\n", apic->lapic_addr); + //map common lapic address + lapic = kmem_map_aligned_table(apic->lapic_addr, sizeof(ApicLocalUnit), VM_PROT_READ); + + if(lapic != NULL){ + printf("lapic mapped in address %x\n", lapic); + apic_lapic_init(lapic); + } + + apic_parse_table(apic); + + ncpus = apic_get_numcpus(); + nioapics = apic_get_num_ioapics(); + + if(ncpus == 0 || nioapics == 0) + { + ret_value = -1; + } + else + { + apic_refit_cpulist(); + printf("%d cpus found. %d ioapics found\n", ncpus, nioapics); + } + } + else{ + ret_value = -1; + } + } + } + else /* apic == NULL */ + { + ret_value = NO_APIC; + } + + return ret_value; +} + diff --git a/i386/i386at/acpi_parse_apic.h b/i386/i386at/acpi_parse_apic.h new file mode 100644 index 00000000..ce60279d --- /dev/null +++ b/i386/i386at/acpi_parse_apic.h @@ -0,0 +1,142 @@ +/*Copyright 2018 Juan Bosco Garcia + *This file is part of Min_SMP. + *Min_SMP 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 of the License, or + *(at your option) any later version. + *Min_SMP 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 Min_SMP. If not, see . + */ + +#ifndef __ACPI_H__ +#define __ACPI_H__ + +#include //uint16_t, uint32_t_t... + + +#define ACPI_RSDP_ALIGN 16 +#define ACPI_RSDP_SIG "RSD PTR " + + +struct acpi_rsdp +{ + uint8_t signature[8]; + uint8_t checksum; + uint8_t oem_id[6]; + uint8_t revision[1]; + uint32_t rsdt_addr; +} __attribute__((__packed__)); + + +/* RSDT Entry Header + * + * Header which stores the descriptors of tables pointed from RDSP's Entry Field + * Includes the signature of the table, to identify each table + * + * In MADT, the signature is 'APIC' + */ +struct acpi_dhdr +{ + uint8_t signature[4]; + uint32_t length; + uint8_t revision; + uint8_t checksum; + uint8_t oem_id[6]; + uint8_t oem_table_id[8]; + uint32_t oem_revision; + uint8_t creator_id[4]; + uint32_t creator_revision; +} __attribute__((__packed__)); + + +#define ACPI_RSDT_SIG "RSDT" + +struct acpi_rsdt +{ + struct acpi_dhdr header; + uint32_t entry[0]; +} __attribute__((__packed__)); + +//APIC table signature +#define ACPI_APIC_SIG "APIC" + +//Types value for MADT entries: Local APIC, IOAPIC and IRQ Override +#define ACPI_APIC_ENTRY_LAPIC 0 +#define ACPI_APIC_ENTRY_IOAPIC 1 +#define ACPI_APIC_IRQ_OVERRIDE 2 +#define ACPI_APIC_ENTRY_NONMASK_IRQ 4 + +/* APIC descriptor header + * Define the type of the structure (Local APIC, I/O APIC or others) + * Type: Local APIC (0), I/O APIC (1) + */ +struct acpi_apic_dhdr +{ + uint8_t type; + uint8_t length; +} __attribute__((__packed__)); + + +/* Multiple APIC Description Table (MADT) + * + * Describes the APIC structures which exist in the machine + * Includes the common address where Local APIC is mapped in main memory + * + * Entry field stores the descriptors of APIC structures + */ +struct acpi_apic +{ + struct acpi_dhdr header; //Header, which stores the descriptor for RDST's Entry field + uint32_t lapic_addr; //Local Interrupt Controller Address + uint32_t flags; + struct acpi_apic_dhdr entry[0]; //Interrupt Controller Structure +} __attribute__((__packed__)); + +/* Processor Local APIC Structure + * + * Stores information about APIC ID, flags and ACPI Processor UID + */ + +struct acpi_apic_lapic +{ + struct acpi_apic_dhdr header; + uint8_t processor_id; //ACPI Processor UID + uint8_t apic_id; + uint32_t flags; +} __attribute__((__packed__)); + + +/* I/O APIC Structure + * + * Stores information about APIC ID, and I/O APIC tables + */ + +struct acpi_apic_ioapic +{ + struct acpi_apic_dhdr header; + uint8_t apic_id; + uint8_t reserved; + uint32_t addr; + uint32_t base; +} __attribute__((__packed__)); + + +struct acpi_apic_irq_override +{ + struct acpi_apic_dhdr header; + uint8_t bus; + uint8_t irq; + uint32_t gsi; + uint16_t flags; +} __attribute__((__packed__)); + +int acpi_apic_init(void); +void acpi_print_info(struct acpi_rsdp *rsdp, struct acpi_rsdt *rsdt, int acpi_rsdt_n); + + +#endif /* __ACPI_H__ */ + diff --git a/i386/i386at/model_dep.c b/i386/i386at/model_dep.c index aaeed807..75a902d5 100644 --- a/i386/i386at/model_dep.c +++ b/i386/i386at/model_dep.c @@ -72,6 +72,8 @@ #include #include #include +#include + #ifdef MACH_XEN #include #include @@ -170,6 +172,14 @@ void machine_init(void) linux_init(); #endif +#if NCPUS > 1 + int smp_success = smp_init(); + + if(smp_success != 0) { + printf("Error: no SMP found"); + } +#endif // NCPUS > 1 + /* * Find the devices */ diff --git a/kern/smp.c b/kern/smp.c new file mode 100644 index 00000000..0dec3563 --- /dev/null +++ b/kern/smp.c @@ -0,0 +1,69 @@ +/* smp.c - Template for generic SMP controller 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 + +struct smp_data smp_info; + +#ifdef __i386__ + #include + #include + + /* smp_data_init: initialize smp_data structure + * Must be called after smp_init(), once all APIC structures + * has been initialized + */ + + void smp_data_init(void) + { + smp_info.num_cpus = apic_get_numcpus(); + } + + /* smp_get_numcpus: returns the number of cpus existing in the machine */ + + int smp_get_numcpus(void) + { + return smp_info.num_cpus; + } + + /* smp_get_current_cpu: return the hardware identifier (APIC ID in x86) of current CPU */ + + int smp_get_current_cpu(void) + { + return apic_get_current_cpu(); + } + + /* smp_init: initialize the SMP support, starting the cpus searching + * and enumeration */ + + int smp_init(void) + { + int apic_success; + + apic_success = acpi_apic_init(); + if (apic_success) + { + smp_data_init(); + } + + return apic_success; + } + +#endif diff --git a/kern/smp.h b/kern/smp.h new file mode 100644 index 00000000..0b191f9d --- /dev/null +++ b/kern/smp.h @@ -0,0 +1,28 @@ +/* smp.h - Template for generic SMP controller for Mach. Header file + 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. */ + +struct smp_data{ + int num_cpus; +}; + +void smp_data_init(void); +int smp_get_numcpus(void); +int smp_get_current_cpu(void); +int smp_init(void); diff --git a/vm/vm_kern.c b/vm/vm_kern.c index 2e333ee1..ce97bad3 100644 --- a/vm/vm_kern.c +++ b/vm/vm_kern.c @@ -66,14 +66,14 @@ vm_map_t kernel_pageable_map; /* * projected_buffer_allocate * - * Allocate a wired-down buffer shared between kernel and user task. + * Allocate a wired-down buffer shared between kernel and user task. * Fresh, zero-filled memory is allocated. * If persistence is false, this buffer can only be deallocated from - * user task using projected_buffer_deallocate, and deallocation + * user task using projected_buffer_deallocate, and deallocation * from user task also deallocates the buffer from the kernel map. * projected_buffer_collect is called from vm_map_deallocate to * automatically deallocate projected buffers on task_deallocate. - * Sharing with more than one user task is achieved by using + * Sharing with more than one user task is achieved by using * projected_buffer_map for the second and subsequent tasks. * The user is precluded from manipulating the VM entry of this buffer * (i.e. changing protection, inheritance or machine attributes). @@ -99,7 +99,7 @@ projected_buffer_allocate( return(KERN_INVALID_ARGUMENT); /* - * Allocate a new object. + * Allocate a new object. */ size = round_page(size); @@ -191,7 +191,7 @@ projected_buffer_map( kern_return_t kr; /* - * Find entry in kernel map + * Find entry in kernel map */ size = round_page(size); @@ -252,7 +252,7 @@ projected_buffer_map( kern_return_t projected_buffer_deallocate( vm_map_t map, - vm_offset_t start, + vm_offset_t start, vm_offset_t end) { vm_map_entry_t entry, k_entry; @@ -281,7 +281,7 @@ projected_buffer_deallocate( vm_map_entry_delete(map, entry); vm_map_unlock(map); - /*Check if the buffer is not persistent and only the + /*Check if the buffer is not persistent and only the kernel mapping remains, and if so delete it*/ vm_map_lock(kernel_map); if (k_entry->projected_on == (vm_map_entry_t) -1 && @@ -324,14 +324,14 @@ projected_buffer_collect(vm_map_t map) /* * projected_buffer_in_range * - * Verifies whether a projected buffer exists in the address range + * Verifies whether a projected buffer exists in the address range * given. */ boolean_t projected_buffer_in_range( vm_map_t map, - vm_offset_t start, + vm_offset_t start, vm_offset_t end) { vm_map_entry_t entry; @@ -635,6 +635,39 @@ retry: return KERN_SUCCESS; } +/* kmem_map_aligned_table: map a table or structure in a virtual memory page + * Align the table initial address with the page initial address + * + * Parameters: + * phys_address: physical address, the start address of the table + * size: size of the table + * mode: access mode. VM_PROT_READ for read, VM_PROT_WRITE for write + * + * Returns a reference to the virtual address if success, NULL if failure + */ + +void* +kmem_map_aligned_table (unsigned long phys_address, unsigned long size, int mode) +{ + vm_offset_t virt_addr; + kern_return_t ret; + uintptr_t into_page = phys_address % PAGE_SIZE; + uintptr_t nearest_page = (uintptr_t)trunc_page(phys_address); + + size += into_page; + + ret = kmem_alloc_wired(kernel_map, &virt_addr, round_page (size)); + + if (ret != KERN_SUCCESS) + return NULL; + + (void) pmap_map_bd (virt_addr, nearest_page, nearest_page + round_page (size), mode); + + /* XXX remember mapping somewhere so we can free it? */ + + return (void *) (virt_addr + into_page); +} + /* * kmem_alloc_pageable: * @@ -695,7 +728,7 @@ void kmem_alloc_pages( vm_object_t object, vm_offset_t offset, - vm_offset_t start, + vm_offset_t start, vm_offset_t end, vm_prot_t protection) { @@ -751,7 +784,7 @@ void kmem_remap_pages( vm_object_t object, vm_offset_t offset, - vm_offset_t start, + vm_offset_t start, vm_offset_t end, vm_prot_t protection) { @@ -808,9 +841,9 @@ kmem_remap_pages( void kmem_submap( - vm_map_t map, + vm_map_t map, vm_map_t parent, - vm_offset_t *min, + vm_offset_t *min, vm_offset_t *max, vm_size_t size) { @@ -915,7 +948,7 @@ kmem_io_map_copyout( /* * If total size is larger than one page list and * we don't have to do more than one page list, then - * only do one page list. + * only do one page list. * * XXX Could be much smarter about this ... like trimming length * XXX if we need more than one page list but not all of them. @@ -962,7 +995,7 @@ kmem_io_map_copyout( /* * Onward to the next page_list. The extend_cont - * leaves the current page list's pages alone; + * leaves the current page list's pages alone; * they'll be cleaned up at discard. Reset this * copy's continuation to discard the next one. */ @@ -998,7 +1031,7 @@ kmem_io_map_deallocate( /* * Remove the mappings. The pmap_remove is needed. */ - + pmap_remove(vm_map_pmap(map), addr, addr + size); vm_map_remove(map, addr, addr + size); } @@ -1014,7 +1047,7 @@ kmem_io_map_deallocate( int copyinmap( vm_map_t map, - char *fromaddr, + char *fromaddr, char *toaddr, int length) { @@ -1041,7 +1074,7 @@ int copyinmap( int copyoutmap( vm_map_t map, - char *fromaddr, + char *fromaddr, char *toaddr, int length) { diff --git a/vm/vm_kern.h b/vm/vm_kern.h index 0cdb19db..46a1a274 100644 --- a/vm/vm_kern.h +++ b/vm/vm_kern.h @@ -55,6 +55,8 @@ extern kern_return_t kmem_alloc_pageable(vm_map_t, vm_offset_t *, extern kern_return_t kmem_valloc(vm_map_t, vm_offset_t *, vm_size_t); extern kern_return_t kmem_alloc_wired(vm_map_t, vm_offset_t *, vm_size_t); extern kern_return_t kmem_alloc_aligned(vm_map_t, vm_offset_t *, vm_size_t); +extern void* kmem_map_aligned_table (unsigned long phys_address, unsigned long size, int mode); + extern void kmem_free(vm_map_t, vm_offset_t, vm_size_t); extern void kmem_submap(vm_map_t, vm_map_t, vm_offset_t *,