<commit_before>use std::error::Error;
use std::fs;
use std::fs::File;
use std::io::{Read, Write};
use std::path::Path;

pub mod oncoprint;
pub mod table_report;

pub fn embed_js(
    output_path: &str,
    custom_table_report_js: Option<&str>,
) -> Result<(), Box<dyn Error>> {
    let js_path = output_path.to_owned() + "/js/";
    fs::create_dir(Path::new(&js_path)).unwrap_or_else(|_| {
        panic!(
            "Could not create directory for js files at location: {:?}",
            js_path
        )
    });
    let mut files = vec![
        ("vega.min.js", include_str!("js/vega.min.js")),
        ("vega-lite.min.js", include_str!("js/vega-lite.min.js")),
        ("vega-embed.min.js", include_str!("js/vega-embed.min.js")),
        ("jsonm.min.js", include_str!("js/jsonm.min.js")),
        ("jquery.min.js", include_str!("js/jquery.min.js")),
        (
            "bootstrap-table.min.js",
            include_str!("js/bootstrap-table.min.js"),
        ),
        ("report.js", include_str!("js/report.js")),
        ("gene-report.js", include_str!("js/gene-report.js")),
    ];
    if let Some(path) = custom_table_report_js {
        let mut file_string = String::new();
        let mut custom_file = File::open(path).expect("Unable to open custom JS file");
        custom_file
            .read_to_string(&mut file_string)
            .expect("Unable to read string");
        let mut out_file = File::create(js_path.to_owned() + "table-report.js")?;
        out_file.write_all(file_string.as_bytes())?;
    } else {
        files.push(("report.js", include_str!("js/table-report.js")))
    }
    for (name, file) in files {
        let mut out_file = File::create(js_path.to_owned() + name)?;
        out_file.write_all(file.as_bytes())?;
    }
    Ok(())
}

pub fn embed_css(output_path: &str) -> Result<(), Box<dyn Error>> {
    let css_path = output_path.to_owned() + "/css/";
    fs::create_dir(Path::new(&css_path)).unwrap_or_else(|_| {
        panic!(
            "Could not create directory for js files at location: {:?}",
            css_path
        )
    });
    let files = vec![
        ("bootstrap.min.css", include_str!("css/bootstrap.min.css")),
        (
            "bootstrap-table.min.css",
            include_str!("css/bootstrap-table.min.css"),
        ),
        ("oncoprint.css", include_str!("css/oncoprint.css")),
    ];
    for (name, file) in files {
        let mut out_file = File::create(css_path.to_owned() + name)?;
        out_file.write_all(file.as_bytes())?;
    }
    Ok(())
}

