diff --git a/src/arch/x86_64/driver/apic/ioapic.rs b/src/arch/x86_64/driver/apic/ioapic.rs
index 64da4eb..0231ecd 100644
--- a/src/arch/x86_64/driver/apic/ioapic.rs
+++ b/src/arch/x86_64/driver/apic/ioapic.rs
@@ -4,14 +4,14 @@
 /// http://www.intel.com/design/chipsets/datashts/29056601.pdf
 /// See also picirq.c.
 
-use core::ptr::{Unique};
 use syscall::io::{Io, Mmio};
 use bit_field::BitField;
 use consts::irq::T_IRQ0;
+use spin::Mutex;
 
-pub unsafe fn init(ioapic_id: u8)
+pub fn init(ioapic_id: u8)
 {
-	let ioapic = IOAPIC.as_mut();
+	let mut ioapic = IOAPIC.lock();
 	assert!(ioapic.id() == ioapic_id, "ioapicinit: id isn't equal to ioapicid; not a MP");
 
 	// Mark all interrupts edge-triggered, active high, disabled,
@@ -43,33 +43,42 @@ bitflags! {
 	}
 }
 
-static mut IOAPIC: Unique<IoApic> = unsafe{ Unique::new_unchecked(IOAPIC_ADDRESS as *mut _) };
+lazy_static! {
+	pub static ref IOAPIC: Mutex<IoApic> = Mutex::new(unsafe{IoApic::new()});
+}
 
 // IO APIC MMIO structure: write reg, then read or write data.
 #[repr(C)]
