Index: kernel/Makefile
===================================================================
--- kernel/Makefile	(revision 6fa9a99d9fa209ffacbdbc40837bf0f435c98e1b)
+++ kernel/Makefile	(revision 91db0280db48f5f3ca71923f5d70cbafe2611a02)
@@ -212,4 +212,5 @@
 	generic/src/debug/debug.c \
 	generic/src/interrupt/interrupt.c \
+	generic/src/log/log.c \
 	generic/src/main/main.c \
 	generic/src/main/kinit.c \
Index: kernel/generic/include/console/console.h
===================================================================
--- kernel/generic/include/console/console.h	(revision 6fa9a99d9fa209ffacbdbc40837bf0f435c98e1b)
+++ kernel/generic/include/console/console.h	(revision 91db0280db48f5f3ca71923f5d70cbafe2611a02)
@@ -39,4 +39,5 @@
 #include <print.h>
 #include <console/chardev.h>
+#include <synch/spinlock.h>
 
 #define PAGING(counter, increment, before, after) \
@@ -64,4 +65,7 @@
 extern void kio_init(void);
 extern void kio_update(void *);
+extern void kio_flush(void);
+extern void kio_push_char(const wchar_t);
+SPINLOCK_EXTERN(kio_lock);
 
 extern wchar_t getc(indev_t *indev);
