github.com/jlmucb/cloudproxy@v0.0.0-20170830161738-b5aa0b619bc4/cpvmm/vmm/libc/vmm_serial.c (about) 1 /* 2 * Copyright (c) 2013 Intel Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * Unless required by applicable law or agreed to in writing, software 9 * distributed under the License is distributed on an "AS IS" BASIS, 10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 * See the License for the specific language governing permissions and 12 * limitations under the License. 13 */ 14 15 #include "vmm_defs.h" 16 #include "vmm_serial.h" 17 #include "hw_utils.h" 18 #include "vmm_dbg.h" 19 #include "cli.h" 20 #include "file_codes.h" 21 22 #define VMM_DEADLOOP() VMM_DEADLOOP_LOG(VMM_SERIAL_LIBC) 23 #define VMM_ASSERT(__condition) VMM_ASSERT_LOG(VMM_SERIAL_LIBC, __condition) 24 25 26 // Private Definitions 27 28 29 #define VMM_SERIAL_DEVICES_MAX 8 30 31 typedef struct 32 { 33 UINT16 io_base; 34 UINT8 reserved[2]; 35 UART_PROG_IF_TYPE prog_if; 36 UART_HANDSHAKE_MODE handshake_mode; 37 // Handshake mode, as set on initialization 38 UINT32 hw_fifo_size; 39 // FIFO size of the UART h/w. Set according to 40 // prog_if. 41 UINT32 chars_in_tx_fifo; 42 // Current # of chars in h/w transmit FIFO. 43 // Note that this is a maximum estimate, actual 44 // number may be lower - this is the case where 45 // vmm_serial_putc() is interrupted by 46 // vmm_serial_putc_nolock(). 47 BOOLEAN puts_lock; 48 // Used by puts_nolocked() to lock out regular 49 // prints 50 BOOLEAN in_putc; // Flags that putc() is executing 51 BOOLEAN in_puts; // Flags that puts() is executing 52 UINT32 hw_handshake_stopped_count; 53 // Counts the number of consecutive times h/w 54 // handshake has been found to be "stop" 55 BOOLEAN is_initialized; 56 // Indicates that the device has been 57 // initialized. Used for sanity checks. 58 59 // Statistics - Used Mainly for Debug 60 61 UINT32 num_tx_chars_lock; 62 UINT32 num_tx_chars_nolock; 63 // Counters of transmitted characters in locked 64 // mode (normal) and nolock mode 65 UINT32 num_rx_chars; 66 // Counter of received characters 67 UINT32 wait_for_tx_ready_max; 68 // Max number of times tx paused until tx 69 // ready (for any reason). 70 UINT32 wait_for_tx_ready_avg; 71 // Average number of times tx paused until tx 72 // ready (for any reason). In units of 73 // 1 / 2^16 (i.e., upper 16 bits is the whole 74 // number part, lower 16 bit is the fraction 75 UINT32 wait_for_tx_fifo_empty_max; 76 // Max number of times tx paused until tx 77 // FIFO was empty. In units of 78 UINT32 wait_for_tx_fifo_empty_avg; 79 // Average number of times tx paused until tx 80 // FIFO was empty. In units of 81 // 1 / 2^16 (i.e., upper 16 bits is the whole 82 // number part, lower 16 bit is the fraction 83 // part). 84 UINT32 num_puts_interrupted_by_putc_nolock; 85 UINT32 num_puts_interrupted_by_puts_nolock; 86 // Count the number of times the normal puts() 87 // was interrupted by puts_nolock() and 88 // puts_nolock() 89 UINT32 num_putc_blocked_by_puts_nolock; 90 // Counts the number of times the normal putc() 91 // was blocked by puts_nolock() 92 UINT32 num_chars_hw_handshake_stopped; 93 // Number of characters for which tx stopped 94 // due to h/w handshake 95 UINT32 num_chars_hw_handshake_auto_go; 96 // Number of characters for which tx h/w 97 // handshake stop was auto-released 98 UINT32 hw_handshake_stopped_count_max; 99 // Maximum value of hw_handshake_stopped_count, 100 // for statistics & debug 101 UINT32 hw_handshake_stopped_count_avg; 102 // Average value of hw_handshake_stopped_count, 103 // for statistics & debug. In units of 104 // 1 / 2^16 (i.e., upper 16 bits is the whole 105 // number part, lower 16 bit is the fraction 106 // part). 107 } VMM_SERIAL_DEVICE; 108 109 110 // Static Variables 111 112 113 static VMM_SERIAL_DEVICE serial_devices[VMM_SERIAL_DEVICES_MAX]; 114 static UINT32 initialized_serial_devices = 0; 115 static const UINT32 serial_tx_stall_usec = 100; 116 // Number of usecs to stall between tx 117 // attempts if not ready. at 115200 baud, 118 // each character is about 87 usec. 119 // TODO: tune for tx fifo size 120 static const UINT32 hw_handshake_stopped_limit = 50000; // 5 sec 121 // In auto mode, number of stalls at h/w 122 // handshake stop until thestatus is 123 // considered "go" 124 static const UINT32 avg_factor = 16; 125 // Factor for averaging pause counters. In 126 // units of 1 / 2^16 127 128 129 // Static Functions 130 131 // Updates the maximum of a counter 132 133 static 134 void 135 update_max(UINT32 *p_max, // Maximum 136 UINT32 count) // New input 137 138 { 139 if (count > *p_max) 140 *p_max = count; 141 } 142 143 144 // Updates the running average of a counter, using a simple 1-stage IIR 145 // algorithm 146 static void update_avg(UINT32 *p_avg, // Running average, as UINT16.UINT16 147 UINT32 count) // New input 148 149 { 150 UINT64 avg; 151 152 // Extend to 64 bits to prevent overflow during calculation 153 avg = (UINT64)(*p_avg); 154 155 // Do the IIR. The formula is: 156 // avg = (1 - f) * avg + f * counter 157 // Here the calculation is factored to UINT48.UINT16 representation 158 avg = ((((1 << 16) - avg_factor) * avg) + 159 ( avg_factor * ((UINT64)count << 16)) 160 ) >> 16; 161 162 // Assign back, taking care of overflows 163 if (avg > (UINT32)-1) 164 *p_avg = (UINT32)-1; 165 else 166 *p_avg = (UINT32)avg; 167 } 168 169 170 // Updates the running average and maximum of a counter 171 static void update_max_and_avg(UINT32 *p_max, // Maximum 172 UINT32 *p_avg, // Running average, as UINT16.UINT16 173 UINT32 count) // New input 174 175 { 176 update_max(p_max, count); 177 update_avg(p_avg, count); 178 } 179 180 181 // Checks h/w handshake lines for "go" status. Count the number of times it 182 // was "stop". In UART_HANDSHAKE_AUTO mode, after a limit is reached force 183 // the status to "go". 184 static BOOLEAN is_hw_tx_handshake_go(VMM_SERIAL_DEVICE *p_device) 185 186 { 187 UART_MSR msr; // Modem Status Register image 188 BOOLEAN hw_handshake_go; // Flags transmit "go" by h/w handshake 189 190 if ((p_device->handshake_mode == UART_HANDSHAKE_AUTO) || 191 (p_device->handshake_mode == UART_HANDSHAKE_HW)) { 192 // Read the h/w handshake signals 193 194 msr.data = hw_read_port_8(p_device->io_base + UART_REGISTER_MSR); 195 hw_handshake_go = (msr.bits.CTS == 1) && (msr.bits.DSR == 1); 196 197 if (hw_handshake_go) { 198 // Other side set h/w handshake to "go". Reset the counter. 199 200 update_avg(&p_device->hw_handshake_stopped_count_avg, 201 p_device->hw_handshake_stopped_count); 202 p_device->hw_handshake_stopped_count = 0; 203 } 204 205 else if (p_device->hw_handshake_stopped_count >= hw_handshake_stopped_limit) { 206 // Other side has indicated h/w handshake "stop" for too long. 207 208 if (p_device->handshake_mode == UART_HANDSHAKE_AUTO) { 209 // In auto mode, assume the h/w handshake is stuck and force 210 // the status to "go" 211 212 hw_handshake_go = TRUE; 213 p_device->num_chars_hw_handshake_auto_go++; 214 if (p_device->hw_handshake_stopped_count == hw_handshake_stopped_limit) { 215 // Update the statistic only on the first character 216 // we decided to auto-go 217 218 update_avg(&p_device->hw_handshake_stopped_count_avg, 219 p_device->hw_handshake_stopped_count); 220 p_device->hw_handshake_stopped_count++; 221 } 222 } 223 } 224 else { 225 // Increment the stop count and update the statistics 226 227 if (p_device->hw_handshake_stopped_count == 0) { 228 // We just stopped, increment the stops statistics counter 229 230 p_device->num_chars_hw_handshake_stopped++; 231 } 232 233 p_device->hw_handshake_stopped_count++; 234 235 update_max(&p_device->hw_handshake_stopped_count_max, 236 p_device->hw_handshake_stopped_count); 237 } 238 } 239 240 else { 241 // No h/w handshake, always "go" 242 hw_handshake_go = TRUE; 243 } 244 245 return hw_handshake_go; 246 } 247 248 249 // Display serial ports' information on the CLI 250 #ifdef DEBUG 251 #pragma warning(push ) 252 #pragma warning(disable : 4100) // Supress warnings about unreferenced formal parameter 253 static 254 int 255 cli_display_serial_info(unsigned argc UNUSED, char *args[] UNUSED) 256 { 257 UINT32 i; 258 259 CLI_PRINT("Serial Device # :"); 260 for (i = 0; i < initialized_serial_devices; i++) 261 CLI_PRINT(" %1d ", i); 262 263 CLI_PRINT("\nI/O Base :"); 264 for (i = 0; i < initialized_serial_devices; i++) 265 CLI_PRINT(" 0x%04x ", serial_devices[i].io_base); 266 267 CLI_PRINT("\nChars Tx (lock) :"); 268 for (i = 0; i < initialized_serial_devices; i++) 269 CLI_PRINT(" %8d ", serial_devices[i].num_tx_chars_lock); 270 271 CLI_PRINT("\nChars Tx (nolock) :"); 272 for (i = 0; i < initialized_serial_devices; i++) 273 CLI_PRINT(" %8d ", serial_devices[i].num_tx_chars_nolock); 274 275 CLI_PRINT("\nChars Rx :"); 276 for (i = 0; i < initialized_serial_devices; i++) 277 CLI_PRINT(" %8d ", serial_devices[i].num_rx_chars); 278 279 CLI_PRINT("\nTx Ready Wait Time (max) :"); 280 for (i = 0; i < initialized_serial_devices; i++) 281 CLI_PRINT(" %8d uSec", 282 serial_devices[i].wait_for_tx_ready_max * 283 serial_tx_stall_usec); 284 285 CLI_PRINT("\nTx Ready Wait Time (avg) :"); 286 for (i = 0; i < initialized_serial_devices; i++) 287 CLI_PRINT(" %8d uSec", 288 (serial_devices[i].wait_for_tx_ready_avg >> 16) * 289 serial_tx_stall_usec); 290 291 CLI_PRINT("\nTx FIFO Empty Wait Time (max) :"); 292 for (i = 0; i < initialized_serial_devices; i++) 293 CLI_PRINT(" %8d uSec", 294 serial_devices[i].wait_for_tx_fifo_empty_max * 295 serial_tx_stall_usec); 296 297 CLI_PRINT("\nTx FIFO Empty Wait Time (avg) :"); 298 for (i = 0; i < initialized_serial_devices; i++) 299 CLI_PRINT(" %8d uSec", 300 (serial_devices[i].wait_for_tx_fifo_empty_avg >> 16) * 301 serial_tx_stall_usec); 302 303 CLI_PRINT("\nTx H/S Mode :"); 304 for (i = 0; i < initialized_serial_devices; i++) { 305 switch (serial_devices[i].handshake_mode) { 306 case UART_HANDSHAKE_AUTO: 307 if (serial_devices[i].hw_handshake_stopped_count < 308 hw_handshake_stopped_limit) { 309 CLI_PRINT(" Auto-H/W "); 310 } 311 else { 312 CLI_PRINT(" Auto-None"); 313 } 314 break; 315 316 caseUART_HANDSHAKE_HW: 317 CLI_PRINT(" H/W "); 318 break; 319 320 case UART_HANDSHAKE_NONE: 321 CLI_PRINT(" None "); 322 break; 323 324 default: 325 CLI_PRINT(" ?????????????"); 326 } 327 } 328 329 CLI_PRINT("\nTx H/S Stopped Chars :"); 330 for (i = 0; i < initialized_serial_devices; i++) 331 CLI_PRINT(" %8d ", 332 serial_devices[i].num_chars_hw_handshake_stopped); 333 334 CLI_PRINT("\nTx H/S Auto-Go Chars :"); 335 for (i = 0; i < initialized_serial_devices; i++) 336 CLI_PRINT(" %8d ", 337 serial_devices[i].num_chars_hw_handshake_auto_go); 338 339 CLI_PRINT("\nTx H/S Stopped Time (max) :"); 340 for (i = 0; i < initialized_serial_devices; i++) 341 CLI_PRINT(" %8d mSec", 342 serial_devices[i].hw_handshake_stopped_count_max * 343 serial_tx_stall_usec / 344 1000); 345 346 CLI_PRINT("\nTx H/S Stopped Time (avg) :"); 347 for (i = 0; i < initialized_serial_devices; i++) 348 CLI_PRINT(" %8d mSec", 349 (serial_devices[i].hw_handshake_stopped_count_avg >> 16) * 350 serial_tx_stall_usec / 351 1000); 352 353 CLI_PRINT("\nString Tx inter. by putc_nolock:"); 354 for (i = 0; i < initialized_serial_devices; i++) 355 CLI_PRINT(" %8d ", 356 serial_devices[i].num_puts_interrupted_by_putc_nolock); 357 358 CLI_PRINT("\nString Tx inter. by puts_nolock:"); 359 for (i = 0; i < initialized_serial_devices; i++) 360 CLI_PRINT(" %8d ", 361 serial_devices[i].num_puts_interrupted_by_puts_nolock); 362 363 CLI_PRINT("\nChars Tx blocked by puts_nolock:"); 364 for (i = 0; i < initialized_serial_devices; i++) 365 CLI_PRINT(" %8d ", 366 serial_devices[i].num_putc_blocked_by_puts_nolock); 367 368 CLI_PRINT("\n"); 369 370 return 0; 371 } 372 #pragma warning(pop) 373 #endif //DEBUG 374 375 // Initialize a new serial device's parameters 376 void * vmm_serial_new( UINT16 io_base, // In: I/O Base Address 377 UART_PROG_IF_TYPE prog_if, // In: Programming interface 378 UART_HANDSHAKE_MODE handshake_mode // In: Handshake mode 379 ) 380 381 { 382 VMM_SERIAL_DEVICE *p_device; 383 384 if (initialized_serial_devices >= VMM_SERIAL_DEVICES_MAX) 385 return NULL; 386 387 p_device = &serial_devices[initialized_serial_devices++]; 388 389 p_device->io_base = io_base; 390 p_device->prog_if = prog_if; 391 switch (prog_if) 392 { 393 case UART_PROG_IF_GENERIC: 394 case UART_PROG_IF_16450 : 395 p_device->hw_fifo_size = 1; 396 break; 397 398 case UART_PROG_IF_16550 : 399 case UART_PROG_IF_16650 : 400 case UART_PROG_IF_16750 : 401 case UART_PROG_IF_16850 : 402 case UART_PROG_IF_16950 : 403 p_device->hw_fifo_size = 16; // TODO: correct sizes 404 break; 405 406 default: 407 return NULL; 408 }; 409 p_device->chars_in_tx_fifo = p_device->hw_fifo_size; 410 // This forces polling of the transmit 411 // empty status bit 412 p_device->handshake_mode = handshake_mode; 413 414 p_device->num_tx_chars_lock = 0; 415 p_device->num_tx_chars_nolock = 0; 416 p_device->num_rx_chars = 0; 417 418 p_device->wait_for_tx_ready_max = 0; 419 p_device->wait_for_tx_ready_avg = 0; 420 p_device->wait_for_tx_fifo_empty_max = 0; 421 p_device->wait_for_tx_fifo_empty_avg = 0; 422 423 p_device->puts_lock = FALSE; 424 p_device->in_putc = FALSE; 425 p_device->in_puts = FALSE; 426 p_device->num_puts_interrupted_by_putc_nolock = 0; 427 p_device->num_puts_interrupted_by_puts_nolock = 0; 428 p_device->num_putc_blocked_by_puts_nolock = 0; 429 430 p_device->num_chars_hw_handshake_stopped = 0; 431 p_device->num_chars_hw_handshake_auto_go = 0; 432 p_device->hw_handshake_stopped_count = 0; 433 p_device->hw_handshake_stopped_count_max = 0; 434 p_device->hw_handshake_stopped_count_avg = 0; 435 436 p_device->is_initialized = FALSE; 437 438 return p_device; 439 } 440 441 442 // Initialize a serial device 443 444 void vmm_serial_init(void *h_device) // In: Handle of the device 445 { 446 VMM_SERIAL_DEVICE *p_device; 447 UART_IER ier; 448 UART_FCR fcr; 449 UART_LCR lcr; 450 UART_MCR mcr; 451 452 p_device = h_device; 453 VMM_ASSERT(p_device); 454 VMM_ASSERT(! p_device->is_initialized); 455 456 // MCR: Reset DTR, RTS, Out1, Out2 & Loop 457 mcr.bits.DTRC = 0; 458 mcr.bits.RTS = 0; 459 mcr.bits.OUT1 = 0; 460 mcr.bits.OUT2 = 0; 461 mcr.bits.LME = 0; 462 mcr.bits.Reserved = 0; 463 hw_write_port_8(p_device->io_base + UART_REGISTER_MCR, mcr.data); 464 465 // LCR: Reset DLAB 466 lcr.bits.SERIALDB = 0x03; // 8 data bits 467 lcr.bits.STOPB = 0; // 1 stop bit 468 lcr.bits.PAREN = 0; // No parity 469 lcr.bits.EVENPAR = 0; // N/A 470 lcr.bits.STICPAR = 0; // N/A 471 lcr.bits.BRCON = 0; // No break 472 lcr.bits.DLAB = 0; 473 hw_write_port_8(p_device->io_base + UART_REGISTER_LCR, lcr.data); 474 475 // IER: Disable interrupts 476 ier.bits.RAVIE = 0; 477 ier.bits.THEIE = 0; 478 ier.bits.RIE = 0; 479 ier.bits.MIE = 0; 480 ier.bits.Reserved = 0; 481 hw_write_port_8(p_device->io_base + UART_REGISTER_IER, ier.data); 482 483 // FCR: Disable FIFOs 484 fcr.bits.TRFIFOE = 0; 485 fcr.bits.RESETRF = 0; 486 fcr.bits.RESETTF = 0; 487 fcr.bits.DMS = 0; 488 fcr.bits.Reserved = 0; 489 fcr.bits.RTB = 0; 490 hw_write_port_8(p_device->io_base + UART_REGISTER_FCR, fcr.data); 491 492 // SCR: Scratch register 493 hw_write_port_8(p_device->io_base + UART_REGISTER_SCR, 0x00); 494 495 // LCR: Set DLAB 496 lcr.bits.DLAB = 1; 497 hw_write_port_8(p_device->io_base + UART_REGISTER_LCR, lcr.data); 498 499 // DLL & DLM: Divisor value 1 for 115200 baud 500 hw_write_port_8(p_device->io_base + UART_REGISTER_DLL, 0x01); 501 hw_write_port_8(p_device->io_base + UART_REGISTER_DLM, 0x00); 502 503 // LCR: Reset DLAB 504 lcr.bits.DLAB = 0; 505 hw_write_port_8(p_device->io_base + UART_REGISTER_LCR, lcr.data); 506 507 // FCR: Enable and reset Rx & Tx FIFOs 508 fcr.bits.TRFIFOE = 1; 509 fcr.bits.RESETRF = 1; 510 fcr.bits.RESETTF = 1; 511 hw_write_port_8(p_device->io_base + UART_REGISTER_FCR, fcr.data); 512 513 // MCR: Set DTR, RTS 514 mcr.bits.DTRC = 1; 515 mcr.bits.RTS = 1; 516 hw_write_port_8(p_device->io_base + UART_REGISTER_MCR, mcr.data); 517 518 p_device->is_initialized = TRUE; 519 } 520 521 void vmm_serial_reset(void *h_device) 522 { 523 VMM_SERIAL_DEVICE *p_device; 524 525 p_device = h_device; 526 p_device->is_initialized = FALSE; 527 } 528 529 // Returns the I/O range occupied by the device 530 void vmm_serial_get_io_range(void *h_device, // In: Handle of the device 531 UINT16 *p_io_base, // Out: Base of I/O range 532 UINT16 *p_io_end) // Out: End of I/O range 533 534 { 535 VMM_SERIAL_DEVICE *p_device; 536 537 p_device = h_device; 538 *p_io_base = p_device->io_base; 539 *p_io_end = p_device->io_base + 7; 540 } 541 542 // Write a single character to a serial device in a non-locked mode. 543 // This function is reentrant, and can be safely called even while the normal 544 // vmm_serial_putc() runs. However, it is not optimized for performance and 545 // should only be used when necessary, e.g., from an exception handler. 546 547 char vmm_serial_putc_nolock(void *h_device, // In: Handle of the device 548 char c) // In: Character to send 549 { 550 VMM_SERIAL_DEVICE *p_device; 551 UART_LSR lsr; // Line Status Register image 552 BOOLEAN is_ready; 553 // The Tx FIFO is empty and hw handshake is "go" 554 UINT32 num_wait_for_tx_ready; 555 556 p_device = h_device; 557 558 VMM_ASSERT(p_device->is_initialized); 559 560 if (p_device->in_puts) 561 p_device->num_puts_interrupted_by_putc_nolock++; 562 563 // Another instance of the vmm_serial_putc*() functions can be running in 564 // parallel (e.g., on another h/w thread). We rely on the Tx FIFO to 565 // be deed enough absorb the writes (and hope for the best...). Thus, we 566 // first loop until the Tx FIFO is empty and h/w handshake is OK 567 // (if applicable) 568 569 num_wait_for_tx_ready = 0; 570 do { 571 lsr.data = hw_read_port_8(p_device->io_base + UART_REGISTER_LSR); 572 is_ready = (lsr.bits.THRE == 1) && is_hw_tx_handshake_go(p_device); 573 if (! is_ready) { 574 hw_stall_using_tsc(serial_tx_stall_usec); 575 num_wait_for_tx_ready++; 576 } 577 } while (! is_ready); 578 579 update_max_and_avg(&p_device->wait_for_tx_fifo_empty_max, 580 &p_device->wait_for_tx_fifo_empty_avg, 581 num_wait_for_tx_ready); 582 update_max_and_avg(&p_device->wait_for_tx_ready_max, 583 &p_device->wait_for_tx_ready_avg, 584 num_wait_for_tx_ready); 585 586 // Now write the output character 587 hw_write_port_8(p_device->io_base + UART_REGISTER_THR, c); 588 589 // Update the statistics 590 p_device->num_tx_chars_nolock++; 591 592 // Loop again until the Tx FIFO is empty and h/w handshake is OK 593 // (if applicable). This is done so normal vmm_serial_putc() that we may 594 // have interrupted can safely resume. 595 596 num_wait_for_tx_ready = 0; 597 do { 598 lsr.data = hw_read_port_8(p_device->io_base + UART_REGISTER_LSR); 599 is_ready = is_hw_tx_handshake_go(p_device) && (lsr.bits.THRE == 1); 600 if (! is_ready) { 601 hw_stall_using_tsc(serial_tx_stall_usec); 602 num_wait_for_tx_ready++; 603 } 604 } while (! is_ready); 605 606 update_max_and_avg(&p_device->wait_for_tx_fifo_empty_max, 607 &p_device->wait_for_tx_fifo_empty_avg, num_wait_for_tx_ready); 608 update_max_and_avg(&p_device->wait_for_tx_ready_max, 609 &p_device->wait_for_tx_ready_avg, 610 num_wait_for_tx_ready); 611 612 // Note that we do NOT update chars_in_tx_fifo to be 0. This allows 613 // parallel putc's that will be absorbed by the FIFO. 614 615 return c; 616 } 617 618 619 // Write a single character to a serial device. 620 // This function is not reentrant, and is for use in the normal case, where the 621 // serial device has been previously locked. It may be interrupted by 622 // vmm_serial_putc_nolock(). The function attempts to use the full depth of 623 // the UART's transmit FIFO to avoid busy loops. 624 char vmm_serial_putc(void *h_device, // In: Handle of the device 625 char c) // In: Character to send 626 { 627 VMM_SERIAL_DEVICE *p_device; 628 UART_LSR lsr; // Line Status Register image 629 BOOLEAN is_ready; 630 // The Tx FIFO is not full and hw handshake is "go" 631 BOOLEAN locked_out = FALSE; 632 // Indicate that the function was locked out at 633 // least once by puts_lock 634 UINT32 num_wait_for_tx_ready; 635 UINT32 num_wait_for_tx_fifo_empty; 636 637 p_device = h_device; 638 VMM_ASSERT(p_device->is_initialized); 639 640 // Loop until there's room in the Tx FIFO, h/w handshake is OK 641 // (if applicable), and there is no lock by vmm_serial_puts_nolock(). 642 num_wait_for_tx_ready = 0; 643 num_wait_for_tx_fifo_empty = 0; 644 645 do { 646 lsr.data = hw_read_port_8(p_device->io_base + UART_REGISTER_LSR); 647 if (lsr.bits.THRE == 1) // The Tx FIFO is empty 648 p_device->chars_in_tx_fifo = 0; 649 is_ready = is_hw_tx_handshake_go(p_device); 650 if (is_ready) { 651 if (p_device->chars_in_tx_fifo >= p_device->hw_fifo_size) { 652 is_ready = FALSE; 653 num_wait_for_tx_fifo_empty++; 654 } 655 } 656 if (is_ready && p_device->puts_lock) { 657 // There's an on going string print by vmm_serial_puts_nolock() 658 is_ready = FALSE; 659 locked_out = TRUE; 660 } 661 if (! is_ready) { 662 hw_stall_using_tsc(serial_tx_stall_usec); 663 num_wait_for_tx_ready++; 664 } 665 } while (! is_ready); 666 667 update_max_and_avg(&p_device->wait_for_tx_ready_max, 668 &p_device->wait_for_tx_ready_avg, num_wait_for_tx_ready); 669 update_max_and_avg(&p_device->wait_for_tx_fifo_empty_max, 670 &p_device->wait_for_tx_fifo_empty_avg, num_wait_for_tx_fifo_empty); 671 672 // Now write the output character 673 hw_write_port_8(p_device->io_base + UART_REGISTER_THR, c); 674 p_device->chars_in_tx_fifo++; 675 676 // Update the statistics 677 p_device->num_tx_chars_lock++; 678 if (locked_out) 679 p_device->num_putc_blocked_by_puts_nolock++; 680 return c; 681 } 682 683 684 // Write a string to a serial device in a non-locked mode. 685 // This function is reentrant, and can be safely called even while the normal 686 // vmm_serial_putc() runs. However, it should be used only when necessary, 687 // e.g. from an exception handler. 688 int vmm_serial_puts_nolock(void *h_device, // In: Handle of the device 689 const char string[]) // In: String to send 690 { 691 VMM_SERIAL_DEVICE *p_device; 692 UINT32 i; 693 694 p_device = h_device; 695 p_device->puts_lock = TRUE; // Block the normal putc() 696 // Not reliable in case this function is 697 // called by two h/w threads in parallel, but 698 // the impact is not fatal 699 if (p_device->in_puts) 700 p_device->num_puts_interrupted_by_puts_nolock++; 701 for (i = 0; string[i] != 0; i++) 702 vmm_serial_putc_nolock(h_device, string[i]); 703 p_device->puts_lock = FALSE; // Unblock the normal putc() 704 return 1; // return any nonnegative value 705 } 706 707 708 // Write a string to a serial device 709 // This function is not reentrant, and is for use in the normal case, where the 710 // serial device has been previously locked. It may be interrupted by 711 // vmm_serial_put*_nolock(). 712 int vmm_serial_puts(void *h_device, // In: Handle of the device 713 const char string[]) // In: String to send 714 { 715 VMM_SERIAL_DEVICE *p_device; 716 UINT32 i; 717 718 p_device = h_device; 719 for (i = 0; string[i] != 0; i++) { 720 vmm_serial_putc(h_device, string[i]); 721 p_device->in_puts = TRUE; 722 } 723 p_device->in_puts = FALSE; 724 return 1; // return any nonnegative value 725 } 726 727 728 // Poll the serial device and read a single character if ready. 729 // This function is not reentrant. Calling it while it runs in another thread 730 // may result in a junk character returned, but the s/w will not crash. 731 732 char vmm_serial_getc(void *h_device) // In: Handle of the device 733 734 { 735 VMM_SERIAL_DEVICE *p_device; 736 UART_LSR lsr; 737 char c; 738 739 p_device = h_device; 740 VMM_ASSERT(p_device->is_initialized); 741 lsr.data = hw_read_port_8(p_device->io_base + UART_REGISTER_LSR); 742 if (lsr.bits.DR) { 743 c = hw_read_port_8(p_device->io_base + UART_REGISTER_RBR); 744 p_device->num_rx_chars++; 745 } 746 else 747 c = 0; 748 return c; 749 } 750 751 752 // Initialize CLI command(s) for serial ports 753 void vmm_serial_cli_init(void) 754 { 755 #ifdef DEBUG 756 CLI_AddCommand(cli_display_serial_info, "debug serial info", 757 "Print serial ports information", "", CLI_ACCESS_LEVEL_SYSTEM); 758 #endif 759 } 760