// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2021 Andre Richter <andre.o.richter@gmail.com>

//! BSP Memory Management.
//!
//! The physical memory layout after the kernel has been loaded by the Raspberry's firmware, which
//! copies the binary to 0x8_0000:
//!
//! +---------------------------------------------+
//! |                                             |
//! | Unmapped                                    |
//! |                                             |
//! +---------------------------------------------+
//! |                                             | rx_start @ 0x8_0000
//! | .text                                       |
//! | .rodata                                     |
//! | .got                                        |
//! |                                             | rx_end_inclusive
//! +---------------------------------------------+
//! |                                             | rw_start == rx_end
//! | .data                                       |
//! | .bss                                        |
//! |                                             | rw_end_inclusive
//! +---------------------------------------------+
//! |                                             | rw_end
//! | Unmapped Boot-core Stack Guard Page         |
//! |                                             |
//! +---------------------------------------------+
//! |                                             | boot_core_stack_start          ^
//! |                                             |                                | stack
//! | Boot-core Stack                             |                                | growth
//! |                                             |                                | direction
//! |                                             | boot_core_stack_end_inclusive  |
//! +---------------------------------------------+

pub mod mmu;

use crate::memory::{Address, Physical, Virtual};
use core::cell::UnsafeCell;

//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------

// Symbols from the linker script.
extern "Rust" {
    static __rx_start: UnsafeCell<()>;
    static __rx_end_exclusive: UnsafeCell<()>;

    static __rw_start: UnsafeCell<()>;
    static __rw_end_exclusive: UnsafeCell<()>;

    static __boot_core_stack_start: UnsafeCell<()>;
    static __boot_core_stack_end_exclusive: UnsafeCell<()>;

    static __boot_core_stack_guard_page_start: UnsafeCell<()>;
    static __boot_core_stack_guard_page_end_exclusive: UnsafeCell<()>;
}

//--------------------------------------------------------------------------------------------------
// Public Definitions
//--------------------------------------------------------------------------------------------------

/// The board's physical memory map.
#[rustfmt::skip]
pub(super) mod map {
    use super::*;

    /// Physical devices.
    #[cfg(feature = "bsp_rpi3")]
    pub mod mmio {
        use super::*;

        pub const PERIPHERAL_IC_START: Address<Physical> = Address::new(0x3F00_B200);
        pub const PERIPHERAL_IC_SIZE:  usize             =              0x24;

        pub const GPIO_START:          Address<Physical> = Address::new(0x3F20_0000);
        pub const GPIO_SIZE:           usize             =              0xA0;

        pub const PL011_UART_START:    Address<Physical> = Address::new(0x3F20_1000);
        pub const PL011_UART_SIZE:     usize             =              0x48;

        pub const LOCAL_IC_START:      Address<Physical> = Address::new(0x4000_0000);
        pub const LOCAL_IC_SIZE:       usize             =              0x100;

        pub const END:                 Address<Physical> = Address::new(0x4001_0000);
    }

    /// Physical devices.
    #[cfg(feature = "bsp_rpi4")]
    pub mod mmio {
        use super::*;

        pub const GPIO_START:       Address<Physical> = Address::new(0xFE20_0000);
        pub const GPIO_SIZE:        usize             =              0xA0;

        pub const PL011_UART_START: Address<Physical> = Address::new(0xFE20_1000);
        pub const PL011_UART_SIZE:  usize             =              0x48;

        pub const GICD_START:       Address<Physical> = Address::new(0xFF84_1000);
        pub const GICD_SIZE:        usize             =              0x824;

        pub const GICC_START:       Address<Physical> = Address::new(0xFF84_2000);
        pub const GICC_SIZE:        usize             =              0x14;

        pub const END:              Address<Physical> = Address::new(0xFF85_0000);
    }

    pub const END: Address<Physical> = mmio::END;
}

//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------

/// Start address of the Read+Execute (RX) range.
///
/// # Safety
///
/// - Value is provided by the linker script and must be trusted as-is.
#[inline(always)]
fn virt_rx_start() -> Address<Virtual> {
    Address::new(unsafe { __rx_start.get() as usize })
}

/// Size of the Read+Execute (RX) range.
///
/// # Safety
///
/// - Value is provided by the linker script and must be trusted as-is.
#[inline(always)]
fn rx_size() -> usize {
    unsafe { (__rx_end_exclusive.get() as usize) - (__rx_start.get() as usize) }
}

/// Start address of the Read+Write (RW) range.
#[inline(always)]
fn virt_rw_start() -> Address<Virtual> {
    Address::new(unsafe { __rw_start.get() as usize })
}

/// Size of the Read+Write (RW) range.
///
/// # Safety
///
/// - Value is provided by the linker script and must be trusted as-is.
#[inline(always)]
fn rw_size() -> usize {
    unsafe { (__rw_end_exclusive.get() as usize) - (__rw_start.get() as usize) }
}

/// Start address of the boot core's stack.
#[inline(always)]
fn virt_boot_core_stack_start() -> Address<Virtual> {
    Address::new(unsafe { __boot_core_stack_start.get() as usize })
}

/// Size of the boot core's stack.
#[inline(always)]
fn boot_core_stack_size() -> usize {
    unsafe {
        (__boot_core_stack_end_exclusive.get() as usize) - (__boot_core_stack_start.get() as usize)
    }
}

/// Start address of the boot core's stack guard page.
#[inline(always)]
fn virt_boot_core_stack_guard_page_start() -> Address<Virtual> {
    Address::new(unsafe { __boot_core_stack_guard_page_start.get() as usize })
}

/// Size of the boot core's stack guard page.
#[inline(always)]
fn boot_core_stack_guard_page_size() -> usize {
    unsafe {
        (__boot_core_stack_guard_page_end_exclusive.get() as usize)
            - (__boot_core_stack_guard_page_start.get() as usize)
    }
}

/// Exclusive end address of the physical address space.
#[inline(always)]
fn phys_addr_space_end() -> Address<Physical> {
    map::END
}
