8 files changed, 453 insertions(+) user/test/lib/libcflat.h | 37 +++++++++ user/test/lib/panic.c | 13 +++ user/test/lib/printf.c | 179 ++++++++++++++++++++++++++++++++++++++++++++++ user/test/lib/string.c | 21 +++++ user/test/lib/x86/apic.h | 14 +++ user/test/lib/x86/io.c | 23 +++++ user/test/lib/x86/smp.c | 150 ++++++++++++++++++++++++++++++++++++++ user/test/lib/x86/smp.h | 16 ++++ This patch lays the ground work for a sinlge libcflat library that can be used for x86. But this allows for other archs to share common code, and build a single archive for tests to use libcflat functions. Signed-off-by: Jerone Young <jyoung5@.us.com> diff --git a/user/test/lib/libcflat.h b/user/test/lib/libcflat.h new file mode 100644 --- /dev/null +++ b/user/test/lib/libcflat.h @@ -0,0 +1,37 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright IBM Corp. 2008 + * + * Authors: Hollis Blanchard <hollisb@xxxxxxxxxx> + */ + +#ifndef __LIBCFLAT_H +#define __LIBCFLAT_H + +#include <stdarg.h> + +extern int main(void); +extern void exit(int code); +extern void panic(char *fmt, ...); + +extern unsigned long strlen(const char *buf); +extern char *strcat(char *dest, const char *src); + +extern int printf(const char *fmt, ...); +extern int vsnprintf(char *buf, int size, const char *fmt, va_list va); + +extern void puts(const char *s); + +#endif diff --git a/user/test/lib/panic.c b/user/test/lib/panic.c new file mode 100644 --- /dev/null +++ b/user/test/lib/panic.c @@ -0,0 +1,13 @@ +#include "libcflat.h" + +void panic(char *fmt, ...) +{ + va_list va; + char buf[2000]; + + va_start(va, fmt); + vsnprintf(buf, sizeof(buf), fmt, va); + va_end(va); + puts(buf); + exit(-1); +} diff --git a/user/test/lib/printf.c b/user/test/lib/printf.c new file mode 100644 --- /dev/null +++ b/user/test/lib/printf.c @@ -0,0 +1,179 @@ +#include "libcflat.h" + +typedef struct pstream { + char *buffer; + int remain; + int added; +} pstream_t; + +static void addchar(pstream_t *p, char c) +{ + if (p->remain) { + *p->buffer++ = c; + --p->remain; + } + ++p->added; +} + +void print_str(pstream_t *p, const char *s) +{ + while (*s) + addchar(p, *s++); +} + +static char digits[16] = "0123456789abcdef"; + +void print_int(pstream_t *ps, long long n, int base) +{ + char buf[sizeof(long) * 3 + 2], *p = buf; + int s = 0, i; + + if (n < 0) { + n = -n; + s = 1; + } + + while (n) { + *p++ = digits[n % base]; + n /= base; + } + + if (s) + *p++ = '-'; + + if (p == buf) + *p++ = '0'; + + for (i = 0; i < (p - buf) / 2; ++i) { + char tmp; + + tmp = buf[i]; + buf[i] = p[-1-i]; + p[-1-i] = tmp; + } + + *p = 0; + + print_str(ps, buf); +} + +void print_unsigned(pstream_t *ps, unsigned long long n, int base) +{ + char buf[sizeof(long) * 3 + 1], *p = buf; + int i; + + while (n) { + *p++ = digits[n % base]; + n /= base; + } + + if (p == buf) + *p++ = '0'; + + for (i = 0; i < (p - buf) / 2; ++i) { + char tmp; + + tmp = buf[i]; + buf[i] = p[-1-i]; + p[-1-i] = tmp; + } + + *p = 0; + + print_str(ps, buf); +} + +int vsnprintf(char *buf, int size, const char *fmt, va_list va) +{ + pstream_t s; + + s.buffer = buf; + s.remain = size - 1; + s.added = 0; + while (*fmt) { + char f = *fmt++; + int nlong = 0; + + if (f != '%') { + addchar(&s, f); + continue; + } + morefmt: + f = *fmt++; + switch (f) { + case '%': + addchar(&s, '%'); + break; + case '\0': + --fmt; + break; + case 'l': + ++nlong; + goto morefmt; + case 'd': + switch (nlong) { + case 0: + print_int(&s, va_arg(va, int), 10); + break; + case 1: + print_int(&s, va_arg(va, long), 10); + break; + default: + print_int(&s, va_arg(va, long long), 10); + break; + } + break; + case 'x': + switch (nlong) { + case 0: + print_unsigned(&s, va_arg(va, unsigned), 16); + break; + case 1: + print_unsigned(&s, va_arg(va, unsigned long), 16); + break; + default: + print_unsigned(&s, va_arg(va, unsigned long long), 16); + break; + } + break; + case 'p': + print_str(&s, "0x"); + print_unsigned(&s, (unsigned long)va_arg(va, void *), 16); + break; + case 's': + print_str(&s, va_arg(va, const char *)); + break; + default: + addchar(&s, f); + break; + } + } + *s.buffer = 0; + ++s.added; + return s.added; +} + + +int snprintf(char *buf, int size, const char *fmt, ...) +{ + va_list va; + int r; + + va_start(va, fmt); + r = vsnprintf(buf, size, fmt, va); + va_end(va); + return r; +} + +int printf(const char *fmt, ...) +{ + va_list va; + char buf[2000]; + int r; + + va_start(va, fmt); + r = vsnprintf(buf, sizeof buf, fmt, va); + va_end(va); + puts(buf); + return r; +} diff --git a/user/test/lib/string.c b/user/test/lib/string.c new file mode 100644 --- /dev/null +++ b/user/test/lib/string.c @@ -0,0 +1,21 @@ +#include "libcflat.h" + +unsigned long strlen(const char *buf) +{ + unsigned long len = 0; + + while (*buf++) + ++len; + return len; +} + +char *strcat(char *dest, const char *src) +{ + char *p = dest; + + while (*p) + ++p; + while ((*p++ = *src++) != 0) + ; + return dest; +} diff --git a/user/test/lib/x86/apic.h b/user/test/lib/x86/apic.h new file mode 100644 --- /dev/null +++ b/user/test/lib/x86/apic.h @@ -0,0 +1,14 @@ +#ifndef SILLY_APIC_H +#define SILLY_APIC_H + +#define APIC_BASE 0x1000 +#define APIC_SIZE 0x100 + +#define APIC_REG_NCPU 0x00 +#define APIC_REG_ID 0x04 +#define APIC_REG_SIPI_ADDR 0x08 +#define APIC_REG_SEND_SIPI 0x0c +#define APIC_REG_IPI_VECTOR 0x10 +#define APIC_REG_SEND_IPI 0x14 + +#endif diff --git a/user/test/lib/x86/io.c b/user/test/lib/x86/io.c new file mode 100644 --- /dev/null +++ b/user/test/lib/x86/io.c @@ -0,0 +1,23 @@ +#include <libcflat.h> +#include "smp.h" + +static struct spinlock lock; + +static void print_serial(const char *buf) +{ + unsigned long len = strlen(buf); + + asm volatile ("rep/outsb" : "+S"(buf), "+c"(len) : "d"(0xf1)); +} + +void puts(const char *s) +{ + spin_lock(&lock); + print_serial(s); + spin_unlock(&lock); +} + +void exit(int code) +{ + asm volatile("out %0, %1" : : "a"(code), "d"((short)0xf4)); +} diff --git a/user/test/lib/x86/smp.c b/user/test/lib/x86/smp.c new file mode 100644 --- /dev/null +++ b/user/test/lib/x86/smp.c @@ -0,0 +1,150 @@ + +#include <libcflat.h> +#include "smp.h" +#include "apic.h" + +#define IPI_VECTOR 0x20 + +static int apic_read(int reg) +{ + unsigned short port = APIC_BASE + reg; + unsigned v; + + asm volatile ("in %1, %0" : "=a"(v) : "d"(port)); + return v; +} + +static void apic_write(int reg, unsigned v) +{ + unsigned short port = APIC_BASE + reg; + + asm volatile ("out %0, %1" : : "a"(v), "d"(port)); +} + +static int apic_get_cpu_count() +{ + return apic_read(APIC_REG_NCPU); +} + +static int apic_get_id() +{ + return apic_read(APIC_REG_ID); +} + +static void apic_set_ipi_vector(int vector) +{ + apic_write(APIC_REG_IPI_VECTOR, vector); +} + +static void apic_send_ipi(int cpu) +{ + apic_write(APIC_REG_SEND_IPI, cpu); +} + +static struct spinlock ipi_lock; +static void (*ipi_function)(void *data); +static void *ipi_data; +static volatile int ipi_done; + +static __attribute__((used)) void ipi() +{ + ipi_function(ipi_data); + ipi_done = 1; +} + +asm ( + "ipi_entry: \n" + " call ipi \n" +#ifndef __x86_64__ + " iret" +#else + " iretq" +#endif + ); + + +static void set_ipi_descriptor(void (*ipi_entry)(void)) +{ + unsigned short *desc = (void *)(IPI_VECTOR * sizeof(long) * 2); + unsigned short cs; + unsigned long ipi = (unsigned long)ipi_entry; + + asm ("mov %%cs, %0" : "=r"(cs)); + desc[0] = ipi; + desc[1] = cs; + desc[2] = 0x8e00; + desc[3] = ipi >> 16; +#ifdef __x86_64__ + desc[4] = ipi >> 32; + desc[5] = ipi >> 48; + desc[6] = 0; + desc[7] = 0; +#endif +} + +void spin_lock(struct spinlock *lock) +{ + int v = 1; + + do { + asm volatile ("xchg %1, %0" : "+m"(lock->v), "+r"(v)); + } while (v); + asm volatile ("" : : : "memory"); +} + +void spin_unlock(struct spinlock *lock) +{ + asm volatile ("" : : : "memory"); + lock->v = 0; +} + +int cpu_count(void) +{ + return apic_get_cpu_count(); +} + +int smp_id(void) +{ + return apic_get_id(); +} + +void on_cpu(int cpu, void (*function)(void *data), void *data) +{ + spin_lock(&ipi_lock); + if (cpu == apic_get_id()) + function(data); + else { + ipi_function = function; + ipi_data = data; + apic_send_ipi(cpu); + while (!ipi_done) + ; + ipi_done = 0; + } + spin_unlock(&ipi_lock); +} + +static void (*smp_main_func)(void); +static volatile int smp_main_running; + +asm ("smp_init_entry: \n" + "incl smp_main_running \n" + "sti \n" + "call *smp_main_func"); + +void smp_init(void (*smp_main)(void)) +{ + int i; + void smp_init_entry(void); + void ipi_entry(void); + + apic_set_ipi_vector(IPI_VECTOR); + set_ipi_descriptor(smp_init_entry); + smp_main_func = smp_main; + for (i = 1; i < cpu_count(); ++i) { + apic_send_ipi(i); + while (smp_main_running < i) + ; + } + set_ipi_descriptor(ipi_entry); +} diff --git a/user/test/lib/x86/smp.h b/user/test/lib/x86/smp.h new file mode 100644 --- /dev/null +++ b/user/test/lib/x86/smp.h @@ -0,0 +1,16 @@ +#ifndef __SMP_H +#define __SMP_H + +struct spinlock { + int v; +}; + +void smp_init(void (*smp_main)(void)); + +int cpu_count(void); +int smp_id(void); +void on_cpu(int cpu, void (*function)(void *data), void *data); +void spin_lock(struct spinlock *lock); +void spin_unlock(struct spinlock *lock); + +#endif -- To unsubscribe from this list: send the line "unsubscribe kvm-ppc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html