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
use crate::{parse_validated_rsdp, rsdp::Rsdp, Acpi, AcpiError, AcpiHandler};
use core::{mem, ops::RangeInclusive};
use log::warn;

/// The pointer to the EBDA (Extended Bios Data Area) start segment pointer
const EBDA_START_SEGMENT_PTR: usize = 0x40e;
/// The earliest (lowest) memory address an EBDA (Extended Bios Data Area) can start
const EBDA_EARLIEST_START: usize = 0x80000;
/// The end of the EBDA (Extended Bios Data Area)
const EBDA_END: usize = 0x9ffff;
/// The start of the main bios area below 1mb in which to search for the RSDP
/// (Root System Description Pointer)
const RSDP_BIOS_AREA_START: usize = 0xe0000;
/// The end of the main bios area below 1mb in which to search for the RSDP
/// (Root System Description Pointer)
const RSDP_BIOS_AREA_END: usize = 0xfffff;
/// The RSDP (Root System Description Pointer)'s signature, "RSD PTR " (note trailing space)
const RSDP_SIGNATURE: &'static [u8; 8] = b"RSD PTR ";

/// Find the begining of the EBDA (Extended Bios Data Area) and return `None` if the ptr at
/// `0x40e` is invalid.
pub fn find_search_areas<H>(handler: &mut H) -> [RangeInclusive<usize>; 2]
where
    H: AcpiHandler,
{
    // Read base segment from BIOS area. This is not always given by the bios, so it needs to be
    // checked. We left shift 4 because it is a segment ptr.
    let ebda_start_mapping =
        unsafe { handler.map_physical_region::<u16>(EBDA_START_SEGMENT_PTR, mem::size_of::<u16>()) };
    let ebda_start = (*ebda_start_mapping as usize) << 4;
    handler.unmap_physical_region(ebda_start_mapping);

    [
        // Main bios area below 1 mb
        // In practice (from my [Restioson's] testing, at least), the RSDP is more often here than
        // the in EBDA. Also, if we cannot find the EBDA, then we don't want to search the largest
        // possible EBDA first.
        RSDP_BIOS_AREA_START..=RSDP_BIOS_AREA_END,
        // Check if base segment ptr is in valid range for EBDA base
        if (EBDA_EARLIEST_START..EBDA_END).contains(&ebda_start) {
            // First kb of EBDA
            ebda_start..=ebda_start + 1024
        } else {
            // We don't know where the EBDA starts, so just search the largest possible EBDA
            EBDA_EARLIEST_START..=EBDA_END
        },
    ]
}

/// This is the entry point of `acpi` if you have no information except that the machine is running
/// BIOS and not UEFI. It maps the RSDP, works out what version of ACPI the hardware supports, and
/// passes the physical address of the RSDT/XSDT to `parse_rsdt`.
///
/// # Unsafety
///
/// This function is unsafe because it may read from protected memory if the computer is using UEFI.
/// Only use this function if you are sure the computer is using BIOS.
pub unsafe fn search_for_rsdp_bios<H>(handler: &mut H) -> Result<Acpi, AcpiError>
where
    H: AcpiHandler,
{
    // The areas that will be searched for the RSDP
    let areas = find_search_areas(handler);

    // On x86 it is more efficient to map 4096 bytes at a time because of how paging works
    let mut area_mapping = handler.map_physical_region::<[[u8; 8]; 0x1000 / 8]>(
        areas[0].clone().next().unwrap() & !0xfff, // Get frame addr
        0x1000,
    );

    // Signature is always on a 16 byte boundary so only search there
    for address in areas.iter().flat_map(|i| i.clone()).step_by(16) {
        let mut mapping_start = area_mapping.physical_start as usize;
        if !(mapping_start..mapping_start + 0x1000).contains(&address) {
            handler.unmap_physical_region(area_mapping);
            area_mapping = handler.map_physical_region::<[[u8; 8]; 0x1000 / 8]>(
                address & !0xfff, // Get frame addr
                0x1000,
            );

            // Update if mapping remapped
            mapping_start = area_mapping.physical_start as usize;
        }

        let index = (address - mapping_start) / 8;
        let signature = (*area_mapping)[index];

        if signature != *RSDP_SIGNATURE {
            continue;
        }

        let rsdp_mapping = handler.map_physical_region::<Rsdp>(address, mem::size_of::<Rsdp>());

        if let Err(e) = (*rsdp_mapping).validate() {
            warn!("Invalid RSDP found at 0x{:x}: {:?}", address, e);
            continue;
        }

        handler.unmap_physical_region(area_mapping);
        return parse_validated_rsdp(handler, rsdp_mapping);
    }

    Err(AcpiError::NoValidRsdp)
}