1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
//! bootstrap logging on rs232
//!
//! A pale copy of the rs232 kernel device.
//! Used by the bootstrap stage to provide some logging.
//!
//! This driver is meant to be as simple as possible

const COM1: u16 = 0x3F8;

/// Init the rs232 COM1. Must be called before logging anything.
///
/// # Safety
///
/// May only be called once.
pub unsafe fn init_bootstrap_log() {
    let _data_port      = COM1 + 0;
    let interrupt_port  = COM1 + 1;
    let baud_diviser_lo = COM1 + 0; // when DLB is set, data and intr
    let baud_diviser_hi = COM1 + 1; // become baud divisor lo and hi
    let fifo_port       = COM1 + 2;
    let lcr_port        = COM1 + 3;
    let _mcr_port       = COM1 + 4;
    let _status_port    = COM1 + 5;

    bootstrap_outb(interrupt_port , 0x00);       // Disable interrupts
    bootstrap_outb(lcr_port       , 0x80);       // Enable DLAB (set baud rate divisor)
    bootstrap_outb(baud_diviser_lo, 0x03); // set divisor to 3 (lo byte) 38400 baud rate
    bootstrap_outb(baud_diviser_hi, 0x00); //                  (hi byte)
    bootstrap_outb(lcr_port       , 0x03);       // 8 bits, no parity, one stop bit. Disables DLAB
    bootstrap_outb(fifo_port      , 0xC7);       // Enable FIFO, clear them, with 14-byte threshold
                                                        // Note : no idea what this is
    //mcr_port     .write(0x0B);                        // IRQs enabled, RTS/DSR set
}

/// Sends a string to COM1.
pub fn bootstrap_log(string: &str) {
    let status_port = COM1 + 5;
    for byte in string.bytes() {
        // Wait for the transmit buffer to be empty
        unsafe {
            while bootstrap_inb(status_port) & 0x20 == 0 { }
            bootstrap_outb(COM1, byte);
        }
    }
}

unsafe fn bootstrap_inb(port: u16) -> u8 {
    let value: u8;
    llvm_asm!("in $0, $1" : "={al}"(value) : "{dx}"(port) : "memory" : "intel", "volatile");
    value
}

unsafe fn bootstrap_outb(port: u16, value: u8) {
    llvm_asm!("out $1, $0" : : "{al}"(value), "{dx}"(port) : "memory" : "intel", "volatile");
}

/// A logger that sends its output to COM1.
///
/// Use it like this:
/// ```
/// use ::core::fmt::Write;
///
/// write!(Serial, "I got {} problems, but logging ain't one", 99);
/// ```
pub struct Serial;

impl ::core::fmt::Write for Serial {
    /// Writes a string to COM1.
    fn write_str(&mut self, s: &str) -> Result<(), ::core::fmt::Error> {
        bootstrap_log(s);
        Ok(())
	}
}