#include <i386/mp.h>
#include <i386/cpu_data.h>
#include <i386/machine_cpu.h>
#include <i386/machine_routines.h>
#include <i386/misc_protos.h>
#include <vm/vm_kern.h>
#include <console/video_console.h>
#include <console/serial_protos.h>
#include <kern/kalloc.h>
static struct {
char *buffer;
int len;
int used;
char *write_ptr;
char *read_ptr;
decl_simple_lock_data(,read_lock);
decl_simple_lock_data(,write_lock);
} console_ring;
hw_lock_data_t cnputc_lock;
static volatile long console_output = 0;
typedef struct console_buf {
char *buf_base;
char *buf_end;
char *buf_ptr;
#define CPU_BUFFER_LEN (256 - 3*(sizeof(char*)))
char buf[CPU_BUFFER_LEN];
} console_buf_t;
extern int serial_getc(void);
extern void serial_putc(int);
static void _serial_putc(int, int, int);
int vcgetc(int, int, boolean_t, boolean_t);
console_ops_t cons_ops[] = {
{_serial_putc, _serial_getc},
{vcputc, vcgetc}
};
uint32_t nconsops = (sizeof cons_ops / sizeof cons_ops[0]);
uint32_t cons_ops_index = VC_CONS_OPS;
#define SIMPLE_LOCK_NO_INTRS(l) \
MACRO_BEGIN \
boolean_t istate = ml_get_interrupts_enabled(); \
while (!simple_lock_try((l))) \
{ \
if (!istate) \
handle_pending_TLB_flushes(); \
cpu_pause(); \
} \
MACRO_END
void
console_init(void)
{
int ret;
console_ring.len = PAGE_SIZE;
ret = kmem_alloc(kernel_map, (vm_offset_t *) &console_ring.buffer,
console_ring.len);
if (ret != KERN_SUCCESS)
panic("console_ring_init() "
"failed to allocate ring buffer, error %d\n", ret);
console_ring.used = 0;
console_ring.read_ptr = console_ring.buffer;
console_ring.write_ptr = console_ring.buffer;
simple_lock_init(&console_ring.read_lock, 0);
simple_lock_init(&console_ring.write_lock, 0);
hw_lock_init(&cnputc_lock);
}
void *
console_cpu_alloc(__unused boolean_t boot_processor)
{
int ret;
console_buf_t *cbp;
ret = kmem_alloc(kernel_map, (vm_offset_t *) &cbp,
sizeof(console_buf_t));
if (ret != KERN_SUCCESS) {
printf("console_cpu_alloc() "
"failed to allocate cpu buffer, error=%d\n", ret);
return NULL;
}
cbp->buf_base = (char *) &cbp->buf;
cbp->buf_ptr = cbp->buf_base;
cbp->buf_end = cbp->buf_base + CPU_BUFFER_LEN;
return (void *) cbp;
}
void
console_cpu_free(void *buf)
{
if (buf != NULL)
kfree((void *) buf, sizeof(console_buf_t));
}
static inline int
console_ring_space(void)
{
return console_ring.len - console_ring.used;
}
static boolean_t
console_ring_put(char ch)
{
if (console_ring.used < console_ring.len) {
console_ring.used++;;
*console_ring.write_ptr++ = ch;
if (console_ring.write_ptr - console_ring.buffer
== console_ring.len)
console_ring.write_ptr = console_ring.buffer;
return TRUE;
} else {
return FALSE;
}
}
static int
console_ring_get(void)
{
char ch = 0;
if (console_ring.used > 0) {
console_ring.used--;
ch = *console_ring.read_ptr++;
if (console_ring.read_ptr - console_ring.buffer
== console_ring.len)
console_ring.read_ptr = console_ring.buffer;
}
return (int) ch;
}
static inline void
cpu_buffer_put(console_buf_t *cbp, char ch)
{
if (ch != '\0' && cbp->buf_ptr < cbp->buf_end)
*(cbp->buf_ptr++) = ch;
}
static inline void
_cnputc(char c)
{
mp_disable_preemption();
if (!hw_lock_to(&cnputc_lock, LockTimeOut*10)) {
if (debug_mode) {
mp_enable_preemption();
hw_lock_init(&cnputc_lock);
hw_lock_lock(&cnputc_lock);
}
else
panic("Lock acquire timeout in _cnputc()");
}
cons_ops[cons_ops_index].putc(0, 0, c);
if (c == '\n')
cons_ops[cons_ops_index].putc(0, 0, '\r');
hw_lock_unlock(&cnputc_lock);
mp_enable_preemption();
}
void
cnputcusr(char c)
{
while (console_output != 0);
_cnputc(c);
}
static void
console_ring_try_empty(void)
{
boolean_t state = ml_get_interrupts_enabled();
if (!simple_lock_try(&console_ring.read_lock))
return;
atomic_incl(&console_output, 1);
for (;;) {
char ch;
if (!state)
handle_pending_TLB_flushes();
SIMPLE_LOCK_NO_INTRS(&console_ring.write_lock);
ch = console_ring_get();
simple_unlock(&console_ring.write_lock);
if (ch == 0)
break;
_cnputc(ch);
}
atomic_decl(&console_output, 1);
simple_unlock(&console_ring.read_lock);
}
void
cnputc(char c)
{
console_buf_t *cbp;
#if MACH_KDB
if (kdb_cpu == cpu_number()) {
_cnputc(c);
return;
}
#endif
mp_disable_preemption();
cbp = (console_buf_t *) current_cpu_datap()->cpu_console_buf;
if (cbp == NULL) {
mp_enable_preemption();
_cnputc(c);
return;
}
if (c != '\n') {
cpu_buffer_put(cbp, c);
} else {
boolean_t state;
char *cp;
state = ml_set_interrupts_enabled(FALSE);
SIMPLE_LOCK_NO_INTRS(&console_ring.write_lock);
while (cbp->buf_ptr-cbp->buf_base + 1 > console_ring_space()) {
simple_unlock(&console_ring.write_lock);
console_ring_try_empty();
SIMPLE_LOCK_NO_INTRS(&console_ring.write_lock);
}
for (cp = cbp->buf_base; cp < cbp->buf_ptr; cp++)
console_ring_put(*cp);
console_ring_put('\n');
cbp->buf_ptr = cbp->buf_base;
simple_unlock(&console_ring.write_lock);
ml_set_interrupts_enabled(state);
}
console_ring_try_empty();
mp_enable_preemption();
}
int _serial_getc(__unused int a, __unused int b, boolean_t wait, __unused boolean_t raw)
{
int c;
do {
c = serial_getc();
} while (wait && c < 0);
return c;
}
static void _serial_putc(__unused int a, __unused int b, int c)
{
serial_putc(c);
}
int
cngetc(void)
{
return cons_ops[cons_ops_index].getc(0, 0,
TRUE, FALSE);
}
int
cnmaygetc(void)
{
return cons_ops[cons_ops_index].getc(0, 0,
FALSE, FALSE);
}
int
vcgetc(__unused int l,
__unused int u,
__unused boolean_t wait,
__unused boolean_t raw)
{
char c;
if( 0 == (*PE_poll_input)( 0, &c))
return( c);
else
return( 0);
}