pub fn embed_html(output_path: &str) -> Result<(), Box<dyn Error>> {
    let files = vec![("index.html", include_str!("html/index.html"))];
    for (name, file) in files {
        let mut out_file = File::create(output_path.to_owned() + "/" + name).unwrap_or_else(|_| {
            panic!(
                "Could not create file for index html at location: {:?}",
                output_path.to_owned() + "/" + name
            )
        });
        out_file.write_all(file.as_bytes())?;
    }
    Ok(())
}
<commit_msg>Reanable default table template (#72)<commit_after>use std::error::Error;
use std::fs;
use std::fs::File;
use std::io::{Read, Write};
use std::path::Path;

pub mod oncoprint;
pub mod table_report;

pub fn embed_js(
    output_path: &str,
    custom_table_report_js: Option<&str>,
) -> Result<(), Box<dyn Error>> {
    let js_path = output_path.to_owned() + "/js/";
    fs::create_dir(Path::new(&js_path)).unwrap_or_else(|_| {
        panic!(
            "Could not create directory for js files at location: {:?}",
            js_path
        )
    });
    let mut files = vec![
        ("vega.min.js", include_str!("js/vega.min.js")),
        ("vega-lite.min.js", include_str!("js/vega-lite.min.js")),
        ("vega-embed.min.js", include_str!("js/vega-embed.min.js")),
        ("jsonm.min.js", include_str!("js/jsonm.min.js")),
        ("jquery.min.js", include_str!("js/jquery.min.js")),
        (
            "bootstrap-table.min.js",
            include_str!("js/bootstrap-table.min.js"),
        ),
        ("report.js", include_str!("js/report.js")),
        ("gene-report.js", include_str!("js/gene-report.js")),
    ];
    if let Some(path) = custom_table_report_js {
        let mut file_string = String::new();
        let mut custom_file = File::open(path).expect("Unable to open custom JS file");
        custom_file
            .read_to_string(&mut file_string)
            .expect("Unable to read string");
        let mut out_file = File::create(js_path.to_owned() + "table-report.js")?;
        out_file.write_all(file_string.as_bytes())?;
    } else {
        files.push(("table-report.js", include_str!("js/table-report.js")))
    }
    for (name, file) in files {
        let mut out_file = File::create(js_path.to_owned() + name)?;
        out_file.write_all(file.as_bytes())?;
    }
    Ok(())
}

pub fn embed_css(output_path: &str) -> Result<(), Box<dyn Error>> {
    let css_path = output_path.to_owned() + "/css/";
    fs::create_dir(Path::new(&css_path)).unwrap_or_else(|_| {
        panic!(
            "Could not create directory for js files at location: {:?}",
            css_path
        )
    });
    let files = vec![
        ("bootstrap.min.css", include_str!("css/bootstrap.min.css")),
        (
            "bootstrap-table.min.css",
            include_str!("css/bootstrap-table.min.css"),
        ),
        ("oncoprint.css", include_str!("css/oncoprint.css")),
    ];
    for (name, file) in files {
        let mut out_file = File::create(css_path.to_owned() + name)?;
        out_file.write_all(file.as_bytes())?;
    }
    Ok(())
}

pub fn embed_html(output_path: &str) -> Result<(), Box<dyn Error>> {
    let files = vec![("index.html", include_str!("html/index.html"))];
    for (name, file) in files {
        let mut out_file = File::create(output_path.to_owned() + "/" + name).unwrap_or_else(|_| {
            panic!(
                "Could not create file for index html at location: {:?}",
                output_path.to_owned() + "/" + name
            )
        });
        out_file.write_all(file.as_bytes())?;
    }
    Ok(())
}
<|endoftext|>


<commit_before>use std::ops::Deref;

use config::gobjects::GObject;
use env::Env;
use library;
use nameutil::*;
use super::*;
use super::child_properties::ChildProperties;
use super::imports::Imports;
use super::info_base::InfoBase;
use super::signatures::Signatures;
use traits::*;

#[derive(Default)]
pub struct Info {
    pub base: InfoBase,
    pub c_type: String,
    pub get_type: String,
    pub supertypes: Vec<general::StatusedTypeId>,
    pub has_children: bool,
    pub has_constructors: bool,
    pub has_methods: bool,
    pub has_functions: bool,
    pub signals: Vec<signals::Info>,
    pub trampolines: trampolines::Trampolines,
    pub properties: Vec<properties::Property>,
    pub child_properties: ChildProperties,
    pub signatures: Signatures,
}

impl Info {
    pub fn has_signals(&self) -> bool {
        self.signals.iter().any(|s| s.trampoline_name.is_ok())
    }
}

impl Deref for Info {
    type Target = InfoBase;

    fn deref(&self) -> &InfoBase {
        &self.base
    }
}

pub fn class(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option<Info> {
    info!("Analyzing class {}", obj.name);
    let full_name = obj.name.clone();

    let class_tid = match env.library.find_type(0, &full_name) {
        Some(tid) => tid,
        None => return None,
    };

    let type_ = env.type_(class_tid);

    let name: String = split_namespace_name(&full_name).1.into();

    let klass: &library::Class = match type_.maybe_ref() {
        Some(klass) => klass,
        None => return None,
    };

    let mut imports = Imports::new();
    imports.add("glib::translate::*", None);
    imports.add("ffi", None);

    let supertypes = supertypes::analyze(env, class_tid, &mut imports);

    let mut has_children = obj.force_trait;

    for child_tid in env.class_hierarchy.subtypes(class_tid) {
        let child_name = child_tid.full_name(&env.library);
        let status = env.config.objects.get(&child_name)
            .map(|o| o.status)
            .unwrap_or_default();
        if status.normal() {
            has_children = true;
            break;
        }
    }

    if has_children {
        imports.add("glib::object::IsA", None);
    }

    let mut trampolines = trampolines::Trampolines::with_capacity(klass.signals.len());
    let mut signatures = Signatures::with_capacity(klass.functions.len());

    let mut functions = functions::analyze(env, &klass.functions, class_tid, obj,
                                           &mut imports, Some(&mut signatures), Some(deps));
    let specials = special_functions::extract(&mut functions);
    // `copy` will duplicate an object while `clone` just adds a reference
    special_functions::unhide(&mut functions, &specials, special_functions::Type::Copy);
    special_functions::analyze_imports(&specials, &mut imports);

    let signals = signals::analyze(env, &klass.signals, class_tid, has_children,
                                   &mut trampolines, obj, &mut imports);
    let properties = properties::analyze(env, &klass.properties, class_tid, obj, &mut imports,
                                         &signatures, deps);

    let (version, deprecated_version) = info_base::versions(env, obj, &functions, klass.version,
         klass.deprecated_version);

    let child_properties = child_properties::analyze(env, obj.child_properties.as_ref(), class_tid,
                                                     &mut imports);

    if has_children && !properties.is_empty() {
        imports.add("Object", None);
    }
    //don't `use` yourself
    imports.remove(&name);

    imports.clean_glib(env);

    let base = InfoBase {
        full_name: full_name,
        type_id: class_tid,
        name: name,
        functions: functions,
        specials: specials,
        imports: imports,
        version: version,
        deprecated_version: deprecated_version,
        cfg_condition: obj.cfg_condition.clone(),
    };

    // patch up trait methods in the symbol table
    if has_children {
        let mut symbols = env.symbols.borrow_mut();
        for func in base.methods() {
            if let Some(symbol) = symbols.by_c_name_mut(&func.glib_name) {
                symbol.make_trait_method();
            }
        }
    }

    let has_constructors = !base.constructors().is_empty();
    let has_methods = !base.methods().is_empty();
    let has_functions = !base.functions().is_empty();

    let info = Info {
        base: base,
        c_type: klass.c_type.clone(),
        get_type: klass.glib_get_type.clone(),
        supertypes: supertypes,
        has_children: has_children,
        has_constructors: has_constructors,
        has_methods: has_methods,
        has_functions: has_functions,
        signals: signals,
        trampolines: trampolines,
        properties: properties,
        child_properties: child_properties,
        signatures: signatures,
    };

    Some(info)
}

pub fn interface(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option<Info> {
    info!("Analyzing interface {}", obj.name);
    let full_name = obj.name.clone();

    let iface_tid = match env.library.find_type(0, &full_name) {
        Some(tid) => tid,
        None => return None,
    };

    let type_ = env.type_(iface_tid);

    let name: String = split_namespace_name(&full_name).1.into();

    let iface: &library::Interface = match type_.maybe_ref() {
        Some(iface) => iface,
        None => return None,
    };

    let mut imports = Imports::new();
    imports.add("glib::translate::*", None);
    imports.add("ffi", None);
    imports.add("glib::object::IsA", None);

    let supertypes = supertypes::analyze(env, iface_tid, &mut imports);

    let mut trampolines = trampolines::Trampolines::with_capacity(iface.signals.len());
    let mut signatures = Signatures::with_capacity(iface.functions.len());

    let functions = functions::analyze(env, &iface.functions, iface_tid, obj,
                                       &mut imports, Some(&mut signatures), Some(deps));

    let signals = signals::analyze(env, &iface.signals, iface_tid, true,
                                   &mut trampolines, obj, &mut imports);
    let properties = properties::analyze(env, &iface.properties, iface_tid, obj, &mut imports,
                                         &signatures, deps);

    let (version, deprecated_version) = info_base::versions(env, obj, &functions, iface.version,
         iface.deprecated_version);

    if !properties.is_empty() {
        imports.add("Object", None);
    }

    //don't `use` yourself
    imports.remove(&name);

    imports.clean_glib(env);

    let base = InfoBase {
        full_name: full_name,
        type_id: iface_tid,
        name: name,
        functions: functions,
        specials: Default::default(),
        imports: imports,
        version: version,
        deprecated_version: deprecated_version,
        cfg_condition: obj.cfg_condition.clone(),
    };

    let has_methods = !base.methods().is_empty();

    let info = Info {
        base: base,
        c_type: iface.c_type.clone(),
        get_type: iface.glib_get_type.clone(),
        supertypes: supertypes,
        has_children: true,
        has_methods: has_methods,
        signals: signals,
        trampolines: trampolines,
        properties: properties,
        signatures: signatures,
        .. Default::default()
    };

    Some(info)
}
<commit_msg>Generate interfaces functions<commit_after>use std::ops::Deref;

use config::gobjects::GObject;
use env::Env;
use library;
use nameutil::*;
use super::*;
use super::child_properties::ChildProperties;
use super::imports::Imports;
use super::info_base::InfoBase;
use super::signatures::Signatures;
use traits::*;

#[derive(Default)]
pub struct Info {
    pub base: InfoBase,
    pub c_type: String,
    pub get_type: String,
    pub supertypes: Vec<general::StatusedTypeId>,
    pub has_children: bool,
    pub has_constructors: bool,
    pub has_methods: bool,
    pub has_functions: bool,
    pub signals: Vec<signals::Info>,
    pub trampolines: trampolines::Trampolines,
    pub properties: Vec<properties::Property>,
    pub child_properties: ChildProperties,
    pub signatures: Signatures,
}

impl Info {
    pub fn has_signals(&self) -> bool {
        self.signals.iter().any(|s| s.trampoline_name.is_ok())
    }
}

impl Deref for Info {
    type Target = InfoBase;

    fn deref(&self) -> &InfoBase {
        &self.base
    }
}

pub fn class(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option<Info> {
    info!("Analyzing class {}", obj.name);
    let full_name = obj.name.clone();

    let class_tid = match env.library.find_type(0, &full_name) {
        Some(tid) => tid,
        None => return None,
    };

    let type_ = env.type_(class_tid);

    let name: String = split_namespace_name(&full_name).1.into();

    let klass: &library::Class = match type_.maybe_ref() {
        Some(klass) => klass,
        None => return None,
    };

    let mut imports = Imports::new();
    imports.add("glib::translate::*", None);
    imports.add("ffi", None);

    let supertypes = supertypes::analyze(env, class_tid, &mut imports);

    let mut has_children = obj.force_trait;

    for child_tid in env.class_hierarchy.subtypes(class_tid) {
        let child_name = child_tid.full_name(&env.library);
        let status = env.config.objects.get(&child_name)
            .map(|o| o.status)
            .unwrap_or_default();
        if status.normal() {
            has_children = true;
            break;
        }
    }

    if has_children {
        imports.add("glib::object::IsA", None);
    }

    let mut trampolines = trampolines::Trampolines::with_capacity(klass.signals.len());
    let mut signatures = Signatures::with_capacity(klass.functions.len());

    let mut functions = functions::analyze(env, &klass.functions, class_tid, obj,
                                           &mut imports, Some(&mut signatures), Some(deps));
    let specials = special_functions::extract(&mut functions);
    // `copy` will duplicate an object while `clone` just adds a reference
    special_functions::unhide(&mut functions, &specials, special_functions::Type::Copy);
    special_functions::analyze_imports(&specials, &mut imports);

    let signals = signals::analyze(env, &klass.signals, class_tid, has_children,
                                   &mut trampolines, obj, &mut imports);
    let properties = properties::analyze(env, &klass.properties, class_tid, obj, &mut imports,
                                         &signatures, deps);

    let (version, deprecated_version) = info_base::versions(env, obj, &functions, klass.version,
         klass.deprecated_version);

    let child_properties = child_properties::analyze(env, obj.child_properties.as_ref(), class_tid,
                                                     &mut imports);

    if has_children && !properties.is_empty() {
        imports.add("Object", None);
    }
    //don't `use` yourself
    imports.remove(&name);

    imports.clean_glib(env);

    let base = InfoBase {
        full_name: full_name,
        type_id: class_tid,
        name: name,
        functions: functions,
        specials: specials,
        imports: imports,
        version: version,
        deprecated_version: deprecated_version,
        cfg_condition: obj.cfg_condition.clone(),
    };

    // patch up trait methods in the symbol table
    if has_children {
        let mut symbols = env.symbols.borrow_mut();
        for func in base.methods() {
            if let Some(symbol) = symbols.by_c_name_mut(&func.glib_name) {
                symbol.make_trait_method();
            }
        }
    }

    let has_constructors = !base.constructors().is_empty();
    let has_methods = !base.methods().is_empty();
    let has_functions = !base.functions().is_empty();

    let info = Info {
        base: base,
        c_type: klass.c_type.clone(),
        get_type: klass.glib_get_type.clone(),
        supertypes: supertypes,
        has_children: has_children,
        has_constructors: has_constructors,
        has_methods: has_methods,
        has_functions: has_functions,
        signals: signals,
        trampolines: trampolines,
        properties: properties,
        child_properties: child_properties,
        signatures: signatures,
    };

    Some(info)
}

pub fn interface(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option<Info> {
    info!("Analyzing interface {}", obj.name);
    let full_name = obj.name.clone();

    let iface_tid = match env.library.find_type(0, &full_name) {
        Some(tid) => tid,
        None => return None,
    };

    let type_ = env.type_(iface_tid);

    let name: String = split_namespace_name(&full_name).1.into();

    let iface: &library::Interface = match type_.maybe_ref() {
        Some(iface) => iface,
        None => return None,
    };

    let mut imports = Imports::new();
    imports.add("glib::translate::*", None);
    imports.add("ffi", None);
    imports.add("glib::object::IsA", None);

    let supertypes = supertypes::analyze(env, iface_tid, &mut imports);

    let mut trampolines = trampolines::Trampolines::with_capacity(iface.signals.len());
    let mut signatures = Signatures::with_capacity(iface.functions.len());

    let functions = functions::analyze(env, &iface.functions, iface_tid, obj,
                                       &mut imports, Some(&mut signatures), Some(deps));

    let signals = signals::analyze(env, &iface.signals, iface_tid, true,
                                   &mut trampolines, obj, &mut imports);
    let properties = properties::analyze(env, &iface.properties, iface_tid, obj, &mut imports,
                                         &signatures, deps);

    let (version, deprecated_version) = info_base::versions(env, obj, &functions, iface.version,
         iface.deprecated_version);

    if !properties.is_empty() {
        imports.add("Object", None);
    }

    //don't `use` yourself
    imports.remove(&name);

    imports.clean_glib(env);

    let base = InfoBase {
        full_name: full_name,
        type_id: iface_tid,
        name: name,
        functions: functions,
        specials: Default::default(),
        imports: imports,
        version: version,
        deprecated_version: deprecated_version,
        cfg_condition: obj.cfg_condition.clone(),
    };

    let has_methods = !base.methods().is_empty();
    let has_functions = !base.functions().is_empty();

    let info = Info {
        base: base,
        c_type: iface.c_type.clone(),
        get_type: iface.glib_get_type.clone(),
        supertypes: supertypes,
        has_children: true,
        has_methods: has_methods,
        has_functions: has_functions,
        signals: signals,
        trampolines: trampolines,
        properties: properties,
        signatures: signatures,
        .. Default::default()
    };

    Some(info)
}
<|endoftext|>


<commit_before>use std::ops::Deref;

use config::gobjects::GObject;
use env::Env;
use library;
use nameutil::*;
use super::*;
use super::child_properties::ChildProperties;
use super::imports::Imports;
use super::info_base::InfoBase;
use super::signatures::Signatures;
use traits::*;

#[derive(Default)]
pub struct Info {
    pub base: InfoBase,
    pub c_type: String,
    pub get_type: String,
    pub supertypes: Vec<general::StatusedTypeId>,
    pub generate_trait: bool,
    pub has_constructors: bool,
    pub has_methods: bool,
    pub has_functions: bool,
    pub signals: Vec<signals::Info>,
    pub trampolines: trampolines::Trampolines,
    pub properties: Vec<properties::Property>,
    pub child_properties: ChildProperties,
    pub signatures: Signatures,
}

impl Info {
    pub fn has_signals(&self) -> bool {
        self.signals.iter().any(|s| s.trampoline_name.is_ok())
    }
}

impl Deref for Info {
    type Target = InfoBase;

    fn deref(&self) -> &InfoBase {
        &self.base
    }
}

pub fn class(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option<Info> {
    info!("Analyzing class {}", obj.name);
    let full_name = obj.name.clone();

    let class_tid = match env.library.find_type(0, &full_name) {
        Some(tid) => tid,
        None => return None,
    };

    let type_ = env.type_(class_tid);

    let name: String = split_namespace_name(&full_name).1.into();

    let klass: &library::Class = match type_.maybe_ref() {
        Some(klass) => klass,
        None => return None,
    };

    let mut imports = Imports::new();
    imports.add("glib::translate::*", None);
    imports.add("ffi", None);

    let supertypes = supertypes::analyze(env, class_tid, &mut imports);

    let mut generate_trait = obj.generate_trait;

    // Sanity check the user's configuration. It's unlikely that not generating
    // a trait is wanted if there are subtypes in this very crate
    if !generate_trait {
        for child_tid in env.class_hierarchy.subtypes(class_tid) {
            let child_name = child_tid.full_name(&env.library);
            let status = env.config.objects.get(&child_name)
                .map(|o| o.status)
                .unwrap_or_default();
            if status.normal() {
                error!("Not generating trait for {} although subtypes exist", full_name);
                break;
            }
        }
    }

    let mut trampolines = trampolines::Trampolines::with_capacity(klass.signals.len());
    let mut signatures = Signatures::with_capacity(klass.functions.len());

    let mut functions = functions::analyze(env, &klass.functions, class_tid, obj,
                                           &mut imports, Some(&mut signatures), Some(deps));
    let specials = special_functions::extract(&mut functions);
    // `copy` will duplicate an object while `clone` just adds a reference
    special_functions::unhide(&mut functions, &specials, special_functions::Type::Copy);
    special_functions::analyze_imports(&specials, &mut imports);

    let signals = signals::analyze(env, &klass.signals, class_tid, generate_trait,
                                   &mut trampolines, obj, &mut imports);
    let properties = properties::analyze(env, &klass.properties, class_tid, obj, &mut imports,
                                         &signatures, deps);

    let (version, deprecated_version) = info_base::versions(env, obj, &functions, klass.version,
         klass.deprecated_version);

    let child_properties = child_properties::analyze(env, obj.child_properties.as_ref(), class_tid,
                                                     &mut imports);

    let has_methods = functions.iter()
            .any(|f| f.kind == library::FunctionKind::Method);
    let has_signals = signals.iter().any(|s| s.trampoline_name.is_ok());

    // There's no point in generating a trait if there are no signals, methods, properties
    // and child properties: it would be empty
    if generate_trait && !has_signals && !has_methods && properties.is_empty() && child_properties.is_empty() {
        generate_trait = false;
    }

    if generate_trait && (!properties.is_empty() || has_signals) {
        imports.add("glib", None);
    }
    if generate_trait && (has_methods || !properties.is_empty() || !child_properties.is_empty() || has_signals) {
        imports.add("glib::object::IsA", None);
    }

    //don't `use` yourself
    imports.remove(&name);

    imports.clean_glib(env);

    let base = InfoBase {
        full_name: full_name,
        type_id: class_tid,
        name: name,
        functions: functions,
        specials: specials,
        imports: imports,
        version: version,
        deprecated_version: deprecated_version,
        cfg_condition: obj.cfg_condition.clone(),
    };

    // patch up trait methods in the symbol table
    if generate_trait {
        let mut symbols = env.symbols.borrow_mut();
        for func in base.methods() {
            if let Some(symbol) = symbols.by_c_name_mut(&func.glib_name) {
                symbol.make_trait_method();
            }
        }
    }

    let has_constructors = !base.constructors().is_empty();
    let has_functions = !base.functions().is_empty();

    let info = Info {
        base: base,
        c_type: klass.c_type.clone(),
        get_type: klass.glib_get_type.clone(),
        supertypes: supertypes,
        generate_trait: generate_trait,
        has_constructors: has_constructors,
        has_methods: has_methods,
        has_functions: has_functions,
        signals: signals,
        trampolines: trampolines,
        properties: properties,
        child_properties: child_properties,
        signatures: signatures,
    };

    Some(info)
}

pub fn interface(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option<Info> {
    info!("Analyzing interface {}", obj.name);
    let full_name = obj.name.clone();

    let iface_tid = match env.library.find_type(0, &full_name) {
        Some(tid) => tid,
        None => return None,
    };

    let type_ = env.type_(iface_tid);

    let name: String = split_namespace_name(&full_name).1.into();

    let iface: &library::Interface = match type_.maybe_ref() {
        Some(iface) => iface,
        None => return None,
    };

    let mut imports = Imports::new();
    imports.add("glib::translate::*", None);
    imports.add("ffi", None);
    imports.add("glib::object::IsA", None);

    let supertypes = supertypes::analyze(env, iface_tid, &mut imports);

    let mut trampolines = trampolines::Trampolines::with_capacity(iface.signals.len());
    let mut signatures = Signatures::with_capacity(iface.functions.len());

    let functions = functions::analyze(env, &iface.functions, iface_tid, obj,
                                       &mut imports, Some(&mut signatures), Some(deps));

    let signals = signals::analyze(env, &iface.signals, iface_tid, true,
                                   &mut trampolines, obj, &mut imports);
    let properties = properties::analyze(env, &iface.properties, iface_tid, obj, &mut imports,
                                         &signatures, deps);

    let (version, deprecated_version) = info_base::versions(env, obj, &functions, iface.version,
         iface.deprecated_version);

    if !properties.is_empty() {
        imports.add("glib", None);
    }

    //don't `use` yourself
    imports.remove(&name);

    imports.clean_glib(env);

    let base = InfoBase {
        full_name: full_name,
        type_id: iface_tid,
        name: name,
        functions: functions,
        specials: Default::default(),
        imports: imports,
        version: version,
        deprecated_version: deprecated_version,
        cfg_condition: obj.cfg_condition.clone(),
    };

    let has_methods = !base.methods().is_empty();
    let has_functions = !base.functions().is_empty();

    let info = Info {
        base: base,
        c_type: iface.c_type.clone(),
        get_type: iface.glib_get_type.clone(),
        supertypes: supertypes,
        generate_trait: true,
        has_methods: has_methods,
        has_functions: has_functions,
        signals: signals,
        trampolines: trampolines,
        properties: properties,
        signatures: signatures,
        .. Default::default()
    };

    Some(info)
}
<commit_msg>Refactor known-subtypes check into its own function<commit_after>use std::ops::Deref;

use config::gobjects::GObject;
use env::Env;
use library;
use nameutil::*;
use super::*;
use super::child_properties::ChildProperties;
use super::imports::Imports;
use super::info_base::InfoBase;
use super::signatures::Signatures;
use traits::*;

#[derive(Default)]
pub struct Info {
    pub base: InfoBase,
    pub c_type: String,
    pub get_type: String,
    pub supertypes: Vec<general::StatusedTypeId>,
    pub generate_trait: bool,
    pub has_constructors: bool,
    pub has_methods: bool,
    pub has_functions: bool,
    pub signals: Vec<signals::Info>,
    pub trampolines: trampolines::Trampolines,
    pub properties: Vec<properties::Property>,
    pub child_properties: ChildProperties,
    pub signatures: Signatures,
}

impl Info {
    pub fn has_signals(&self) -> bool {
        self.signals.iter().any(|s| s.trampoline_name.is_ok())
    }
}

impl Deref for Info {
    type Target = InfoBase;

    fn deref(&self) -> &InfoBase {
        &self.base
    }
}

pub fn has_known_subtypes(env: &Env, class_tid: library::TypeId) -> bool {
    for child_tid in env.class_hierarchy.subtypes(class_tid) {
        let child_name = child_tid.full_name(&env.library);
        let status = env.config.objects.get(&child_name)
            .map(|o| o.status)
            .unwrap_or_default();
        if status.normal() {
            return true;
        }
    }

    false
}

pub fn class(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option<Info> {
    info!("Analyzing class {}", obj.name);
    let full_name = obj.name.clone();

    let class_tid = match env.library.find_type(0, &full_name) {
        Some(tid) => tid,
        None => return None,
    };

    let type_ = env.type_(class_tid);

    let name: String = split_namespace_name(&full_name).1.into();

    let klass: &library::Class = match type_.maybe_ref() {
        Some(klass) => klass,
        None => return None,
    };

    let mut imports = Imports::new();
    imports.add("glib::translate::*", None);
    imports.add("ffi", None);

    let supertypes = supertypes::analyze(env, class_tid, &mut imports);

    let mut generate_trait = obj.generate_trait;

    // Sanity check the user's configuration. It's unlikely that not generating
    // a trait is wanted if there are subtypes in this very crate
    if !generate_trait && has_known_subtypes(env, class_tid) {
        error!("Not generating trait for {} although subtypes exist", full_name);
    }

    let mut trampolines = trampolines::Trampolines::with_capacity(klass.signals.len());
    let mut signatures = Signatures::with_capacity(klass.functions.len());

    let mut functions = functions::analyze(env, &klass.functions, class_tid, obj,
                                           &mut imports, Some(&mut signatures), Some(deps));
    let specials = special_functions::extract(&mut functions);
    // `copy` will duplicate an object while `clone` just adds a reference
    special_functions::unhide(&mut functions, &specials, special_functions::Type::Copy);
    special_functions::analyze_imports(&specials, &mut imports);

    let signals = signals::analyze(env, &klass.signals, class_tid, generate_trait,
                                   &mut trampolines, obj, &mut imports);
    let properties = properties::analyze(env, &klass.properties, class_tid, obj, &mut imports,
                                         &signatures, deps);

    let (version, deprecated_version) = info_base::versions(env, obj, &functions, klass.version,
         klass.deprecated_version);

    let child_properties = child_properties::analyze(env, obj.child_properties.as_ref(), class_tid,
                                                     &mut imports);

    let has_methods = functions.iter()
            .any(|f| f.kind == library::FunctionKind::Method);
    let has_signals = signals.iter().any(|s| s.trampoline_name.is_ok());

    // There's no point in generating a trait if there are no signals, methods, properties
    // and child properties: it would be empty
    if generate_trait && !has_signals && !has_methods && properties.is_empty() && child_properties.is_empty() {
        generate_trait = false;
    }

    if generate_trait && (!properties.is_empty() || has_signals) {
        imports.add("glib", None);
    }
    if generate_trait && (has_methods || !properties.is_empty() || !child_properties.is_empty() || has_signals) {
        imports.add("glib::object::IsA", None);
    }

    //don't `use` yourself
    imports.remove(&name);

    imports.clean_glib(env);

    let base = InfoBase {
        full_name: full_name,
        type_id: class_tid,
        name: name,
        functions: functions,
        specials: specials,
        imports: imports,
        version: version,
        deprecated_version: deprecated_version,
        cfg_condition: obj.cfg_condition.clone(),
    };

    // patch up trait methods in the symbol table
    if generate_trait {
        let mut symbols = env.symbols.borrow_mut();
        for func in base.methods() {
            if let Some(symbol) = symbols.by_c_name_mut(&func.glib_name) {
                symbol.make_trait_method();
            }
        }
    }

    let has_constructors = !base.constructors().is_empty();
    let has_functions = !base.functions().is_empty();

    let info = Info {
        base: base,
        c_type: klass.c_type.clone(),
        get_type: klass.glib_get_type.clone(),
        supertypes: supertypes,
        generate_trait: generate_trait,
        has_constructors: has_constructors,
        has_methods: has_methods,
        has_functions: has_functions,
        signals: signals,
        trampolines: trampolines,
        properties: properties,
        child_properties: child_properties,
        signatures: signatures,
    };

    Some(info)
}

pub fn interface(env: &Env, obj: &GObject, deps: &[library::TypeId]) -> Option<Info> {
    info!("Analyzing interface {}", obj.name);
    let full_name = obj.name.clone();

    let iface_tid = match env.library.find_type(0, &full_name) {
        Some(tid) => tid,
        None => return None,
    };

    let type_ = env.type_(iface_tid);

    let name: String = split_namespace_name(&full_name).1.into();

    let iface: &library::Interface = match type_.maybe_ref() {
        Some(iface) => iface,
        None => return None,
    };

    let mut imports = Imports::new();
    imports.add("glib::translate::*", None);
    imports.add("ffi", None);
    imports.add("glib::object::IsA", None);

    let supertypes = supertypes::analyze(env, iface_tid, &mut imports);

    let mut trampolines = trampolines::Trampolines::with_capacity(iface.signals.len());
    let mut signatures = Signatures::with_capacity(iface.functions.len());

    let functions = functions::analyze(env, &iface.functions, iface_tid, obj,
                                       &mut imports, Some(&mut signatures), Some(deps));

    let signals = signals::analyze(env, &iface.signals, iface_tid, true,
                                   &mut trampolines, obj, &mut imports);
    let properties = properties::analyze(env, &iface.properties, iface_tid, obj, &mut imports,
                                         &signatures, deps);

    let (version, deprecated_version) = info_base::versions(env, obj, &functions, iface.version,
         iface.deprecated_version);

    if !properties.is_empty() {
        imports.add("glib", None);
    }

    //don't `use` yourself
    imports.remove(&name);

    imports.clean_glib(env);

    let base = InfoBase {
        full_name: full_name,
        type_id: iface_tid,
        name: name,
        functions: functions,
        specials: Default::default(),
        imports: imports,
        version: version,
        deprecated_version: deprecated_version,
        cfg_condition: obj.cfg_condition.clone(),
    };

    let has_methods = !base.methods().is_empty();
    let has_functions = !base.functions().is_empty();

    let info = Info {
        base: base,
        c_type: iface.c_type.clone(),
        get_type: iface.glib_get_type.clone(),
        supertypes: supertypes,
        generate_trait: true,
        has_methods: has_methods,
        has_functions: has_functions,
        signals: signals,
        trampolines: trampolines,
        properties: properties,
        signatures: signatures,
        .. Default::default()
    };

    Some(info)
}
<|endoftext|>


<commit_before>use core::num::NonZeroU8;
use core::sync::atomic::{AtomicU64, Ordering};
use core::mem;

use alloc::boxed::Box;
use alloc::collections::BTreeMap;

use x86::segmentation::Descriptor as X86IdtEntry;
use x86::dtables::{self, DescriptorTablePointer};

use crate::interrupt::*;
use crate::ipi::IpiKind;

use spin::RwLock;

pub static mut INIT_IDTR: DescriptorTablePointer<X86IdtEntry> = DescriptorTablePointer {
    limit: 0,
    base: 0 as *const X86IdtEntry
};

#[thread_local]
pub static mut IDTR: DescriptorTablePointer<X86IdtEntry> = DescriptorTablePointer {
    limit: 0,
    base: 0 as *const X86IdtEntry
};

pub type IdtEntries = [IdtEntry; 256];
pub type IdtReservations = [AtomicU64; 4];

#[repr(packed)]
pub struct Idt {
    entries: IdtEntries,
    reservations: IdtReservations,
}
impl Idt {
    pub const fn new() -> Self {
        Self {
            entries: [IdtEntry::new(); 256],
            reservations: new_idt_reservations(),
        }
    }
    #[inline]
    pub fn is_reserved(&self, index: u8) -> bool {
        let byte_index = index / 64;
        let bit = index % 64;

        unsafe { &self.reservations[usize::from(byte_index)] }.load(Ordering::Acquire) & (1 << bit) != 0
    }

    #[inline]
    pub fn set_reserved(&self, index: u8, reserved: bool) {
        let byte_index = index / 64;
        let bit = index % 64;

        unsafe { &self.reservations[usize::from(byte_index)] }.fetch_or(u64::from(reserved) << bit, Ordering::AcqRel);
    }
    #[inline]
    pub fn is_reserved_mut(&mut self, index: u8) -> bool {
        let byte_index = index / 64;
        let bit = index % 64;

        *unsafe { &mut self.reservations[usize::from(byte_index)] }.get_mut() & (1 << bit) != 0
    }

    #[inline]
    pub fn set_reserved_mut(&mut self, index: u8, reserved: bool) {
        let byte_index = index / 64;
        let bit = index % 64;

        *unsafe { &mut self.reservations[usize::from(byte_index)] }.get_mut() |= u64::from(reserved) << bit;
    }
}

static mut INIT_BSP_IDT: Idt = Idt::new();

// TODO: VecMap?
pub static IDTS: RwLock<Option<BTreeMap<usize, &'static mut Idt>>> = RwLock::new(None);

#[inline]
pub fn is_reserved(cpu_id: usize, index: u8) -> bool {
    let byte_index = index / 64;
    let bit = index % 64;

    unsafe { &IDTS.read().as_ref().unwrap().get(&cpu_id).unwrap().reservations[usize::from(byte_index)] }.load(Ordering::Acquire) & (1 << bit) != 0
}

#[inline]
pub fn set_reserved(cpu_id: usize, index: u8, reserved: bool) {
    let byte_index = index / 64;
    let bit = index % 64;

    unsafe { &IDTS.read().as_ref().unwrap().get(&cpu_id).unwrap().reservations[usize::from(byte_index)] }.fetch_or(u64::from(reserved) << bit, Ordering::AcqRel);
}

pub fn allocate_interrupt() -> Option<NonZeroU8> {
    let cpu_id = crate::cpu_id();
    for number in 50..=254 {
        if ! is_reserved(cpu_id, number) {
            set_reserved(cpu_id, number, true);
            return Some(unsafe { NonZeroU8::new_unchecked(number) });
        }
    }
    None
}

pub fn available_irqs_iter(cpu_id: usize) -> impl Iterator<Item = u8> + 'static {
    (32..=254).filter(move |&index| !is_reserved(cpu_id, index))
}

macro_rules! use_irq(
    ( $idt: expr, $number:literal, $func:ident ) => {{
        $idt[$number].set_func($func);
    }}
);

macro_rules! use_default_irqs(
    ($idt:expr) => {{
        use crate::interrupt::irq::*;
        default_irqs!($idt, use_irq);
    }}
);

pub unsafe fn init() {
    dtables::lidt(&INIT_IDTR);
}

const fn new_idt_reservations() -> [AtomicU64; 4] {
    [AtomicU64::new(0), AtomicU64::new(0), AtomicU64::new(0), AtomicU64::new(0)]
}

/// Initialize the IDT for a
pub unsafe fn init_paging_post_heap(is_bsp: bool, cpu_id: usize) {
    let mut idts_guard = IDTS.write();
    let idts_btree = idts_guard.get_or_insert_with(|| BTreeMap::new());

    if is_bsp {
        idts_btree.insert(cpu_id, &mut INIT_BSP_IDT);
    } else {
        let idt = idts_btree.entry(cpu_id).or_insert_with(|| Box::leak(Box::new(Idt::new())));
        init_generic(is_bsp, idt);
    }
}

/// Initializes a fully functional IDT for use before it be moved into the map. This is ONLY called
/// on the BSP, since the kernel heap is ready for the APs.
pub unsafe fn init_paging_bsp() {
    init_generic(true, &mut INIT_BSP_IDT);
}

/// Initializes an IDT for any type of processor.
pub unsafe fn init_generic(is_bsp: bool, idt: &mut Idt) {
    let (current_idt, current_reservations) = (&mut idt.entries, &mut idt.reservations);

    IDTR.limit = (current_idt.len() * mem::size_of::<IdtEntry>() - 1) as u16;
    IDTR.base = current_idt.as_ptr() as *const X86IdtEntry;

    // Set up exceptions
    current_idt[0].set_func(exception::divide_by_zero);
    current_idt[1].set_func(exception::debug);
    current_idt[2].set_func(exception::non_maskable);
    current_idt[3].set_func(exception::breakpoint);
    current_idt[3].set_flags(IdtFlags::PRESENT | IdtFlags::RING_3 | IdtFlags::INTERRUPT);
    current_idt[4].set_func(exception::overflow);
    current_idt[5].set_func(exception::bound_range);
    current_idt[6].set_func(exception::invalid_opcode);
    current_idt[7].set_func(exception::device_not_available);
    current_idt[8].set_func(exception::double_fault);
    // 9 no longer available
    current_idt[10].set_func(exception::invalid_tss);
    current_idt[11].set_func(exception::segment_not_present);
    current_idt[12].set_func(exception::stack_segment);
    current_idt[13].set_func(exception::protection);
    current_idt[14].set_func(exception::page);
    // 15 reserved
    current_idt[16].set_func(exception::fpu_fault);
    current_idt[17].set_func(exception::alignment_check);
    current_idt[18].set_func(exception::machine_check);
    current_idt[19].set_func(exception::simd);
    current_idt[20].set_func(exception::virtualization);
    // 21 through 29 reserved
    current_idt[30].set_func(exception::security);
    // 31 reserved

    // reserve bits 31:0, i.e. the first 32 interrupts, which are reserved for exceptions
    *current_reservations[0].get_mut() |= 0x0000_0000_FFFF_FFFF;

    if is_bsp {
        // Set up IRQs
        current_idt[32].set_func(irq::pit_stack);
        current_idt[33].set_func(irq::keyboard);
        current_idt[34].set_func(irq::cascade);
        current_idt[35].set_func(irq::com2);
        current_idt[36].set_func(irq::com1);
        current_idt[37].set_func(irq::lpt2);
        current_idt[38].set_func(irq::floppy);
        current_idt[39].set_func(irq::lpt1);
        current_idt[40].set_func(irq::rtc);
        current_idt[41].set_func(irq::pci1);
        current_idt[42].set_func(irq::pci2);
        current_idt[43].set_func(irq::pci3);
        current_idt[44].set_func(irq::mouse);
        current_idt[45].set_func(irq::fpu);
        current_idt[46].set_func(irq::ata1);
        current_idt[47].set_func(irq::ata2);
        current_idt[48].set_func(irq::lapic_timer);
        current_idt[49].set_func(irq::lapic_error);

        // reserve bits 49:32, which are for the standard IRQs, and for the local apic timer and error.
        *current_reservations[0].get_mut() |= 0x0003_FFFF_0000_0000;
    } else {
        // TODO: use_default_irqs! but also the legacy IRQs that are only needed on one CPU
    }

    use_default_irqs!(current_idt);

    // Set IPI handlers
    current_idt[IpiKind::Wakeup as usize].set_func(ipi::wakeup);
    current_idt[IpiKind::Switch as usize].set_func(ipi::switch);
    current_idt[IpiKind::Tlb as usize].set_func(ipi::tlb);
    current_idt[IpiKind::Pit as usize].set_func(ipi::pit);
    idt.set_reserved_mut(IpiKind::Wakeup as u8, true);
    idt.set_reserved_mut(IpiKind::Switch as u8, true);
    idt.set_reserved_mut(IpiKind::Tlb as u8, true);
    idt.set_reserved_mut(IpiKind::Pit as u8, true);
    let current_idt = &mut idt.entries;

    // Set syscall function
    current_idt[0x80].set_func(syscall::syscall);
    current_idt[0x80].set_flags(IdtFlags::PRESENT | IdtFlags::RING_3 | IdtFlags::INTERRUPT);
    idt.set_reserved_mut(0x80, true);

    dtables::lidt(&IDTR);
}

bitflags! {
    pub struct IdtFlags: u8 {
        const PRESENT = 1 << 7;
        const RING_0 = 0 << 5;
        const RING_1 = 1 << 5;
        const RING_2 = 2 << 5;
        const RING_3 = 3 << 5;
        const SS = 1 << 4;
        const INTERRUPT = 0xE;
        const TRAP = 0xF;
    }
}

#[derive(Copy, Clone, Debug, Default)]
#[repr(packed)]
pub struct IdtEntry {
    offsetl: u16,
    selector: u16,
    zero: u8,
    attribute: u8,
    offsetm: u16,
    offseth: u32,
    zero2: u32
}

impl IdtEntry {
    pub const fn new() -> IdtEntry {
        IdtEntry {
            offsetl: 0,
            selector: 0,
            zero: 0,
            attribute: 0,
            offsetm: 0,
            offseth: 0,
            zero2: 0
        }
    }

    pub fn set_flags(&mut self, flags: IdtFlags) {
        self.attribute = flags.bits;
    }

    pub fn set_offset(&mut self, selector: u16, base: usize) {
        self.selector = selector;
        self.offsetl = base as u16;
        self.offsetm = (base >> 16) as u16;
        self.offseth = (base >> 32) as u32;
    }

    // A function to set the offset more easily
    pub fn set_func(&mut self, func: unsafe extern fn()) {
        self.set_flags(IdtFlags::PRESENT | IdtFlags::RING_0 | IdtFlags::INTERRUPT);
        self.set_offset((crate::gdt::GDT_KERNEL_CODE as u16) << 3, func as usize);
    }
}
<commit_msg>Give NMI, #DF, and #MC handlers a special stack.<commit_after>use core::num::NonZeroU8;
use core::sync::atomic::{AtomicU64, Ordering};
use core::mem;

use alloc::boxed::Box;
use alloc::collections::BTreeMap;

use x86::segmentation::Descriptor as X86IdtEntry;
use x86::dtables::{self, DescriptorTablePointer};

use crate::interrupt::*;
use crate::ipi::IpiKind;

use spin::RwLock;

pub static mut INIT_IDTR: DescriptorTablePointer<X86IdtEntry> = DescriptorTablePointer {
    limit: 0,
    base: 0 as *const X86IdtEntry
};

#[thread_local]
pub static mut IDTR: DescriptorTablePointer<X86IdtEntry> = DescriptorTablePointer {
    limit: 0,
    base: 0 as *const X86IdtEntry
};

pub type IdtEntries = [IdtEntry; 256];
pub type IdtReservations = [AtomicU64; 4];

#[repr(packed)]
pub struct Idt {
    entries: IdtEntries,
    reservations: IdtReservations,
}
impl Idt {
    pub const fn new() -> Self {
        Self {
            entries: [IdtEntry::new(); 256],
            reservations: new_idt_reservations(),
        }
    }
    #[inline]
    pub fn is_reserved(&self, index: u8) -> bool {
        let byte_index = index / 64;
        let bit = index % 64;

        unsafe { &self.reservations[usize::from(byte_index)] }.load(Ordering::Acquire) & (1 << bit) != 0
    }

    #[inline]
    pub fn set_reserved(&self, index: u8, reserved: bool) {
        let byte_index = index / 64;
        let bit = index % 64;

        unsafe { &self.reservations[usize::from(byte_index)] }.fetch_or(u64::from(reserved) << bit, Ordering::AcqRel);
    }
    #[inline]
    pub fn is_reserved_mut(&mut self, index: u8) -> bool {
        let byte_index = index / 64;
        let bit = index % 64;

        *unsafe { &mut self.reservations[usize::from(byte_index)] }.get_mut() & (1 << bit) != 0
    }

    #[inline]
    pub fn set_reserved_mut(&mut self, index: u8, reserved: bool) {
        let byte_index = index / 64;
        let bit = index % 64;

        *unsafe { &mut self.reservations[usize::from(byte_index)] }.get_mut() |= u64::from(reserved) << bit;
    }
}

static mut INIT_BSP_IDT: Idt = Idt::new();

// TODO: VecMap?
pub static IDTS: RwLock<Option<BTreeMap<usize, &'static mut Idt>>> = RwLock::new(None);

#[inline]
pub fn is_reserved(cpu_id: usize, index: u8) -> bool {
    let byte_index = index / 64;
    let bit = index % 64;

    unsafe { &IDTS.read().as_ref().unwrap().get(&cpu_id).unwrap().reservations[usize::from(byte_index)] }.load(Ordering::Acquire) & (1 << bit) != 0
}

#[inline]
pub fn set_reserved(cpu_id: usize, index: u8, reserved: bool) {
    let byte_index = index / 64;
    let bit = index % 64;

    unsafe { &IDTS.read().as_ref().unwrap().get(&cpu_id).unwrap().reservations[usize::from(byte_index)] }.fetch_or(u64::from(reserved) << bit, Ordering::AcqRel);
}

pub fn allocate_interrupt() -> Option<NonZeroU8> {
    let cpu_id = crate::cpu_id();
    for number in 50..=254 {
        if ! is_reserved(cpu_id, number) {
            set_reserved(cpu_id, number, true);
            return Some(unsafe { NonZeroU8::new_unchecked(number) });
        }
    }
    None
}

pub fn available_irqs_iter(cpu_id: usize) -> impl Iterator<Item = u8> + 'static {
    (32..=254).filter(move |&index| !is_reserved(cpu_id, index))
}

macro_rules! use_irq(
    ( $idt: expr, $number:literal, $func:ident ) => {{
        $idt[$number].set_func($func);
    }}
);

macro_rules! use_default_irqs(
    ($idt:expr) => {{
        use crate::interrupt::irq::*;
        default_irqs!($idt, use_irq);
    }}
);

pub unsafe fn init() {
    dtables::lidt(&INIT_IDTR);
}

const fn new_idt_reservations() -> [AtomicU64; 4] {
    [AtomicU64::new(0), AtomicU64::new(0), AtomicU64::new(0), AtomicU64::new(0)]
}

/// Initialize the IDT for a
pub unsafe fn init_paging_post_heap(is_bsp: bool, cpu_id: usize) {
    let mut idts_guard = IDTS.write();
    let idts_btree = idts_guard.get_or_insert_with(|| BTreeMap::new());

    if is_bsp {
        idts_btree.insert(cpu_id, &mut INIT_BSP_IDT);
    } else {
        let idt = idts_btree.entry(cpu_id).or_insert_with(|| Box::leak(Box::new(Idt::new())));
        init_generic(is_bsp, idt);
    }
}

/// Initializes a fully functional IDT for use before it be moved into the map. This is ONLY called
/// on the BSP, since the kernel heap is ready for the APs.
pub unsafe fn init_paging_bsp() {
    init_generic(true, &mut INIT_BSP_IDT);
}

/// Initializes an IDT for any type of processor.
pub unsafe fn init_generic(is_bsp: bool, idt: &mut Idt) {
    let (current_idt, current_reservations) = (&mut idt.entries, &mut idt.reservations);

    IDTR.limit = (current_idt.len() * mem::size_of::<IdtEntry>() - 1) as u16;
    IDTR.base = current_idt.as_ptr() as *const X86IdtEntry;

    let backup_ist = {
        // A problem with SWAPGS, is that if a non-maskable interrupt were to occur in the middle
        // of swapping, the CS would now point to the new kernel CS from the kernel-triggered
        // interrupt, and no swap would occur. Thus, we give the NMI handler a separate stack. This
        // is also true for Machine Check, and for Double Faults, but for other reasons.
        //
        // Note that each CPU has its own "backup interrupt stack".
        let index = 1_u8;

        // Allocate 64 KiB of stack space for the backup stack.
        const BACKUP_STACK_SIZE: usize = 65536;
        assert_eq!(BACKUP_STACK_SIZE % crate::memory::PAGE_SIZE, 0);
        let page_count = BACKUP_STACK_SIZE / crate::memory::PAGE_SIZE;
        let frames = crate::memory::allocate_frames(page_count)
            .expect("failed to allocate pages for backup interrupt stack");

        // Map them linearly, i.e. KERNEL_OFFSET + physaddr.
        let base_address = {
            use crate::memory::{Frame, PhysicalAddress};
            use crate::paging::{ActivePageTable, Page, VirtualAddress};
            use crate::paging::entry::EntryFlags;

            let mut active_table = ActivePageTable::new();
            let base_virtual_address = VirtualAddress::new(frames.start_address().data() + crate::KERNEL_OFFSET);

            for i in 0..page_count {
                let virtual_address = VirtualAddress::new(base_virtual_address.data() + i * crate::memory::PAGE_SIZE);
                let physical_address = PhysicalAddress::new(frames.start_address().data() + i * crate::memory::PAGE_SIZE);
                let page = Page::containing_address(virtual_address);

                let flags = EntryFlags::PRESENT | EntryFlags::WRITABLE | EntryFlags::NO_EXECUTE;

                let flusher = if let Some(already_mapped) = active_table.translate_page(page) {
                    assert_eq!(already_mapped.start_address(), physical_address, "address already mapped, but non-linearly");
                    active_table.remap(page, flags)
                } else {
                    active_table.map_to(page, Frame::containing_address(physical_address), flags)
                };
                flusher.flush(&mut active_table);
            }

            base_virtual_address
        };
        // Stack always grows downwards.
        let address = base_address.data() + BACKUP_STACK_SIZE;

        // Put them in the 1st entry of the IST.
        crate::gdt::TSS.ist[usize::from(index - 1)] = address as u64;

        index
    };

    // Set up exceptions
    current_idt[0].set_func(exception::divide_by_zero);
    current_idt[1].set_func(exception::debug);
    current_idt[2].set_func(exception::non_maskable);
    current_idt[2].set_ist(backup_ist);
    current_idt[3].set_func(exception::breakpoint);
    current_idt[3].set_flags(IdtFlags::PRESENT | IdtFlags::RING_3 | IdtFlags::INTERRUPT);
    current_idt[4].set_func(exception::overflow);
    current_idt[5].set_func(exception::bound_range);
    current_idt[6].set_func(exception::invalid_opcode);
    current_idt[7].set_func(exception::device_not_available);
    current_idt[8].set_func(exception::double_fault);
    current_idt[8].set_ist(backup_ist);
    // 9 no longer available
    current_idt[10].set_func(exception::invalid_tss);
    current_idt[11].set_func(exception::segment_not_present);
    current_idt[12].set_func(exception::stack_segment);
    current_idt[13].set_func(exception::protection);
    current_idt[14].set_func(exception::page);
    // 15 reserved
    current_idt[16].set_func(exception::fpu_fault);
    current_idt[17].set_func(exception::alignment_check);
    current_idt[18].set_func(exception::machine_check);
    current_idt[18].set_ist(backup_ist);
    current_idt[19].set_func(exception::simd);
    current_idt[20].set_func(exception::virtualization);
    // 21 through 29 reserved
    current_idt[30].set_func(exception::security);
    // 31 reserved

    // reserve bits 31:0, i.e. the first 32 interrupts, which are reserved for exceptions
    *current_reservations[0].get_mut() |= 0x0000_0000_FFFF_FFFF;

    if is_bsp {
        // Set up IRQs
        current_idt[32].set_func(irq::pit_stack);
        current_idt[33].set_func(irq::keyboard);
        current_idt[34].set_func(irq::cascade);
        current_idt[35].set_func(irq::com2);
        current_idt[36].set_func(irq::com1);
        current_idt[37].set_func(irq::lpt2);
        current_idt[38].set_func(irq::floppy);
        current_idt[39].set_func(irq::lpt1);
        current_idt[40].set_func(irq::rtc);
        current_idt[41].set_func(irq::pci1);
        current_idt[42].set_func(irq::pci2);
        current_idt[43].set_func(irq::pci3);
        current_idt[44].set_func(irq::mouse);
        current_idt[45].set_func(irq::fpu);
        current_idt[46].set_func(irq::ata1);
        current_idt[47].set_func(irq::ata2);
        current_idt[48].set_func(irq::lapic_timer);
        current_idt[49].set_func(irq::lapic_error);

        // reserve bits 49:32, which are for the standard IRQs, and for the local apic timer and error.
        *current_reservations[0].get_mut() |= 0x0003_FFFF_0000_0000;
    } else {
        // TODO: use_default_irqs! but also the legacy IRQs that are only needed on one CPU
    }

    use_default_irqs!(current_idt);

    // Set IPI handlers
    current_idt[IpiKind::Wakeup as usize].set_func(ipi::wakeup);
    current_idt[IpiKind::Switch as usize].set_func(ipi::switch);
    current_idt[IpiKind::Tlb as usize].set_func(ipi::tlb);
    current_idt[IpiKind::Pit as usize].set_func(ipi::pit);
    idt.set_reserved_mut(IpiKind::Wakeup as u8, true);
    idt.set_reserved_mut(IpiKind::Switch as u8, true);
    idt.set_reserved_mut(IpiKind::Tlb as u8, true);
    idt.set_reserved_mut(IpiKind::Pit as u8, true);
    let current_idt = &mut idt.entries;

    // Set syscall function
    current_idt[0x80].set_func(syscall::syscall);
    current_idt[0x80].set_flags(IdtFlags::PRESENT | IdtFlags::RING_3 | IdtFlags::INTERRUPT);
    idt.set_reserved_mut(0x80, true);

    dtables::lidt(&IDTR);
}

bitflags! {
    pub struct IdtFlags: u8 {
        const PRESENT = 1 << 7;
        const RING_0 = 0 << 5;
        const RING_1 = 1 << 5;
        const RING_2 = 2 << 5;
        const RING_3 = 3 << 5;
        const SS = 1 << 4;
        const INTERRUPT = 0xE;
        const TRAP = 0xF;
    }
}

#[derive(Copy, Clone, Debug, Default)]
#[repr(packed)]
pub struct IdtEntry {
    offsetl: u16,
    selector: u16,
    zero: u8,
    attribute: u8,
    offsetm: u16,
    offseth: u32,
    zero2: u32
}

impl IdtEntry {
    pub const fn new() -> IdtEntry {
        IdtEntry {
            offsetl: 0,
            selector: 0,
            zero: 0,
            attribute: 0,
            offsetm: 0,
            offseth: 0,
            zero2: 0
        }
    }

    pub fn set_flags(&mut self, flags: IdtFlags) {
        self.attribute = flags.bits;
    }

    pub fn set_ist(&mut self, ist: u8) {
        assert_eq!(ist & 0x07, ist, "interrupt stack table must be within 0..=7");
        self.zero &= 0xF8;
        self.zero |= ist;
    }

    pub fn set_offset(&mut self, selector: u16, base: usize) {
        self.selector = selector;
        self.offsetl = base as u16;
        self.offsetm = (base >> 16) as u16;
        self.offseth = (base >> 32) as u32;
    }

    // A function to set the offset more easily
    pub fn set_func(&mut self, func: unsafe extern fn()) {
        self.set_flags(IdtFlags::PRESENT | IdtFlags::RING_0 | IdtFlags::INTERRUPT);
        self.set_offset((crate::gdt::GDT_KERNEL_CODE as u16) << 3, func as usize);
    }
}
<|endoftext|>


<commit_before>use core::{
    cmp,
    mem,
    slice,
};
use rmm::{
    KILOBYTE,
    MEGABYTE,
    Arch,
    BuddyAllocator,
    BumpAllocator,
    FrameAllocator,
    FrameCount,
    FrameUsage,
    MemoryArea,
    PageFlags,
    PageMapper,
    PhysicalAddress,
    VirtualAddress,
    X8664Arch as RmmA,
};

use spin::Mutex;

extern "C" {
    /// The starting byte of the text (code) data segment.
    static mut __text_start: u8;
    /// The ending byte of the text (code) data segment.
    static mut __text_end: u8;
    /// The starting byte of the _.rodata_ (read-only data) segment.
    static mut __rodata_start: u8;
    /// The ending byte of the _.rodata_ (read-only data) segment.
    static mut __rodata_end: u8;
}

// Keep synced with OsMemoryKind in bootloader
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u64)]
pub enum BootloaderMemoryKind {
    Null = 0,
    Free = 1,
    Reclaim = 2,
    Reserved = 3,
}

// Keep synced with OsMemoryEntry in bootloader
#[derive(Clone, Copy, Debug)]
#[repr(packed)]
pub struct BootloaderMemoryEntry {
    pub base: u64,
    pub size: u64,
    pub kind: BootloaderMemoryKind,
}

unsafe fn page_flags<A: Arch>(virt: VirtualAddress) -> PageFlags<A> {
    let virt_addr = virt.data();

    // Test for being inside a region
    macro_rules! in_section {
        ($n: ident) => {
            virt_addr >= &concat_idents!(__, $n, _start) as *const u8 as usize
                && virt_addr < &concat_idents!(__, $n, _end) as *const u8 as usize
        };
    }

    if in_section!(text) {
        // Remap text read-only, execute
        PageFlags::new().execute(true)
    } else if in_section!(rodata) {
        // Remap rodata read-only, no execute
        PageFlags::new()
    } else {
        // Remap everything else read-write, no execute
        PageFlags::new().write(true)
    }
}

unsafe fn inner<A: Arch>(
    areas: &'static [MemoryArea],
    kernel_base: usize, kernel_size_aligned: usize,
    stack_base: usize, stack_size_aligned: usize,
    env_base: usize, env_size_aligned: usize,
    acpi_base: usize, acpi_size_aligned: usize
) -> BuddyAllocator<A> {
    // First, calculate how much memory we have
    let mut size = 0;
    for area in areas.iter() {
        if area.size > 0 {
            println!("{:X?}", area);
            size += area.size;
        }
    }

    println!("Memory: {} MB", (size + (MEGABYTE - 1)) / MEGABYTE);

    // Create a basic allocator for the first pages
    let mut bump_allocator = BumpAllocator::<A>::new(areas, 0);

    {
        let mut mapper = PageMapper::<A, _>::create(
            &mut bump_allocator
        ).expect("failed to create Mapper");

        // Map all physical areas at PHYS_OFFSET
        for area in areas.iter() {
            for i in 0..area.size / A::PAGE_SIZE {
                let phys = area.base.add(i * A::PAGE_SIZE);
                let virt = A::phys_to_virt(phys);
                let flags = page_flags::<A>(virt);
                let flush = mapper.map_phys(
                    virt,
                    phys,
                    flags
                ).expect("failed to map frame");
                flush.ignore(); // Not the active table
            }
        }

        // Map kernel at KERNEL_OFFSET and identity map too
        for i in 0..kernel_size_aligned / A::PAGE_SIZE {
            let phys = PhysicalAddress::new(kernel_base + i * A::PAGE_SIZE);
            let virt = VirtualAddress::new(crate::KERNEL_OFFSET + i * A::PAGE_SIZE);
            let flags = page_flags::<A>(virt);
            let flush = mapper.map_phys(
                virt,
                phys,
                flags
            ).expect("failed to map frame");
            flush.ignore(); // Not the active table

            let virt = A::phys_to_virt(phys);
            let flush = mapper.map_phys(
                virt,
                phys,
                flags
            ).expect("failed to map frame");
            flush.ignore(); // Not the active table
        }

        // Map stack with identity mapping
        for i in 0..stack_size_aligned / A::PAGE_SIZE {
            let phys = PhysicalAddress::new(stack_base + i * A::PAGE_SIZE);
            let virt = A::phys_to_virt(phys);
            let flags = page_flags::<A>(virt);
            let flush = mapper.map_phys(
                virt,
                phys,
                flags
            ).expect("failed to map frame");
            flush.ignore(); // Not the active table
        }

        // Map env with identity mapping
        for i in 0..env_size_aligned / A::PAGE_SIZE {
            let phys = PhysicalAddress::new(env_base + i * A::PAGE_SIZE);
            let virt = A::phys_to_virt(phys);
            let flags = page_flags::<A>(virt);
            let flush = mapper.map_phys(
                virt,
                phys,
                flags
            ).expect("failed to map frame");
            flush.ignore(); // Not the active table
        }

        // Map acpi with identity mapping
        for i in 0..acpi_size_aligned / A::PAGE_SIZE {
            let phys = PhysicalAddress::new(acpi_base + i * A::PAGE_SIZE);
            let virt = A::phys_to_virt(phys);
            let flags = page_flags::<A>(virt);
            let flush = mapper.map_phys(
                virt,
                phys,
                flags
            ).expect("failed to map frame");
            flush.ignore(); // Not the active table
        }

        // Ensure graphical debug region remains paged
        #[cfg(feature = "graphical_debug")]
        {
            use super::graphical_debug::DEBUG_DISPLAY;
            use super::paging::entry::EntryFlags;

            let (base, size) = if let Some(debug_display) = &*DEBUG_DISPLAY.lock() {
                let data = &debug_display.display.data;
                (
                    data.as_ptr() as usize - crate::PHYS_OFFSET,
                    data.len() * 4
                )
            } else {
                (0, 0)
            };

            let pages = (size + A::PAGE_SIZE - 1) / A::PAGE_SIZE;
            for i in 0..pages {
                let phys = PhysicalAddress::new(base + i * A::PAGE_SIZE);
                let virt = A::phys_to_virt(phys);
                let flags = PageFlags::new().write(true);
                let flush = mapper.map_phys(
                    virt,
                    phys,
                    flags
                ).expect("failed to map frame");
                flush.ignore(); // Not the active table
            }
        }

        println!("Table: {:X}", mapper.table().phys().data());
        for i in 0..512 {
            if let Some(entry) = mapper.table().entry(i) {
                if entry.present() {
                    println!("{}: {:X}", i, entry.data());
                }
            }
        }

        // Use the new table
        mapper.make_current();
    }

    // Create the physical memory map
    let offset = bump_allocator.offset();
    println!("Permanently used: {} KB", (offset + (KILOBYTE - 1)) / KILOBYTE);

    BuddyAllocator::<A>::new(bump_allocator).expect("failed to create BuddyAllocator")
}

pub struct LockedAllocator {
    inner: Mutex<Option<BuddyAllocator<RmmA>>>,
}

impl LockedAllocator {
    const fn new() -> Self {
        Self {
            inner: Mutex::new(None)
        }
    }
}

impl FrameAllocator for LockedAllocator {
    unsafe fn allocate(&mut self, count: FrameCount) -> Option<PhysicalAddress> {
        if let Some(ref mut allocator) = *self.inner.lock() {
            allocator.allocate(count)
        } else {
            None
        }
    }

    unsafe fn free(&mut self, address: PhysicalAddress, count: FrameCount) {
        if let Some(ref mut allocator) = *self.inner.lock() {
            allocator.free(address, count)
        }
    }

    unsafe fn usage(&self) -> FrameUsage {
        if let Some(ref allocator) = *self.inner.lock() {
            allocator.usage()
        } else {
            FrameUsage::new(FrameCount::new(0), FrameCount::new(0))
        }
    }
}

static mut AREAS: [MemoryArea; 512] = [MemoryArea {
    base: PhysicalAddress::new(0),
    size: 0,
}; 512];

pub static mut FRAME_ALLOCATOR: LockedAllocator = LockedAllocator::new();

pub unsafe fn mapper_new(table_addr: PhysicalAddress) -> PageMapper<'static, RmmA, LockedAllocator> {
    PageMapper::new(table_addr, &mut FRAME_ALLOCATOR)
}

//TODO: global paging lock?
pub unsafe fn mapper_create() -> Option<PageMapper<'static, RmmA, LockedAllocator>> {
    PageMapper::create(&mut FRAME_ALLOCATOR)
}

pub unsafe fn mapper_current() -> PageMapper<'static, RmmA, LockedAllocator> {
    PageMapper::current(&mut FRAME_ALLOCATOR)
}

pub unsafe fn init(
    kernel_base: usize, kernel_size: usize,
    stack_base: usize, stack_size: usize,
    env_base: usize, env_size: usize,
    acpi_base: usize, acpi_size: usize,
    areas_base: usize, areas_size: usize,
) {
    type A = RmmA;

    let real_base = 0;
    let real_size = 0x100000;
    let real_end = real_base + real_size;

    let kernel_size_aligned = ((kernel_size + (A::PAGE_SIZE - 1))/A::PAGE_SIZE) * A::PAGE_SIZE;
    let kernel_end = kernel_base + kernel_size_aligned;

    let stack_size_aligned = ((stack_size + (A::PAGE_SIZE - 1))/A::PAGE_SIZE) * A::PAGE_SIZE;
    let stack_end = stack_base + stack_size_aligned;

    let env_size_aligned = ((env_size + (A::PAGE_SIZE - 1))/A::PAGE_SIZE) * A::PAGE_SIZE;
    let env_end = env_base + env_size_aligned;

    let acpi_size_aligned = ((acpi_size + (A::PAGE_SIZE - 1))/A::PAGE_SIZE) * A::PAGE_SIZE;
    let acpi_end = acpi_base + acpi_size_aligned;

    let bootloader_areas = slice::from_raw_parts(
        areas_base as *const BootloaderMemoryEntry,
        areas_size / mem::size_of::<BootloaderMemoryEntry>()
    );

    // Copy memory map from bootloader location, and page align it
    let mut area_i = 0;
    for bootloader_area in bootloader_areas.iter() {
        if bootloader_area.kind != BootloaderMemoryKind::Free {
            // Not a free area
            continue;
        }

        let mut base = bootloader_area.base as usize;
        let mut size = bootloader_area.size as usize;

        print!("{:X}:{:X}", base, size);

        // Page align base
        let base_offset = (A::PAGE_SIZE - (base & A::PAGE_OFFSET_MASK)) & A::PAGE_OFFSET_MASK;
        if base_offset > size {
            // Area is too small to page align base
            continue;
        }
        base += base_offset;
        size -= base_offset;

        // Page align size
        size &= !A::PAGE_OFFSET_MASK;
        println!(" => {:X}:{:X}", base, size);

        let mut new_base = base;

        // Ensure real-mode areas are not used
        if base < real_end && base + size > real_base {
            println!("{:X}:{:X} overlaps with real mode {:X}:{:X}", base, size, real_base, real_size);
            new_base = cmp::max(new_base, real_end);
        }

        // Ensure kernel areas are not used
        if base < kernel_end && base + size > kernel_base {
            println!("{:X}:{:X} overlaps with kernel {:X}:{:X}", base, size, kernel_base, kernel_size);
            new_base = cmp::max(new_base, kernel_end);
        }

        // Ensure stack areas are not used
        if base < stack_end && base + size > stack_base {
            println!("{:X}:{:X} overlaps with stack {:X}:{:X}", base, size, stack_base, stack_size);
            new_base = cmp::max(new_base, stack_end);
        }

        // Ensure env areas are not used
        if base < env_end && base + size > env_base {
            println!("{:X}:{:X} overlaps with env {:X}:{:X}", base, size, env_base, env_size);
            new_base = cmp::max(new_base, env_end);
        }

        // Ensure acpi areas are not used
        if base < acpi_end && base + size > acpi_base {
            println!("{:X}:{:X} overlaps with acpi {:X}:{:X}", base, size, acpi_base, acpi_size);
            new_base = cmp::max(new_base, acpi_end);
        }

        if new_base != base {
            let end = base + size;
            base = new_base;
            size = end.checked_sub(base).unwrap_or(0);
            println!("moved to {:X}:{:X}", base, size);
        }

        if size == 0 {
            // Area is zero sized
            continue;
        }

        AREAS[area_i].base = PhysicalAddress::new(base);
        AREAS[area_i].size = size;
        area_i += 1;
    }

    let allocator = inner::<A>(
        &AREAS,
        kernel_base, kernel_size_aligned,
        stack_base, stack_size_aligned,
        env_base, env_size_aligned,
        acpi_base, acpi_size_aligned
    );
    *FRAME_ALLOCATOR.inner.lock() = Some(allocator);
}
<commit_msg>Improve RMM logging<commit_after>use core::{
    cmp,
    mem,
    slice,
};
use rmm::{
    KILOBYTE,
    MEGABYTE,
    Arch,
    BuddyAllocator,
    BumpAllocator,
    FrameAllocator,
    FrameCount,
    FrameUsage,
    MemoryArea,
    PageFlags,
    PageMapper,
    PhysicalAddress,
    VirtualAddress,
    X8664Arch as RmmA,
};

use spin::Mutex;

extern "C" {
    /// The starting byte of the text (code) data segment.
    static mut __text_start: u8;
    /// The ending byte of the text (code) data segment.
    static mut __text_end: u8;
    /// The starting byte of the _.rodata_ (read-only data) segment.
    static mut __rodata_start: u8;
    /// The ending byte of the _.rodata_ (read-only data) segment.
    static mut __rodata_end: u8;
}

// Keep synced with OsMemoryKind in bootloader
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u64)]
pub enum BootloaderMemoryKind {
    Null = 0,
    Free = 1,
    Reclaim = 2,
    Reserved = 3,
}

