tail_core 0.1.0

Core library for the Tail operating system
Documentation
// Copyright 2025, TAIL OS. All Rights Reserved.
//
// You must obtain a written license from and pay applicable license fees to TAIL OS
// before you may reproduce, modify, or distribute this software, or any work that
// includes all or part of this software.
//
// Free development licenses are available for evaluation, research, and non-commercial
// purposes, which may include access to the source code under these terms. Redistribution
// or commercial use without a license is strictly prohibited.
//
// This file may contain contributions from others. Please review this entire file for
// other proprietary rights or license notices, as well as the TAIL OS License Guide at
// https://tail-os.com/license-guide/ for more information.
//
// For licensing inquiries, visit https://tail-os.com or email license@tail-os.com.


#![allow(dead_code)]

use core:: {
    fmt,
    result::Result::{Ok, Err},
};

// __Kout is used in _print_sys() and kprint!()
pub struct __Kout<'a> {
    buf: &'a mut [u8],
    pos: usize,
}

impl<'a> __Kout<'a> {
    fn new(buf: &'a mut [u8]) -> Self {
        __Kout { buf, pos: 0 }
    }

    fn as_bytes(&self) -> &[u8] {
        &self.buf[..self.pos]
    }

    fn as_str(&self) -> &str {
        core::str::from_utf8(self.as_bytes()).unwrap_or("")
    }

    fn len(&self) -> usize {
        self.pos
    }
}

impl<'a> fmt::Write for __Kout<'a> {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        for b in s.bytes() {
            if self.pos == self.buf.len() {
                return Err(fmt::Error);
            }
            /*
             * Issue symptom
             * : for example, we pass "success\n" to kprint!()
             *   Then, s is "success\n" which is from .rodata of the ELF file.
             *   The issue seen was that s.bytes() iterates more than "success\n" and
             *   and those after-characters seem to be from another string in .rodata
             *   It seems that s.bytes() does not recognize where the string ends.
             *
             * Root cause
             * : According to str of rust, str does not terminate with null character, '\0'
             *   However, the binary we built for startup is based on C compiler, gcc.
             *   That means rust str with the concept has no way to terminate a string.
             *   Maybe, that's why rust's binary has each .rodata.something for each string?
             *   Note that we use a linker script to merge all the .rodata sections to one .rodata
             * Solution
             * : Use '\0' to recognize the end of a string.
             */
            if b == '\0' as u8 {
                break;
            }

            self.buf[self.pos] = b;
            self.pos += 1;
        }

        Ok(())
    }
}

/*
pub fn set_puts_callout(func_ptr: fn(&str))
{
    unsafe {
        PUTS_CALLOUT = core::prelude::v1::Some(func_ptr);
    }
}*/

pub fn initialize(uart_base_address: u64, uart_read_offset: u64, uart_write_offset: u64, uart_put_bit: u64, uart_get_bit: u64)
{
    crate::getc_putc::set_uart(uart_base_address, uart_read_offset, uart_write_offset, uart_put_bit, uart_get_bit);
}

pub fn kprint(args: fmt::Arguments<'_>)
{
    let mut __buffer = [0u8; 512];
    let mut __kout = __Kout::new(&mut __buffer);
    let __result = fmt::write(&mut __kout, args);  // format_args!() parse the print!()-like format and produces a single string in.
                                                                        // __result means "we ignore the Result"
    crate::getc_putc::puts(__kout.as_str());   // change "uft8" to "str"
}

/// kprint for this crate internal usage.
#[macro_export]
macro_rules! _kprint {
    ($($args:tt)*) => {{
        #[allow(unused_imports)]
        {
            use crate::kprint::kprint;
            use crate::rich_text::*;
            kprint(format_args!($($args)*));
        }
    }};
}

/// kdebug for this crate internal usage.
#[macro_export]
macro_rules! _kdebug {
    ($($args:tt)*) => {{
        #[cfg(debug_assertions)]
        #[allow(unused_imports)]
        {
            use crate::kprint::kprint;
            use crate::rich_text::*;
            kprint(format_args!("[DEBUG] [{}] {}", core::intrinsics::caller_location(), format_args!($($args)*)));
        }
    }};
}

/// print on raw console
///
/// # Arguments
/// * "string", arg1, arg2, ...
///
/// # Examples
/// kprint!("Hello, World")
/// kprint!("Hello, World\n")
/// kprint!("value={}", x)
///
#[macro_export]
macro_rules! kprint {
    ($($args:tt)*) => {{
        #[allow(unused_imports)]
        {
            use tail_core::kprint::kprint;
            use tail_core::rich_text::*;
            kprint(format_args!($($args)*));
        }
    }};
}

#[macro_export]
macro_rules! kdebug {
    ($($args:tt)*) => {{
        #[cfg(debug_assertions)]
        #[allow(unused_imports)]
        {
            use tail_core::kprint::kprint;
            use tail_core::rich_text::*;
            kprint(format_args!("[DEBUG] [{}] {}", core::intrinsics::caller_location(), format_args!($($args)*)));
        }
    }};
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_kprint() {
        _kprint!("Hello, World\n");
    }
}