diff --git a/src/boot/args.rs b/src/boot/args.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e80327853823285166a8d5223e7480302f1ac77f
--- /dev/null
+++ b/src/boot/args.rs
@@ -0,0 +1,65 @@
+use crate::{
+    boot,
+    log::{self, LogLevel},
+};
+use spin::{Lazy, Once};
+
+//
+
+pub fn get() -> Arguments {
+    Arguments::get()
+}
+
+//
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
+pub struct Arguments {
+    pub log_level: LogLevel,
+    // log_color: bool,
+    pub had_unrecognized: bool,
+
+    pub cmdline: &'static str,
+}
+
+//
+
+impl Arguments {
+    pub fn parse(s: &'static str) -> Self {
+        let mut iter = s.split(|c: char| c.is_whitespace() || c == '=');
+        let mut result = Arguments {
+            cmdline: s,
+            ..<_>::default()
+        };
+
+        while let Some(item) = iter.next() {
+            match item {
+                "log" => {
+                    if let Some(level) = iter.next() {
+                        if let Some(l) = LogLevel::parse(level) {
+                            result.log_level = l
+                        } else {
+                            result.had_unrecognized = true
+                        }
+                    }
+                }
+                _ => result.had_unrecognized = true,
+            }
+        }
+
+        result
+    }
+
+    pub fn get() -> Self {
+        static ARGUMENTS: Lazy<Arguments> = Lazy::new(|| {
+            boot::cmdline()
+                .map(Arguments/*Self doesn't work??*/::parse)
+                .unwrap_or_default()
+        });
+        *ARGUMENTS
+    }
+
+    pub fn apply(&self) {
+        log::set_log_level(self.log_level);
+        // log::set_log_color(self.log_color);
+    }
+}
diff --git a/src/boot/limine/cmdline.rs b/src/boot/limine/cmdline.rs
index 26c4b5b0c6fb1c18b26c189b3fcbf084b7455188..9d92ae6900e4850953f03b09e90d42c722b1ef42 100644
--- a/src/boot/limine/cmdline.rs
+++ b/src/boot/limine/cmdline.rs
@@ -1,18 +1,12 @@
-use crate::env::Arguments;
 use limine::LimineKernelFileRequest;
 
 //
 
-pub fn init() {
+pub fn cmdline() -> Option<&'static str> {
     static REQ: LimineKernelFileRequest = LimineKernelFileRequest::new(0);
-
-    if let Some(cmdline) = REQ
-        .get_response()
+    REQ.get_response()
         .get()
         .and_then(|resp| resp.kernel_file.get())
         .and_then(|file| file.cmdline.to_str())
         .and_then(|cmdline| cmdline.to_str().ok())
-    {
-        Arguments::parse(cmdline);
-    }
 }
diff --git a/src/boot/limine/framebuffer.rs b/src/boot/limine/framebuffer.rs
index 1238512be3c892fa02aaac7cb0fe8439e49cb5f4..a07ec4c7b508c7cf239185cb7f6b7d8e6085a2e4 100644
--- a/src/boot/limine/framebuffer.rs
+++ b/src/boot/limine/framebuffer.rs
@@ -1,17 +1,16 @@
 use crate::{
     debug,
-    video::framebuffer::{get_fbo, Framebuffer, FramebufferInfo, FBO},
+    video::framebuffer::{Framebuffer, FramebufferInfo},
 };
 use core::slice;
 use limine::LimineFramebufferRequest;
-use spin::Mutex;
 
 //
 
-pub fn init() {
+pub fn framebuffer() -> Option<Framebuffer> {
     static FB_REQ: LimineFramebufferRequest = LimineFramebufferRequest::new(0);
 
-    let fbo = FB_REQ
+    FB_REQ
         .get_response()
         .get()
         .into_iter()
@@ -22,6 +21,7 @@ pub fn init() {
             }
 
             let buf = unsafe { slice::from_raw_parts_mut(fb.address.as_ptr()?, fb.size()) };
+
             Some(Framebuffer {
                 buf,
                 info: FramebufferInfo {
@@ -30,11 +30,5 @@ pub fn init() {
                     pitch: fb.pitch as _,
                 },
             })
-        });
-
-    if let Some(mut fbo) = fbo {
-        fbo.clear();
-        FBO.call_once(|| Mutex::new(fbo));
-    }
-    debug!("Global framebuffer: {:#?}", get_fbo().map(|f| f.info))
+        })
 }