// Keep synced with OsMemoryEntry in bootloader
#[derive(Clone, Copy, Debug)]
#[repr(packed)]
pub struct BootloaderMemoryEntry {
    pub base: u64,
    pub size: u64,
    pub kind: BootloaderMemoryKind,
}

unsafe fn page_flags<A: Arch>(virt: VirtualAddress) -> PageFlags<A> {
    let virt_addr = virt.data();

    // Test for being inside a region
    macro_rules! in_section {
        ($n: ident) => {
            virt_addr >= &concat_idents!(__, $n, _start) as *const u8 as usize
                && virt_addr < &concat_idents!(__, $n, _end) as *const u8 as usize
        };
    }

    if in_section!(text) {
        // Remap text read-only, execute
        PageFlags::new().execute(true)
    } else if in_section!(rodata) {
        // Remap rodata read-only, no execute
        PageFlags::new()
    } else {
        // Remap everything else read-write, no execute
        PageFlags::new().write(true)
    }
}

unsafe fn inner<A: Arch>(
    areas: &'static [MemoryArea],
    kernel_base: usize, kernel_size_aligned: usize,
    stack_base: usize, stack_size_aligned: usize,
    env_base: usize, env_size_aligned: usize,
    acpi_base: usize, acpi_size_aligned: usize
) -> BuddyAllocator<A> {
    // First, calculate how much memory we have
    let mut size = 0;
    for area in areas.iter() {
        if area.size > 0 {
            log::debug!("{:X?}", area);
            size += area.size;
        }
    }

    log::info!("Memory: {} MB", (size + (MEGABYTE - 1)) / MEGABYTE);

    // Create a basic allocator for the first pages
    let mut bump_allocator = BumpAllocator::<A>::new(areas, 0);

    {
        let mut mapper = PageMapper::<A, _>::create(
            &mut bump_allocator
        ).expect("failed to create Mapper");

        // Map all physical areas at PHYS_OFFSET
        for area in areas.iter() {
            for i in 0..area.size / A::PAGE_SIZE {
                let phys = area.base.add(i * A::PAGE_SIZE);
                let virt = A::phys_to_virt(phys);
                let flags = page_flags::<A>(virt);
                let flush = mapper.map_phys(
                    virt,
                    phys,
                    flags
                ).expect("failed to map frame");
                flush.ignore(); // Not the active table
            }
        }

        // Map kernel at KERNEL_OFFSET and identity map too
        for i in 0..kernel_size_aligned / A::PAGE_SIZE {
            let phys = PhysicalAddress::new(kernel_base + i * A::PAGE_SIZE);
            let virt = VirtualAddress::new(crate::KERNEL_OFFSET + i * A::PAGE_SIZE);
            let flags = page_flags::<A>(virt);
            let flush = mapper.map_phys(
                virt,
                phys,
                flags
            ).expect("failed to map frame");
            flush.ignore(); // Not the active table

            let virt = A::phys_to_virt(phys);
            let flush = mapper.map_phys(
                virt,
                phys,
                flags
            ).expect("failed to map frame");
            flush.ignore(); // Not the active table
        }

        // Map stack with identity mapping
        for i in 0..stack_size_aligned / A::PAGE_SIZE {
            let phys = PhysicalAddress::new(stack_base + i * A::PAGE_SIZE);
            let virt = A::phys_to_virt(phys);
            let flags = page_flags::<A>(virt);
            let flush = mapper.map_phys(
                virt,
                phys,
                flags
            ).expect("failed to map frame");
            flush.ignore(); // Not the active table
        }

        // Map env with identity mapping
        for i in 0..env_size_aligned / A::PAGE_SIZE {
            let phys = PhysicalAddress::new(env_base + i * A::PAGE_SIZE);
            let virt = A::phys_to_virt(phys);
            let flags = page_flags::<A>(virt);
            let flush = mapper.map_phys(
                virt,
                phys,
                flags
            ).expect("failed to map frame");
            flush.ignore(); // Not the active table
        }

        // Map acpi with identity mapping
        for i in 0..acpi_size_aligned / A::PAGE_SIZE {
            let phys = PhysicalAddress::new(acpi_base + i * A::PAGE_SIZE);
            let virt = A::phys_to_virt(phys);
            let flags = page_flags::<A>(virt);
            let flush = mapper.map_phys(
                virt,
                phys,
                flags
            ).expect("failed to map frame");
            flush.ignore(); // Not the active table
        }

        // Ensure graphical debug region remains paged
        #[cfg(feature = "graphical_debug")]
        {
            use super::graphical_debug::DEBUG_DISPLAY;
            use super::paging::entry::EntryFlags;

            let (base, size) = if let Some(debug_display) = &*DEBUG_DISPLAY.lock() {
                let data = &debug_display.display.data;
                (
                    data.as_ptr() as usize - crate::PHYS_OFFSET,
                    data.len() * 4
                )
            } else {
                (0, 0)
            };

            let pages = (size + A::PAGE_SIZE - 1) / A::PAGE_SIZE;
            for i in 0..pages {
                let phys = PhysicalAddress::new(base + i * A::PAGE_SIZE);
                let virt = A::phys_to_virt(phys);
                let flags = PageFlags::new().write(true);
                let flush = mapper.map_phys(
                    virt,
                    phys,
                    flags
                ).expect("failed to map frame");
                flush.ignore(); // Not the active table
            }
        }

        log::debug!("Table: {:X}", mapper.table().phys().data());
        for i in 0..512 {
            if let Some(entry) = mapper.table().entry(i) {
                if entry.present() {
                    log::debug!("{}: {:X}", i, entry.data());
                }
            }
        }

        // Use the new table
        mapper.make_current();
    }

    // Create the physical memory map
    let offset = bump_allocator.offset();
    log::info!("Permanently used: {} KB", (offset + (KILOBYTE - 1)) / KILOBYTE);

    BuddyAllocator::<A>::new(bump_allocator).expect("failed to create BuddyAllocator")
}

