source: mainline/kernel/generic/src/log/log.c@ fc0de8c

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since fc0de8c was 5a5269d, checked in by GitHub <noreply@…>, 6 years ago

Change type of uspace pointers in kernel from pointer type to numeric (#170)

From kernel's perspective, userspace addresses are not valid pointers,
and can only be used in calls to copy_to/from_uspace().
Therefore, we change the type of those arguments and variables to
uspace_addr_t which is an alias for sysarg_t.

This allows the compiler to catch accidental direct accesses to
userspace addresses.

Additionally, to avoid losing the type information in code,
a macro uspace_ptr(type) is used that translates to uspace_addr_t.
I makes no functional difference, but allows keeping the type information
in code in case we implement some sort of static checking for it in the future.

However, ccheck doesn't like that, so instead of using uspace_ptr(char),
we use uspace_ptr_char which is defined as
#define uspace_ptr_char uspace_ptr(char).

  • Property mode set to 100644
File size: 9.2 KB
Line 
1/*
2 * Copyright (c) 2013 Martin Sucha
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup kernel_generic
30 * @{
31 */
32/** @file
33 */
34
35#include <sysinfo/sysinfo.h>
36#include <synch/spinlock.h>
37#include <typedefs.h>
38#include <ddi/irq.h>
39#include <ddi/ddi.h>
40#include <ipc/event.h>
41#include <ipc/irq.h>
42#include <arch.h>
43#include <panic.h>
44#include <putchar.h>
45#include <atomic.h>
46#include <syscall/copy.h>
47#include <errno.h>
48#include <str.h>
49#include <print.h>
50#include <printf/printf_core.h>
51#include <stdarg.h>
52#include <log.h>
53#include <console/console.h>
54#include <abi/log.h>
55#include <stdlib.h>
56
57#define LOG_PAGES 8
58#define LOG_LENGTH (LOG_PAGES * PAGE_SIZE)
59#define LOG_ENTRY_HEADER_LENGTH (sizeof(size_t) + sizeof(uint32_t))
60
61/** Cyclic buffer holding the data for kernel log */
62uint8_t log_buffer[LOG_LENGTH] __attribute__((aligned(PAGE_SIZE)));
63
64/** Kernel log initialized */
65static atomic_bool log_inited = false;
66
67/** Position in the cyclic buffer where the first log entry starts */
68size_t log_start = 0;
69
70/** Sum of length of all log entries currently stored in the cyclic buffer */
71size_t log_used = 0;
72
73/** Log spinlock */
74SPINLOCK_STATIC_INITIALIZE_NAME(log_lock, "log_lock");
75
76/** Overall count of logged messages, which may overflow as needed */
77static uint32_t log_counter = 0;
78
79/** Starting position of the entry currently being written to the log */
80static size_t log_current_start = 0;
81
82/** Length (including header) of the entry currently being written to the log */
83static size_t log_current_len = 0;
84
85/** Start of the next entry to be handed to uspace starting from log_start */
86static size_t next_for_uspace = 0;
87
88static void log_update(void *);
89
90/** Initialize kernel logging facility
91 *
92 */
93void log_init(void)
94{
95 event_set_unmask_callback(EVENT_KLOG, log_update);
96 atomic_store(&log_inited, true);
97}
98
99static size_t log_copy_from(uint8_t *data, size_t pos, size_t len)
100{
101 for (size_t i = 0; i < len; i++, pos = (pos + 1) % LOG_LENGTH) {
102 data[i] = log_buffer[pos];
103 }
104 return pos;
105}
106
107static size_t log_copy_to(const uint8_t *data, size_t pos, size_t len)
108{
109 for (size_t i = 0; i < len; i++, pos = (pos + 1) % LOG_LENGTH) {
110 log_buffer[pos] = data[i];
111 }
112 return pos;
113}
114
115/** Append data to the currently open log entry.
116 *
117 * This function requires that the log_lock is acquired by the caller.
118 */
119static void log_append(const uint8_t *data, size_t len)
120{
121 /* Cap the length so that the entry entirely fits into the buffer */
122 if (len > LOG_LENGTH - log_current_len) {
123 len = LOG_LENGTH - log_current_len;
124 }
125
126 if (len == 0)
127 return;
128
129 size_t log_free = LOG_LENGTH - log_used - log_current_len;
130
131 /* Discard older entries to make space, if necessary */
132 while (len > log_free) {
133 size_t entry_len;
134 log_copy_from((uint8_t *) &entry_len, log_start, sizeof(size_t));
135 log_start = (log_start + entry_len) % LOG_LENGTH;
136 log_used -= entry_len;
137 log_free += entry_len;
138 next_for_uspace -= entry_len;
139 }
140
141 size_t pos = (log_current_start + log_current_len) % LOG_LENGTH;
142 log_copy_to(data, pos, len);
143 log_current_len += len;
144}
145
146/** Begin writing an entry to the log.
147 *
148 * This acquires the log and output buffer locks, so only calls to log_* functions should
149 * be used until calling log_end.
150 */
151void log_begin(log_facility_t fac, log_level_t level)
152{
153 spinlock_lock(&log_lock);
154 spinlock_lock(&kio_lock);
155
156 log_current_start = (log_start + log_used) % LOG_LENGTH;
157 log_current_len = 0;
158
159 /* Write header of the log entry, the length will be written in log_end() */
160 log_append((uint8_t *) &log_current_len, sizeof(size_t));
161 log_append((uint8_t *) &log_counter, sizeof(uint32_t));
162 uint32_t fac32 = fac;
163 uint32_t lvl32 = level;
164 log_append((uint8_t *) &fac32, sizeof(uint32_t));
165 log_append((uint8_t *) &lvl32, sizeof(uint32_t));
166
167 log_counter++;
168}
169
170/** Finish writing an entry to the log.
171 *
172 * This releases the log and output buffer locks.
173 */
174void log_end(void)
175{
176 /* Set the length in the header to correct value */
177 log_copy_to((uint8_t *) &log_current_len, log_current_start, sizeof(size_t));
178 log_used += log_current_len;
179
180 kio_push_char('\n');
181 spinlock_unlock(&kio_lock);
182 spinlock_unlock(&log_lock);
183
184 /* This has to be called after we released the locks above */
185 kio_flush();
186 kio_update(NULL);
187 log_update(NULL);
188}
189
190static void log_update(void *event)
191{
192 if (!atomic_load(&log_inited))
193 return;
194
195 spinlock_lock(&log_lock);
196 if (next_for_uspace < log_used)
197 event_notify_0(EVENT_KLOG, true);
198 spinlock_unlock(&log_lock);
199}
200
201static int log_printf_str_write(const char *str, size_t size, void *data)
202{
203 size_t offset = 0;
204 size_t chars = 0;
205
206 while (offset < size) {
207 kio_push_char(str_decode(str, &offset, size));
208 chars++;
209 }
210
211 log_append((const uint8_t *)str, size);
212
213 return chars;
214}
215
216static int log_printf_wstr_write(const wchar_t *wstr, size_t size, void *data)
217{
218 char buffer[16];
219 size_t offset = 0;
220 size_t chars = 0;
221
222 for (offset = 0; offset < size; offset += sizeof(wchar_t), chars++) {
223 kio_push_char(wstr[chars]);
224
225 size_t buffer_offset = 0;
226 errno_t rc = chr_encode(wstr[chars], buffer, &buffer_offset, 16);
227 if (rc != EOK) {
228 return EOF;
229 }
230
231 log_append((const uint8_t *)buffer, buffer_offset);
232 }
233
234 return chars;
235}
236
237/** Append a message to the currently being written entry.
238 *
239 * Requires that an entry has been started using log_begin()
240 */
241int log_vprintf(const char *fmt, va_list args)
242{
243 int ret;
244
245 printf_spec_t ps = {
246 log_printf_str_write,
247 log_printf_wstr_write,
248 NULL
249 };
250
251 ret = printf_core(fmt, &ps, args);
252
253 return ret;
254}
255
256/** Append a message to the currently being written entry.
257 *
258 * Requires that an entry has been started using log_begin()
259 */
260int log_printf(const char *fmt, ...)
261{
262 int ret;
263 va_list args;
264
265 va_start(args, fmt);
266 ret = log_vprintf(fmt, args);
267 va_end(args);
268
269 return ret;
270}
271
272/** Log a message to the kernel log.
273 *
274 * This atomically appends a log entry.
275 * The resulting message should not contain a trailing newline, as the log
276 * entries are explicitly delimited when stored in the log.
277 */
278int log(log_facility_t fac, log_level_t level, const char *fmt, ...)
279{
280 int ret;
281 va_list args;
282
283 log_begin(fac, level);
284
285 va_start(args, fmt);
286 ret = log_vprintf(fmt, args);
287 va_end(args);
288
289 log_end();
290
291 return ret;
292}
293
294/** Control of the log from uspace
295 *
296 */
297sys_errno_t sys_klog(sysarg_t operation, uspace_addr_t buf, size_t size,
298 sysarg_t level, uspace_ptr_size_t uspace_nread)
299{
300 char *data;
301 errno_t rc;
302
303 if (size > PAGE_SIZE)
304 return (sys_errno_t) ELIMIT;
305
306 switch (operation) {
307 case KLOG_WRITE:
308 data = (char *) malloc(size + 1);
309 if (!data)
310 return (sys_errno_t) ENOMEM;
311
312 rc = copy_from_uspace(data, buf, size);
313 if (rc) {
314 free(data);
315 return (sys_errno_t) rc;
316 }
317 data[size] = 0;
318
319 if (level >= LVL_LIMIT)
320 level = LVL_NOTE;
321
322 log(LF_USPACE, level, "%s", data);
323
324 free(data);
325 return EOK;
326 case KLOG_READ:
327 data = (char *) malloc(size);
328 if (!data)
329 return (sys_errno_t) ENOMEM;
330
331 size_t entry_len = 0;
332 size_t copied = 0;
333
334 rc = EOK;
335
336 spinlock_lock(&log_lock);
337
338 while (next_for_uspace < log_used) {
339 size_t pos = (log_start + next_for_uspace) % LOG_LENGTH;
340 log_copy_from((uint8_t *) &entry_len, pos, sizeof(size_t));
341
342 if (entry_len > PAGE_SIZE) {
343 /*
344 * Since we limit data transfer
345 * to uspace to a maximum of PAGE_SIZE
346 * bytes, skip any entries larger
347 * than this limit to prevent
348 * userspace being stuck trying to
349 * read them.
350 */
351 next_for_uspace += entry_len;
352 continue;
353 }
354
355 if (size < copied + entry_len) {
356 if (copied == 0)
357 rc = EOVERFLOW;
358 break;
359 }
360
361 log_copy_from((uint8_t *) (data + copied), pos, entry_len);
362 copied += entry_len;
363 next_for_uspace += entry_len;
364 }
365
366 spinlock_unlock(&log_lock);
367
368 if (rc != EOK) {
369 free(data);
370 return (sys_errno_t) rc;
371 }
372
373 rc = copy_to_uspace(buf, data, size);
374
375 free(data);
376
377 if (rc != EOK)
378 return (sys_errno_t) rc;
379
380 return copy_to_uspace(uspace_nread, &copied, sizeof(copied));
381 return EOK;
382 default:
383 return (sys_errno_t) ENOTSUP;
384 }
385}
386
387/** @}
388 */
Note: See TracBrowser for help on using the repository browser.