diff --git a/src/boot/limine/mod.rs b/src/boot/limine/mod.rs
index f2d6c0db49c328244e07f7be7b16b89dc37218a9..aa6ea11587b1bfeb4a329668dd700d1061288c46 100644
--- a/src/boot/limine/mod.rs
+++ b/src/boot/limine/mod.rs
@@ -1,3 +1,4 @@
+use super::args;
 use super::BOOT_NAME;
 use crate::{arch, kernel_main};
 
@@ -6,6 +7,8 @@ use crate::{arch, kernel_main};
 pub use addr::hhdm_offset;
 pub use addr::phys_addr;
 pub use addr::virt_addr;
+pub use cmdline::cmdline;
+pub use framebuffer::framebuffer;
 pub use mem::memmap;
 pub use term::_print;
 
@@ -24,8 +27,7 @@ mod term;
 pub extern "C" fn _start() -> ! {
     BOOT_NAME.call_once(|| "Limine");
 
-    framebuffer::init();
-    cmdline::init();
+    args::get().apply();
 
     arch::early_boot_cpu();
     arch::early_per_cpu();
diff --git a/src/boot/mod.rs b/src/boot/mod.rs
index 4e7b712fdb9b3928bbc69f90747284474fc80b46..054ee29874739cc6ce7e2670d68fa56ac91aa85d 100644
--- a/src/boot/mod.rs
+++ b/src/boot/mod.rs
@@ -2,6 +2,10 @@ use spin::Once;
 
 //
 
+pub use boot::*;
+
+//
+
 #[cfg(feature = "multiboot1")]
 #[path = "multiboot1/mod.rs"]
 #[allow(clippy::module_inception)]
@@ -19,9 +23,7 @@ mod boot;
 #[allow(clippy::module_inception)]
 mod boot;
 
-//
-
-pub use boot::*;
+pub mod args;
 
 //
 
diff --git a/src/env.rs b/src/env.rs
index 3ddc7b56e1850f5443e79310b27ee0e123893715..139597f9cb07c5d48bed18984ec4747f4b4f3438 100644
--- a/src/env.rs
+++ b/src/env.rs
@@ -1,63 +1,2 @@
-use crate::log::{self, LogLevel};
-use spin::Once;
 
-//
 
-pub fn args() -> Arguments {
-    Arguments::get()
-}
-
-//
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
-pub struct Arguments {
-    pub log_level: LogLevel,
-    // log_color: bool,
-    pub had_unrecognized: bool,
-
-    pub cmdline: &'static str,
-}
-
-//
-
-impl Arguments {
-    pub fn parse(s: &'static str) {
-        ARGUMENTS.call_once(|| {
-            let mut iter = s.split(|c: char| c.is_whitespace() || c == '=');
-            let mut result = Arguments {
-                cmdline: s,
-                ..<_>::default()
-            };
-
-            while let Some(item) = iter.next() {
-                match item {
-                    "log" => {
-                        if let Some(level) = iter.next() {
-                            if let Some(l) = LogLevel::parse(level) {
-                                result.log_level = l
-                            } else {
-                                result.had_unrecognized = true
-                            }
-                        }
-                    }
-                    _ => result.had_unrecognized = true,
-                }
-            }
-
-            result.assign();
-
-            result
-        });
-    }
-
-    pub fn get() -> Self {
-        ARGUMENTS.get().copied().unwrap_or(Self::default())
-    }
-
-    pub fn assign(&self) {
-        log::set_log_level(self.log_level);
-        // log::set_log_color(self.log_color);
-    }
-}
-
-static ARGUMENTS: Once<Arguments> = Once::new();
diff --git a/src/main.rs b/src/main.rs
index 3d8220d647eedf6bc8d144c4c7c023103c911d3d..9451487099efce171bec72756e4fa461adcf0594 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -11,6 +11,7 @@
 #![feature(let_chains)]
 #![test_runner(crate::testfw::test_runner)]
 #![reexport_test_harness_main = "test_main"]
+#![doc = include_str!("../README.md")]
 
 //
 
@@ -47,17 +48,25 @@ pub static KERNEL_VERS: &str = env!("CARGO_PKG_VERSION");
 
 // the actual entry exists in [´crate::boot::boot´]
 fn kernel_main() -> ! {
+    let args = boot::args::get();
+    args.apply();
+
     debug!("Entering kernel_main");
-    debug!("Cmdline: {:?}", env::Arguments::get());
+    debug!("Cmdline: {args:?}");
 
     debug!(
-        "Kernel addr: {:?}, {:?}, HDDM Offset: {:#0X?}",
+        "Kernel addr: {:?}, {:?}, HHDM Offset: {:#0X?}",
         boot::virt_addr(),
         boot::phys_addr(),
         boot::hhdm_offset()
     );
 
-    mem::init();
+    debug!(
+        "{:?}",
+        (0u32..32)
+            .map(|i| 2u32.pow(i))
+            .collect::<alloc::vec::Vec<_>>()
+    );
 
     // ofc. every kernel has to have this cringy ascii name splash
     info!("\n{}\n", include_str!("./splash"));
diff --git a/src/mem/bump.rs b/src/mem/bump.rs
index def5fef270dad68065445a78489af1f8d04f1d52..2d47afe1cebb3470749655d64e25c75889cb8e53 100644
--- a/src/mem/bump.rs
+++ b/src/mem/bump.rs
@@ -1,87 +1,60 @@
-use super::{map::Memmap, to_higher_half};
-use crate::{boot, error};
+use super::{
+    pmm::{PageFrame, PageFrameAllocator},
+    to_higher_half,
+};
+use crate::error;
 use core::{
     alloc::{GlobalAlloc, Layout},
     ptr::null_mut,
 };
-use spin::{Mutex, Once};
-
-//
-
-const MAX_BUMP_ALLOC: u64 = 2u64.pow(16); // 64KiB
-
-//
-
-pub fn init() {
-    let mut map = boot::memmap()
-        .min_by_key(|Memmap { len, .. }| *len)
-        .expect("No memory");
-
-    map.len = map.len.min(MAX_BUMP_ALLOC);
-
-    ALLOC.inner.call_once(|| BumpAllocInner {
-        remaining: Mutex::new(map.len),
-        map,
-    });
-}
-
-pub fn map() -> Option<Memmap> {
-    ALLOC.inner.get().map(|i| i.map)
-}
+use spin::{Lazy, Mutex};
+use x86_64::{align_up, PhysAddr};
 
 //
 
 #[global_allocator]
-static ALLOC: BumpAllocator = BumpAllocator { inner: Once::new() };
+static ALLOC: BumpAllocator = BumpAllocator {
+    inner: Lazy::new(|| {
+        let pages = PageFrameAllocator::get().alloc(4);
+        BumpAllocInner {
+            marker: Mutex::new(pages.addr()),
+            pages,
+        }
+    }),
+};
 
 //
 
 struct BumpAllocator {
-    inner: Once<BumpAllocInner>,
+    inner: Lazy<BumpAllocInner>,
 }
 
 struct BumpAllocInner {
-    map: Memmap,
-    remaining: Mutex<u64>,
+    pages: PageFrame,
+    marker: Mutex<PhysAddr>,
 }
 
 //
 
 unsafe impl GlobalAlloc for BumpAllocator {
     unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
-        let Some(inner) = self.inner.get() else {
-            error!("Allocator used before init");
-            return null_mut();
-        };
+        let inner = &*self.inner;
 
-        let memory = inner.map.base;
-        let mut remaining = inner.remaining.lock();
+        let pages = &inner.pages;
+        let mut marker = inner.marker.lock();
 
-        let top = (memory + *remaining).as_u64();
-        let Some(tmp) = top.checked_sub(layout.size() as u64) else {
-            error!("OUT OF MEMORY");
-            error!(
-                "ALLOC: size: {} align: {} top: {top} memory: {memory:?} remaining: {remaining}",
-                layout.size(),
-                layout.align()
-                );
-            return null_mut();
-        };
-        let new_top = tmp / layout.align() as u64 * layout.align() as u64;
-        let reservation = top - new_top;
+        let alloc_bottom = PhysAddr::new(align_up(marker.as_u64(), layout.align() as u64));
+        let alloc_top = alloc_bottom + layout.size() as u64;
 
-        if let Some(left) = remaining.checked_sub(reservation) {
-            *remaining = left;
-            to_higher_half(memory + left).as_mut_ptr()
-        } else {
-            error!("OUT OF MEMORY");
-            error!(
-                "ALLOC: size: {} align: {} top: {top} new: {new_top} memory: {memory:?} remaining: {remaining}",
-                layout.size(),
-                layout.align()
-            );
-            null_mut()
+        if alloc_top > pages.addr() + pages.byte_len() as u64 {
+            error!("OOM");
+            error!("layout: {layout:?} pages: {pages:?} marker: {marker:?}");
+            return null_mut();
         }
+
+        *marker = alloc_top;
+
+        to_higher_half(alloc_bottom).as_mut_ptr()
     }
 
     unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
diff --git a/src/mem/mod.rs b/src/mem/mod.rs
index 55c7a3f3dc3a42710ecda7af5226cd3fc534c9fc..8892892183ad16d2753aa8fc5868345054fd213f 100644
--- a/src/mem/mod.rs
+++ b/src/mem/mod.rs
@@ -15,13 +15,6 @@ pub mod pmm;
 
 //
 
-pub fn init() {
-    bump::init();
-    pmm::init();
-}
-
-//
-
 #[allow(unused)]
 fn is_higher_half(addr: u64) -> bool {
     addr >= boot::hhdm_offset()
diff --git a/src/mem/pmm.rs b/src/mem/pmm.rs
index cec1e49e095fa9e4d12f3a66f55d352cfcbc09ea..ef03e67b40aab9d6163bc72daa084e6d78fe27cc 100755
--- a/src/mem/pmm.rs
+++ b/src/mem/pmm.rs
@@ -1,15 +1,13 @@
 use super::{map::Memmap, to_higher_half};
 use crate::{
-    boot, debug,
-    mem::bump,
-    trace,
+    boot, debug, trace,
     util::{bitmap::Bitmap, fmt::NumberPostfix},
 };
 use core::{
     fmt, slice,
     sync::atomic::{AtomicU64, AtomicUsize, Ordering},
 };
-use spin::{Mutex, Once};
+use spin::{Lazy, Mutex};
 use x86_64::{align_up, PhysAddr};
 
 //
@@ -18,11 +16,11 @@ const PAGE_SIZE: u64 = 2u64.pow(12); // 4KiB pages
 
 // const PAGE_SIZE: u64 = 2u64.pow(21); // 2MiB pages
 
-static PFA: Once<PageFrameAllocator> = Once::new();
+static PFA: Lazy<PageFrameAllocator> = Lazy::new(init);
 
 //
 
-pub fn init() {
+fn init() -> PageFrameAllocator {
     // usable system memory
     let usable: u64 = boot::memmap()
         .filter(Memmap::is_usable)
@@ -61,11 +59,6 @@ pub fn init() {
         ty: _,
     } in boot::memmap().filter(Memmap::is_usable)
     {
-        if let Some(map) = bump::map() && map.base == base {
-            // skip the BumpAllocator spot
-            base += map.base.as_u64();
-            len -= map.len;
-        }
         if base == bitmap_data {
             // skip the bitmap allocation spot
             base += bitmap_data.as_u64();
@@ -89,20 +82,18 @@ pub fn init() {
         }
     }
 
-    let used = bump::map().map(|Memmap { len, .. }| len).unwrap_or(0) + bitmap_size;
-
     let pfa = PageFrameAllocator {
         bitmap: bitmap.into(),
         usable: usable.into(),
-        used: used.into(),
+        used: bitmap_size.into(),
         total: total.into(),
 
         last_alloc_index: 0.into(),
     };
 
-    debug!("PFA:\n{pfa}");
+    debug!("PFA initialized:\n{pfa}");
 
-    PFA.call_once(|| pfa);
+    pfa
 }
 
 //
@@ -126,6 +117,10 @@ pub struct PageFrame {
 //
 
 impl PageFrameAllocator {
+    pub fn get() -> &'static PageFrameAllocator {
+        &*PFA
+    }
+
     /// System total memory in bytes
     pub fn total_mem(&self) -> u64 {
         self.total.load(Ordering::SeqCst)
@@ -285,20 +280,46 @@ impl fmt::Display for PageFrameAllocator {
 }
 
 impl PageFrame {
+    // physical address of the first page
     pub fn addr(&self) -> PhysAddr {
         self.first
     }
+
+    /// number of pages
+    pub fn len(&self) -> usize {
+        self.count
+    }
+
+    /// number of bytes
+    pub fn byte_len(&self) -> usize {
+        self.count * PAGE_SIZE as usize
+    }
+
+    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
+        // SAFETY: &mut self makes sure that this is the only safe mut ref
+        unsafe { self.as_bytes_mut_unsafe() }
+    }
+
+    pub fn as_bytes(&self) -> &[u8] {
+        // SAFETY: the mut ref is immediately downgraded to a const ref
+        unsafe { self.as_bytes_mut_unsafe() }
+    }
+
+    /// SAFETY: only 1 mutable slice at one time
+    unsafe fn as_bytes_mut_unsafe(&self) -> &mut [u8] {
+        slice::from_raw_parts_mut(to_higher_half(self.first).as_mut_ptr(), self.byte_len())
+    }
 }
 
 //
 
 #[cfg(test)]
 mod tests {
-    use crate::mem::pmm::PFA;
+    use super::PageFrameAllocator;
 
     #[test_case]
     fn pfa_simple() {
-        let pfa = PFA.get().unwrap();
+        let pfa = PageFrameAllocator::get();
 
         let a = pfa.alloc(1);
         assert_ne!(a.addr().as_u64(), 0);
diff --git a/src/term/escape/decode.rs b/src/term/escape/decode.rs
index d16c624fa2e45a139bb5943926195f577dc79cbb..4cd253b5b7118640a9e1fe318bd3a60d77c9a7ad 100644
--- a/src/term/escape/decode.rs
+++ b/src/term/escape/decode.rs
@@ -1,7 +1,7 @@
 use crate::video::color::Color;
 
-/// foreground color can be changed like this: "\x1B[38;2;<r>;<g>;<b>m"
-/// background color can be changed like this: "\x1B[48;2;<r>;<g>;<b>m"
+/// foreground color can be changed like this: `"\x1B[38;2;<r>;<g>;<b>m"`
+/// background color can be changed like this: `"\x1B[48;2;<r>;<g>;<b>m"`
 ///
 /// THESE ARE NON STANDARD ESCAPE SEQUENCES
 pub struct EscapeDecoder {
diff --git a/src/testfw.rs b/src/testfw.rs
index a3179284f819ef5ec900a2875441e1fa3525411e..9685f7d756e3da3b6ee7badd3eae12809914e048 100644
--- a/src/testfw.rs
+++ b/src/testfw.rs
@@ -20,7 +20,7 @@ pub trait TestCase {
 impl<F: Fn()> TestCase for F {
     fn run(&self) {
         let name = type_name::<Self>();
-        print!(" - {name:.<40}");
+        print!(" - {name:.<60}");
         self();
         println!("[ok]");
     }
diff --git a/src/video/framebuffer.rs b/src/video/framebuffer.rs
index ae4b93da18ea1265139b410e0cea890158d1e4b9..a781a279cccb786d1b00e77ef67c63653d440b06 100644
--- a/src/video/framebuffer.rs
+++ b/src/video/framebuffer.rs
@@ -1,15 +1,15 @@
 use super::{color::Color, font::FONT};
-use core::ops::{Deref, DerefMut};
-use spin::{Mutex, MutexGuard, Once};
+use crate::boot;
+use core::{
+    fmt,
+    ops::{Deref, DerefMut},
+};
+use spin::{Lazy, Mutex, MutexGuard};
 
 //
 
-pub static FBO: Once<Mutex<Framebuffer>> = Once::new();
-
-//
-
-pub fn get_fbo() -> Option<MutexGuard<'static, Framebuffer>> {
-    FBO.get().map(|mtx| mtx.lock())
+pub fn get() -> Option<MutexGuard<'static, Framebuffer>> {
+    FBO.as_ref().map(|mtx| mtx.lock())
 }
 
 //
@@ -29,6 +29,15 @@ pub struct FramebufferInfo {
 
 //
 
+static FBO: Lazy<Option<Mutex<Framebuffer>>> = Lazy::new(|| {
+    let fbo = boot::framebuffer();
+    let mut fbo = fbo?;
+    fbo.clear();
+    Some(Mutex::new(fbo))
+});
+
+//
+
 impl Framebuffer {
     pub fn set(&mut self, x: usize, y: usize, color: Color) {
         let spot = x * 4 + y * self.pitch;
@@ -91,18 +100,26 @@ impl DerefMut for Framebuffer {
     }
 }
 
+impl fmt::Debug for Framebuffer {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("Framebuffer")
+            .field("info", &self.info)
+            .finish()
+    }
+}
+
 //
 
 #[cfg(test)]
 mod tests {
-    use super::get_fbo;
+    use super::get;
     use crate::video::color::Color;
 
     //
 
     #[test_case]
     fn fbo_draw() {
-        if let Some(mut fbo) = get_fbo() {
+        if let Some(mut fbo) = get() {
             fbo.fill(440, 340, 40, 40, Color::RED);
             fbo.fill(450, 350, 60, 40, Color::GREEN);
             fbo.fill(405, 315, 80, 20, Color::BLUE);
diff --git a/src/video/logger.rs b/src/video/logger.rs
index 75dcfc91416002b898e470fd3b7a74790b611f11..84ab28aa5c4a0080e1d7285d47425e955977e252 100644
--- a/src/video/logger.rs
+++ b/src/video/logger.rs
@@ -1,7 +1,7 @@
 use super::{
     color::Color,
     font::FONT,
-    framebuffer::{get_fbo, Framebuffer},
+    framebuffer::{get, Framebuffer},
 };
 use crate::term::escape::decode::{DecodedPart, EscapeDecoder};
 use core::fmt::{self, Arguments, Write};
@@ -39,7 +39,7 @@ impl Writer {
     pub fn write_byte(&mut self, byte: u8) {
         match self.escapes.next(byte) {
             DecodedPart::Byte(b'\n') => {
-                if let Some(mut fbo) = get_fbo() {
+                if let Some(mut fbo) = get() {
                     #[cfg(debug_assertions)]
                     let lines = if self.cursor[1] + 1 >= Self::size(&mut fbo)[1] {
                         // scroll more if the cursor is near the bottom
@@ -76,7 +76,7 @@ impl Writer {
     }
 
     pub fn write_byte_raw(&mut self, byte: u8) {
-        if let Some(mut fbo) = get_fbo() {
+        if let Some(mut fbo) = get() {
             let size = Self::size(&mut fbo);
             if size[0] == 0 || size[1] == 0 {
                 return;