pub struct LockedAllocator {
    inner: Mutex<Option<BuddyAllocator<RmmA>>>,
}

impl LockedAllocator {
    const fn new() -> Self {
        Self {
            inner: Mutex::new(None)
        }
    }
}

impl FrameAllocator for LockedAllocator {
    unsafe fn allocate(&mut self, count: FrameCount) -> Option<PhysicalAddress> {
        if let Some(ref mut allocator) = *self.inner.lock() {
            allocator.allocate(count)
        } else {
            None
        }
    }

    unsafe fn free(&mut self, address: PhysicalAddress, count: FrameCount) {
        if let Some(ref mut allocator) = *self.inner.lock() {
            allocator.free(address, count)
        }
    }

    unsafe fn usage(&self) -> FrameUsage {
        if let Some(ref allocator) = *self.inner.lock() {
            allocator.usage()
        } else {
            FrameUsage::new(FrameCount::new(0), FrameCount::new(0))
        }
    }
}

static mut AREAS: [MemoryArea; 512] = [MemoryArea {
    base: PhysicalAddress::new(0),
    size: 0,
}; 512];

pub static mut FRAME_ALLOCATOR: LockedAllocator = LockedAllocator::new();

pub unsafe fn mapper_new(table_addr: PhysicalAddress) -> PageMapper<'static, RmmA, LockedAllocator> {
    PageMapper::new(table_addr, &mut FRAME_ALLOCATOR)
}

