1
0

log.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. /* Copyright (C) 2013 by John Cronin <jncronin@tysos.org>
  2. *
  3. * Permission is hereby granted, free of charge, to any person obtaining a copy
  4. * of this software and associated documentation files (the "Software"), to deal
  5. * in the Software without restriction, including without limitation the rights
  6. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. * copies of the Software, and to permit persons to whom the Software is
  8. * furnished to do so, subject to the following conditions:
  9. * The above copyright notice and this permission notice shall be included in
  10. * all copies or substantial portions of the Software.
  11. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  12. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  13. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  14. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  15. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  16. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  17. * THE SOFTWARE.
  18. */
  19. /* Based on an idea by https://github.com/JamesC1 */
  20. /* Support for writing the console log to a file.
  21. *
  22. * The main problem here is that some devices (e.g. SD cards) have limited
  23. * write cycles, so writing every character to them is problematic. Thus, we
  24. * buffer writes to try and do, e.g. one block at a time. The problem here is
  25. * that if the guest OS crashes at some point, the buffer may not be flushed to
  26. * disk. We try and ameliorate this by 1) providing the guest OS with a
  27. * fflush() function to flush at important moments, and 2) automatically
  28. * flushing if log_putc is called and it has been more than a certain time
  29. * since the last flush.
  30. *
  31. * This can still miss certain log outputs, but is probably the best
  32. * compromise. The ideal situation would be to have a background thread
  33. * periodically flushing the buffer to disk, but as we are only a bootloader,
  34. * we cannot hijack the timer interrupt that the guest OS would probably want
  35. * to use itself.
  36. */
  37. #include <stdint.h>
  38. #include <stdlib.h>
  39. #include "timer.h"
  40. #include "vfs.h"
  41. #include "output.h"
  42. FILE *log_fp = NULL;
  43. uint8_t *log_buf = NULL;
  44. size_t buf_size;
  45. size_t buf_ptr;
  46. struct timer_wait last_update;
  47. // Time between flushes (requires that log_putc is actually called at some point)
  48. #define LOG_TIMEOUT 5000000
  49. int log_putc(int c)
  50. {
  51. if(last_update.trigger_value == 0)
  52. last_update = register_timer(LOG_TIMEOUT);
  53. if(log_buf && buf_size)
  54. {
  55. log_buf[buf_ptr++] = c;
  56. if(buf_ptr >= buf_size)
  57. {
  58. if((log_fp && log_fp->fflush_cb) || (last_update.rollover && compare_timer(last_update)))
  59. {
  60. log_fp->fflush_cb(log_fp);
  61. last_update = register_timer(LOG_TIMEOUT);
  62. }
  63. buf_ptr = 0;
  64. }
  65. return 0;
  66. }
  67. else if(log_fp)
  68. {
  69. // Disable output to the log for the write
  70. rpi_boot_output_state state = output_get_state();
  71. output_disable_log();
  72. // Write one character
  73. fwrite(&c, 1, 1, log_fp);
  74. // Restore saved output state
  75. output_restore_state(state);
  76. return 0;
  77. }
  78. return EOF;
  79. }
  80. static int log_fflush(FILE *fp)
  81. {
  82. // Flush the buffer
  83. if(log_fp && log_buf)
  84. {
  85. // Disable output to the log for the write
  86. rpi_boot_output_state state = output_get_state();
  87. output_disable_log();
  88. // Write the buffer
  89. fwrite(log_buf, 1, buf_ptr, fp);
  90. // Restore the state
  91. output_restore_state(state);
  92. }
  93. return 0;
  94. }
  95. int register_log_file(FILE *fp, size_t buffer_size)
  96. {
  97. // If we have a current log, flush it
  98. if(log_fp)
  99. {
  100. fflush(log_fp);
  101. // deregister fflush callback
  102. log_fp->fflush_cb = NULL;
  103. }
  104. // If passed NULL, then set no log file
  105. if(fp == NULL)
  106. {
  107. if(log_buf)
  108. free(log_buf);
  109. // We can still use a buffer without a file, for flushing
  110. // later to the file
  111. if(buffer_size)
  112. log_buf = (uint8_t *)malloc(buffer_size);
  113. else
  114. log_buf = NULL;
  115. buf_size = buffer_size;
  116. buf_ptr = 0;
  117. log_fp = NULL;
  118. return 0;
  119. }
  120. // Store the fflush callback
  121. fp->fflush_cb = log_fflush;
  122. // If no current log, and there is a buffer, then flush
  123. // what's in it to the new file
  124. if(!log_fp && log_buf)
  125. {
  126. log_fp = fp;
  127. fflush(fp);
  128. }
  129. // If we have a buffer free it
  130. if((buf_size != buffer_size) && log_buf)
  131. log_buf = (uint8_t *)realloc(log_buf, buffer_size);
  132. else if(log_buf == NULL)
  133. log_buf = (uint8_t *)malloc(buffer_size);
  134. buf_size = buffer_size;
  135. buf_ptr = 0;
  136. // Store the log file pointer
  137. log_fp = fp;
  138. return 0;
  139. }
  140. FILE *get_log_file()
  141. {
  142. return log_fp;
  143. }