From 0f50e9527c68168c8114a38b211a8744e3bd2473 Mon Sep 17 00:00:00 2001
From: Robert Morris <rtm@csail.mit.edu>
Date: Wed, 22 Jul 2020 10:31:46 -0400
Subject: [PATCH] fix printf() in interrupts

---
 kernel/console.c |  6 +++---
 kernel/defs.h    |  3 ++-
 kernel/uart.c    | 45 ++++++++++++++++++++++++---------------------
 3 files changed, 29 insertions(+), 25 deletions(-)

diff --git a/kernel/console.c b/kernel/console.c
index a97ef30..9a18cd9 100644
--- a/kernel/console.c
+++ b/kernel/console.c
@@ -42,9 +42,9 @@ consputc(int c)
 
   if(c == BACKSPACE){
     // if the user typed backspace, overwrite with a space.
-    uartputc('\b', 0); uartputc(' ', 0); uartputc('\b', 0);
+    uartputc_sync('\b'); uartputc_sync(' '); uartputc_sync('\b');
   } else {
-    uartputc(c, 0);
+    uartputc_sync(c);
   }
 }
 
@@ -72,7 +72,7 @@ consolewrite(int user_src, uint64 src, int n)
     char c;
     if(either_copyin(&c, user_src, src+i, 1) == -1)
       break;
-    uartputc(c, 1);
+    uartputc(c);
   }
   release(&cons.lock);
 
diff --git a/kernel/defs.h b/kernel/defs.h
index 1b164a4..4eedd89 100644
--- a/kernel/defs.h
+++ b/kernel/defs.h
@@ -149,7 +149,8 @@ void            usertrapret(void);
 // uart.c
 void            uartinit(void);
 void            uartintr(void);
-void            uartputc(int, int);
+void            uartputc(int);
+void            uartputc_sync(int);
 int             uartgetc(void);
 
 // vm.c
diff --git a/kernel/uart.c b/kernel/uart.c
index 4d70b42..32cb575 100644
--- a/kernel/uart.c
+++ b/kernel/uart.c
@@ -69,33 +69,19 @@ uartinit(void)
 
 // add a character to the output buffer and tell the
 // UART to start sending if it isn't already.
-//
-// usually called from the top-half -- by a process
-// calling write(). can also be called from a uart
-// interrupt to echo a received character, or by printf
-// or panic from anywhere in the kernel.
-//
-// the block argument controls what happens if the
-// buffer is full. for write(), block is 1, and the
-// process waits. for kernel printf's and echoed
-// characters, block is 0, and the character is
-// discarded; this is necessary since sleep() is
-// not possible in interrupts.
+// blocks if the output buffer is full.
+// because it may block, it can't be called
+// from interrupts; it's only suitable for use
+// by write().
 void
-uartputc(int c, int block)
+uartputc(int c)
 {
   acquire(&uart_tx_lock);
   while(1){
     if(((uart_tx_w + 1) % UART_TX_BUF_SIZE) == uart_tx_r){
       // buffer is full.
-      if(block){
-        // wait for uartstart() to open up space in the buffer.
-        sleep(&uart_tx_r, &uart_tx_lock);
-      } else {
-        // caller does not want us to wait.
-        release(&uart_tx_lock);
-        return;
-      }
+      // wait for uartstart() to open up space in the buffer.
+      sleep(&uart_tx_r, &uart_tx_lock);
     } else {
       uart_tx_buf[uart_tx_w] = c;
       uart_tx_w = (uart_tx_w + 1) % UART_TX_BUF_SIZE;
@@ -106,6 +92,23 @@ uartputc(int c, int block)
   }
 }
 
+// alternate version of uartputc() that doesn't 
+// use interrupts, for use by kernel printf() and
+// to echo characters. it spins waiting for the uart's
+// output register to be empty.
+void
+uartputc_sync(int c)
+{
+  push_off();
+
+  // wait for Transmit Holding Empty to be set in LSR.
+  while((ReadReg(LSR) & (1 << 5)) == 0)
+    ;
+  WriteReg(THR, c);
+
+  pop_off();
+}
+
 // if the UART is idle, and a character is waiting
 // in the transmit buffer, send it.
 // caller must hold uart_tx_lock.