1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//! ACPI detection
//!
//! This module is in charge of detecting the presence of ACPI and provide other part of the kernel with the data about the system.

#![allow(dead_code)]

use acpi::Acpi;
use acpi::AcpiHandler;
use acpi::PhysicalMapping;

use crate::sync::Once;

use crate::mem::{VirtualAddress, PhysicalAddress};
use crate::paging;
use crate::paging::PageState;
use crate::paging::PAGE_SIZE;
use crate::paging::MappingAccessRights;
use crate::frame_allocator::PhysicalMemRegion;

use crate::utils;

use super::multiboot;

/// Stores the ACPI data
static ACPI_INFO: Once<Acpi> = Once::new();


/// Get a reference to the ACPI information.
///
/// # Panics
///
/// Panics if the module hasn't been inited yet or if ACPI isn't availaible.
pub fn get_acpi_information() -> &'static Acpi {
    ACPI_INFO.r#try().expect("Acpi is not availaible")
}

/// Tries to get a pointer to the acpi information structure.
///
/// Returns `None` if the module hasn't been inited yet or if ACPI isn't availaible.
pub fn try_get_acpi_information() -> Option<&'static Acpi> {
    ACPI_INFO.r#try()
}

/// ACPI Memory handler
struct MemoryHandler;

impl AcpiHandler for MemoryHandler {
    fn map_physical_region<T>(
        &mut self,
        physical_address: usize,
        size: usize,
    ) -> PhysicalMapping<T> {
        let physical_address_aligned = utils::align_down(physical_address, PAGE_SIZE);

        let offset = physical_address - physical_address_aligned;
        let aligned_size = utils::align_up(offset + size, PAGE_SIZE);

        let physical_mem = unsafe { PhysicalMemRegion::new_unchecked(PhysicalAddress(physical_address_aligned), aligned_size) };
        let virtual_address = paging::kernel_memory::get_kernel_memory().map_phys_region(physical_mem, MappingAccessRights::k_r());

        PhysicalMapping {
            physical_start: physical_address,
            virtual_start: unsafe { core::ptr::NonNull::new_unchecked((virtual_address.0 + offset) as *mut T) },
            region_length: aligned_size,
            mapped_length: aligned_size,
        }
    }

    fn unmap_physical_region<T>(&mut self, region: PhysicalMapping<T>) {
        let virtual_address_aligned = utils::align_down(region.virtual_start.as_ptr() as usize, PAGE_SIZE);
        paging::kernel_memory::get_kernel_memory().unmap_no_dealloc(VirtualAddress(virtual_address_aligned), region.mapped_length);
    }
}

/// Parse RSDP from multiboot2 a tag.
unsafe fn parse_rsdp_tag(memory_handler: &mut MemoryHandler, rsdp_virtual_address: usize) -> bool {
    let rsdp_virtual_address_aligned = utils::align_down(rsdp_virtual_address, PAGE_SIZE);

    let offset = rsdp_virtual_address - rsdp_virtual_address_aligned;
    let rsdp_physical_address = match paging::kernel_memory::get_kernel_memory().mapping_state(VirtualAddress(rsdp_virtual_address_aligned)) {
        PageState::Present(rsdp_physical_address) => rsdp_physical_address.addr() + offset,
        _ => panic!("RSDP VIRTUAL MAPPING DOESN'T MAP TO ANYTHING???")
    };

    if let Ok(acpi) = acpi::parse_rsdp(memory_handler, rsdp_physical_address) {
        ACPI_INFO.call_once(|| {
            acpi
        });
        true
    } else {
        false
    }
}

/// Parse ACPI tables and store them.
///
/// # Safety
///
/// May only be called once.
pub unsafe fn init() {
    let mut handler = MemoryHandler;
    let mut is_init = false;

    if let Some(multiboot_info) = multiboot::try_get_boot_information() {
        if let Some(rsdp_v1_info) = multiboot_info.rsdp_v1_tag() {
            info!("Found RSDP v1 multiboot2 tag at address {:x}", rsdp_v1_info.rsdt_address());

            // Multiboot2 hold a copy of the RSDP but have two extra fields at the begining, we are ignoring them.
            let rsdp_virtual_address = (rsdp_v1_info as *const _ as usize) + 0x8;
            is_init = parse_rsdp_tag(&mut handler, rsdp_virtual_address);
        }
        else if let Some(rsdp_v2_info) = multiboot_info.rsdp_v2_tag() {
            info!("Found RSDP v2 multiboot2 tag at address {:x}", rsdp_v2_info.xsdt_address());

            let rsdp_virtual_address = (rsdp_v2_info as *const _ as usize) + 0x8;
            is_init = parse_rsdp_tag(&mut handler, rsdp_virtual_address);
        }
    }
    if !is_init {
        if let Ok(acpi) = acpi::search_for_rsdp_bios(&mut handler) {
            info!("Found RSDP inside BIOS memory");

            ACPI_INFO.call_once(|| {
                acpi
            });

            is_init = true;
        }
    }


    if !is_init {
        info!("ACPI is not supported by this system");
    } else {
        info!("ACPI is supported by this system")
    }
}