//TODO: global paging lock?
pub unsafe fn mapper_create() -> Option<PageMapper<'static, RmmA, LockedAllocator>> {
    PageMapper::create(&mut FRAME_ALLOCATOR)
}

pub unsafe fn mapper_current() -> PageMapper<'static, RmmA, LockedAllocator> {
    PageMapper::current(&mut FRAME_ALLOCATOR)
}

pub unsafe fn init(
    kernel_base: usize, kernel_size: usize,
    stack_base: usize, stack_size: usize,
    env_base: usize, env_size: usize,
    acpi_base: usize, acpi_size: usize,
    areas_base: usize, areas_size: usize,
) {
    type A = RmmA;

    let real_base = 0;
    let real_size = 0x100000;
    let real_end = real_base + real_size;

    let kernel_size_aligned = ((kernel_size + (A::PAGE_SIZE - 1))/A::PAGE_SIZE) * A::PAGE_SIZE;
    let kernel_end = kernel_base + kernel_size_aligned;

    let stack_size_aligned = ((stack_size + (A::PAGE_SIZE - 1))/A::PAGE_SIZE) * A::PAGE_SIZE;
    let stack_end = stack_base + stack_size_aligned;

    let env_size_aligned = ((env_size + (A::PAGE_SIZE - 1))/A::PAGE_SIZE) * A::PAGE_SIZE;
    let env_end = env_base + env_size_aligned;

    let acpi_size_aligned = ((acpi_size + (A::PAGE_SIZE - 1))/A::PAGE_SIZE) * A::PAGE_SIZE;
    let acpi_end = acpi_base + acpi_size_aligned;

    let bootloader_areas = slice::from_raw_parts(
        areas_base as *const BootloaderMemoryEntry,
        areas_size / mem::size_of::<BootloaderMemoryEntry>()
    );

    // Copy memory map from bootloader location, and page align it
    let mut area_i = 0;
    for bootloader_area in bootloader_areas.iter() {
        if bootloader_area.kind != BootloaderMemoryKind::Free {
            // Not a free area
            continue;
        }

        let mut base = bootloader_area.base as usize;
        let mut size = bootloader_area.size as usize;

        log::debug!("{:X}:{:X}", base, size);

        // Page align base
        let base_offset = (A::PAGE_SIZE - (base & A::PAGE_OFFSET_MASK)) & A::PAGE_OFFSET_MASK;
        if base_offset > size {
            // Area is too small to page align base
            continue;
        }
        base += base_offset;
        size -= base_offset;

        // Page align size
        size &= !A::PAGE_OFFSET_MASK;
        log::debug!(" => {:X}:{:X}", base, size);

        let mut new_base = base;

        // Ensure real-mode areas are not used
        if base < real_end && base + size > real_base {
            log::warn!("{:X}:{:X} overlaps with real mode {:X}:{:X}", base, size, real_base, real_size);
            new_base = cmp::max(new_base, real_end);
        }

        // Ensure kernel areas are not used
        if base < kernel_end && base + size > kernel_base {
            log::warn!("{:X}:{:X} overlaps with kernel {:X}:{:X}", base, size, kernel_base, kernel_size);
            new_base = cmp::max(new_base, kernel_end);
        }

        // Ensure stack areas are not used
        if base < stack_end && base + size > stack_base {
            log::warn!("{:X}:{:X} overlaps with stack {:X}:{:X}", base, size, stack_base, stack_size);
            new_base = cmp::max(new_base, stack_end);
        }

        // Ensure env areas are not used
        if base < env_end && base + size > env_base {
            log::warn!("{:X}:{:X} overlaps with env {:X}:{:X}", base, size, env_base, env_size);
            new_base = cmp::max(new_base, env_end);
        }

        // Ensure acpi areas are not used
        if base < acpi_end && base + size > acpi_base {
            log::warn!("{:X}:{:X} overlaps with acpi {:X}:{:X}", base, size, acpi_base, acpi_size);
            new_base = cmp::max(new_base, acpi_end);
        }

        if new_base != base {
            let end = base + size;
            let new_size = end.checked_sub(new_base).unwrap_or(0);
            log::info!("{:X}:{:X} moved to {:X}:{:X}", base, size, new_base, new_size);
            base = new_base;
            size = new_size;
        }

        if size == 0 {
            // Area is zero sized
            continue;
        }

        AREAS[area_i].base = PhysicalAddress::new(base);
        AREAS[area_i].size = size;
        area_i += 1;
    }

    let allocator = inner::<A>(
        &AREAS,
        kernel_base, kernel_size_aligned,
        stack_base, stack_size_aligned,
        env_base, env_size_aligned,
        acpi_base, acpi_size_aligned
    );
    *FRAME_ALLOCATOR.inner.lock() = Some(allocator);
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
#[macro_use]
extern crate log;
#[macro_use]
extern crate json;
extern crate curl;
extern crate getopts;
extern crate mktemp;
extern crate mpmc;
extern crate regex;
extern crate shuteye;
extern crate tic;
extern crate tiny_http;
extern crate toml;
extern crate sha_1;
extern crate hmac;
extern crate rustc_serialize;

mod common;
mod consumer;
mod webhook;
mod publisher;

use common::logging::set_log_level;
use common::repoconfig;
use consumer::cargo::{Cargo, Channel, Profile, Triple};
use getopts::Options;
use std::{env, process};
use std::path::{Path, PathBuf};

pub const VERSION: &'static str = env!("CARGO_PKG_VERSION");
pub const PROGRAM: &'static str = env!("CARGO_PKG_NAME");

fn print_usage(program: &str, opts: &Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

fn opts() -> Options {
    let mut opts = Options::new();

    // general
    opts.optflag("h", "help", "print this help menu");
    opts.optflag("", "version", "show version and exit");
    opts.optflagmulti("v", "verbose", "verbosity (stacking)");

    // clippy
    opts.optflag("", "no-clippy", "skip clippy");

    // fuzzing
    opts.optflag("", "no-fuzz", "skip fuzzing");
    opts.optopt("d", "fuzz-duration", "time per fuzz target", "[SECONDS]");
    opts.optopt(
        "c",
        "fuzz-cores",
        "number of cores to use during fuzzing",
        "[CORES]",
    );
    opts.optopt("d", "fuzz-length", "max length of fuzz input", "[BYTES]");

    // cross targets
    opts.optflag("", "no-cross", "skip cross targets");

    opts
}

pub fn init() -> getopts::Matches {
    let args: Vec<String> = env::args().collect();
    let program = &args[0];
    let opts = opts();

    let options = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            println!("Failed to parse command line args: {}", f);
            process::exit(1);
        }
    };

    if options.opt_present("help") {
        print_usage(program, &opts);
        process::exit(0);
    }

    if options.opt_present("version") {
        println!("{} {}", PROGRAM, VERSION);
        process::exit(0);
    }

    options
}