Index: kernel/generic/include/debug.h
===================================================================
--- kernel/generic/include/debug.h	(revision 6fa9a99d9fa209ffacbdbc40837bf0f435c98e1b)
+++ kernel/generic/include/debug.h	(revision 91db0280db48f5f3ca71923f5d70cbafe2611a02)
@@ -107,5 +107,6 @@
 #define LOG(format, ...) \
 	do { \
-		printf("%s() from %s at %s:%u: " format "\n", __func__, \
+		log(LF_OTHER, LVL_DEBUG, \
+		    "%s() from %s at %s:%u: " format,__func__, \
 		    symtab_fmt_name_lookup(CALLER), __FILE__, __LINE__, \
 		    ##__VA_ARGS__); \
Index: kernel/generic/include/log.h
===================================================================
--- kernel/generic/include/log.h	(revision 91db0280db48f5f3ca71923f5d70cbafe2611a02)
+++ kernel/generic/include/log.h	(revision 91db0280db48f5f3ca71923f5d70cbafe2611a02)
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2013 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup genericlog
+ * @{
+ */
+/** @file
+ */
+
+#ifndef KERN_LOG_H_
+#define KERN_LOG_H_
+
+#include <typedefs.h>
+#include <stdarg.h>
+#include <printf/verify.h>
+#include <abi/log.h>
+#include <abi/klog.h>
+
+extern void log_init(void);
+extern void log_begin(log_facility_t, log_level_t);
+extern void log_end(void);
+extern int log_vprintf(const char *, va_list);
+extern int log_printf(const char *, ...)
+    PRINTF_ATTRIBUTE(1, 2);
+extern int log(log_facility_t, log_level_t, const char *, ...)
+    PRINTF_ATTRIBUTE(3, 4);
+
+extern sysarg_t sys_klog(sysarg_t, void *buf, size_t size,
+    sysarg_t level);
+
+#endif /* KERN_LOG_H_ */
+
+/** @}
+ */
Index: kernel/generic/src/console/console.c
===================================================================
--- kernel/generic/src/console/console.c	(revision 6fa9a99d9fa209ffacbdbc40837bf0f435c98e1b)
+++ kernel/generic/src/console/console.c	(revision 91db0280db48f5f3ca71923f5d70cbafe2611a02)
@@ -76,5 +76,5 @@
 
 /** Kernel log spinlock */
-SPINLOCK_STATIC_INITIALIZE_NAME(kio_lock, "kio_lock");
+SPINLOCK_INITIALIZE_NAME(kio_lock, "kio_lock");
 
 /** Physical memory area used for kio buffer */
@@ -263,28 +263,40 @@
 }
 
-void putchar(const wchar_t ch)
+/** Flush characters that are stored in the output buffer
+ * 
+ */
+void kio_flush(void)
 {
 	bool ordy = ((stdout) && (stdout->op->write));
 	
+	if (!ordy)
+		return;
+
 	spinlock_lock(&kio_lock);
-	
-	/* Print charaters stored in kernel log */
-	if (ordy) {
-		while (kio_stored > 0) {
-			wchar_t tmp = kio[(kio_start + kio_len - kio_stored) % KIO_LENGTH];
-			kio_stored--;
-			
-			/*
-			 * We need to give up the spinlock for
-			 * the physical operation of writting out
-			 * the character.
-			 */
-			spinlock_unlock(&kio_lock);
-			stdout->op->write(stdout, tmp);
-			spinlock_lock(&kio_lock);
-		}
-	}
-	
-	/* Store character in the cyclic kernel log */
+
+	/* Print characters that weren't printed earlier */
+	while (kio_stored > 0) {
+		wchar_t tmp = kio[(kio_start + kio_len - kio_stored) % KIO_LENGTH];
+		kio_stored--;
+
+		/*
+		 * We need to give up the spinlock for
+		 * the physical operation of writing out
+		 * the character.
+		 */
+		spinlock_unlock(&kio_lock);
+		stdout->op->write(stdout, tmp);
+		spinlock_lock(&kio_lock);
+	}
+
+	spinlock_unlock(&kio_lock);
+}
+
+/** Put a character into the output buffer.
+ * 
+ * The caller is required to hold kio_lock
+ */
+void kio_push_char(const wchar_t ch)
+{
 	kio[(kio_start + kio_len) % KIO_LENGTH] = ch;
 	if (kio_len < KIO_LENGTH)
@@ -293,22 +305,24 @@
 		kio_start = (kio_start + 1) % KIO_LENGTH;
 	
-	if (!ordy) {
-		if (kio_stored < kio_len)
-			kio_stored++;
-	}
+	if (kio_stored < kio_len)
+		kio_stored++;
 	
 	/* The character is stored for uspace */
 	if (kio_uspace < kio_len)
 		kio_uspace++;
-	
+}
+
+void putchar(const wchar_t ch)
+{
+	bool ordy = ((stdout) && (stdout->op->write));
+	
+	spinlock_lock(&kio_lock);
+	kio_push_char(ch);
 	spinlock_unlock(&kio_lock);
 	
-	if (ordy) {
-		/*
-		 * Output the character. In this case
-		 * it should be no longer buffered.
-		 */
-		stdout->op->write(stdout, ch);
-	} else {
+	/* Output stored characters */
+	kio_flush();
+	
+	if (!ordy) {
 		/*
 		 * No standard output routine defined yet.
Index: kernel/generic/src/log/log.c
===================================================================
--- kernel/generic/src/log/log.c	(revision 91db0280db48f5f3ca71923f5d70cbafe2611a02)
+++ kernel/generic/src/log/log.c	(revision 91db0280db48f5f3ca71923f5d70cbafe2611a02)
@@ -0,0 +1,385 @@
+/*
+ * Copyright (c) 2013 Martin Sucha
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup genericlog
+ * @{
+ */
+/** @file
+ */
+
+#include <sysinfo/sysinfo.h>
+#include <synch/spinlock.h>
+#include <typedefs.h>
+#include <ddi/irq.h>
+#include <ddi/ddi.h>
+#include <ipc/event.h>
+#include <ipc/irq.h>
+#include <arch.h>
+#include <panic.h>
+#include <putchar.h>
+#include <atomic.h>
+#include <syscall/copy.h>
+#include <errno.h>
+#include <str.h>
+#include <print.h>
+#include <printf/printf_core.h>
+#include <stdarg.h>
+#include <log.h>
+#include <console/console.h>
+#include <abi/log.h>
+
+#define LOG_PAGES    8
+#define LOG_LENGTH   (LOG_PAGES * PAGE_SIZE)
+#define LOG_ENTRY_HEADER_LENGTH (sizeof(size_t) + sizeof(uint32_t))
+
+/** Cyclic buffer holding the data for kernel log */
+uint8_t log_buffer[LOG_LENGTH] __attribute__((aligned(PAGE_SIZE)));
+
+/** Kernel log initialized */
+static atomic_t log_inited = {false};
+
+/** Position in the cyclic buffer where the first log entry starts */
+size_t log_start = 0;
+
+/** Sum of length of all log entries currently stored in the cyclic buffer */
+size_t log_used = 0;
+
+/** Log spinlock */
+SPINLOCK_STATIC_INITIALIZE_NAME(log_lock, "log_lock");
+
+/** Overall count of logged messages, which may overflow as needed */
+static uint32_t log_counter = 0;
+
+/** Starting position of the entry currently being written to the log */
+static size_t log_current_start = 0;
+
+/** Length (including header) of the entry currently being written to the log */
+static size_t log_current_len = 0;
+
+/** Start of the next entry to be handed to uspace starting from log_start */
+static size_t next_for_uspace = 0;
+
+static void log_update(void *);
+
+/** Initialize kernel logging facility
+ *
+ */
+void log_init(void)
+{	
+	event_set_unmask_callback(EVENT_KLOG, log_update);
+	atomic_set(&log_inited, true);
+}
+
+static size_t log_copy_from(uint8_t *data, size_t pos, size_t len) {
+	for (size_t i = 0; i < len; i++, pos = (pos + 1) % LOG_LENGTH) {
+		data[i] = log_buffer[pos];
+	}
+	return pos;
+}
+
+static size_t log_copy_to(const uint8_t *data, size_t pos, size_t len) {
+	for (size_t i = 0; i < len; i++, pos = (pos + 1) % LOG_LENGTH) {
+		log_buffer[pos] = data[i];
+	}
+	return pos;
+}
+
+/** Append data to the currently open log entry.
+ * 
+ * This function requires that the log_lock is acquired by the caller.
+ */
+static void log_append(const uint8_t *data, size_t len)
+{
+	/* Cap the length so that the entry entirely fits into the buffer */
+	if (len > LOG_LENGTH - log_current_len) {
+		len = LOG_LENGTH - log_current_len;
+	}
+	
+	if (len == 0)
+		return;
+	
+	size_t log_free = LOG_LENGTH - log_used - log_current_len;
+	
+	/* Discard older entries to make space, if necessary */
+	while (len > log_free) {
+		size_t entry_len;
+		log_copy_from((uint8_t *) &entry_len, log_start, sizeof(size_t));
+		log_start = (log_start + entry_len) % LOG_LENGTH;
+		log_used -= entry_len;
+		log_free += entry_len;
+		next_for_uspace -= entry_len;
+	}
+	
+	size_t pos = (log_current_start + log_current_len) % LOG_LENGTH;
+	log_copy_to(data, pos, len);
+	log_current_len += len;
+}
+
+/** Begin writing an entry to the log.
+ * 
+ * This acquires the log and output buffer locks, so only calls to log_* functions should
+ * be used until calling log_end.
+ */
+void log_begin(log_facility_t fac, log_level_t level)
+{
+	spinlock_lock(&log_lock);
+	spinlock_lock(&kio_lock);
+	
+	log_current_start = (log_start + log_used) % LOG_LENGTH;
+	log_current_len = 0;
+	
+	/* Write header of the log entry, the length will be written in log_end() */
+	log_append((uint8_t *) &log_current_len, sizeof(size_t));
+	log_append((uint8_t *) &log_counter, sizeof(uint32_t));
+	uint32_t fac32 = fac;
+	uint32_t lvl32 = level;
+	log_append((uint8_t *) &fac32, sizeof(uint32_t));
+	log_append((uint8_t *) &lvl32, sizeof(uint32_t));
+	
+	log_counter++;
+}
+
+/** Finish writing an entry to the log.
+ * 
+ * This releases the log and output buffer locks.
+ */
+void log_end(void) {
+	/* Set the length in the header to correct value */
+	log_copy_to((uint8_t *) &log_current_len, log_current_start, sizeof(size_t));
+	log_used += log_current_len;
+	
+	kio_push_char('\n');
+	spinlock_unlock(&kio_lock);
+	spinlock_unlock(&log_lock);
+	
+	/* This has to be called after we released the locks above */
+	kio_flush();
+	kio_update(NULL);
+	log_update(NULL);
+}
+
+static void log_update(void *event)
+{
+	if (!atomic_get(&log_inited))
+		return;
+	
+	spinlock_lock(&log_lock);
+	if (next_for_uspace < log_used)
+		event_notify_0(EVENT_KLOG, true);
+	spinlock_unlock(&log_lock);
+}
+
+static int log_printf_str_write(const char *str, size_t size, void *data)
+{
+	size_t offset = 0;
+	size_t chars = 0;
+	
+	while (offset < size) {
+		kio_push_char(str_decode(str, &offset, size));
+		chars++;
+	}
+	
+	log_append((const uint8_t *)str, size);
+	
+	return chars;
+}
+
+static int log_printf_wstr_write(const wchar_t *wstr, size_t size, void *data)
+{
+	char buffer[16];
+	size_t offset = 0;
+	size_t chars = 0;
+	
+	for (offset = 0; offset < size; offset += sizeof(wchar_t), chars++) {
+		kio_push_char(wstr[chars]);
+		
+		size_t buffer_offset = 0;
+		int rc = chr_encode(wstr[chars], buffer, &buffer_offset, 16);
+		if (rc != EOK) {
+			return rc;
+		}
+		
+		log_append((const uint8_t *)buffer, buffer_offset);
+	}
+	
+	return chars;
+}
+
+/** Append a message to the currently being written entry.
+ * 
+ * Requires that an entry has been started using log_begin()
+ */
+int log_vprintf(const char *fmt, va_list args)
+{
+	int ret;
+	
+	printf_spec_t ps = {
+		log_printf_str_write,
+		log_printf_wstr_write,
+		NULL
+	};
+	
+	
+	ret = printf_core(fmt, &ps, args);
+	
+	return ret;
+}
+
+/** Append a message to the currently being written entry.
+ * 
+ * Requires that an entry has been started using log_begin()
+ */
+int log_printf(const char *fmt, ...)
+{
+	int ret;
+	va_list args;
+	
+	va_start(args, fmt);
+	ret = log_vprintf(fmt, args);
+	va_end(args);
+	
+	return ret;
+}
+
+/** Log a message to the kernel log.
+ * 
+ * This atomically appends a log entry.
+ * The resulting message should not contain a trailing newline, as the log
+ * entries are explicitly delimited when stored in the log.
+ */
+int log(log_facility_t fac, log_level_t level, const char *fmt, ...)
+{
+	int ret;
+	va_list args;
+	
+	log_begin(fac, level);
+	
+	va_start(args, fmt);
+	ret = log_vprintf(fmt, args);
+	va_end(args);
+	
+	log_end();
+	
+	return ret;
+}
+
+/** Control of the log from uspace
+ *
+ */
+sysarg_t sys_klog(sysarg_t operation, void *buf, size_t size,
+    sysarg_t level)
+{
+	char *data;
+	int rc;
+	
+	if (size > PAGE_SIZE)
+		return (sysarg_t) ELIMIT;
+	
+	switch (operation) {
+		case KLOG_WRITE:
+			data = (char *) malloc(size + 1, 0);
+			if (!data)
+				return (sysarg_t) ENOMEM;
+			
+			rc = copy_from_uspace(data, buf, size);
+			if (rc) {
+				free(data);
+				return (sysarg_t) rc;
+			}
+			data[size] = 0;
+			
+			if (level >= LVL_LIMIT)
+				level = LVL_NOTE;
+			
+			log(LF_USPACE, level, "%s", data);
+			
+			free(data);
+			return EOK;
+		case KLOG_READ:
+			data = (char *) malloc(size, 0);
+			if (!data)
+				return (sysarg_t) ENOMEM;
+			
+			size_t entry_len = 0;
+			size_t copied = 0;
+			
+			rc = EOK;
+	
+			spinlock_lock(&log_lock);
+			
+			while (next_for_uspace < log_used) {
+				size_t pos = (log_start + next_for_uspace) % LOG_LENGTH;
+				log_copy_from((uint8_t *) &entry_len, pos, sizeof(size_t));
+				
+				if (entry_len > PAGE_SIZE) {
+					/* 
+					 * Since we limit data transfer
+					 * to uspace to a maximum of PAGE_SIZE
+					 * bytes, skip any entries larger
+					 * than this limit to prevent
+					 * userspace being stuck trying to
+					 * read them.
+					 */
+					next_for_uspace += entry_len;
+					continue;
+				}
+				
+				if (size < copied + entry_len) {
+					if (copied == 0)
+						rc = EOVERFLOW;
+					break;
+				}
+				
+				log_copy_from((uint8_t *) (data + copied), pos, entry_len);
+				copied += entry_len;
+				next_for_uspace += entry_len;
+			}
+			
+			spinlock_unlock(&log_lock);
+			
+			if (rc != EOK) {
+				free(data);
+				return (sysarg_t) rc;
+			}
+			
+			rc = copy_to_uspace(buf, data, size);
+			
+			free(data);
+			
+			if (rc != EOK)
+				return (sysarg_t) rc;
+			
+			return copied;
+		default:
+			return (sysarg_t) ENOTSUP;
+	}
+}
+
+
+/** @}
+ */
Index: kernel/generic/src/main/main.c
===================================================================
--- kernel/generic/src/main/main.c	(revision 6fa9a99d9fa209ffacbdbc40837bf0f435c98e1b)
+++ kernel/generic/src/main/main.c	(revision 91db0280db48f5f3ca71923f5d70cbafe2611a02)
@@ -62,4 +62,5 @@
 #include <console/kconsole.h>
 #include <console/console.h>
+#include <log.h>
 #include <cpu.h>
 #include <align.h>
@@ -282,4 +283,5 @@
 	event_init();
 	kio_init();
+	log_init();
 	stats_init();
 	
