Goto sanos source index

//
// klog.c
//
// Kernel logging
//
// Copyright (C) 2002 Michael Ringgaard. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 
// 1. Redistributions of source code must retain the above copyright 
//    notice, this list of conditions and the following disclaimer.  
// 2. 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.  
// 3. Neither the name of the project nor the names of its contributors
//    may be used to endorse or promote products derived from this software
//    without specific prior written permission. 
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
// 

#include <os/krnl.h>

#define KLOG_SIZE (64 * 1024)

struct klogreq {
  struct klogreq *next;
  struct thread *thread;
  err_t rc;
};

int kprint_enabled = 1;

static char klogbuf[KLOG_SIZE];
static unsigned int klog_start;
static unsigned int klog_end;
static unsigned int klog_size;
static struct klogreq *klog_waiters;
static struct dpc klog_dpc;

static int wait_for_klog() {
  struct klogreq req;

  req.thread = self();
  req.next = klog_waiters;
  klog_waiters = &req;

  enter_wait(THREAD_WAIT_DEVIO);

  return req.rc;
}

static void release_klog_waiters(void *arg) {
  struct klogreq *waiter;

  // Defer scheduling of kernel log waiter if we are in a interrupt handler
  if ((eflags() & EFLAG_IF) == 0)  {
    queue_irq_dpc(&klog_dpc, release_klog_waiters, NULL);
    return;
  }

  waiter = klog_waiters;
  while (waiter) {
    waiter->rc = 0;
    mark_thread_ready(waiter->thread, 1, 2);
    waiter = waiter->next;
  }

  klog_waiters = NULL;
}

static void add_to_klog(char *buf, int size) {
  while (size-- > 0) {
    if (klog_size == KLOG_SIZE) {
      while (klogbuf[klog_start] != '\n' && klog_size > 0) {
        klog_size--;
        klog_start++;
        if (klog_start == KLOG_SIZE) klog_start = 0;
      }

      if (klog_size > 0) {
        klog_size--;
        klog_start++;
        if (klog_start == KLOG_SIZE) klog_start = 0;
      }
    }

    klogbuf[klog_end++] = *buf++;
    if (klog_end == KLOG_SIZE) klog_end = 0;
    klog_size++;
  }

  release_klog_waiters(NULL);
}

void kprintf(const char *fmt,...) {
  va_list args;
  char buffer[1024];
  int len;

  va_start(args, fmt);
  len = vsprintf(buffer, fmt, args);
  va_end(args);
    
  add_to_klog(buffer, len);
  
  //if (debugging) dbg_output(buffer);

  if (kprint_enabled) {
    char *msg = buffer;
    int msglen = len;

    if (msg[0] == '<' && msg[1] >= '0' && msg[1] <= '7' && msg[2] == '>') {
      msg += 3;
      msglen -= 3;
    }

    console_print(msg, msglen);
  }
}

static int klog_ioctl(struct dev *dev, int cmd, void *args, size_t size) {
  switch (cmd) {
    case IOCTL_GETDEVSIZE:
      return klog_size;

    case IOCTL_GETBLKSIZE:
      return 1;

    case IOCTL_KPRINT_ENABLED:
      if (!args || size != 4) return -EINVAL;
      kprint_enabled = *(int *) args;
      return 0;

    case IOCTL_KLOG_WAIT:
      return wait_for_klog();
  }

  return -ENOSYS;
}

static int klog_read(struct dev *dev, void *buffer, size_t count, blkno_t blkno, int flags) {
  char *ptr;
  unsigned int idx;
  unsigned int n;

  blkno = blkno % KLOG_SIZE;
  if (blkno > klog_size) return -EFAULT;
  if (blkno + count > klog_size) count = klog_size - blkno;
  if (count == 0) return 0;
  
  ptr = (char *) buffer;
  idx = (klog_start + blkno) % KLOG_SIZE;
  n = count;
  while (n-- > 0) {
    *ptr++ = klogbuf[idx++];
    if (idx == KLOG_SIZE) idx = 0;
  }

  return count;
}

static int klog_write(struct dev *dev, void *buffer, size_t count, blkno_t blkno, int flags) {
  add_to_klog(buffer, count);
  return count;
}

struct driver klog_driver = {
  "klog",
  DEV_TYPE_BLOCK,
  klog_ioctl,
  klog_read,
  klog_write
};

int __declspec(dllexport) klog(struct unit *unit, char *opts) {
  dev_make("klog", &klog_driver, NULL, NULL);
  return 0;
}