fn main() {
    let options = init();

    // initialize logging
    set_log_level(options.opt_count("verbose"));
    info!("{} {}", PROGRAM, VERSION);

    // load config from file, override with command line
    let mut config = repoconfig::load_config(Path::new(".crucible.toml")).unwrap_or_default();
    if let Some(s) = options.opt_str("fuzz-duration") {
        let v = s.parse().expect("ERROR: fuzz-duration invalid");
        config.set_fuzz_seconds(v);
    }
    if let Some(s) = options.opt_str("fuzz-cores") {
        let v = s.parse().expect("ERROR: fuzz-cores invalid");
        config.set_fuzz_cores(v);
    }
    if let Some(s) = options.opt_str("fuzz-length") {
        let v = s.parse().expect("ERROR: fuzz-length invalid");
        config.set_fuzz_len(v);
    }

    // complete set of tests - native target
    let mut cargo = Cargo::new(PathBuf::from("."));
    build_test(&mut cargo);
    cargo.fmt().expect("cargo fmt: failed");
    if !options.opt_present("no-clippy") {
        cargo.clippy().expect("cargo clippy: failed");
    }
    if !options.opt_present("no-fuzz") {
        cargo.set_fuzz_seconds(config.fuzz_seconds());
        cargo.set_fuzz_cores(config.fuzz_cores());
        cargo.set_fuzz_len(config.fuzz_len());
        cargo.fuzz_all().expect("cargo fuzz: failed");
    }

    if !options.opt_present("no-cross") && config.cross() {
        let channels = vec![Channel::Stable, Channel::Nightly];
        let triples = vec![
            Triple::Aarch64LinuxGnu,
            Triple::ArmLinuxGnueabi,
            Triple::ArmLinuxGnueabihf,
            Triple::Armv7LinuxGnueabihf,
            Triple::I686LinuxGnu,
            Triple::I686LinuxMusl,
            Triple::X86_64LinuxGnu,
            Triple::X86_64LinuxMusl,
        ];

        if config.cross() {
            for channel in channels {
                cargo.set_channel(channel);
                for triple in &triples {
                    cargo.set_triple(*triple);
                    build_test(&mut cargo);
                }
            }
        }
    }
}

