/*Copyright 2018 Juan Bosco Garcia *Copyright 2018 2019 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 3 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 volatile ApicLocalUnit* lapic = (void*) 0; uint32_t lapic_addr = 0; int ncpu = 1; int nioapic = 0; struct acpi_rsdp *rsdp; struct acpi_rsdt *rsdt; int acpi_rsdt_n; struct acpi_apic *apic; static int acpi_get_rsdp(); static int acpi_check_rsdt(struct acpi_rsdt *); static int acpi_get_rsdt(); static int acpi_apic_setup(); extern struct machine_slot machine_slot[NCPUS]; int apic2kernel[256]; /*TODO: Implement ioapic support*/ struct ioapic ioapics[16]; int acpi_setup() { int j; for(j = 0; j < 256; j++) { apic2kernel[j] = -1; } //Try to get rsdp pointer if(acpi_get_rsdp() || rsdp==0) return -1; //Try to get rsdt pointer if(acpi_get_rsdt() || rsdt==0) return -1; //Search APIC entries in rsdt array int i; struct acpi_dhdr *descr_header; for(i = 0;i < acpi_rsdt_n; i++){ descr_header = (struct acpi_dhdr*) phystokv(rsdt->entry[i]); //Check if the entry contains an APIC if(memcmp(descr_header->signature, ACPI_APIC_SIG, sizeof(descr_header->signature)) == 0){ //If yes, store the entry in apic apic = (struct acpi_apic*) phystokv(rsdt->entry[i]); } } if(acpi_apic_setup()) return -1; return 0; } void acpi_print_info(){ printf("ACPI:\n"); printf(" rsdp = %x; rsdp->rsdt_addr = %x\n", rsdp, phystokv(rsdp->rsdt_addr)); printf(" rsdt = %x; rsdt->length = %x (n = %x)\n", rsdt, rsdt->header.length, acpi_rsdt_n); int i; struct acpi_dhdr *descr_header; for(i = 0; i < acpi_rsdt_n; i++){ descr_header = (struct acpi_dhdr*) phystokv(rsdt->entry[i]); printf(" %x: %c%c%c%c (%x)\n", i, descr_header->signature[0], descr_header->signature[1], descr_header->signature[2], descr_header->signature[3], phystokv(rsdt->entry[i])); } } 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; } static int acpi_check_rsdp(struct acpi_rsdp *rsdp){ //Check is rsdp signature is equals to ACPI RSDP signature if(memcmp(rsdp->signature, ACPI_RSDP_SIG, sizeof(rsdp->signature)) != 0) return -1; //If yes, calculates rdsp checksum and check It uint32_t checksum; checksum = acpi_checksum(rsdp, sizeof(*rsdp)); if(checksum != 0) return -1; return 0; } static int acpi_search_rsdp(void *addr, uint32_t length){ void *end; /* check alignment */ if((uint32_t)addr & (ACPI_RSDP_ALIGN-1)) return -1; //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; return 0; } } return -1; } static int acpi_get_rsdp(){ 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 if(acpi_search_rsdp((void*)phystokv(base),1024) == 0) return 0; } //If RSDP isn't in EDBA, search in the BIOS read-only memory space between 0E0000h and 0FFFFFh if(acpi_search_rsdp((void*) phystokv(0x0e0000), 0x100000 - 0x0e0000) == 0) return 0; return -1; } static int acpi_check_rsdt(struct acpi_rsdt *rsdt){ return acpi_checksum(rsdt, rsdt->header.length); } static int acpi_get_rsdt(){ //Get rsdt address from rsdp rsdt = (struct acpi_rsdt*) phystokv(rsdp->rsdt_addr); //Check is rsdt signature is equals to ACPI RSDT signature if(memcmp(rsdt->header.signature, ACPI_RSDT_SIG, sizeof(rsdt->header.signature)) != 0) return -1; //Check if rsdt is correct if(acpi_check_rsdt(rsdt)) return -1; //Calculated number of elements stored in rsdt acpi_rsdt_n = (rsdt->header.length - sizeof(rsdt->header)) / sizeof(rsdt->entry[0]); return 0; } static int acpi_apic_setup(){ if(apic == 0) return -1; //Check the checksum of the APIC if(acpi_checksum(apic, apic->header.length)) return -1; ncpu = 0; nioapic = 0; /* * save lapic_addr in order to use it later for updating lapic, * in extra_setup() */ lapic_addr = apic->lapic_addr; struct acpi_apic_dhdr *apic_entry = apic->entry; uint32_t 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; //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; //If cpu flag is correct, and the maximum number of CPUs is not reached if((lapic_entry->flags & 0x1) && ncpu < NCPUS){ //Enumerate CPU and add It to cpu/apic vector machine_slot[ncpu].apic_id = lapic_entry->apic_id; machine_slot[ncpu].is_cpu = TRUE; apic2kernel[lapic_entry->apic_id] = ncpu; //Increase number of CPU ncpu++; } break; //If APIC entry is an IOAPIC case ACPI_APIC_ENTRY_IOAPIC: //Store ioapic ioapic_entry = (struct acpi_apic_ioapic*) apic_entry; /*Insert ioapic in ioapics array*/ ioapics[nioapic].apic_id = ioapic_entry->apic_id; ioapics[nioapic].addr = ioapic_entry->addr; ioapics[nioapic].base = ioapic_entry->base; //Increase number of ioapic nioapic++; break; } //Get next APIC entry apic_entry = (struct acpi_apic_dhdr*)((uint32_t) apic_entry + apic_entry->length); } if(ncpu == 0 || nioapic == 0) return -1; return 0; } int extra_setup() { if (lapic_addr == 0) { printf("LAPIC mapping skipped\n"); return 1; } vm_offset_t virt = 0; // TODO: FIX: it might be desirable to map LAPIC memory with attribute PCD // (Page Cache Disable) long ret = vm_map_physical(&virt, lapic_addr, sizeof(ApicLocalUnit), 0); if (ret) { panic("Could not map LAPIC"); return -1; } else { lapic = (ApicLocalUnit*)virt; printf("LAPIC mapped: physical: 0x%lx virtual: 0x%lx version: 0x%x\n", (unsigned long)lapic_addr, (unsigned long)virt, (unsigned)lapic->version.r); return 0; } }