vmbase: Initialize logger before calling main()

By setting up the console (MMIO_GUARD_MAP-ing it if necessary) and
initializing the logger before entering main, we can centralize the
early error handling instead of expecting clients to deal with it,
provide a standard interface for configuring the log level (relying on
the log crate) which can't fail, and simplify client code.

This will enable vmbase itself to log errors in future changes.

Test: atest DebugPolicyHostTests#testNoAdbInDebugPolicy_withDebugLevelNone_boots
Test: atest rialto_test vmbase_example.integration_test
Change-Id: Id8c7256555d7940174c9f9375435d71851302020
diff --git a/vmbase/src/entry.rs b/vmbase/src/entry.rs
index 0a96d86..3e826a2 100644
--- a/vmbase/src/entry.rs
+++ b/vmbase/src/entry.rs
@@ -14,14 +14,37 @@
 
 //! Rust entry point.
 
-use crate::{console, heap, power::shutdown};
+use crate::{
+    console, heap, logger,
+    power::{reboot, shutdown},
+};
+use hyp::{self, get_mmio_guard};
+
+fn try_console_init() -> Result<(), hyp::Error> {
+    console::init();
+
+    if let Some(mmio_guard) = get_mmio_guard() {
+        mmio_guard.init()?;
+        mmio_guard.map(console::BASE_ADDRESS)?;
+    }
+
+    Ok(())
+}
 
 /// This is the entry point to the Rust code, called from the binary entry point in `entry.S`.
 #[no_mangle]
 extern "C" fn rust_entry(x0: u64, x1: u64, x2: u64, x3: u64) -> ! {
     // SAFETY: Only called once, from here, and inaccessible to client code.
     unsafe { heap::init() };
-    console::init();
+
+    if try_console_init().is_err() {
+        // Don't panic (or log) here to avoid accessing the console.
+        reboot()
+    }
+
+    logger::init().expect("Failed to initialize the logger");
+    // We initialize the logger to Off (like the log crate) and clients should log::set_max_level.
+
     // SAFETY: `main` is provided by the application using the `main!` macro, and we make sure it
     // has the right type.
     unsafe {
@@ -37,16 +60,21 @@
 
 /// Marks the main function of the binary.
 ///
+/// Once main is entered, it can assume that:
+/// - The panic_handler has been configured and panic!() and friends are available;
+/// - The global_allocator has been configured and heap memory is available;
+/// - The logger has been configured and the log::{info, warn, error, ...} macros are available.
+///
 /// Example:
 ///
 /// ```rust
-/// use vmbase::{logger, main};
+/// use vmbase::main;
 /// use log::{info, LevelFilter};
 ///
 /// main!(my_main);
 ///
 /// fn my_main() {
-///     logger::init(LevelFilter::Info).unwrap();
+///     log::set_max_level(LevelFilter::Info);
 ///     info!("Hello world");
 /// }
 /// ```