fn build_test(cargo: &mut Cargo) {
    let mut errors = 0;

    cargo.build().expect("cargo build failure");
    cargo.test().expect("cargo test failure");
    cargo.set_profile(Profile::Release);
    cargo.build().expect("cargo release build failure");
    cargo.test().expect("cargo release test failure");
    cargo.set_profile(Profile::Debug);
}
<commit_msg>bugfix: cargo-crucible config parsing (#40)<commit_after>#![allow(dead_code)]
#[macro_use]
extern crate log;
#[macro_use]
extern crate json;
extern crate curl;
extern crate getopts;
extern crate mktemp;
extern crate mpmc;
extern crate regex;
extern crate shuteye;
extern crate tic;
extern crate tiny_http;
extern crate toml;
extern crate sha_1;
extern crate hmac;
extern crate rustc_serialize;

mod common;
mod consumer;
mod webhook;
mod publisher;

use common::logging::set_log_level;
use common::repoconfig;
use consumer::cargo::{Cargo, Channel, Profile, Triple};
use getopts::Options;
use std::{env, process};
use std::path::{Path, PathBuf};

pub const VERSION: &'static str = env!("CARGO_PKG_VERSION");
pub const PROGRAM: &'static str = env!("CARGO_PKG_NAME");

fn print_usage(program: &str, opts: &Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

fn opts() -> Options {
    let mut opts = Options::new();

    // general
    opts.optflag("h", "help", "print this help menu");
    opts.optflag("", "version", "show version and exit");
    opts.optflagmulti("v", "verbose", "verbosity (stacking)");

    // clippy
    opts.optflag("", "no-clippy", "skip clippy");

    // fuzzing
    opts.optflag("", "no-fuzz", "skip fuzzing");
    opts.optopt("d", "fuzz-duration", "time per fuzz target", "[SECONDS]");
    opts.optopt(
        "c",
        "fuzz-cores",
        "number of cores to use during fuzzing",
        "[CORES]",
    );
    opts.optopt("d", "fuzz-length", "max length of fuzz input", "[BYTES]");

    // cross targets
    opts.optflag("", "no-cross", "skip cross targets");

    opts
}

pub fn init() -> getopts::Matches {
    let args: Vec<String> = env::args().collect();
    let program = &args[0];
    let opts = opts();

    let options = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            println!("Failed to parse command line args: {}", f);
            process::exit(1);
        }
    };

    if options.opt_present("help") {
        print_usage(program, &opts);
        process::exit(0);
    }

    if options.opt_present("version") {
        println!("{} {}", PROGRAM, VERSION);
        process::exit(0);
    }

    options
}