-struct IoApic {
+struct IoApicMmio {
 	reg: Mmio<u32>,
 	pad: [Mmio<u32>; 3],
 	data: Mmio<u32>,
 }
 
+pub struct IoApic {
+	mmio: &'static mut IoApicMmio
+}
+
 impl IoApic {
-	unsafe fn read(&mut self, reg: u8) -> u32
+	unsafe fn new() -> Self {
+		IoApic { mmio: &mut *(IOAPIC_ADDRESS as *mut IoApicMmio) }
+	}
+	fn read(&mut self, reg: u8) -> u32
 	{
-		self.reg.write(reg as u32);
-		self.data.read()
+		self.mmio.reg.write(reg as u32);
+		self.mmio.data.read()
 	}
-	unsafe fn write(&mut self, reg: u8, data: u32)
+	fn write(&mut self, reg: u8, data: u32)
 	{
-		self.reg.write(reg as u32);
-		self.data.write(data);
+		self.mmio.reg.write(reg as u32);
+		self.mmio.data.write(data);
 	}
-	unsafe fn write_irq(&mut self, irq: u8, flags: RedirectionEntry, dest: u8)
+	fn write_irq(&mut self, irq: u8, flags: RedirectionEntry, dest: u8)
 	{
 		self.write(REG_TABLE+2*irq, (T_IRQ0 + irq) as u32 | flags.bits());
 		self.write(REG_TABLE+2*irq+1, (dest as u32) << 24);
 	}
-	unsafe fn enable(&mut self, irq: u8, cpunum: u8)
+	pub fn enable(&mut self, irq: u8, cpunum: u8)
 	{
 		// Mark interrupt edge-triggered, active high,
 		// enabled, and routed to the given cpunum,
@@ -77,12 +86,12 @@ impl IoApic {
 		self.write_irq(irq, NONE, cpunum);
 	}
 	fn id(&mut self) -> u8 {
-		unsafe{ self.read(REG_ID).get_bits(24..28) as u8 }
+		self.read(REG_ID).get_bits(24..28) as u8
 	}
 	fn version(&mut self) -> u8 {
-		unsafe{ self.read(REG_VER).get_bits(0..8) as u8 }
+		self.read(REG_VER).get_bits(0..8) as u8
 	}
 	fn maxintr(&mut self) -> u8 {
-		unsafe{ self.read(REG_VER).get_bits(16..24) as u8 }
+		self.read(REG_VER).get_bits(16..24) as u8
 	}
 }
\ No newline at end of file
diff --git a/src/arch/x86_64/driver/apic/mod.rs b/src/arch/x86_64/driver/apic/mod.rs
index fc46842..64ac839 100644
--- a/src/arch/x86_64/driver/apic/mod.rs
+++ b/src/arch/x86_64/driver/apic/mod.rs
@@ -1,3 +1,5 @@
+pub use self::ioapic::IOAPIC;
+
 mod lapic;
 mod ioapic;
 
diff --git a/src/arch/x86_64/driver/console.rs b/src/arch/x86_64/driver/console.rs
new file mode 100644
index 0000000..c4325dc
--- /dev/null
+++ b/src/arch/x86_64/driver/console.rs
@@ -0,0 +1,6 @@
+pub fn init() {
+	use consts::irq::IRQ_KBD;
+	// TODO set irq handler
+	super::pic::enable_irq(IRQ_KBD);
+	super::apic::IOAPIC.lock().enable(IRQ_KBD, 0);
+}
\ No newline at end of file
diff --git a/src/arch/x86_64/driver/mod.rs b/src/arch/x86_64/driver/mod.rs
index c02bc76..5a93241 100644
--- a/src/arch/x86_64/driver/mod.rs
+++ b/src/arch/x86_64/driver/mod.rs
@@ -3,4 +3,5 @@ pub mod acpi;
 pub mod apic;
 pub mod mp;
 pub mod serial;
-pub mod pic;
\ No newline at end of file
+pub mod pic;
+pub mod console;
\ No newline at end of file
diff --git a/src/arch/x86_64/driver/pic.rs b/src/arch/x86_64/driver/pic.rs
index 3e9c295..4674db5 100644
--- a/src/arch/x86_64/driver/pic.rs
+++ b/src/arch/x86_64/driver/pic.rs
@@ -1,52 +1,65 @@
 use syscall::io::*;
+use spin::Mutex;
 
-pub static mut MASTER: Pic = Pic::new(0x20);
-pub static mut SLAVE: Pic = Pic::new(0xA0);
+static MASTER: Mutex<Pic> = Mutex::new(Pic::new(0x20));
+static SLAVE: Mutex<Pic> = Mutex::new(Pic::new(0xA0));
+
+pub fn init() {
+    let mut master = MASTER.lock();
+    let mut slave = SLAVE.lock();
 
-pub unsafe fn init() {
     // Start initialization
-    MASTER.cmd.write(0x11);
-    SLAVE.cmd.write(0x11);
+    master.cmd.write(0x11);
+    slave.cmd.write(0x11);
 
     // Set offsets
-    MASTER.data.write(0x20);
-    SLAVE.data.write(0x28);
+    master.data.write(0x20);
+    slave.data.write(0x28);
 
     // Set up cascade
-    MASTER.data.write(4);
-    SLAVE.data.write(2);
+    master.data.write(4);
+    slave.data.write(2);
 
     // Set up interrupt mode (1 is 8086/88 mode, 2 is auto EOI)
-    MASTER.data.write(1);
-    SLAVE.data.write(1);
+    master.data.write(1);
+    slave.data.write(1);
 
     // Unmask interrupts
-    MASTER.data.write(0);
-    SLAVE.data.write(0);
+    master.data.write(0);
+    slave.data.write(0);
 
     // Ack remaining interrupts
-    MASTER.ack();
-    SLAVE.ack();
+    master.ack();
+    slave.ack();
+}
+
+pub fn enable_irq(irq: u8)
+{
+    match irq {
+        _ if irq < 8 => MASTER.lock().mask_set(irq),
+        _ if irq < 16 => SLAVE.lock().mask_set(irq-8),
+        _ => panic!("irq not in 0..16"),
+    }
 }
 
-pub struct Pic {
+struct Pic {
     cmd: Pio<u8>,
     data: Pio<u8>,
 }
 
 impl Pic {
-    pub const fn new(port: u16) -> Pic {
+    const fn new(port: u16) -> Pic {
         Pic {
             cmd: Pio::new(port),
             data: Pio::new(port + 1),
         }
     }
 
-    pub fn ack(&mut self) {
+    fn ack(&mut self) {
         self.cmd.write(0x20);
     }
 
-    pub fn mask_set(&mut self, irq: u8) {
+    fn mask_set(&mut self, irq: u8) {
         assert!(irq < 8);
 
         let mut mask = self.data.read();
@@ -54,7 +67,7 @@ impl Pic {
         self.data.write(mask);
     }
 
-    pub fn mask_clear(&mut self, irq: u8) {
+    fn mask_clear(&mut self, irq: u8) {
         assert!(irq < 8);
 
         let mut mask = self.data.read();