Title: The UART alternative
1The UART alternative
- Substituting input from our PCs serial-port for
local keystrokes when we do single-stepping
2Problem background
- Last time we saw how the x86s trap-flag and
debug-breakpoint registers could be used to
support single-stepping through program-code,
to help us diagnose bugs - But a conflict arises when we attempt to debug
code that handles keyboard-input (as in the
isrKBD routine for Project 2) - Our debugger also uses keyboard input!
3Use another control device?
- To circumvent the contention for keyboard
control, we ask can some other peripheral device
substitute for our PCs keyboard as a convenient
debugger-input source? - Our classroom and CS Lab machines offer us a way
to utilize their serial ports as an alternative
device-interface for doing this type of debugger
single-stepping task
4Kudlick Classroom
08
09
10
15
16
17
18
19
20
28
29
30
04
05
06
07
11
12
13
14
24
25
26
27
01
02
03
21
22
23
lectern
Indicates a null-modem PC-to-PC serial cable
connection
5PC-to-PC communications
student workstation
student workstation
KVM cable
KVM cable
rackmount PC system
rackmount PC system
null-modem serial cable
ethernet cables
6Using echo and cat
- Our device-driver module (named uart.c) is
intended to allow unprivileged programs that are
running on a pair of adjacent PCs to communicate
via a null-modem cable
Receiving
Transmitting
echo Hello gt /dev/uart _
cat /dev/uart Hello _
7Instructions in isrDBG
- Our usedebug.s used these instructions to
support user-control of single-stepping
isrDBG .code32 Our trap-handler for Debug
Exceptions (interrupt-0x01) now await the
release of a users keypress kbwait in 0x64,
al poll keyboard-controller status test 0x01,
al a new scancode has arrived? jz kbwait
no, continue polling controller in 0x60, al
else input the new scancode test 0x80, al
was it a key being released? jz kbwait no,
wait for a keypress break
8UARTs line-status
- The PCs 16550 serial-UART interface has a
status port and a data port that behave in a
manner thats similar to those ports in the
keyboard controller, so we can replace
instructions in our isrDBG procedure that
accessed keyboard-controller ports with
instructions that access the UARTs ports - This avoids contention for the keyboard!
9How to program the UART?
- Universal Asynchronous Receiver-Transmitter
- Software controls the UARTs operations by
accessing several registers, using the x86
processors in and out instructions
See our CS630 course website at
lthttp//cs.usfca.edu/cruse/cs630f08gt for links
to the UART manufacturers documentation and to
an in-depth online programming tutorial
10The 16550 UART registers
Divisor Latch Register
Base0
16-bits (R/W)
Transmit Data Register
Base0
8-bits (Write-only)
Received Data Register
Base0
8-bits (Read-only)
Interrupt Enable Register
Base1
8-bits (Read/Write)
Interrupt Identification Register
Base2
8-bits (Read-only)
FIFO Control Register
Base2
8-bits (Write-only)
Line Control Register
Base3
8-bits (Read/Write)
Modem Control Register
Base4
8-bits (Read/Write)
Line Status Register
Base5
8-bits (Read-only)
Modem Status Register
Base6
8-bits (Read-only)
Scratch Pad Register
Base7
8-bits (Read/Write)
11UARTs I/O-port interface
The PC uses eight consecutive I/O-ports to access
the UARTs registers
0x03F8 0x03F9 0x03FA 0x03FB
0x03FC 0x03FD 0x03FE 0x03FF
RxD/TxD
IER
IIR/FCR
LCR
MCR
LSR
MSR
SCR
modem status register
line status register
interrupt enable register
modem control register
line control register
scratchpad register
receive buffer register and transmitter holding
register (also Divisor Latch register)
interrupt identification register
and FIFO control register
12Comparing STATUS ports
Keyboard-controllers status-register (i/o-port
0x64)
7 6 5 4
3 2 1 0
Parity error
Timeout error
Data is from Mouse
Keyboard locked
Last byte went to 0x64
System initialized
Input Buffer Full
Output Buffer Full
Serial-UARTs line-status register (i/o-port
0x03FD)
7 6 5 4
3 2 1 0
Error in Rx FIFO
Transmitter idle
THR empty
Break interrupt
Framing error
Parity error
Overrun error
Received Data Ready
13Changes to isrDBG
keyboard controls single-stepping
serial-UART controls single-stepping
isrDBG kbwait poll for OUTB1 in 0x64,
al test 0x01, al jz kbwait input new
scancode in 0x60, al ignore make
codes test 0x80, al jz kbwait
isrDBG inwait poll for RDR1 mov 0x03FD,
dx in dx, al test 0x01, al jz inwait
input new data-byte mov 0x03F8, dx in dx,
al send back a reply mov ,
al out al, dx
14Using a Linux application
- To control our debugger from another PC, weve
written an application-program that runs under
Linux, and it uses our uart.c device-driver to
circumvent privilege-level restrictions that
Linux imposes on access to i/o-ports by code
which runs in ring3 - Our application is named kb2cable.cpp
- It also illustrates use of i/o multiplexing
15Linux Kernel Modules
Linux allows us to write our own
installable kernel modules and add them to a
running system
Runs in ring3
application
Runs in ring0
device-driver module
call
ret
ret
call
syscall
standard runtime libraries
Operating System kernel
sysret
user space
kernel space
16Linux char-driver components
Device-driver LKM layout
modules payload is a collection of
callback-functions having prescribed
prototypes
function
function
function
AND a package of
function-pointers
fops
. . .
init
the usual pair of module-administration
functions
registers the fops
exit
unregisters the fops
17write() and read()
- Obviously your driver-modules payload will
have to include methods (functions) which
perform the write() and read() operations
that applications will invoke - You may decide your driver needs also to
implement certain additional methods - For example, to support i/o multiplexing our
driver needed to implement poll()
18UART initialization
- For two PCs to communicate via the serial
null-modem cable, their UARTs must be configured
to use identical baudrates and data-formats
(i.e., 115200 bps, 8-N-1) - Our uart.c driver performs this essential
configuration in its module_init() function - Our remotedb.s application does it in an extra
real-mode subroutine weve added
19The sequence of steps
(steps are described below in pseudo-code)
initializing the UART communication parameters
for 115200 bps, 8-N-1 outb 0x00, UART_BASE1
Interrupt Enable register outb 0xC7,
UART_BASE2 FIFO Control register outb 0x83,
UART_BASE3 Line Control (DLAB1) outw 0x0001,
UART_BASE0 Divisor Latch register outb 0x03,
UART_BASE3 Line Control (DLAB0) outb 0x03,
UART_BASE4 Modem Control l inb UART_BASE6
Modem Status inb UART_BASE5 Line
Status inb UART_BASE0 Received
Data inb UART_BASE2 Interrupt Identification
20The i/o-multiplexing problem
- Normally when an application reads from a
device-file, that process will sleep until some
data is available from that device - So if data becomes available on another device,
it will not get processed because the application
is blocked from being given any CPU time by the
OS scheduler - This would spoil our kb2cable application
21read() causes blocking
kb2cable application
Keyboard
Serial UART
read
write
read
write
Whichever device this application attempts to
read from, it will get blocked until that
device has some data to deliver
22Do multiprocessing?
- One idea for getting around this blocking
problem would be to just use the fork()
system-call to create separate processes for
reading from the different device-files - Each process can sleep, and whichever process
receives any new data will be awakened and
scheduled for execution - No changes needed to device-driver code
23Different processes do read()
kb2cable parent- process
write
read
Keyboard
Serial UART
kb2cable child-process
read
write
Using multiple processes can overcome the
blocking-read problem, but complicates the
code for program termination
24Non-blocking read
- It is possible for the application to request
non-blocking read-operations i.e., any
read() calls will immediately return with 0 as
return-value in case no data is available - The standard-input device-driver already has
support for this non-blocking option, and it can
be easily added to the read() function in our
serial UARTs device driver
25Driver-code modification
ssize_t my_read( struct file file, char buf,
size_t len, loff_t pos ) static int rxhead
0 // in case no new data has been received,
then either // return immediately if
non-blocking mode is in effect // or else sleep
until some new data arrives (or until // the
user hits ltCONTROLgt-C to cancel execution) if
( rxhead ioread32( io E1000_RDH ) if (
file-gtf_flags O_NONBLOCK ) return 0 if (
wait_event_interruptible( wq_recv, inb(
UART_LINE_STATUS ) 0x01 ) return
EINTR
26Uses busy-waiting loop
kb2cable application
Keyboard
read
write
Serial UART
write
read
Using the nonblocking-read option overcomes
the problem of a sleeping task, but it
wastefully consumes the CPU time
27The elegant solution
- The select() system-call provides a very
general scheme for doing i/o-multiplexing in a
manner that avoids wasting CPU time or making the
program-code complicated - But it does require adding an extra driver
method the so-called poll() function
28The select() arguments
- Using select() requires an application to setup
an fd_set object, which defines the set of
file-descriptors whose activity needs to be
monitored by the Linux kernel (in our kb2cable
application this would be just the two
device-files handles (the keyboard and the
serial UART) - This fd_set object becomes an argument
29Using select() in kb2cable
int kbd STDIN_FILENO // keyboard ID
int uart open( /dev/uart, O_RDWR ) //
device-file ID fd_set permset // create an
fd_set object FD_ZERO( permset ) //
initialize it to empty FD_SET( kbd, permset
) // add keyboard to set FD_SET( uart,
permset ) // and add the nic to set while
(1) fd_set readset permset if ( select(
1uart, readset, NULL, NULL, NULL ) lt 0 )
break if ( FD_ISSET( kbd, readset ) ) /
process keyboard input / if ( FD_ISSET(
uart, readset ) ) / process network input /
30How it works
- The readset argument to the select()
system-call lets the kernel know which
device-drivers should have their poll() method
invoked - Then each device-drivers poll() method will
perform a test to determine if any new data is
ready to be read from that device - So the application calls read() only when a
device is ready with data immediately!
31In-class demo
- As a proof-of-concept demonstration, we adding a
trivial Interrupt Service Routine for keyboard
interrupts to our remotedb.s program (we called
it addkbisr.s) - Then we used our kb2cable application running
on an adjacent Linux machine to do
single-stepping through linuxapp.o -- and
through the added isrKBD handler