fn main() {
    let options = init();

    // initialize logging
    set_log_level(options.opt_count("verbose"));
    info!("{} {}", PROGRAM, VERSION);

    // load config from file, override with command line
    let mut config = repoconfig::load_config(Path::new(".crucible.toml")).unwrap_or_default();
    if let Some(s) = options.opt_str("fuzz-duration") {
        let v = s.parse().expect("ERROR: fuzz-duration invalid");
        config.set_fuzz_seconds(v);
    }
    if let Some(s) = options.opt_str("fuzz-cores") {
        let v = s.parse().expect("ERROR: fuzz-cores invalid");
        config.set_fuzz_cores(v);
    }
    if let Some(s) = options.opt_str("fuzz-length") {
        let v = s.parse().expect("ERROR: fuzz-length invalid");
        config.set_fuzz_len(v);
    }

    // complete set of tests - native target
    let mut cargo = Cargo::new(PathBuf::from("."));
    build_test(&mut cargo);
    cargo.fmt().expect("cargo fmt: failed");
    if !options.opt_present("no-clippy") {
        cargo.clippy().expect("cargo clippy: failed");
    }
    if !options.opt_present("no-fuzz") && config.fuzz() {
        cargo.set_fuzz_seconds(config.fuzz_seconds());
        cargo.set_fuzz_cores(config.fuzz_cores());
        cargo.set_fuzz_len(config.fuzz_len());
        cargo.fuzz_all().expect("cargo fuzz: failed");
    }

    if !options.opt_present("no-cross") && config.cross() {
        let channels = vec![Channel::Stable, Channel::Nightly];
        let triples = vec![
            Triple::Aarch64LinuxGnu,
            Triple::ArmLinuxGnueabi,
            Triple::ArmLinuxGnueabihf,
            Triple::Armv7LinuxGnueabihf,
            Triple::I686LinuxGnu,
            Triple::I686LinuxMusl,
            Triple::X86_64LinuxGnu,
            Triple::X86_64LinuxMusl,
        ];

        if config.cross() {
            for channel in channels {
                cargo.set_channel(channel);
                for triple in &triples {
                    cargo.set_triple(*triple);
                    build_test(&mut cargo);
                }
            }
        }
    }
}

fn build_test(cargo: &mut Cargo) {
    let mut errors = 0;

    cargo.build().expect("cargo build failure");
    cargo.test().expect("cargo test failure");
    cargo.set_profile(Profile::Release);
    cargo.build().expect("cargo release build failure");
    cargo.test().expect("cargo release test failure");
    cargo.set_profile(Profile::Debug);
}
<|endoftext|>