5125 lines
129 KiB
Diff
5125 lines
129 KiB
Diff
xc_kexec utility, for domU kexec support.
|
|
|
|
Signed-off-by: Gerd Hoffmann <kraxel@suse.de>
|
|
---
|
|
tools/xcutils/Makefile | 10
|
|
tools/xcutils/helper/Makefile | 39 +
|
|
tools/xcutils/helper/console.c | 69 ++
|
|
tools/xcutils/helper/ctype.c | 35 +
|
|
tools/xcutils/helper/ctype.h | 54 +
|
|
tools/xcutils/helper/helper.h | 107 +++
|
|
tools/xcutils/helper/main.c | 651 +++++++++++++++++++
|
|
tools/xcutils/helper/make-offsets.c | 28
|
|
tools/xcutils/helper/printk.c | 1051 ++++++++++++++++++++++++++++++++
|
|
tools/xcutils/helper/string.c | 601 ++++++++++++++++++
|
|
tools/xcutils/helper/vsprintf.c | 842 +++++++++++++++++++++++++
|
|
tools/xcutils/helper/x86_32/div64.h | 48 +
|
|
tools/xcutils/helper/x86_32/entry.S | 49 +
|
|
tools/xcutils/helper/x86_32/hypercall.h | 360 ++++++++++
|
|
tools/xcutils/helper/x86_64/div64.h | 58 +
|
|
tools/xcutils/helper/x86_64/entry.S | 50 +
|
|
tools/xcutils/helper/x86_64/hypercall.h | 355 ++++++++++
|
|
tools/xcutils/kexec-syscall.h | 80 ++
|
|
tools/xcutils/xc_kexec.c | 503 +++++++++++++++
|
|
19 files changed, 4988 insertions(+), 2 deletions(-)
|
|
|
|
Index: xen-3.3.1-testing/tools/xcutils/Makefile
|
|
===================================================================
|
|
--- xen-3.3.1-testing.orig/tools/xcutils/Makefile
|
|
+++ xen-3.3.1-testing/tools/xcutils/Makefile
|
|
@@ -18,7 +18,7 @@ CFLAGS += $(CFLAGS_libxenctrl) $(CFLAGS_
|
|
CFLAGS += -Wp,-MD,.$(@F).d
|
|
PROG_DEP = .*.d
|
|
|
|
-PROGRAMS = xc_restore xc_save readnotes lsevtchn
|
|
+PROGRAMS = xc_restore xc_save readnotes lsevtchn xc_kexec
|
|
|
|
LDLIBS = $(LDFLAGS_libxenctrl) $(LDFLAGS_libxenguest) $(LDFLAGS_libxenstore)
|
|
|
|
@@ -31,6 +31,11 @@ build: $(PROGRAMS)
|
|
$(PROGRAMS): %: %.o
|
|
$(CC) $(CFLAGS) $(LDFLAGS) $^ $(LDLIBS) -o $@
|
|
|
|
+xc_kexec.o: xc_kexec.c helper/blob.h
|
|
+
|
|
+helper/blob.h:
|
|
+ make -C helper
|
|
+
|
|
.PHONY: install
|
|
install: build
|
|
$(INSTALL_DIR) $(DESTDIR)$(PRIVATE_BINDIR)
|
|
@@ -41,5 +46,6 @@ install: build
|
|
clean:
|
|
$(RM) *.o $(PROGRAMS)
|
|
$(RM) $(PROG_DEP)
|
|
+ make -C helper clean
|
|
|
|
-include $(PROG_DEP)
|
|
Index: xen-3.3.1-testing/tools/xcutils/helper/Makefile
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ xen-3.3.1-testing/tools/xcutils/helper/Makefile
|
|
@@ -0,0 +1,39 @@
|
|
+
|
|
+XEN_ROOT = ../../..
|
|
+include $(XEN_ROOT)/tools/Rules.mk
|
|
+
|
|
+OBJS := $(XEN_TARGET_ARCH)/entry.o
|
|
+OBJS += main.o console.o vsprintf.o string.o ctype.o
|
|
+
|
|
+CFLAGS += -g -I$(XEN_ROOT)/tools/libxc -I$(XEN_TARGET_ARCH) -I$(XEN_INCLUDE)
|
|
+
|
|
+ifeq ($(XEN_TARGET_ARCH),x86_32)
|
|
+HLP_LDFLAGS := -melf_i386
|
|
+endif
|
|
+
|
|
+HLP_LDFLAGS += --section-start .text=0x10000
|
|
+HLP_LDFLAGS += --section-start .data=0x20000
|
|
+HLP_LDFLAGS += --emit-relocs
|
|
+
|
|
+#####################
|
|
+# rules
|
|
+
|
|
+all: blob.h
|
|
+
|
|
+clean:
|
|
+ rm -rf helper.elf blob.h
|
|
+ rm -rf $(OBJS) *~
|
|
+
|
|
+helper.elf: $(OBJS)
|
|
+ ld $(HLP_LDFLAGS) -o $@ $^
|
|
+
|
|
+blob.h: helper.elf
|
|
+ hexdump -v -e '1/1 "0x%02x,\n"' $< > $@
|
|
+
|
|
+$(XEN_TARGET_ARCH)/offsets.h: make-offsets
|
|
+ ./make-offsets > $@
|
|
+
|
|
+#####################
|
|
+# dependencies
|
|
+
|
|
+$(XEN_TARGET_ARCH)/entry.o: $(XEN_TARGET_ARCH)/entry.S $(XEN_TARGET_ARCH)/offsets.h
|
|
Index: xen-3.3.1-testing/tools/xcutils/helper/console.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ xen-3.3.1-testing/tools/xcutils/helper/console.c
|
|
@@ -0,0 +1,69 @@
|
|
+#include <inttypes.h>
|
|
+
|
|
+#include <xenctrl.h>
|
|
+#include <xen/io/console.h>
|
|
+#include "hypercall.h"
|
|
+
|
|
+#include "helper.h"
|
|
+
|
|
+static inline int notify_remote_via_evtchn(int port)
|
|
+{
|
|
+ struct evtchn_send send;
|
|
+ send.port = port;
|
|
+ return HYPERVISOR_event_channel_op(EVTCHNOP_send, &send);
|
|
+}
|
|
+
|
|
+static inline struct xencons_interface *xencons_interface(void)
|
|
+{
|
|
+ return (void*)(console_page);
|
|
+}
|
|
+
|
|
+static int xencons_ring_send(const char *data, unsigned len)
|
|
+{
|
|
+ int sent = 0;
|
|
+ struct xencons_interface *intf = xencons_interface();
|
|
+ XENCONS_RING_IDX cons, prod;
|
|
+
|
|
+ cons = intf->out_cons;
|
|
+ prod = intf->out_prod;
|
|
+ xen_mb();
|
|
+
|
|
+ while ((sent < len) && ((prod - cons) < sizeof(intf->out)))
|
|
+ intf->out[MASK_XENCONS_IDX(prod++, intf->out)] = data[sent++];
|
|
+
|
|
+ xen_wmb();
|
|
+ intf->out_prod = prod;
|
|
+
|
|
+ if (0 != notify_remote_via_evtchn(console_evtchn))
|
|
+ return -1;
|
|
+ return sent;
|
|
+}
|
|
+
|
|
+int printk(const char *fmt, ...)
|
|
+{
|
|
+ va_list args;
|
|
+ int r;
|
|
+
|
|
+ va_start(args, fmt);
|
|
+ r = vprintk(fmt, args);
|
|
+ va_end(args);
|
|
+
|
|
+ return r;
|
|
+}
|
|
+
|
|
+int vprintk(const char *fmt, va_list args)
|
|
+{
|
|
+ static char printk_buf[1024];
|
|
+ static char prefix[] = "printk: ";
|
|
+ int printed_len;
|
|
+
|
|
+ /* Emit the output into the temporary buffer */
|
|
+ printed_len = vscnprintf(printk_buf, sizeof(printk_buf), fmt, args);
|
|
+ if (xencons_ring_send(printk_buf, printed_len) < 0) {
|
|
+ /* shouldn't happen, but better have a fallback ;) */
|
|
+ HYPERVISOR_console_io(CONSOLEIO_write, strlen(prefix), prefix);
|
|
+ HYPERVISOR_console_io(CONSOLEIO_write, printed_len, printk_buf);
|
|
+ }
|
|
+
|
|
+ return printed_len;
|
|
+}
|
|
Index: xen-3.3.1-testing/tools/xcutils/helper/ctype.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ xen-3.3.1-testing/tools/xcutils/helper/ctype.c
|
|
@@ -0,0 +1,35 @@
|
|
+/*
|
|
+ * linux/lib/ctype.c
|
|
+ *
|
|
+ * Copyright (C) 1991, 1992 Linus Torvalds
|
|
+ */
|
|
+
|
|
+#include "helper.h"
|
|
+
|
|
+unsigned char _ctype[] = {
|
|
+_C,_C,_C,_C,_C,_C,_C,_C, /* 0-7 */
|
|
+_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C, /* 8-15 */
|
|
+_C,_C,_C,_C,_C,_C,_C,_C, /* 16-23 */
|
|
+_C,_C,_C,_C,_C,_C,_C,_C, /* 24-31 */
|
|
+_S|_SP,_P,_P,_P,_P,_P,_P,_P, /* 32-39 */
|
|
+_P,_P,_P,_P,_P,_P,_P,_P, /* 40-47 */
|
|
+_D,_D,_D,_D,_D,_D,_D,_D, /* 48-55 */
|
|
+_D,_D,_P,_P,_P,_P,_P,_P, /* 56-63 */
|
|
+_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U, /* 64-71 */
|
|
+_U,_U,_U,_U,_U,_U,_U,_U, /* 72-79 */
|
|
+_U,_U,_U,_U,_U,_U,_U,_U, /* 80-87 */
|
|
+_U,_U,_U,_P,_P,_P,_P,_P, /* 88-95 */
|
|
+_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L, /* 96-103 */
|
|
+_L,_L,_L,_L,_L,_L,_L,_L, /* 104-111 */
|
|
+_L,_L,_L,_L,_L,_L,_L,_L, /* 112-119 */
|
|
+_L,_L,_L,_P,_P,_P,_P,_C, /* 120-127 */
|
|
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 128-143 */
|
|
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 144-159 */
|
|
+_S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 160-175 */
|
|
+_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P, /* 176-191 */
|
|
+_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U, /* 192-207 */
|
|
+_U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L, /* 208-223 */
|
|
+_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L, /* 224-239 */
|
|
+_L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L}; /* 240-255 */
|
|
+
|
|
+EXPORT_SYMBOL(_ctype);
|
|
Index: xen-3.3.1-testing/tools/xcutils/helper/ctype.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ xen-3.3.1-testing/tools/xcutils/helper/ctype.h
|
|
@@ -0,0 +1,54 @@
|
|
+#ifndef _LINUX_CTYPE_H
|
|
+#define _LINUX_CTYPE_H
|
|
+
|
|
+/*
|
|
+ * NOTE! This ctype does not handle EOF like the standard C
|
|
+ * library is required to.
|
|
+ */
|
|
+
|
|
+#define _U 0x01 /* upper */
|
|
+#define _L 0x02 /* lower */
|
|
+#define _D 0x04 /* digit */
|
|
+#define _C 0x08 /* cntrl */
|
|
+#define _P 0x10 /* punct */
|
|
+#define _S 0x20 /* white space (space/lf/tab) */
|
|
+#define _X 0x40 /* hex digit */
|
|
+#define _SP 0x80 /* hard space (0x20) */
|
|
+
|
|
+extern unsigned char _ctype[];
|
|
+
|
|
+#define __ismask(x) (_ctype[(int)(unsigned char)(x)])
|
|
+
|
|
+#define isalnum(c) ((__ismask(c)&(_U|_L|_D)) != 0)
|
|
+#define isalpha(c) ((__ismask(c)&(_U|_L)) != 0)
|
|
+#define iscntrl(c) ((__ismask(c)&(_C)) != 0)
|
|
+#define isdigit(c) ((__ismask(c)&(_D)) != 0)
|
|
+#define isgraph(c) ((__ismask(c)&(_P|_U|_L|_D)) != 0)
|
|
+#define islower(c) ((__ismask(c)&(_L)) != 0)
|
|
+#define isprint(c) ((__ismask(c)&(_P|_U|_L|_D|_SP)) != 0)
|
|
+#define ispunct(c) ((__ismask(c)&(_P)) != 0)
|
|
+#define isspace(c) ((__ismask(c)&(_S)) != 0)
|
|
+#define isupper(c) ((__ismask(c)&(_U)) != 0)
|
|
+#define isxdigit(c) ((__ismask(c)&(_D|_X)) != 0)
|
|
+
|
|
+#define isascii(c) (((unsigned char)(c))<=0x7f)
|
|
+#define toascii(c) (((unsigned char)(c))&0x7f)
|
|
+
|
|
+static inline unsigned char __tolower(unsigned char c)
|
|
+{
|
|
+ if (isupper(c))
|
|
+ c -= 'A'-'a';
|
|
+ return c;
|
|
+}
|
|
+
|
|
+static inline unsigned char __toupper(unsigned char c)
|
|
+{
|
|
+ if (islower(c))
|
|
+ c -= 'a'-'A';
|
|
+ return c;
|
|
+}
|
|
+
|
|
+#define tolower(c) __tolower(c)
|
|
+#define toupper(c) __toupper(c)
|
|
+
|
|
+#endif
|
|
Index: xen-3.3.1-testing/tools/xcutils/helper/helper.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ xen-3.3.1-testing/tools/xcutils/helper/helper.h
|
|
@@ -0,0 +1,107 @@
|
|
+#include <stdarg.h>
|
|
+#include <stddef.h>
|
|
+#include <limits.h>
|
|
+#include <inttypes.h>
|
|
+
|
|
+#if defined(__i386__)
|
|
+typedef unsigned int size_t;
|
|
+typedef int ptrdiff_t;
|
|
+#define BITS_PER_LONG 32
|
|
+#elif defined(__x86_64__)
|
|
+typedef unsigned long size_t;
|
|
+typedef long ptrdiff_t;
|
|
+#define BITS_PER_LONG 64
|
|
+#else
|
|
+#error fixme please: unknown arch
|
|
+#endif
|
|
+
|
|
+/* from linux */
|
|
+#include "ctype.h"
|
|
+#include "div64.h"
|
|
+
|
|
+/* some stuff to compile linux kernel sources almost unmodified */
|
|
+#define unlikely(x) (x)
|
|
+#define WARN_ON(x)
|
|
+#define BUG_ON(x)
|
|
+#define EXPORT_SYMBOL(x)
|
|
+
|
|
+#ifndef PAGE_SHIFT
|
|
+# define PAGE_SHIFT 12
|
|
+# define PAGE_SIZE (1<<PAGE_SHIFT)
|
|
+#endif
|
|
+
|
|
+#define _PAGE_PRESENT 0x001
|
|
+#define _PAGE_RW 0x002
|
|
+#define _PAGE_USER 0x004
|
|
+#define _PAGE_PWT 0x008
|
|
+#define _PAGE_PCD 0x010
|
|
+#define _PAGE_ACCESSED 0x020
|
|
+#define _PAGE_DIRTY 0x040
|
|
+#define _PAGE_PAT 0x080
|
|
+#define _PAGE_PSE 0x080
|
|
+#define _PAGE_GLOBAL 0x100
|
|
+
|
|
+/* entry.S: special pages */
|
|
+extern char hypercall_page[];
|
|
+
|
|
+/* entry.S: start kernel */
|
|
+extern void _start_kernel(void);
|
|
+extern void BUG(void);
|
|
+
|
|
+/* main.c */
|
|
+typedef __attribute__ ((aligned (PAGE_SIZE))) char page_t[PAGE_SIZE];
|
|
+extern page_t console_page;
|
|
+extern page_t xenstore_page;
|
|
+extern unsigned long virt_base;
|
|
+extern unsigned long virt_hypercall;
|
|
+extern unsigned long console_evtchn;
|
|
+extern struct vcpu_guest_context vcpu;
|
|
+extern struct start_info *old_info;
|
|
+
|
|
+void start_helper(void);
|
|
+
|
|
+/* console.c */
|
|
+int printk(const char *fmt, ...) __attribute__ ((format(printf, 1, 2)));
|
|
+int vprintk(const char *fmt, va_list args);
|
|
+
|
|
+/* linux: string.c */
|
|
+int strnicmp(const char *s1, const char *s2, size_t len);
|
|
+char *strcpy(char *dest, const char *src);
|
|
+char *strncpy(char *dest, const char *src, size_t count);
|
|
+size_t strlcpy(char *dest, const char *src, size_t size);
|
|
+char *strcat(char *dest, const char *src);
|
|
+char *strncat(char *dest, const char *src, size_t count);
|
|
+size_t strlcat(char *dest, const char *src, size_t count);
|
|
+size_t strlcat(char *dest, const char *src, size_t count);
|
|
+int strcmp(const char *cs, const char *ct);
|
|
+int strncmp(const char *cs, const char *ct, size_t count);
|
|
+char *strchr(const char *s, int c);
|
|
+char *strrchr(const char *s, int c);
|
|
+char *strnchr(const char *s, size_t count, int c);
|
|
+size_t strlen(const char *s);
|
|
+size_t strnlen(const char *s, size_t count);
|
|
+size_t strspn(const char *s, const char *accept);
|
|
+size_t strcspn(const char *s, const char *reject);
|
|
+char *strpbrk(const char *cs, const char *ct);
|
|
+char *strsep(char **s, const char *ct);
|
|
+void *memset(void *s, int c, size_t count);
|
|
+void *memcpy(void *dest, const void *src, size_t count);
|
|
+void *memmove(void *dest, const void *src, size_t count);
|
|
+int memcmp(const void *cs, const void *ct, size_t count);
|
|
+void *memscan(void *addr, int c, size_t size);
|
|
+char *strstr(const char *s1, const char *s2);
|
|
+void *memchr(const void *s, int c, size_t n);
|
|
+
|
|
+/* linux: vsprintf.c */
|
|
+unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base);
|
|
+long simple_strtol(const char *cp,char **endp,unsigned int base);
|
|
+unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base);
|
|
+long long simple_strtoll(const char *cp,char **endp,unsigned int base);
|
|
+int vsnprintf(char *buf, size_t size, const char *fmt, va_list args);
|
|
+int vscnprintf(char *buf, size_t size, const char *fmt, va_list args);
|
|
+int snprintf(char * buf, size_t size, const char *fmt, ...);
|
|
+int scnprintf(char * buf, size_t size, const char *fmt, ...);
|
|
+int vsprintf(char *buf, const char *fmt, va_list args);
|
|
+int sprintf(char * buf, const char *fmt, ...);
|
|
+int vsscanf(const char * buf, const char * fmt, va_list args);
|
|
+int sscanf(const char * buf, const char * fmt, ...);
|
|
Index: xen-3.3.1-testing/tools/xcutils/helper/main.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ xen-3.3.1-testing/tools/xcutils/helper/main.c
|
|
@@ -0,0 +1,651 @@
|
|
+#include <xenctrl.h>
|
|
+#include "hypercall.h"
|
|
+
|
|
+#include "helper.h"
|
|
+#undef machine_to_phys_mapping
|
|
+
|
|
+/* filled by xc_kexec */
|
|
+unsigned long debug_level = 0;
|
|
+unsigned long virt_base;
|
|
+unsigned long virt_hypercall;
|
|
+unsigned long console_evtchn;
|
|
+#if defined(__i386__)
|
|
+unsigned long pae_paging;
|
|
+#endif
|
|
+struct vcpu_guest_context vcpu;
|
|
+
|
|
+/* passed by trampoline in %esi */
|
|
+struct start_info *old_info;
|
|
+
|
|
+/* my data */
|
|
+static struct start_info *new_info;
|
|
+static xen_pfn_t *phys_to_machine_mapping;
|
|
+static xen_pfn_t *machine_to_phys_mapping;
|
|
+static void (*start_kernel)(void);
|
|
+
|
|
+/* magic pages */
|
|
+page_t console_page;
|
|
+page_t xenstore_page;
|
|
+static page_t shared_info_page;
|
|
+static struct shared_info *shared_info = (void*)shared_info_page;
|
|
+
|
|
+#define dprintk if (debug_level >= 1) printk
|
|
+
|
|
+/* ------------------------------------------------------------------ */
|
|
+
|
|
+static unsigned char xen_features[XENFEAT_NR_SUBMAPS * 32];
|
|
+#define xen_feature(flag) (xen_features[flag])
|
|
+
|
|
+static void setup_xen_features(void)
|
|
+{
|
|
+ xen_feature_info_t fi;
|
|
+ int i, j;
|
|
+
|
|
+ for (i = 0; i < XENFEAT_NR_SUBMAPS; i++) {
|
|
+ fi.submap_idx = i;
|
|
+ if (HYPERVISOR_xen_version(XENVER_get_features, &fi) < 0)
|
|
+ break;
|
|
+ for (j=0; j<32; j++)
|
|
+ xen_features[i*32+j] = !!(fi.submap & 1<<j);
|
|
+ }
|
|
+}
|
|
+
|
|
+#if 0 /* DEBUG */
|
|
+#include <errno.h>
|
|
+#include <xen/io/xs_wire.h>
|
|
+static void xenstore_debug(void)
|
|
+{
|
|
+ struct xenstore_domain_interface *xs = (void*)xenstore_page;
|
|
+
|
|
+ printk("xs: req %4d -> %4d\r\n", xs->req_cons, xs->req_prod);
|
|
+ printk("xs: rsp %4d -> %4d\r\n", xs->rsp_cons, xs->rsp_prod);
|
|
+ if (xs->req_cons != xs->req_prod)
|
|
+ return;
|
|
+ if (xs->rsp_cons != xs->rsp_prod)
|
|
+ return;
|
|
+
|
|
+ memset(xenstore_page, 0, PAGE_SIZE);
|
|
+ xen_wmb();
|
|
+ printk("xs: debug fixup [zero page] done\r\n");
|
|
+}
|
|
+#endif
|
|
+
|
|
+/* ------------------------------------------------------------------ */
|
|
+
|
|
+#define INVALID_P2M_ENTRY (~0UL)
|
|
+#define FOREIGN_FRAME_BIT (1UL<<31)
|
|
+#define pfn_to_mfn(pfn) (phys_to_machine_mapping[pfn] & ~FOREIGN_FRAME_BIT)
|
|
+#define mfn_to_pfn(mfn) (machine_to_phys_mapping[mfn])
|
|
+#define set_phys_to_machine(pfn, mfn) (phys_to_machine_mapping[pfn] = mfn)
|
|
+#define phys_to_machine_mapping_valid(pfn) (phys_to_machine_mapping[pfn] != INVALID_P2M_ENTRY)
|
|
+
|
|
+void xen_machphys_update(unsigned long mfn, unsigned long pfn)
|
|
+{
|
|
+ mmu_update_t u;
|
|
+ int rc;
|
|
+
|
|
+ u.ptr = ((unsigned long long)mfn << PAGE_SHIFT) | MMU_MACHPHYS_UPDATE;
|
|
+ u.val = pfn;
|
|
+ rc = HYPERVISOR_mmu_update(&u, 1, NULL, DOMID_SELF);
|
|
+ if (0 == rc)
|
|
+ return;
|
|
+ printk("Oops: HYPERVISOR_mmu_update: rc=%d\r\n",rc);
|
|
+}
|
|
+
|
|
+static void swap_pages_p2m(unsigned long pfn1, unsigned long pfn2)
|
|
+{
|
|
+ unsigned long mfn1, mfn2;
|
|
+
|
|
+ mfn1 = phys_to_machine_mapping_valid(pfn1)
|
|
+ ? pfn_to_mfn(pfn1) : INVALID_P2M_ENTRY;
|
|
+ mfn2 = phys_to_machine_mapping_valid(pfn2)
|
|
+ ? pfn_to_mfn(pfn2) : INVALID_P2M_ENTRY;
|
|
+ dprintk("%s: pfn %5lx / mfn %5lx <=> pfn %5lx / mfn %5lx\r\n",
|
|
+ __FUNCTION__, pfn1, mfn1, pfn2, mfn2);
|
|
+
|
|
+ if (mfn1 != INVALID_P2M_ENTRY)
|
|
+ xen_machphys_update(mfn1, INVALID_P2M_ENTRY);
|
|
+ if (mfn2 != INVALID_P2M_ENTRY)
|
|
+ xen_machphys_update(mfn2, INVALID_P2M_ENTRY);
|
|
+
|
|
+ set_phys_to_machine(pfn2, mfn1);
|
|
+ if (mfn1 != INVALID_P2M_ENTRY)
|
|
+ xen_machphys_update(mfn1, pfn2);
|
|
+
|
|
+ set_phys_to_machine(pfn1, mfn2);
|
|
+ if (mfn2 != INVALID_P2M_ENTRY)
|
|
+ xen_machphys_update(mfn2, pfn1);
|
|
+}
|
|
+
|
|
+static void plug_holes_p2m(void)
|
|
+{
|
|
+ unsigned long pfns = shared_info->arch.max_pfn;
|
|
+ unsigned long take, give;
|
|
+ int count = 0;
|
|
+
|
|
+ take = 0;
|
|
+ give = pfns-1;
|
|
+
|
|
+ for (;;) {
|
|
+ while (take < give && phys_to_machine_mapping_valid(take))
|
|
+ take++;
|
|
+ for (; take < give; give--) {
|
|
+ if (!phys_to_machine_mapping_valid(give))
|
|
+ continue;
|
|
+ break;
|
|
+ }
|
|
+ if (take >= give)
|
|
+ break;
|
|
+ swap_pages_p2m(give--,take++);
|
|
+ count++;
|
|
+ }
|
|
+ if (!phys_to_machine_mapping_valid(take))
|
|
+ take--;
|
|
+
|
|
+ new_info->nr_pages = take+1;
|
|
+ dprintk("%s: %d swaps, nr_pages is 0x%lx (0x%lx max)\r\n",
|
|
+ __FUNCTION__, count, new_info->nr_pages, pfns);
|
|
+}
|
|
+
|
|
+static void fillup_memory_p2m(void)
|
|
+{
|
|
+ struct xen_memory_reservation reservation = {
|
|
+ .mem_flags = 0,
|
|
+ .extent_order = 0,
|
|
+ .nr_extents = 1,
|
|
+ .domid = DOMID_SELF,
|
|
+ };
|
|
+ unsigned long pfns = shared_info->arch.max_pfn;
|
|
+ unsigned long pfn, mfn;
|
|
+ int rc, count = 0;
|
|
+
|
|
+ for (pfn = new_info->nr_pages; pfn < pfns; count++, pfn++) {
|
|
+ set_xen_guest_handle(reservation.extent_start, &mfn);
|
|
+ rc = HYPERVISOR_memory_op(XENMEM_populate_physmap,
|
|
+ &reservation);
|
|
+ if (1 != rc)
|
|
+ break;
|
|
+ xen_machphys_update(mfn, pfn);
|
|
+ set_phys_to_machine(pfn, mfn);
|
|
+ }
|
|
+
|
|
+ new_info->nr_pages = pfn;
|
|
+ printk("%s: got %d pages, nr_pages is 0x%lx (0x%lx max)\r\n",
|
|
+ __FUNCTION__, count, new_info->nr_pages, pfns);
|
|
+}
|
|
+
|
|
+#define P2M_ENTRIES (PAGE_SIZE/sizeof(xen_pfn_t))
|
|
+#define mfn_to_addr(mfn) ((void*)(mfn_to_pfn(mfn) << PAGE_SHIFT))
|
|
+
|
|
+static void p2m_copy(void)
|
|
+{
|
|
+ xen_pfn_t *mfn_lol = NULL, *mfn_list = NULL;
|
|
+ unsigned int off_lol = 0, off_list = 0;
|
|
+ xen_pfn_t pfn;
|
|
+
|
|
+ phys_to_machine_mapping = (unsigned long*)(new_info->mfn_list - virt_base);
|
|
+ for (pfn = 0; pfn < shared_info->arch.max_pfn; pfn += P2M_ENTRIES) {
|
|
+ if (0 == pfn) {
|
|
+ mfn_lol = mfn_to_addr(shared_info->arch.pfn_to_mfn_frame_list_list);
|
|
+ off_lol = 0;
|
|
+ }
|
|
+ if (0 == (pfn % (P2M_ENTRIES * P2M_ENTRIES))) {
|
|
+ mfn_list = mfn_to_addr(mfn_lol[off_lol++]);
|
|
+ off_list = 0;
|
|
+ }
|
|
+ memcpy(phys_to_machine_mapping + pfn,
|
|
+ mfn_to_addr(mfn_list[off_list++]),
|
|
+ PAGE_SIZE);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void xen_tlb_flush(void)
|
|
+{
|
|
+ struct mmuext_op op;
|
|
+ op.cmd = MMUEXT_TLB_FLUSH_LOCAL;
|
|
+ HYPERVISOR_mmuext_op(&op, 1, NULL, DOMID_SELF);
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------------ */
|
|
+
|
|
+#if defined(__i386__)
|
|
+
|
|
+static void map_page_32(page_t page, unsigned long mfn, unsigned long flags)
|
|
+{
|
|
+ unsigned long vaddr = (unsigned long)page;
|
|
+ uint32_t *pgd = (void*)old_info->pt_base;
|
|
+ uint32_t *pte;
|
|
+ unsigned long pfn;
|
|
+ int pgd_off, pte_off;
|
|
+
|
|
+ pgd_off = (vaddr >> 22) & (1024-1);
|
|
+ pte_off = (vaddr >> 12) & (1024-1);
|
|
+
|
|
+ pfn = mfn_to_pfn(pgd[pgd_off] >> PAGE_SHIFT);
|
|
+ pte = (void*)(pfn << PAGE_SHIFT);
|
|
+
|
|
+ pte[pte_off] = (mfn << PAGE_SHIFT) | flags;
|
|
+}
|
|
+
|
|
+static void map_virt_base_32(void)
|
|
+{
|
|
+ mmu_update_t u;
|
|
+ xen_pfn_t pgd_mfn = pfn_to_mfn(old_info->pt_base >> PAGE_SHIFT);
|
|
+ uint32_t *pgd_ma = (void*)(pgd_mfn << PAGE_SHIFT);
|
|
+ uint32_t *pgd = (void*)old_info->pt_base;
|
|
+ uint32_t pgd_virt = virt_base >> 22;
|
|
+ uint32_t pgd_low = 0;
|
|
+
|
|
+ if (pgd_virt << 22 != virt_base) {
|
|
+ printk("%s: warning: virt_base is not at pgd entry border,\r\n"
|
|
+ " that will work only if old and new kernel have\r\n"
|
|
+ " an identical virt_base.\r\n", __FUNCTION__);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ for (;;) {
|
|
+ if (!(pgd[pgd_low] & _PAGE_PRESENT))
|
|
+ return;
|
|
+ u.ptr = (unsigned long)(pgd_ma + pgd_virt);
|
|
+ u.val = pgd[pgd_low];
|
|
+ if (HYPERVISOR_mmu_update(&u, 1, NULL, DOMID_SELF) < 0) {
|
|
+ printk("%s: mmu_update failed [l2 0x%x -> 0x%x]\r\n",
|
|
+ __FUNCTION__, pgd_low, pgd_virt);
|
|
+ return;
|
|
+ }
|
|
+ pgd_virt++;
|
|
+ pgd_low++;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void map_page_pae(page_t page, unsigned long mfn, unsigned long flags)
|
|
+{
|
|
+ unsigned long vaddr = (unsigned long)page;
|
|
+ uint64_t *pgd = (void*)old_info->pt_base;
|
|
+ uint64_t *pmd;
|
|
+ uint64_t *pte;
|
|
+ unsigned long pfn;
|
|
+ int pgd_off, pmd_off, pte_off;
|
|
+
|
|
+ pgd_off = (vaddr >> 30) & (4-1);
|
|
+ pmd_off = (vaddr >> 21) & (512-1);
|
|
+ pte_off = (vaddr >> 12) & (512-1);
|
|
+
|
|
+ pfn = mfn_to_pfn(pgd[pgd_off] >> PAGE_SHIFT);
|
|
+ pmd = (void*)(pfn << PAGE_SHIFT);
|
|
+ pfn = mfn_to_pfn(pmd[pmd_off] >> PAGE_SHIFT);
|
|
+ pte = (void*)(pfn << PAGE_SHIFT);
|
|
+
|
|
+ pte[pte_off] = (mfn << PAGE_SHIFT) | flags;
|
|
+}
|
|
+
|
|
+static void map_virt_base_pae(void)
|
|
+{
|
|
+ mmu_update_t u;
|
|
+ xen_pfn_t pgd_mfn = pfn_to_mfn(old_info->pt_base >> PAGE_SHIFT);
|
|
+ uint64_t *pgd_ma = (void*)(pgd_mfn << PAGE_SHIFT);
|
|
+ uint64_t *pgd = (void*)old_info->pt_base;
|
|
+ int i;
|
|
+
|
|
+ for (i = 1; i <= 2; i++) {
|
|
+ u.ptr = (unsigned long)(pgd_ma + i);
|
|
+ u.val = pgd[0];
|
|
+ if (HYPERVISOR_mmu_update(&u, 1, NULL, DOMID_SELF) < 0) {
|
|
+ printk("%s: mmu_update failed [l3 0 -> %d]\r\n",
|
|
+ __FUNCTION__, i);
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static void map_page(page_t page, unsigned long mfn, unsigned long flags)
|
|
+{
|
|
+ if (pae_paging)
|
|
+ map_page_pae(page, mfn, flags);
|
|
+ else
|
|
+ map_page_32(page, mfn, flags);
|
|
+ xen_tlb_flush();
|
|
+ xen_wmb();
|
|
+}
|
|
+
|
|
+static void map_virt_base(void)
|
|
+{
|
|
+ if (pae_paging)
|
|
+ map_virt_base_pae();
|
|
+ else
|
|
+ map_virt_base_32();
|
|
+ xen_tlb_flush();
|
|
+ xen_wmb();
|
|
+}
|
|
+
|
|
+static void fixup_pagetables_32(void)
|
|
+{
|
|
+ uint32_t *pgd = (void*)new_info->pt_base - virt_base;
|
|
+ uint32_t *pte;
|
|
+ uint32_t flg;
|
|
+ unsigned long pfn;
|
|
+ int l2, l1;
|
|
+
|
|
+ dprintk("%s: pgd at 0x%p\r\n", __FUNCTION__, pgd);
|
|
+ for (l2 = 0; l2 < 1024; l2++) {
|
|
+ if (!pgd[l2] & 1)
|
|
+ continue;
|
|
+ flg = pgd[l2] & (PAGE_SIZE-1);
|
|
+ pfn = pgd[l2] >> PAGE_SHIFT;
|
|
+ pgd[l2] = (pfn_to_mfn(pfn) << PAGE_SHIFT) | flg;
|
|
+
|
|
+#if 1
|
|
+ /* Dirty hack alert: Add identity map for first 4MB,
|
|
+ * so we don't kill ourself when activating the new
|
|
+ * kernel's page tables. May have the side effect of
|
|
+ * killing the kernel later. */
|
|
+ if (!pgd[0])
|
|
+ pgd[0] = pgd[l2];
|
|
+#endif
|
|
+
|
|
+ pte = (void*)(pfn << PAGE_SHIFT);
|
|
+ dprintk("%s: pte at 0x%p\r\n", __FUNCTION__, pte);
|
|
+ for (l1 = 0; l1 < 1024; l1++) {
|
|
+ if (!pte[l1] & 1)
|
|
+ continue;
|
|
+ flg = pte[l1] & (PAGE_SIZE-1);
|
|
+ pfn = pte[l1] >> PAGE_SHIFT;
|
|
+ pte[l1] = (pfn_to_mfn(pfn) << PAGE_SHIFT) | flg;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static void fixup_pagetables_pae(void)
|
|
+{
|
|
+ uint64_t *pgd = (void*)new_info->pt_base - virt_base;
|
|
+ uint64_t *pmd;
|
|
+ uint64_t *pte;
|
|
+ uint64_t flg;
|
|
+ unsigned long pfn;
|
|
+ int l3, l2, l1;
|
|
+
|
|
+ dprintk("%s: pgd at 0x%p\r\n", __FUNCTION__, pgd);
|
|
+ for (l3 = 0; l3 < 4; l3++) {
|
|
+ if (!pgd[l3] & 1)
|
|
+ continue;
|
|
+ flg = pgd[l3] & (PAGE_SIZE-1);
|
|
+ pfn = pgd[l3] >> PAGE_SHIFT;
|
|
+ pgd[l3] = (pfn_to_mfn(pfn) << PAGE_SHIFT) | flg;
|
|
+
|
|
+ pmd = (void*)(pfn << PAGE_SHIFT);
|
|
+ dprintk("%s: pmd at 0x%p\r\n", __FUNCTION__, pmd);
|
|
+ for (l2 = 0; l2 < 512; l2++) {
|
|
+ if (!pmd[l2] & 1)
|
|
+ continue;
|
|
+ flg = pmd[l2] & (PAGE_SIZE-1);
|
|
+ pfn = pmd[l2] >> PAGE_SHIFT;
|
|
+ pmd[l2] = (pfn_to_mfn(pfn) << PAGE_SHIFT) | flg;
|
|
+
|
|
+ pte = (void*)(pfn << PAGE_SHIFT);
|
|
+ dprintk("%s: pte at 0x%p\r\n", __FUNCTION__, pte);
|
|
+ for (l1 = 0; l1 < 512; l1++) {
|
|
+ if (!pte[l1] & 1)
|
|
+ continue;
|
|
+ flg = pte[l1] & (PAGE_SIZE-1);
|
|
+ pfn = pte[l1] >> PAGE_SHIFT;
|
|
+ pte[l1] = (pfn_to_mfn(pfn) << PAGE_SHIFT) | flg;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static void fixup_pagetables(void)
|
|
+{
|
|
+ if (pae_paging)
|
|
+ fixup_pagetables_pae();
|
|
+ else
|
|
+ fixup_pagetables_32();
|
|
+}
|
|
+
|
|
+#endif /* i386 */
|
|
+
|
|
+#if defined(__x86_64__)
|
|
+
|
|
+static void map_page(page_t page, unsigned long mfn, unsigned long flags)
|
|
+{
|
|
+ unsigned long vaddr = (unsigned long)page;
|
|
+ uint64_t *pgd = (void*)old_info->pt_base;
|
|
+ uint64_t *pud;
|
|
+ uint64_t *pmd;
|
|
+ uint64_t *pte;
|
|
+ xen_pfn_t pfn;
|
|
+ int pgd_off, pud_off, pmd_off, pte_off;
|
|
+
|
|
+ pgd_off = (vaddr >> 39) & (512-1);
|
|
+ pud_off = (vaddr >> 30) & (512-1);
|
|
+ pmd_off = (vaddr >> 21) & (512-1);
|
|
+ pte_off = (vaddr >> 12) & (512-1);
|
|
+
|
|
+ pfn = mfn_to_pfn(pgd[pgd_off] >> PAGE_SHIFT);
|
|
+ pud = (void*)(pfn << PAGE_SHIFT);
|
|
+ pfn = mfn_to_pfn(pud[pud_off] >> PAGE_SHIFT);
|
|
+ pmd = (void*)(pfn << PAGE_SHIFT);
|
|
+ pfn = mfn_to_pfn(pmd[pmd_off] >> PAGE_SHIFT);
|
|
+ pte = (void*)(pfn << PAGE_SHIFT);
|
|
+
|
|
+ pte[pte_off] = (mfn << PAGE_SHIFT) | flags;
|
|
+
|
|
+ xen_tlb_flush();
|
|
+ xen_wmb();
|
|
+}
|
|
+
|
|
+static void map_virt_base(void)
|
|
+{
|
|
+ mmu_update_t u;
|
|
+ xen_pfn_t pgd_mfn = pfn_to_mfn(old_info->pt_base >> PAGE_SHIFT);
|
|
+ uint64_t *pgd_ma = (void*)(pgd_mfn << PAGE_SHIFT);
|
|
+ uint64_t *pgd = (void*)old_info->pt_base;
|
|
+ uint64_t pgd_virt = (virt_base >> 39);
|
|
+ uint64_t pgd_low = 0;
|
|
+
|
|
+ if (pgd_virt << 39 != virt_base) {
|
|
+ printk("%s: warning: virt_base is not at pgd entry border,\r\n"
|
|
+ " that will work only if old and new kernel have\r\n"
|
|
+ " an identical virt_base.\r\n", __FUNCTION__);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ for (;;) {
|
|
+ if (!(pgd[pgd_low] & _PAGE_PRESENT))
|
|
+ break;
|
|
+ u.ptr = (unsigned long)(pgd_ma + pgd_virt);
|
|
+ u.val = pgd[pgd_low];
|
|
+ printk("%s: ptr 0x%" PRIx64" val 0x%" PRIx64"\r\n",
|
|
+ __FUNCTION__, u.ptr, u.val);
|
|
+ if (HYPERVISOR_mmu_update(&u, 1, NULL, DOMID_SELF) < 0) {
|
|
+ printk("%s: mmu_update failed [l4 0x%lx -> 0x%lx]\r\n",
|
|
+ __FUNCTION__, pgd_low, pgd_virt);
|
|
+ break;
|
|
+ }
|
|
+ pgd_virt++;
|
|
+ pgd_low++;
|
|
+ }
|
|
+ xen_tlb_flush();
|
|
+ xen_wmb();
|
|
+}
|
|
+
|
|
+static void fixup_pagetables(void)
|
|
+{
|
|
+ uint64_t *pgd = (void*)new_info->pt_base - virt_base;
|
|
+ uint64_t *pud;
|
|
+ uint64_t *pmd;
|
|
+ uint64_t *pte;
|
|
+ uint64_t flg;
|
|
+ unsigned long pfn;
|
|
+ int l4, l3, l2, l1;
|
|
+
|
|
+ dprintk("%s: pgd at 0x%p\r\n", __FUNCTION__, pgd);
|
|
+ for (l4 = 0; l4 < 512; l4++) {
|
|
+ if (!pgd[l4] & 1)
|
|
+ continue;
|
|
+ flg = pgd[l4] & (PAGE_SIZE-1);
|
|
+ pfn = pgd[l4] >> PAGE_SHIFT;
|
|
+ pgd[l4] = (pfn_to_mfn(pfn) << PAGE_SHIFT) | flg;
|
|
+
|
|
+ pud = (void*)(pfn << PAGE_SHIFT);
|
|
+ dprintk("%s: pud at 0x%p\r\n", __FUNCTION__, pud);
|
|
+ for (l3 = 0; l3 < 512; l3++) {
|
|
+ if (!pud[l3] & 1)
|
|
+ continue;
|
|
+ flg = pud[l3] & (PAGE_SIZE-1);
|
|
+ pfn = pud[l3] >> PAGE_SHIFT;
|
|
+ pud[l3] = (pfn_to_mfn(pfn) << PAGE_SHIFT) | flg;
|
|
+
|
|
+ pmd = (void*)(pfn << PAGE_SHIFT);
|
|
+ dprintk("%s: pmd at 0x%p\r\n", __FUNCTION__, pmd);
|
|
+ for (l2 = 0; l2 < 512; l2++) {
|
|
+ if (!pmd[l2] & 1)
|
|
+ continue;
|
|
+ flg = pmd[l2] & (PAGE_SIZE-1);
|
|
+ pfn = pmd[l2] >> PAGE_SHIFT;
|
|
+ pmd[l2] = (pfn_to_mfn(pfn) << PAGE_SHIFT) | flg;
|
|
+
|
|
+ pte = (void*)(pfn << PAGE_SHIFT);
|
|
+ dprintk("%s: pte at 0x%p\r\n", __FUNCTION__, pte);
|
|
+ for (l1 = 0; l1 < 512; l1++) {
|
|
+ if (!pte[l1] & 1)
|
|
+ continue;
|
|
+ flg = pte[l1] & (PAGE_SIZE-1);
|
|
+ pfn = pte[l1] >> PAGE_SHIFT;
|
|
+ pte[l1] = (pfn_to_mfn(pfn) << PAGE_SHIFT) | flg;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+#endif /* x86_64 */
|
|
+
|
|
+/* ------------------------------------------------------------------ */
|
|
+
|
|
+static void print_start_info(char *name, struct start_info *info)
|
|
+{
|
|
+ if (!debug_level)
|
|
+ return;
|
|
+ printk("%s start_info page @ %p\r\n", name, info);
|
|
+ printk(" magic \"%s\"\r\n", info->magic);
|
|
+ printk(" nr_pages 0x%lx\r\n", info->nr_pages);
|
|
+ printk(" shared_info 0x%lx\r\n", info->shared_info);
|
|
+ printk(" flags 0x%x\r\n", info->flags);
|
|
+ printk(" store_mfn 0x%lx\r\n", info->store_mfn);
|
|
+ printk(" store_evtchn %d\r\n", info->store_evtchn);
|
|
+ printk(" console.mfn 0x%lx\r\n", info->console.domU.mfn);
|
|
+ printk(" console.evtchn %d\r\n", info->console.domU.evtchn);
|
|
+ printk(" pt_base 0x%lx\r\n", info->pt_base);
|
|
+ printk(" nr_pt_frames 0x%lx\r\n", info->nr_pt_frames);
|
|
+ printk(" mfn_list 0x%lx\r\n", info->mfn_list);
|
|
+ printk(" mod_start 0x%lx\r\n", info->mod_start);
|
|
+ printk(" mod_len 0x%lx\r\n", info->mod_len);
|
|
+ printk(" cmd_line \"%s\"\r\n", info->cmd_line);
|
|
+ printk("\r\n");
|
|
+}
|
|
+
|
|
+void start_helper(void)
|
|
+{
|
|
+ int shadow_translated;
|
|
+ unsigned long cr3_mfn;
|
|
+
|
|
+ /* find m2p map */
|
|
+#if defined(__i386__)
|
|
+ if (pae_paging) {
|
|
+ machine_to_phys_mapping = (xen_pfn_t*)0xf5800000;
|
|
+ } else {
|
|
+ machine_to_phys_mapping = (xen_pfn_t*)0xfc000000;
|
|
+ }
|
|
+#endif
|
|
+#if defined(__x86_64__)
|
|
+ machine_to_phys_mapping = ((xen_pfn_t*)HYPERVISOR_VIRT_START);
|
|
+#endif
|
|
+
|
|
+ /* map magic pages */
|
|
+ map_page(console_page, old_info->console.domU.mfn,
|
|
+ _PAGE_PRESENT | _PAGE_RW);
|
|
+ map_page(xenstore_page, old_info->store_mfn,
|
|
+ _PAGE_PRESENT | _PAGE_RW);
|
|
+ map_page(shared_info_page, old_info->shared_info >> PAGE_SHIFT,
|
|
+ _PAGE_PRESENT | _PAGE_RW);
|
|
+
|
|
+ /* setup console, say hello world */
|
|
+ console_evtchn = old_info->console.domU.evtchn;
|
|
+ printk("\r\nHello world from xen kexec helper\r\n\r\n");
|
|
+
|
|
+ /* print old start info page */
|
|
+ print_start_info("old", old_info);
|
|
+
|
|
+ /* figure and print some info */
|
|
+ setup_xen_features();
|
|
+ shadow_translated = xen_feature(XENFEAT_auto_translated_physmap);
|
|
+ dprintk("shadow_translated is %s\r\n",
|
|
+ shadow_translated ? "on" : "off");
|
|
+#if defined(__i386__)
|
|
+ dprintk("pae paging is %s\r\n",
|
|
+ pae_paging ? "on" : "off");
|
|
+#endif
|
|
+ dprintk("virtual base is 0x%lx\r\n", virt_base);
|
|
+ if (-1 != virt_hypercall)
|
|
+ dprintk("hypercall page at is 0x%lx\r\n", virt_hypercall);
|
|
+ dprintk("kernel entry point is 0x%lx\r\n", (unsigned long)vcpu.user_regs.eip);
|
|
+ dprintk("start_info page is 0x%lx\r\n", (unsigned long)vcpu.user_regs.esi);
|
|
+ dprintk("kernel boot stack is 0x%lx\r\n", (unsigned long)vcpu.user_regs.esp);
|
|
+ dprintk("\r\n");
|
|
+
|
|
+// xenstore_debug();
|
|
+
|
|
+ /* fixup new start info page */
|
|
+ new_info = (struct start_info*)(vcpu.user_regs.esi - virt_base);
|
|
+ new_info->shared_info = old_info->shared_info;
|
|
+ new_info->store_mfn = old_info->store_mfn;
|
|
+ new_info->store_evtchn = old_info->store_evtchn;
|
|
+ new_info->console.domU.mfn = old_info->console.domU.mfn;
|
|
+ new_info->console.domU.evtchn = old_info->console.domU.evtchn;
|
|
+
|
|
+ if (shadow_translated) {
|
|
+ printk("TODO: handle magic pages\r\n");
|
|
+ cr3_mfn = (new_info->pt_base - virt_base) >> PAGE_SHIFT;
|
|
+ } else {
|
|
+ /* copy p2m table to final place */
|
|
+ dprintk("copy p2m map\r\n");
|
|
+ p2m_copy();
|
|
+
|
|
+ /* move magic pages, page order is important here:
|
|
+ * start_info, xenstore, console */
|
|
+ dprintk("setup magic pages: xenstore and console\r\n");
|
|
+ swap_pages_p2m(mfn_to_pfn(new_info->store_mfn),
|
|
+ ((unsigned long)new_info >> PAGE_SHIFT)+1);
|
|
+ swap_pages_p2m(mfn_to_pfn(new_info->console.domU.mfn),
|
|
+ ((unsigned long)new_info >> PAGE_SHIFT)+2);
|
|
+
|
|
+ if (-1 != virt_hypercall) {
|
|
+ dprintk("setup hypercall page\r\n");
|
|
+ memcpy((void*)(virt_hypercall - virt_base),
|
|
+ hypercall_page, PAGE_SIZE);
|
|
+ }
|
|
+
|
|
+ dprintk("setup main memory\r\n");
|
|
+ plug_holes_p2m();
|
|
+ fillup_memory_p2m();
|
|
+
|
|
+ dprintk("mirror lowmem at virt_base [0x%lx]\r\n", virt_base);
|
|
+ map_virt_base();
|
|
+ start_kernel = _start_kernel + virt_base;
|
|
+
|
|
+ dprintk("setup new kernel's page tables\r\n");
|
|
+ fixup_pagetables();
|
|
+ cr3_mfn = pfn_to_mfn((new_info->pt_base - virt_base) >> PAGE_SHIFT);
|
|
+ }
|
|
+ printk("\r\n");
|
|
+
|
|
+ /* print new start info page */
|
|
+ print_start_info("new", new_info);
|
|
+
|
|
+ /* boot kernel */
|
|
+ vcpu.ctrlreg[3] = cr3_mfn << PAGE_SHIFT;
|
|
+ printk("All done, bye folks, trying to boot the kernel ...\r\n");
|
|
+ printk("\r\n");
|
|
+ start_kernel();
|
|
+}
|
|
Index: xen-3.3.1-testing/tools/xcutils/helper/make-offsets.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ xen-3.3.1-testing/tools/xcutils/helper/make-offsets.c
|
|
@@ -0,0 +1,28 @@
|
|
+#include <stdio.h>
|
|
+#include <xenctrl.h>
|
|
+
|
|
+#define vcpu_off(name,elem) printf("#define vcpu_%s 0x%lx\n", name, \
|
|
+ (unsigned long)offsetof(struct vcpu_guest_context, elem))
|
|
+
|
|
+int main(int argc, char **argv)
|
|
+{
|
|
+ vcpu_off("eax", user_regs.eax);
|
|
+ vcpu_off("ebx", user_regs.ebx);
|
|
+ vcpu_off("ecx", user_regs.ecx);
|
|
+ vcpu_off("edx", user_regs.edx);
|
|
+ vcpu_off("esi", user_regs.esi);
|
|
+ vcpu_off("edi", user_regs.edi);
|
|
+ vcpu_off("ebp", user_regs.ebp);
|
|
+ vcpu_off("esp", user_regs.esp);
|
|
+
|
|
+ vcpu_off("cs", user_regs.cs);
|
|
+ vcpu_off("ds", user_regs.ds);
|
|
+ vcpu_off("es", user_regs.es);
|
|
+ vcpu_off("fs", user_regs.fs);
|
|
+ vcpu_off("gs", user_regs.gs);
|
|
+ vcpu_off("ss", user_regs.ss);
|
|
+
|
|
+ vcpu_off("eip", user_regs.eip);
|
|
+ vcpu_off("cr3", ctrlreg[3]);
|
|
+ return 0;
|
|
+}
|
|
Index: xen-3.3.1-testing/tools/xcutils/helper/printk.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ xen-3.3.1-testing/tools/xcutils/helper/printk.c
|
|
@@ -0,0 +1,1051 @@
|
|
+/*
|
|
+ * linux/kernel/printk.c
|
|
+ *
|
|
+ * Copyright (C) 1991, 1992 Linus Torvalds
|
|
+ *
|
|
+ * Modified to make sys_syslog() more flexible: added commands to
|
|
+ * return the last 4k of kernel messages, regardless of whether
|
|
+ * they've been read or not. Added option to suppress kernel printk's
|
|
+ * to the console. Added hook for sending the console messages
|
|
+ * elsewhere, in preparation for a serial line console (someday).
|
|
+ * Ted Ts'o, 2/11/93.
|
|
+ * Modified for sysctl support, 1/8/97, Chris Horn.
|
|
+ * Fixed SMP synchronization, 08/08/99, Manfred Spraul
|
|
+ * manfreds@colorfullife.com
|
|
+ * Rewrote bits to get rid of console_lock
|
|
+ * 01Mar01 Andrew Morton <andrewm@uow.edu.au>
|
|
+ */
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/mm.h>
|
|
+#include <linux/tty.h>
|
|
+#include <linux/tty_driver.h>
|
|
+#include <linux/smp_lock.h>
|
|
+#include <linux/console.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/interrupt.h> /* For in_interrupt() */
|
|
+#include <linux/config.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/smp.h>
|
|
+#include <linux/security.h>
|
|
+#include <linux/bootmem.h>
|
|
+#include <linux/syscalls.h>
|
|
+
|
|
+#include <asm/uaccess.h>
|
|
+
|
|
+#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
|
|
+
|
|
+/* printk's without a loglevel use this.. */
|
|
+#define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING */
|
|
+
|
|
+/* We show everything that is MORE important than this.. */
|
|
+#define MINIMUM_CONSOLE_LOGLEVEL 1 /* Minimum loglevel we let people use */
|
|
+#define DEFAULT_CONSOLE_LOGLEVEL 7 /* anything MORE serious than KERN_DEBUG */
|
|
+
|
|
+DECLARE_WAIT_QUEUE_HEAD(log_wait);
|
|
+
|
|
+int console_printk[4] = {
|
|
+ DEFAULT_CONSOLE_LOGLEVEL, /* console_loglevel */
|
|
+ DEFAULT_MESSAGE_LOGLEVEL, /* default_message_loglevel */
|
|
+ MINIMUM_CONSOLE_LOGLEVEL, /* minimum_console_loglevel */
|
|
+ DEFAULT_CONSOLE_LOGLEVEL, /* default_console_loglevel */
|
|
+};
|
|
+
|
|
+EXPORT_SYMBOL(console_printk);
|
|
+
|
|
+/*
|
|
+ * Low lever drivers may need that to know if they can schedule in
|
|
+ * their unblank() callback or not. So let's export it.
|
|
+ */
|
|
+int oops_in_progress;
|
|
+EXPORT_SYMBOL(oops_in_progress);
|
|
+
|
|
+/*
|
|
+ * console_sem protects the console_drivers list, and also
|
|
+ * provides serialisation for access to the entire console
|
|
+ * driver system.
|
|
+ */
|
|
+static DECLARE_MUTEX(console_sem);
|
|
+struct console *console_drivers;
|
|
+/*
|
|
+ * This is used for debugging the mess that is the VT code by
|
|
+ * keeping track if we have the console semaphore held. It's
|
|
+ * definitely not the perfect debug tool (we don't know if _WE_
|
|
+ * hold it are racing, but it helps tracking those weird code
|
|
+ * path in the console code where we end up in places I want
|
|
+ * locked without the console sempahore held
|
|
+ */
|
|
+static int console_locked;
|
|
+
|
|
+/*
|
|
+ * logbuf_lock protects log_buf, log_start, log_end, con_start and logged_chars
|
|
+ * It is also used in interesting ways to provide interlocking in
|
|
+ * release_console_sem().
|
|
+ */
|
|
+static DEFINE_SPINLOCK(logbuf_lock);
|
|
+
|
|
+#define LOG_BUF_MASK (log_buf_len-1)
|
|
+#define LOG_BUF(idx) (log_buf[(idx) & LOG_BUF_MASK])
|
|
+
|
|
+/*
|
|
+ * The indices into log_buf are not constrained to log_buf_len - they
|
|
+ * must be masked before subscripting
|
|
+ */
|
|
+static unsigned long log_start; /* Index into log_buf: next char to be read by syslog() */
|
|
+static unsigned long con_start; /* Index into log_buf: next char to be sent to consoles */
|
|
+static unsigned long log_end; /* Index into log_buf: most-recently-written-char + 1 */
|
|
+
|
|
+/*
|
|
+ * Array of consoles built from command line options (console=)
|
|
+ */
|
|
+struct console_cmdline
|
|
+{
|
|
+ char name[8]; /* Name of the driver */
|
|
+ int index; /* Minor dev. to use */
|
|
+ char *options; /* Options for the driver */
|
|
+};
|
|
+
|
|
+#define MAX_CMDLINECONSOLES 8
|
|
+
|
|
+static struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES];
|
|
+static int selected_console = -1;
|
|
+static int preferred_console = -1;
|
|
+
|
|
+/* Flag: console code may call schedule() */
|
|
+static int console_may_schedule;
|
|
+
|
|
+#ifdef CONFIG_PRINTK
|
|
+
|
|
+static char __log_buf[__LOG_BUF_LEN];
|
|
+static char *log_buf = __log_buf;
|
|
+static int log_buf_len = __LOG_BUF_LEN;
|
|
+static unsigned long logged_chars; /* Number of chars produced since last read+clear operation */
|
|
+
|
|
+/*
|
|
+ * Setup a list of consoles. Called from init/main.c
|
|
+ */
|
|
+static int __init console_setup(char *str)
|
|
+{
|
|
+ char name[sizeof(console_cmdline[0].name)];
|
|
+ char *s, *options;
|
|
+ int idx;
|
|
+
|
|
+ /*
|
|
+ * Decode str into name, index, options.
|
|
+ */
|
|
+ if (str[0] >= '0' && str[0] <= '9') {
|
|
+ strcpy(name, "ttyS");
|
|
+ strncpy(name + 4, str, sizeof(name) - 5);
|
|
+ } else
|
|
+ strncpy(name, str, sizeof(name) - 1);
|
|
+ name[sizeof(name) - 1] = 0;
|
|
+ if ((options = strchr(str, ',')) != NULL)
|
|
+ *(options++) = 0;
|
|
+#ifdef __sparc__
|
|
+ if (!strcmp(str, "ttya"))
|
|
+ strcpy(name, "ttyS0");
|
|
+ if (!strcmp(str, "ttyb"))
|
|
+ strcpy(name, "ttyS1");
|
|
+#endif
|
|
+ for (s = name; *s; s++)
|
|
+ if ((*s >= '0' && *s <= '9') || *s == ',')
|
|
+ break;
|
|
+ idx = simple_strtoul(s, NULL, 10);
|
|
+ *s = 0;
|
|
+
|
|
+ add_preferred_console(name, idx, options);
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+__setup("console=", console_setup);
|
|
+
|
|
+static int __init log_buf_len_setup(char *str)
|
|
+{
|
|
+ unsigned long size = memparse(str, &str);
|
|
+ unsigned long flags;
|
|
+
|
|
+ if (size)
|
|
+ size = roundup_pow_of_two(size);
|
|
+ if (size > log_buf_len) {
|
|
+ unsigned long start, dest_idx, offset;
|
|
+ char *new_log_buf;
|
|
+
|
|
+ new_log_buf = alloc_bootmem(size);
|
|
+ if (!new_log_buf) {
|
|
+ printk(KERN_WARNING "log_buf_len: allocation failed\n");
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ spin_lock_irqsave(&logbuf_lock, flags);
|
|
+ log_buf_len = size;
|
|
+ log_buf = new_log_buf;
|
|
+
|
|
+ offset = start = min(con_start, log_start);
|
|
+ dest_idx = 0;
|
|
+ while (start != log_end) {
|
|
+ log_buf[dest_idx] = __log_buf[start & (__LOG_BUF_LEN - 1)];
|
|
+ start++;
|
|
+ dest_idx++;
|
|
+ }
|
|
+ log_start -= offset;
|
|
+ con_start -= offset;
|
|
+ log_end -= offset;
|
|
+ spin_unlock_irqrestore(&logbuf_lock, flags);
|
|
+
|
|
+ printk(KERN_NOTICE "log_buf_len: %d\n", log_buf_len);
|
|
+ }
|
|
+out:
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+__setup("log_buf_len=", log_buf_len_setup);
|
|
+
|
|
+/*
|
|
+ * Commands to do_syslog:
|
|
+ *
|
|
+ * 0 -- Close the log. Currently a NOP.
|
|
+ * 1 -- Open the log. Currently a NOP.
|
|
+ * 2 -- Read from the log.
|
|
+ * 3 -- Read all messages remaining in the ring buffer.
|
|
+ * 4 -- Read and clear all messages remaining in the ring buffer
|
|
+ * 5 -- Clear ring buffer.
|
|
+ * 6 -- Disable printk's to console
|
|
+ * 7 -- Enable printk's to console
|
|
+ * 8 -- Set level of messages printed to console
|
|
+ * 9 -- Return number of unread characters in the log buffer
|
|
+ * 10 -- Return size of the log buffer
|
|
+ */
|
|
+int do_syslog(int type, char __user *buf, int len)
|
|
+{
|
|
+ unsigned long i, j, limit, count;
|
|
+ int do_clear = 0;
|
|
+ char c;
|
|
+ int error = 0;
|
|
+
|
|
+ error = security_syslog(type);
|
|
+ if (error)
|
|
+ return error;
|
|
+
|
|
+ switch (type) {
|
|
+ case 0: /* Close log */
|
|
+ break;
|
|
+ case 1: /* Open log */
|
|
+ break;
|
|
+ case 2: /* Read from log */
|
|
+ error = -EINVAL;
|
|
+ if (!buf || len < 0)
|
|
+ goto out;
|
|
+ error = 0;
|
|
+ if (!len)
|
|
+ goto out;
|
|
+ if (!access_ok(VERIFY_WRITE, buf, len)) {
|
|
+ error = -EFAULT;
|
|
+ goto out;
|
|
+ }
|
|
+ error = wait_event_interruptible(log_wait,
|
|
+ (log_start - log_end));
|
|
+ if (error)
|
|
+ goto out;
|
|
+ i = 0;
|
|
+ spin_lock_irq(&logbuf_lock);
|
|
+ while (!error && (log_start != log_end) && i < len) {
|
|
+ c = LOG_BUF(log_start);
|
|
+ log_start++;
|
|
+ spin_unlock_irq(&logbuf_lock);
|
|
+ error = __put_user(c,buf);
|
|
+ buf++;
|
|
+ i++;
|
|
+ cond_resched();
|
|
+ spin_lock_irq(&logbuf_lock);
|
|
+ }
|
|
+ spin_unlock_irq(&logbuf_lock);
|
|
+ if (!error)
|
|
+ error = i;
|
|
+ break;
|
|
+ case 4: /* Read/clear last kernel messages */
|
|
+ do_clear = 1;
|
|
+ /* FALL THRU */
|
|
+ case 3: /* Read last kernel messages */
|
|
+ error = -EINVAL;
|
|
+ if (!buf || len < 0)
|
|
+ goto out;
|
|
+ error = 0;
|
|
+ if (!len)
|
|
+ goto out;
|
|
+ if (!access_ok(VERIFY_WRITE, buf, len)) {
|
|
+ error = -EFAULT;
|
|
+ goto out;
|
|
+ }
|
|
+ count = len;
|
|
+ if (count > log_buf_len)
|
|
+ count = log_buf_len;
|
|
+ spin_lock_irq(&logbuf_lock);
|
|
+ if (count > logged_chars)
|
|
+ count = logged_chars;
|
|
+ if (do_clear)
|
|
+ logged_chars = 0;
|
|
+ limit = log_end;
|
|
+ /*
|
|
+ * __put_user() could sleep, and while we sleep
|
|
+ * printk() could overwrite the messages
|
|
+ * we try to copy to user space. Therefore
|
|
+ * the messages are copied in reverse. <manfreds>
|
|
+ */
|
|
+ for (i = 0; i < count && !error; i++) {
|
|
+ j = limit-1-i;
|
|
+ if (j + log_buf_len < log_end)
|
|
+ break;
|
|
+ c = LOG_BUF(j);
|
|
+ spin_unlock_irq(&logbuf_lock);
|
|
+ error = __put_user(c,&buf[count-1-i]);
|
|
+ cond_resched();
|
|
+ spin_lock_irq(&logbuf_lock);
|
|
+ }
|
|
+ spin_unlock_irq(&logbuf_lock);
|
|
+ if (error)
|
|
+ break;
|
|
+ error = i;
|
|
+ if (i != count) {
|
|
+ int offset = count-error;
|
|
+ /* buffer overflow during copy, correct user buffer. */
|
|
+ for (i = 0; i < error; i++) {
|
|
+ if (__get_user(c,&buf[i+offset]) ||
|
|
+ __put_user(c,&buf[i])) {
|
|
+ error = -EFAULT;
|
|
+ break;
|
|
+ }
|
|
+ cond_resched();
|
|
+ }
|
|
+ }
|
|
+ break;
|
|
+ case 5: /* Clear ring buffer */
|
|
+ logged_chars = 0;
|
|
+ break;
|
|
+ case 6: /* Disable logging to console */
|
|
+ console_loglevel = minimum_console_loglevel;
|
|
+ break;
|
|
+ case 7: /* Enable logging to console */
|
|
+ console_loglevel = default_console_loglevel;
|
|
+ break;
|
|
+ case 8: /* Set level of messages printed to console */
|
|
+ error = -EINVAL;
|
|
+ if (len < 1 || len > 8)
|
|
+ goto out;
|
|
+ if (len < minimum_console_loglevel)
|
|
+ len = minimum_console_loglevel;
|
|
+ console_loglevel = len;
|
|
+ error = 0;
|
|
+ break;
|
|
+ case 9: /* Number of chars in the log buffer */
|
|
+ error = log_end - log_start;
|
|
+ break;
|
|
+ case 10: /* Size of the log buffer */
|
|
+ error = log_buf_len;
|
|
+ break;
|
|
+ default:
|
|
+ error = -EINVAL;
|
|
+ break;
|
|
+ }
|
|
+out:
|
|
+ return error;
|
|
+}
|
|
+
|
|
+asmlinkage long sys_syslog(int type, char __user *buf, int len)
|
|
+{
|
|
+ return do_syslog(type, buf, len);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Call the console drivers on a range of log_buf
|
|
+ */
|
|
+static void __call_console_drivers(unsigned long start, unsigned long end)
|
|
+{
|
|
+ struct console *con;
|
|
+
|
|
+ for (con = console_drivers; con; con = con->next) {
|
|
+ if ((con->flags & CON_ENABLED) && con->write)
|
|
+ con->write(con, &LOG_BUF(start), end - start);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Write out chars from start to end - 1 inclusive
|
|
+ */
|
|
+static void _call_console_drivers(unsigned long start,
|
|
+ unsigned long end, int msg_log_level)
|
|
+{
|
|
+ if (msg_log_level < console_loglevel &&
|
|
+ console_drivers && start != end) {
|
|
+ if ((start & LOG_BUF_MASK) > (end & LOG_BUF_MASK)) {
|
|
+ /* wrapped write */
|
|
+ __call_console_drivers(start & LOG_BUF_MASK,
|
|
+ log_buf_len);
|
|
+ __call_console_drivers(0, end & LOG_BUF_MASK);
|
|
+ } else {
|
|
+ __call_console_drivers(start, end);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Call the console drivers, asking them to write out
|
|
+ * log_buf[start] to log_buf[end - 1].
|
|
+ * The console_sem must be held.
|
|
+ */
|
|
+static void call_console_drivers(unsigned long start, unsigned long end)
|
|
+{
|
|
+ unsigned long cur_index, start_print;
|
|
+ static int msg_level = -1;
|
|
+
|
|
+ if (((long)(start - end)) > 0)
|
|
+ BUG();
|
|
+
|
|
+ cur_index = start;
|
|
+ start_print = start;
|
|
+ while (cur_index != end) {
|
|
+ if (msg_level < 0 && ((end - cur_index) > 2) &&
|
|
+ LOG_BUF(cur_index + 0) == '<' &&
|
|
+ LOG_BUF(cur_index + 1) >= '0' &&
|
|
+ LOG_BUF(cur_index + 1) <= '7' &&
|
|
+ LOG_BUF(cur_index + 2) == '>') {
|
|
+ msg_level = LOG_BUF(cur_index + 1) - '0';
|
|
+ cur_index += 3;
|
|
+ start_print = cur_index;
|
|
+ }
|
|
+ while (cur_index != end) {
|
|
+ char c = LOG_BUF(cur_index);
|
|
+
|
|
+ cur_index++;
|
|
+ if (c == '\n') {
|
|
+ if (msg_level < 0) {
|
|
+ /*
|
|
+ * printk() has already given us loglevel tags in
|
|
+ * the buffer. This code is here in case the
|
|
+ * log buffer has wrapped right round and scribbled
|
|
+ * on those tags
|
|
+ */
|
|
+ msg_level = default_message_loglevel;
|
|
+ }
|
|
+ _call_console_drivers(start_print, cur_index, msg_level);
|
|
+ msg_level = -1;
|
|
+ start_print = cur_index;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ _call_console_drivers(start_print, end, msg_level);
|
|
+}
|
|
+
|
|
+static void emit_log_char(char c)
|
|
+{
|
|
+ LOG_BUF(log_end) = c;
|
|
+ log_end++;
|
|
+ if (log_end - log_start > log_buf_len)
|
|
+ log_start = log_end - log_buf_len;
|
|
+ if (log_end - con_start > log_buf_len)
|
|
+ con_start = log_end - log_buf_len;
|
|
+ if (logged_chars < log_buf_len)
|
|
+ logged_chars++;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Zap console related locks when oopsing. Only zap at most once
|
|
+ * every 10 seconds, to leave time for slow consoles to print a
|
|
+ * full oops.
|
|
+ */
|
|
+static void zap_locks(void)
|
|
+{
|
|
+ static unsigned long oops_timestamp;
|
|
+
|
|
+ if (time_after_eq(jiffies, oops_timestamp) &&
|
|
+ !time_after(jiffies, oops_timestamp + 30 * HZ))
|
|
+ return;
|
|
+
|
|
+ oops_timestamp = jiffies;
|
|
+
|
|
+ /* If a crash is occurring, make sure we can't deadlock */
|
|
+ spin_lock_init(&logbuf_lock);
|
|
+ /* And make sure that we print immediately */
|
|
+ init_MUTEX(&console_sem);
|
|
+}
|
|
+
|
|
+#if defined(CONFIG_PRINTK_TIME)
|
|
+static int printk_time = 1;
|
|
+#else
|
|
+static int printk_time = 0;
|
|
+#endif
|
|
+
|
|
+static int __init printk_time_setup(char *str)
|
|
+{
|
|
+ if (*str)
|
|
+ return 0;
|
|
+ printk_time = 1;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+__setup("time", printk_time_setup);
|
|
+
|
|
+__attribute__((weak)) unsigned long long printk_clock(void)
|
|
+{
|
|
+ return sched_clock();
|
|
+}
|
|
+
|
|
+/**
|
|
+ * printk - print a kernel message
|
|
+ * @fmt: format string
|
|
+ *
|
|
+ * This is printk. It can be called from any context. We want it to work.
|
|
+ *
|
|
+ * We try to grab the console_sem. If we succeed, it's easy - we log the output and
|
|
+ * call the console drivers. If we fail to get the semaphore we place the output
|
|
+ * into the log buffer and return. The current holder of the console_sem will
|
|
+ * notice the new output in release_console_sem() and will send it to the
|
|
+ * consoles before releasing the semaphore.
|
|
+ *
|
|
+ * One effect of this deferred printing is that code which calls printk() and
|
|
+ * then changes console_loglevel may break. This is because console_loglevel
|
|
+ * is inspected when the actual printing occurs.
|
|
+ *
|
|
+ * See also:
|
|
+ * printf(3)
|
|
+ */
|
|
+
|
|
+asmlinkage int printk(const char *fmt, ...)
|
|
+{
|
|
+ va_list args;
|
|
+ int r;
|
|
+
|
|
+ va_start(args, fmt);
|
|
+ r = vprintk(fmt, args);
|
|
+ va_end(args);
|
|
+
|
|
+ return r;
|
|
+}
|
|
+
|
|
+/* cpu currently holding logbuf_lock */
|
|
+static volatile unsigned int printk_cpu = UINT_MAX;
|
|
+
|
|
+asmlinkage int vprintk(const char *fmt, va_list args)
|
|
+{
|
|
+ unsigned long flags;
|
|
+ int printed_len;
|
|
+ char *p;
|
|
+ static char printk_buf[1024];
|
|
+ static int log_level_unknown = 1;
|
|
+
|
|
+ preempt_disable();
|
|
+ if (unlikely(oops_in_progress) && printk_cpu == smp_processor_id())
|
|
+ /* If a crash is occurring during printk() on this CPU,
|
|
+ * make sure we can't deadlock */
|
|
+ zap_locks();
|
|
+
|
|
+ /* This stops the holder of console_sem just where we want him */
|
|
+ spin_lock_irqsave(&logbuf_lock, flags);
|
|
+ printk_cpu = smp_processor_id();
|
|
+
|
|
+ /* Emit the output into the temporary buffer */
|
|
+ printed_len = vscnprintf(printk_buf, sizeof(printk_buf), fmt, args);
|
|
+
|
|
+ /*
|
|
+ * Copy the output into log_buf. If the caller didn't provide
|
|
+ * appropriate log level tags, we insert them here
|
|
+ */
|
|
+ for (p = printk_buf; *p; p++) {
|
|
+ if (log_level_unknown) {
|
|
+ /* log_level_unknown signals the start of a new line */
|
|
+ if (printk_time) {
|
|
+ int loglev_char;
|
|
+ char tbuf[50], *tp;
|
|
+ unsigned tlen;
|
|
+ unsigned long long t;
|
|
+ unsigned long nanosec_rem;
|
|
+
|
|
+ /*
|
|
+ * force the log level token to be
|
|
+ * before the time output.
|
|
+ */
|
|
+ if (p[0] == '<' && p[1] >='0' &&
|
|
+ p[1] <= '7' && p[2] == '>') {
|
|
+ loglev_char = p[1];
|
|
+ p += 3;
|
|
+ printed_len += 3;
|
|
+ } else {
|
|
+ loglev_char = default_message_loglevel
|
|
+ + '0';
|
|
+ }
|
|
+ t = printk_clock();
|
|
+ nanosec_rem = do_div(t, 1000000000);
|
|
+ tlen = sprintf(tbuf,
|
|
+ "<%c>[%5lu.%06lu] ",
|
|
+ loglev_char,
|
|
+ (unsigned long)t,
|
|
+ nanosec_rem/1000);
|
|
+
|
|
+ for (tp = tbuf; tp < tbuf + tlen; tp++)
|
|
+ emit_log_char(*tp);
|
|
+ printed_len += tlen - 3;
|
|
+ } else {
|
|
+ if (p[0] != '<' || p[1] < '0' ||
|
|
+ p[1] > '7' || p[2] != '>') {
|
|
+ emit_log_char('<');
|
|
+ emit_log_char(default_message_loglevel
|
|
+ + '0');
|
|
+ emit_log_char('>');
|
|
+ }
|
|
+ printed_len += 3;
|
|
+ }
|
|
+ log_level_unknown = 0;
|
|
+ if (!*p)
|
|
+ break;
|
|
+ }
|
|
+ emit_log_char(*p);
|
|
+ if (*p == '\n')
|
|
+ log_level_unknown = 1;
|
|
+ }
|
|
+
|
|
+ if (!cpu_online(smp_processor_id())) {
|
|
+ /*
|
|
+ * Some console drivers may assume that per-cpu resources have
|
|
+ * been allocated. So don't allow them to be called by this
|
|
+ * CPU until it is officially up. We shouldn't be calling into
|
|
+ * random console drivers on a CPU which doesn't exist yet..
|
|
+ */
|
|
+ printk_cpu = UINT_MAX;
|
|
+ spin_unlock_irqrestore(&logbuf_lock, flags);
|
|
+ goto out;
|
|
+ }
|
|
+ if (!down_trylock(&console_sem)) {
|
|
+ console_locked = 1;
|
|
+ /*
|
|
+ * We own the drivers. We can drop the spinlock and let
|
|
+ * release_console_sem() print the text
|
|
+ */
|
|
+ printk_cpu = UINT_MAX;
|
|
+ spin_unlock_irqrestore(&logbuf_lock, flags);
|
|
+ console_may_schedule = 0;
|
|
+ release_console_sem();
|
|
+ } else {
|
|
+ /*
|
|
+ * Someone else owns the drivers. We drop the spinlock, which
|
|
+ * allows the semaphore holder to proceed and to call the
|
|
+ * console drivers with the output which we just produced.
|
|
+ */
|
|
+ printk_cpu = UINT_MAX;
|
|
+ spin_unlock_irqrestore(&logbuf_lock, flags);
|
|
+ }
|
|
+out:
|
|
+ preempt_enable();
|
|
+ return printed_len;
|
|
+}
|
|
+EXPORT_SYMBOL(printk);
|
|
+EXPORT_SYMBOL(vprintk);
|
|
+
|
|
+#else
|
|
+
|
|
+asmlinkage long sys_syslog(int type, char __user *buf, int len)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int do_syslog(int type, char __user *buf, int len)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void call_console_drivers(unsigned long start, unsigned long end)
|
|
+{
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+/**
|
|
+ * add_preferred_console - add a device to the list of preferred consoles.
|
|
+ * @name: device name
|
|
+ * @idx: device index
|
|
+ * @options: options for this console
|
|
+ *
|
|
+ * The last preferred console added will be used for kernel messages
|
|
+ * and stdin/out/err for init. Normally this is used by console_setup
|
|
+ * above to handle user-supplied console arguments; however it can also
|
|
+ * be used by arch-specific code either to override the user or more
|
|
+ * commonly to provide a default console (ie from PROM variables) when
|
|
+ * the user has not supplied one.
|
|
+ */
|
|
+int __init add_preferred_console(char *name, int idx, char *options)
|
|
+{
|
|
+ struct console_cmdline *c;
|
|
+ int i;
|
|
+
|
|
+ /*
|
|
+ * See if this tty is not yet registered, and
|
|
+ * if we have a slot free.
|
|
+ */
|
|
+ for(i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++)
|
|
+ if (strcmp(console_cmdline[i].name, name) == 0 &&
|
|
+ console_cmdline[i].index == idx) {
|
|
+ selected_console = i;
|
|
+ return 0;
|
|
+ }
|
|
+ if (i == MAX_CMDLINECONSOLES)
|
|
+ return -E2BIG;
|
|
+ selected_console = i;
|
|
+ c = &console_cmdline[i];
|
|
+ memcpy(c->name, name, sizeof(c->name));
|
|
+ c->name[sizeof(c->name) - 1] = 0;
|
|
+ c->options = options;
|
|
+ c->index = idx;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * acquire_console_sem - lock the console system for exclusive use.
|
|
+ *
|
|
+ * Acquires a semaphore which guarantees that the caller has
|
|
+ * exclusive access to the console system and the console_drivers list.
|
|
+ *
|
|
+ * Can sleep, returns nothing.
|
|
+ */
|
|
+void acquire_console_sem(void)
|
|
+{
|
|
+ if (in_interrupt())
|
|
+ BUG();
|
|
+ down(&console_sem);
|
|
+ console_locked = 1;
|
|
+ console_may_schedule = 1;
|
|
+}
|
|
+EXPORT_SYMBOL(acquire_console_sem);
|
|
+
|
|
+int try_acquire_console_sem(void)
|
|
+{
|
|
+ if (down_trylock(&console_sem))
|
|
+ return -1;
|
|
+ console_locked = 1;
|
|
+ console_may_schedule = 0;
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL(try_acquire_console_sem);
|
|
+
|
|
+int is_console_locked(void)
|
|
+{
|
|
+ return console_locked;
|
|
+}
|
|
+EXPORT_SYMBOL(is_console_locked);
|
|
+
|
|
+/**
|
|
+ * release_console_sem - unlock the console system
|
|
+ *
|
|
+ * Releases the semaphore which the caller holds on the console system
|
|
+ * and the console driver list.
|
|
+ *
|
|
+ * While the semaphore was held, console output may have been buffered
|
|
+ * by printk(). If this is the case, release_console_sem() emits
|
|
+ * the output prior to releasing the semaphore.
|
|
+ *
|
|
+ * If there is output waiting for klogd, we wake it up.
|
|
+ *
|
|
+ * release_console_sem() may be called from any context.
|
|
+ */
|
|
+void release_console_sem(void)
|
|
+{
|
|
+ unsigned long flags;
|
|
+ unsigned long _con_start, _log_end;
|
|
+ unsigned long wake_klogd = 0;
|
|
+
|
|
+ for ( ; ; ) {
|
|
+ spin_lock_irqsave(&logbuf_lock, flags);
|
|
+ wake_klogd |= log_start - log_end;
|
|
+ if (con_start == log_end)
|
|
+ break; /* Nothing to print */
|
|
+ _con_start = con_start;
|
|
+ _log_end = log_end;
|
|
+ con_start = log_end; /* Flush */
|
|
+ spin_unlock(&logbuf_lock);
|
|
+ call_console_drivers(_con_start, _log_end);
|
|
+ local_irq_restore(flags);
|
|
+ }
|
|
+ console_locked = 0;
|
|
+ console_may_schedule = 0;
|
|
+ up(&console_sem);
|
|
+ spin_unlock_irqrestore(&logbuf_lock, flags);
|
|
+ if (wake_klogd && !oops_in_progress && waitqueue_active(&log_wait))
|
|
+ wake_up_interruptible(&log_wait);
|
|
+}
|
|
+EXPORT_SYMBOL(release_console_sem);
|
|
+
|
|
+/**
|
|
+ * console_conditional_schedule - yield the CPU if required
|
|
+ *
|
|
+ * If the console code is currently allowed to sleep, and
|
|
+ * if this CPU should yield the CPU to another task, do
|
|
+ * so here.
|
|
+ *
|
|
+ * Must be called within acquire_console_sem().
|
|
+ */
|
|
+void __sched console_conditional_schedule(void)
|
|
+{
|
|
+ if (console_may_schedule)
|
|
+ cond_resched();
|
|
+}
|
|
+EXPORT_SYMBOL(console_conditional_schedule);
|
|
+
|
|
+void console_print(const char *s)
|
|
+{
|
|
+ printk(KERN_EMERG "%s", s);
|
|
+}
|
|
+EXPORT_SYMBOL(console_print);
|
|
+
|
|
+void console_unblank(void)
|
|
+{
|
|
+ struct console *c;
|
|
+
|
|
+ /*
|
|
+ * console_unblank can no longer be called in interrupt context unless
|
|
+ * oops_in_progress is set to 1..
|
|
+ */
|
|
+ if (oops_in_progress) {
|
|
+ if (down_trylock(&console_sem) != 0)
|
|
+ return;
|
|
+ } else
|
|
+ acquire_console_sem();
|
|
+
|
|
+ console_locked = 1;
|
|
+ console_may_schedule = 0;
|
|
+ for (c = console_drivers; c != NULL; c = c->next)
|
|
+ if ((c->flags & CON_ENABLED) && c->unblank)
|
|
+ c->unblank();
|
|
+ release_console_sem();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return the console tty driver structure and its associated index
|
|
+ */
|
|
+struct tty_driver *console_device(int *index)
|
|
+{
|
|
+ struct console *c;
|
|
+ struct tty_driver *driver = NULL;
|
|
+
|
|
+ acquire_console_sem();
|
|
+ for (c = console_drivers; c != NULL; c = c->next) {
|
|
+ if (!c->device)
|
|
+ continue;
|
|
+ driver = c->device(c, index);
|
|
+ if (driver)
|
|
+ break;
|
|
+ }
|
|
+ release_console_sem();
|
|
+ return driver;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Prevent further output on the passed console device so that (for example)
|
|
+ * serial drivers can disable console output before suspending a port, and can
|
|
+ * re-enable output afterwards.
|
|
+ */
|
|
+void console_stop(struct console *console)
|
|
+{
|
|
+ acquire_console_sem();
|
|
+ console->flags &= ~CON_ENABLED;
|
|
+ release_console_sem();
|
|
+}
|
|
+EXPORT_SYMBOL(console_stop);
|
|
+
|
|
+void console_start(struct console *console)
|
|
+{
|
|
+ acquire_console_sem();
|
|
+ console->flags |= CON_ENABLED;
|
|
+ release_console_sem();
|
|
+}
|
|
+EXPORT_SYMBOL(console_start);
|
|
+
|
|
+/*
|
|
+ * The console driver calls this routine during kernel initialization
|
|
+ * to register the console printing procedure with printk() and to
|
|
+ * print any messages that were printed by the kernel before the
|
|
+ * console driver was initialized.
|
|
+ */
|
|
+void register_console(struct console *console)
|
|
+{
|
|
+ int i;
|
|
+ unsigned long flags;
|
|
+
|
|
+ if (preferred_console < 0)
|
|
+ preferred_console = selected_console;
|
|
+
|
|
+ /*
|
|
+ * See if we want to use this console driver. If we
|
|
+ * didn't select a console we take the first one
|
|
+ * that registers here.
|
|
+ */
|
|
+ if (preferred_console < 0) {
|
|
+ if (console->index < 0)
|
|
+ console->index = 0;
|
|
+ if (console->setup == NULL ||
|
|
+ console->setup(console, NULL) == 0) {
|
|
+ console->flags |= CON_ENABLED | CON_CONSDEV;
|
|
+ preferred_console = 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * See if this console matches one we selected on
|
|
+ * the command line.
|
|
+ */
|
|
+ for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0];
|
|
+ i++) {
|
|
+ if (strcmp(console_cmdline[i].name, console->name) != 0)
|
|
+ continue;
|
|
+ if (console->index >= 0 &&
|
|
+ console->index != console_cmdline[i].index)
|
|
+ continue;
|
|
+ if (console->index < 0)
|
|
+ console->index = console_cmdline[i].index;
|
|
+ if (console->setup &&
|
|
+ console->setup(console, console_cmdline[i].options) != 0)
|
|
+ break;
|
|
+ console->flags |= CON_ENABLED;
|
|
+ console->index = console_cmdline[i].index;
|
|
+ if (i == selected_console) {
|
|
+ console->flags |= CON_CONSDEV;
|
|
+ preferred_console = selected_console;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (!(console->flags & CON_ENABLED))
|
|
+ return;
|
|
+
|
|
+ if (console_drivers && (console_drivers->flags & CON_BOOT)) {
|
|
+ unregister_console(console_drivers);
|
|
+ console->flags &= ~CON_PRINTBUFFER;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Put this console in the list - keep the
|
|
+ * preferred driver at the head of the list.
|
|
+ */
|
|
+ acquire_console_sem();
|
|
+ if ((console->flags & CON_CONSDEV) || console_drivers == NULL) {
|
|
+ console->next = console_drivers;
|
|
+ console_drivers = console;
|
|
+ if (console->next)
|
|
+ console->next->flags &= ~CON_CONSDEV;
|
|
+ } else {
|
|
+ console->next = console_drivers->next;
|
|
+ console_drivers->next = console;
|
|
+ }
|
|
+ if (console->flags & CON_PRINTBUFFER) {
|
|
+ /*
|
|
+ * release_console_sem() will print out the buffered messages
|
|
+ * for us.
|
|
+ */
|
|
+ spin_lock_irqsave(&logbuf_lock, flags);
|
|
+ con_start = log_start;
|
|
+ spin_unlock_irqrestore(&logbuf_lock, flags);
|
|
+ }
|
|
+ release_console_sem();
|
|
+}
|
|
+EXPORT_SYMBOL(register_console);
|
|
+
|
|
+int unregister_console(struct console *console)
|
|
+{
|
|
+ struct console *a, *b;
|
|
+ int res = 1;
|
|
+
|
|
+ acquire_console_sem();
|
|
+ if (console_drivers == console) {
|
|
+ console_drivers=console->next;
|
|
+ res = 0;
|
|
+ } else if (console_drivers) {
|
|
+ for (a=console_drivers->next, b=console_drivers ;
|
|
+ a; b=a, a=b->next) {
|
|
+ if (a == console) {
|
|
+ b->next = a->next;
|
|
+ res = 0;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* If last console is removed, we re-enable picking the first
|
|
+ * one that gets registered. Without that, pmac early boot console
|
|
+ * would prevent fbcon from taking over.
|
|
+ *
|
|
+ * If this isn't the last console and it has CON_CONSDEV set, we
|
|
+ * need to set it on the next preferred console.
|
|
+ */
|
|
+ if (console_drivers == NULL)
|
|
+ preferred_console = selected_console;
|
|
+ else if (console->flags & CON_CONSDEV)
|
|
+ console_drivers->flags |= CON_CONSDEV;
|
|
+
|
|
+ release_console_sem();
|
|
+ return res;
|
|
+}
|
|
+EXPORT_SYMBOL(unregister_console);
|
|
+
|
|
+/**
|
|
+ * tty_write_message - write a message to a certain tty, not just the console.
|
|
+ * @tty: the destination tty_struct
|
|
+ * @msg: the message to write
|
|
+ *
|
|
+ * This is used for messages that need to be redirected to a specific tty.
|
|
+ * We don't put it into the syslog queue right now maybe in the future if
|
|
+ * really needed.
|
|
+ */
|
|
+void tty_write_message(struct tty_struct *tty, char *msg)
|
|
+{
|
|
+ if (tty && tty->driver->write)
|
|
+ tty->driver->write(tty, msg, strlen(msg));
|
|
+ return;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * printk rate limiting, lifted from the networking subsystem.
|
|
+ *
|
|
+ * This enforces a rate limit: not more than one kernel message
|
|
+ * every printk_ratelimit_jiffies to make a denial-of-service
|
|
+ * attack impossible.
|
|
+ */
|
|
+int __printk_ratelimit(int ratelimit_jiffies, int ratelimit_burst)
|
|
+{
|
|
+ static DEFINE_SPINLOCK(ratelimit_lock);
|
|
+ static unsigned long toks = 10 * 5 * HZ;
|
|
+ static unsigned long last_msg;
|
|
+ static int missed;
|
|
+ unsigned long flags;
|
|
+ unsigned long now = jiffies;
|
|
+
|
|
+ spin_lock_irqsave(&ratelimit_lock, flags);
|
|
+ toks += now - last_msg;
|
|
+ last_msg = now;
|
|
+ if (toks > (ratelimit_burst * ratelimit_jiffies))
|
|
+ toks = ratelimit_burst * ratelimit_jiffies;
|
|
+ if (toks >= ratelimit_jiffies) {
|
|
+ int lost = missed;
|
|
+
|
|
+ missed = 0;
|
|
+ toks -= ratelimit_jiffies;
|
|
+ spin_unlock_irqrestore(&ratelimit_lock, flags);
|
|
+ if (lost)
|
|
+ printk(KERN_WARNING "printk: %d messages suppressed.\n", lost);
|
|
+ return 1;
|
|
+ }
|
|
+ missed++;
|
|
+ spin_unlock_irqrestore(&ratelimit_lock, flags);
|
|
+ return 0;
|
|
+}
|
|
+EXPORT_SYMBOL(__printk_ratelimit);
|
|
+
|
|
+/* minimum time in jiffies between messages */
|
|
+int printk_ratelimit_jiffies = 5 * HZ;
|
|
+
|
|
+/* number of messages we send before ratelimiting */
|
|
+int printk_ratelimit_burst = 10;
|
|
+
|
|
+int printk_ratelimit(void)
|
|
+{
|
|
+ return __printk_ratelimit(printk_ratelimit_jiffies,
|
|
+ printk_ratelimit_burst);
|
|
+}
|
|
+EXPORT_SYMBOL(printk_ratelimit);
|
|
Index: xen-3.3.1-testing/tools/xcutils/helper/string.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ xen-3.3.1-testing/tools/xcutils/helper/string.c
|
|
@@ -0,0 +1,601 @@
|
|
+/*
|
|
+ * linux/lib/string.c
|
|
+ *
|
|
+ * Copyright (C) 1991, 1992 Linus Torvalds
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * stupid library routines.. The optimized versions should generally be found
|
|
+ * as inline code in <asm-xx/string.h>
|
|
+ *
|
|
+ * These are buggy as well..
|
|
+ *
|
|
+ * * Fri Jun 25 1999, Ingo Oeser <ioe@informatik.tu-chemnitz.de>
|
|
+ * - Added strsep() which will replace strtok() soon (because strsep() is
|
|
+ * reentrant and should be faster). Use only strsep() in new code, please.
|
|
+ *
|
|
+ * * Sat Feb 09 2002, Jason Thomas <jason@topic.com.au>,
|
|
+ * Matthew Hawkins <matt@mh.dropbear.id.au>
|
|
+ * - Kissed strtok() goodbye
|
|
+ */
|
|
+
|
|
+#include "helper.h"
|
|
+
|
|
+#ifndef __HAVE_ARCH_STRNICMP
|
|
+/**
|
|
+ * strnicmp - Case insensitive, length-limited string comparison
|
|
+ * @s1: One string
|
|
+ * @s2: The other string
|
|
+ * @len: the maximum number of characters to compare
|
|
+ */
|
|
+int strnicmp(const char *s1, const char *s2, size_t len)
|
|
+{
|
|
+ /* Yes, Virginia, it had better be unsigned */
|
|
+ unsigned char c1, c2;
|
|
+
|
|
+ c1 = c2 = 0;
|
|
+ if (len) {
|
|
+ do {
|
|
+ c1 = *s1;
|
|
+ c2 = *s2;
|
|
+ s1++;
|
|
+ s2++;
|
|
+ if (!c1)
|
|
+ break;
|
|
+ if (!c2)
|
|
+ break;
|
|
+ if (c1 == c2)
|
|
+ continue;
|
|
+ c1 = tolower(c1);
|
|
+ c2 = tolower(c2);
|
|
+ if (c1 != c2)
|
|
+ break;
|
|
+ } while (--len);
|
|
+ }
|
|
+ return (int)c1 - (int)c2;
|
|
+}
|
|
+EXPORT_SYMBOL(strnicmp);
|
|
+#endif
|
|
+
|
|
+#ifndef __HAVE_ARCH_STRCPY
|
|
+/**
|
|
+ * strcpy - Copy a %NUL terminated string
|
|
+ * @dest: Where to copy the string to
|
|
+ * @src: Where to copy the string from
|
|
+ */
|
|
+#undef strcpy
|
|
+char *strcpy(char *dest, const char *src)
|
|
+{
|
|
+ char *tmp = dest;
|
|
+
|
|
+ while ((*dest++ = *src++) != '\0')
|
|
+ /* nothing */;
|
|
+ return tmp;
|
|
+}
|
|
+EXPORT_SYMBOL(strcpy);
|
|
+#endif
|
|
+
|
|
+#ifndef __HAVE_ARCH_STRNCPY
|
|
+/**
|
|
+ * strncpy - Copy a length-limited, %NUL-terminated string
|
|
+ * @dest: Where to copy the string to
|
|
+ * @src: Where to copy the string from
|
|
+ * @count: The maximum number of bytes to copy
|
|
+ *
|
|
+ * The result is not %NUL-terminated if the source exceeds
|
|
+ * @count bytes.
|
|
+ *
|
|
+ * In the case where the length of @src is less than that of
|
|
+ * count, the remainder of @dest will be padded with %NUL.
|
|
+ *
|
|
+ */
|
|
+char *strncpy(char *dest, const char *src, size_t count)
|
|
+{
|
|
+ char *tmp = dest;
|
|
+
|
|
+ while (count) {
|
|
+ if ((*tmp = *src) != 0)
|
|
+ src++;
|
|
+ tmp++;
|
|
+ count--;
|
|
+ }
|
|
+ return dest;
|
|
+}
|
|
+EXPORT_SYMBOL(strncpy);
|
|
+#endif
|
|
+
|
|
+#ifndef __HAVE_ARCH_STRLCPY
|
|
+/**
|
|
+ * strlcpy - Copy a %NUL terminated string into a sized buffer
|
|
+ * @dest: Where to copy the string to
|
|
+ * @src: Where to copy the string from
|
|
+ * @size: size of destination buffer
|
|
+ *
|
|
+ * Compatible with *BSD: the result is always a valid
|
|
+ * NUL-terminated string that fits in the buffer (unless,
|
|
+ * of course, the buffer size is zero). It does not pad
|
|
+ * out the result like strncpy() does.
|
|
+ */
|
|
+size_t strlcpy(char *dest, const char *src, size_t size)
|
|
+{
|
|
+ size_t ret = strlen(src);
|
|
+
|
|
+ if (size) {
|
|
+ size_t len = (ret >= size) ? size - 1 : ret;
|
|
+ memcpy(dest, src, len);
|
|
+ dest[len] = '\0';
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+EXPORT_SYMBOL(strlcpy);
|
|
+#endif
|
|
+
|
|
+#ifndef __HAVE_ARCH_STRCAT
|
|
+/**
|
|
+ * strcat - Append one %NUL-terminated string to another
|
|
+ * @dest: The string to be appended to
|
|
+ * @src: The string to append to it
|
|
+ */
|
|
+#undef strcat
|
|
+char *strcat(char *dest, const char *src)
|
|
+{
|
|
+ char *tmp = dest;
|
|
+
|
|
+ while (*dest)
|
|
+ dest++;
|
|
+ while ((*dest++ = *src++) != '\0')
|
|
+ ;
|
|
+ return tmp;
|
|
+}
|
|
+EXPORT_SYMBOL(strcat);
|
|
+#endif
|
|
+
|
|
+#ifndef __HAVE_ARCH_STRNCAT
|
|
+/**
|
|
+ * strncat - Append a length-limited, %NUL-terminated string to another
|
|
+ * @dest: The string to be appended to
|
|
+ * @src: The string to append to it
|
|
+ * @count: The maximum numbers of bytes to copy
|
|
+ *
|
|
+ * Note that in contrast to strncpy, strncat ensures the result is
|
|
+ * terminated.
|
|
+ */
|
|
+char *strncat(char *dest, const char *src, size_t count)
|
|
+{
|
|
+ char *tmp = dest;
|
|
+
|
|
+ if (count) {
|
|
+ while (*dest)
|
|
+ dest++;
|
|
+ while ((*dest++ = *src++) != 0) {
|
|
+ if (--count == 0) {
|
|
+ *dest = '\0';
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ return tmp;
|
|
+}
|
|
+EXPORT_SYMBOL(strncat);
|
|
+#endif
|
|
+
|
|
+#ifndef __HAVE_ARCH_STRLCAT
|
|
+/**
|
|
+ * strlcat - Append a length-limited, %NUL-terminated string to another
|
|
+ * @dest: The string to be appended to
|
|
+ * @src: The string to append to it
|
|
+ * @count: The size of the destination buffer.
|
|
+ */
|
|
+size_t strlcat(char *dest, const char *src, size_t count)
|
|
+{
|
|
+ size_t dsize = strlen(dest);
|
|
+ size_t len = strlen(src);
|
|
+ size_t res = dsize + len;
|
|
+
|
|
+ /* This would be a bug */
|
|
+ BUG_ON(dsize >= count);
|
|
+
|
|
+ dest += dsize;
|
|
+ count -= dsize;
|
|
+ if (len >= count)
|
|
+ len = count-1;
|
|
+ memcpy(dest, src, len);
|
|
+ dest[len] = 0;
|
|
+ return res;
|
|
+}
|
|
+EXPORT_SYMBOL(strlcat);
|
|
+#endif
|
|
+
|
|
+#ifndef __HAVE_ARCH_STRCMP
|
|
+/**
|
|
+ * strcmp - Compare two strings
|
|
+ * @cs: One string
|
|
+ * @ct: Another string
|
|
+ */
|
|
+#undef strcmp
|
|
+int strcmp(const char *cs, const char *ct)
|
|
+{
|
|
+ signed char __res;
|
|
+
|
|
+ while (1) {
|
|
+ if ((__res = *cs - *ct++) != 0 || !*cs++)
|
|
+ break;
|
|
+ }
|
|
+ return __res;
|
|
+}
|
|
+EXPORT_SYMBOL(strcmp);
|
|
+#endif
|
|
+
|
|
+#ifndef __HAVE_ARCH_STRNCMP
|
|
+/**
|
|
+ * strncmp - Compare two length-limited strings
|
|
+ * @cs: One string
|
|
+ * @ct: Another string
|
|
+ * @count: The maximum number of bytes to compare
|
|
+ */
|
|
+int strncmp(const char *cs, const char *ct, size_t count)
|
|
+{
|
|
+ signed char __res = 0;
|
|
+
|
|
+ while (count) {
|
|
+ if ((__res = *cs - *ct++) != 0 || !*cs++)
|
|
+ break;
|
|
+ count--;
|
|
+ }
|
|
+ return __res;
|
|
+}
|
|
+EXPORT_SYMBOL(strncmp);
|
|
+#endif
|
|
+
|
|
+#ifndef __HAVE_ARCH_STRCHR
|
|
+/**
|
|
+ * strchr - Find the first occurrence of a character in a string
|
|
+ * @s: The string to be searched
|
|
+ * @c: The character to search for
|
|
+ */
|
|
+char *strchr(const char *s, int c)
|
|
+{
|
|
+ for (; *s != (char)c; ++s)
|
|
+ if (*s == '\0')
|
|
+ return NULL;
|
|
+ return (char *)s;
|
|
+}
|
|
+EXPORT_SYMBOL(strchr);
|
|
+#endif
|
|
+
|
|
+#ifndef __HAVE_ARCH_STRRCHR
|
|
+/**
|
|
+ * strrchr - Find the last occurrence of a character in a string
|
|
+ * @s: The string to be searched
|
|
+ * @c: The character to search for
|
|
+ */
|
|
+char *strrchr(const char *s, int c)
|
|
+{
|
|
+ const char *p = s + strlen(s);
|
|
+ do {
|
|
+ if (*p == (char)c)
|
|
+ return (char *)p;
|
|
+ } while (--p >= s);
|
|
+ return NULL;
|
|
+}
|
|
+EXPORT_SYMBOL(strrchr);
|
|
+#endif
|
|
+
|
|
+#ifndef __HAVE_ARCH_STRNCHR
|
|
+/**
|
|
+ * strnchr - Find a character in a length limited string
|
|
+ * @s: The string to be searched
|
|
+ * @count: The number of characters to be searched
|
|
+ * @c: The character to search for
|
|
+ */
|
|
+char *strnchr(const char *s, size_t count, int c)
|
|
+{
|
|
+ for (; count-- && *s != '\0'; ++s)
|
|
+ if (*s == (char)c)
|
|
+ return (char *)s;
|
|
+ return NULL;
|
|
+}
|
|
+EXPORT_SYMBOL(strnchr);
|
|
+#endif
|
|
+
|
|
+#ifndef __HAVE_ARCH_STRLEN
|
|
+/**
|
|
+ * strlen - Find the length of a string
|
|
+ * @s: The string to be sized
|
|
+ */
|
|
+size_t strlen(const char *s)
|
|
+{
|
|
+ const char *sc;
|
|
+
|
|
+ for (sc = s; *sc != '\0'; ++sc)
|
|
+ /* nothing */;
|
|
+ return sc - s;
|
|
+}
|
|
+EXPORT_SYMBOL(strlen);
|
|
+#endif
|
|
+
|
|
+#ifndef __HAVE_ARCH_STRNLEN
|
|
+/**
|
|
+ * strnlen - Find the length of a length-limited string
|
|
+ * @s: The string to be sized
|
|
+ * @count: The maximum number of bytes to search
|
|
+ */
|
|
+size_t strnlen(const char *s, size_t count)
|
|
+{
|
|
+ const char *sc;
|
|
+
|
|
+ for (sc = s; count-- && *sc != '\0'; ++sc)
|
|
+ /* nothing */;
|
|
+ return sc - s;
|
|
+}
|
|
+EXPORT_SYMBOL(strnlen);
|
|
+#endif
|
|
+
|
|
+#ifndef __HAVE_ARCH_STRSPN
|
|
+/**
|
|
+ * strspn - Calculate the length of the initial substring of @s which only
|
|
+ * contain letters in @accept
|
|
+ * @s: The string to be searched
|
|
+ * @accept: The string to search for
|
|
+ */
|
|
+size_t strspn(const char *s, const char *accept)
|
|
+{
|
|
+ const char *p;
|
|
+ const char *a;
|
|
+ size_t count = 0;
|
|
+
|
|
+ for (p = s; *p != '\0'; ++p) {
|
|
+ for (a = accept; *a != '\0'; ++a) {
|
|
+ if (*p == *a)
|
|
+ break;
|
|
+ }
|
|
+ if (*a == '\0')
|
|
+ return count;
|
|
+ ++count;
|
|
+ }
|
|
+ return count;
|
|
+}
|
|
+
|
|
+EXPORT_SYMBOL(strspn);
|
|
+#endif
|
|
+
|
|
+/**
|
|
+ * strcspn - Calculate the length of the initial substring of @s which does
|
|
+ * not contain letters in @reject
|
|
+ * @s: The string to be searched
|
|
+ * @reject: The string to avoid
|
|
+ */
|
|
+size_t strcspn(const char *s, const char *reject)
|
|
+{
|
|
+ const char *p;
|
|
+ const char *r;
|
|
+ size_t count = 0;
|
|
+
|
|
+ for (p = s; *p != '\0'; ++p) {
|
|
+ for (r = reject; *r != '\0'; ++r) {
|
|
+ if (*p == *r)
|
|
+ return count;
|
|
+ }
|
|
+ ++count;
|
|
+ }
|
|
+ return count;
|
|
+}
|
|
+EXPORT_SYMBOL(strcspn);
|
|
+
|
|
+#ifndef __HAVE_ARCH_STRPBRK
|
|
+/**
|
|
+ * strpbrk - Find the first occurrence of a set of characters
|
|
+ * @cs: The string to be searched
|
|
+ * @ct: The characters to search for
|
|
+ */
|
|
+char *strpbrk(const char *cs, const char *ct)
|
|
+{
|
|
+ const char *sc1, *sc2;
|
|
+
|
|
+ for (sc1 = cs; *sc1 != '\0'; ++sc1) {
|
|
+ for (sc2 = ct; *sc2 != '\0'; ++sc2) {
|
|
+ if (*sc1 == *sc2)
|
|
+ return (char *)sc1;
|
|
+ }
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+EXPORT_SYMBOL(strpbrk);
|
|
+#endif
|
|
+
|
|
+#ifndef __HAVE_ARCH_STRSEP
|
|
+/**
|
|
+ * strsep - Split a string into tokens
|
|
+ * @s: The string to be searched
|
|
+ * @ct: The characters to search for
|
|
+ *
|
|
+ * strsep() updates @s to point after the token, ready for the next call.
|
|
+ *
|
|
+ * It returns empty tokens, too, behaving exactly like the libc function
|
|
+ * of that name. In fact, it was stolen from glibc2 and de-fancy-fied.
|
|
+ * Same semantics, slimmer shape. ;)
|
|
+ */
|
|
+char *strsep(char **s, const char *ct)
|
|
+{
|
|
+ char *sbegin = *s;
|
|
+ char *end;
|
|
+
|
|
+ if (sbegin == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ end = strpbrk(sbegin, ct);
|
|
+ if (end)
|
|
+ *end++ = '\0';
|
|
+ *s = end;
|
|
+ return sbegin;
|
|
+}
|
|
+EXPORT_SYMBOL(strsep);
|
|
+#endif
|
|
+
|
|
+#ifndef __HAVE_ARCH_MEMSET
|
|
+/**
|
|
+ * memset - Fill a region of memory with the given value
|
|
+ * @s: Pointer to the start of the area.
|
|
+ * @c: The byte to fill the area with
|
|
+ * @count: The size of the area.
|
|
+ *
|
|
+ * Do not use memset() to access IO space, use memset_io() instead.
|
|
+ */
|
|
+void *memset(void *s, int c, size_t count)
|
|
+{
|
|
+ char *xs = s;
|
|
+
|
|
+ while (count--)
|
|
+ *xs++ = c;
|
|
+ return s;
|
|
+}
|
|
+EXPORT_SYMBOL(memset);
|
|
+#endif
|
|
+
|
|
+#ifndef __HAVE_ARCH_MEMCPY
|
|
+/**
|
|
+ * memcpy - Copy one area of memory to another
|
|
+ * @dest: Where to copy to
|
|
+ * @src: Where to copy from
|
|
+ * @count: The size of the area.
|
|
+ *
|
|
+ * You should not use this function to access IO space, use memcpy_toio()
|
|
+ * or memcpy_fromio() instead.
|
|
+ */
|
|
+void *memcpy(void *dest, const void *src, size_t count)
|
|
+{
|
|
+ char *tmp = dest;
|
|
+ const char *s = src;
|
|
+
|
|
+ while (count--)
|
|
+ *tmp++ = *s++;
|
|
+ return dest;
|
|
+}
|
|
+EXPORT_SYMBOL(memcpy);
|
|
+#endif
|
|
+
|
|
+#ifndef __HAVE_ARCH_MEMMOVE
|
|
+/**
|
|
+ * memmove - Copy one area of memory to another
|
|
+ * @dest: Where to copy to
|
|
+ * @src: Where to copy from
|
|
+ * @count: The size of the area.
|
|
+ *
|
|
+ * Unlike memcpy(), memmove() copes with overlapping areas.
|
|
+ */
|
|
+void *memmove(void *dest, const void *src, size_t count)
|
|
+{
|
|
+ char *tmp;
|
|
+ const char *s;
|
|
+
|
|
+ if (dest <= src) {
|
|
+ tmp = dest;
|
|
+ s = src;
|
|
+ while (count--)
|
|
+ *tmp++ = *s++;
|
|
+ } else {
|
|
+ tmp = dest;
|
|
+ tmp += count;
|
|
+ s = src;
|
|
+ s += count;
|
|
+ while (count--)
|
|
+ *--tmp = *--s;
|
|
+ }
|
|
+ return dest;
|
|
+}
|
|
+EXPORT_SYMBOL(memmove);
|
|
+#endif
|
|
+
|
|
+#ifndef __HAVE_ARCH_MEMCMP
|
|
+/**
|
|
+ * memcmp - Compare two areas of memory
|
|
+ * @cs: One area of memory
|
|
+ * @ct: Another area of memory
|
|
+ * @count: The size of the area.
|
|
+ */
|
|
+#undef memcmp
|
|
+int memcmp(const void *cs, const void *ct, size_t count)
|
|
+{
|
|
+ const unsigned char *su1, *su2;
|
|
+ int res = 0;
|
|
+
|
|
+ for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--)
|
|
+ if ((res = *su1 - *su2) != 0)
|
|
+ break;
|
|
+ return res;
|
|
+}
|
|
+EXPORT_SYMBOL(memcmp);
|
|
+#endif
|
|
+
|
|
+#ifndef __HAVE_ARCH_MEMSCAN
|
|
+/**
|
|
+ * memscan - Find a character in an area of memory.
|
|
+ * @addr: The memory area
|
|
+ * @c: The byte to search for
|
|
+ * @size: The size of the area.
|
|
+ *
|
|
+ * returns the address of the first occurrence of @c, or 1 byte past
|
|
+ * the area if @c is not found
|
|
+ */
|
|
+void *memscan(void *addr, int c, size_t size)
|
|
+{
|
|
+ unsigned char *p = addr;
|
|
+
|
|
+ while (size) {
|
|
+ if (*p == c)
|
|
+ return (void *)p;
|
|
+ p++;
|
|
+ size--;
|
|
+ }
|
|
+ return (void *)p;
|
|
+}
|
|
+EXPORT_SYMBOL(memscan);
|
|
+#endif
|
|
+
|
|
+#ifndef __HAVE_ARCH_STRSTR
|
|
+/**
|
|
+ * strstr - Find the first substring in a %NUL terminated string
|
|
+ * @s1: The string to be searched
|
|
+ * @s2: The string to search for
|
|
+ */
|
|
+char *strstr(const char *s1, const char *s2)
|
|
+{
|
|
+ int l1, l2;
|
|
+
|
|
+ l2 = strlen(s2);
|
|
+ if (!l2)
|
|
+ return (char *)s1;
|
|
+ l1 = strlen(s1);
|
|
+ while (l1 >= l2) {
|
|
+ l1--;
|
|
+ if (!memcmp(s1, s2, l2))
|
|
+ return (char *)s1;
|
|
+ s1++;
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+EXPORT_SYMBOL(strstr);
|
|
+#endif
|
|
+
|
|
+#ifndef __HAVE_ARCH_MEMCHR
|
|
+/**
|
|
+ * memchr - Find a character in an area of memory.
|
|
+ * @s: The memory area
|
|
+ * @c: The byte to search for
|
|
+ * @n: The size of the area.
|
|
+ *
|
|
+ * returns the address of the first occurrence of @c, or %NULL
|
|
+ * if @c is not found
|
|
+ */
|
|
+void *memchr(const void *s, int c, size_t n)
|
|
+{
|
|
+ const unsigned char *p = s;
|
|
+ while (n-- != 0) {
|
|
+ if ((unsigned char)c == *p++) {
|
|
+ return (void *)(p - 1);
|
|
+ }
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+EXPORT_SYMBOL(memchr);
|
|
+#endif
|
|
Index: xen-3.3.1-testing/tools/xcutils/helper/vsprintf.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ xen-3.3.1-testing/tools/xcutils/helper/vsprintf.c
|
|
@@ -0,0 +1,842 @@
|
|
+/*
|
|
+ * linux/lib/vsprintf.c
|
|
+ *
|
|
+ * Copyright (C) 1991, 1992 Linus Torvalds
|
|
+ */
|
|
+
|
|
+/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
|
|
+/*
|
|
+ * Wirzenius wrote this portably, Torvalds fucked it up :-)
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * Fri Jul 13 2001 Crutcher Dunnavant <crutcher+kernel@datastacks.com>
|
|
+ * - changed to provide snprintf and vsnprintf functions
|
|
+ * So Feb 1 16:51:32 CET 2004 Juergen Quade <quade@hsnr.de>
|
|
+ * - scnprintf and vscnprintf
|
|
+ */
|
|
+
|
|
+#include "helper.h"
|
|
+
|
|
+/**
|
|
+ * simple_strtoul - convert a string to an unsigned long
|
|
+ * @cp: The start of the string
|
|
+ * @endp: A pointer to the end of the parsed string will be placed here
|
|
+ * @base: The number base to use
|
|
+ */
|
|
+unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base)
|
|
+{
|
|
+ unsigned long result = 0,value;
|
|
+
|
|
+ if (!base) {
|
|
+ base = 10;
|
|
+ if (*cp == '0') {
|
|
+ base = 8;
|
|
+ cp++;
|
|
+ if ((toupper(*cp) == 'X') && isxdigit(cp[1])) {
|
|
+ cp++;
|
|
+ base = 16;
|
|
+ }
|
|
+ }
|
|
+ } else if (base == 16) {
|
|
+ if (cp[0] == '0' && toupper(cp[1]) == 'X')
|
|
+ cp += 2;
|
|
+ }
|
|
+ while (isxdigit(*cp) &&
|
|
+ (value = isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) {
|
|
+ result = result*base + value;
|
|
+ cp++;
|
|
+ }
|
|
+ if (endp)
|
|
+ *endp = (char *)cp;
|
|
+ return result;
|
|
+}
|
|
+
|
|
+EXPORT_SYMBOL(simple_strtoul);
|
|
+
|
|
+/**
|
|
+ * simple_strtol - convert a string to a signed long
|
|
+ * @cp: The start of the string
|
|
+ * @endp: A pointer to the end of the parsed string will be placed here
|
|
+ * @base: The number base to use
|
|
+ */
|
|
+long simple_strtol(const char *cp,char **endp,unsigned int base)
|
|
+{
|
|
+ if(*cp=='-')
|
|
+ return -simple_strtoul(cp+1,endp,base);
|
|
+ return simple_strtoul(cp,endp,base);
|
|
+}
|
|
+
|
|
+EXPORT_SYMBOL(simple_strtol);
|
|
+
|
|
+/**
|
|
+ * simple_strtoull - convert a string to an unsigned long long
|
|
+ * @cp: The start of the string
|
|
+ * @endp: A pointer to the end of the parsed string will be placed here
|
|
+ * @base: The number base to use
|
|
+ */
|
|
+unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base)
|
|
+{
|
|
+ unsigned long long result = 0,value;
|
|
+
|
|
+ if (!base) {
|
|
+ base = 10;
|
|
+ if (*cp == '0') {
|
|
+ base = 8;
|
|
+ cp++;
|
|
+ if ((toupper(*cp) == 'X') && isxdigit(cp[1])) {
|
|
+ cp++;
|
|
+ base = 16;
|
|
+ }
|
|
+ }
|
|
+ } else if (base == 16) {
|
|
+ if (cp[0] == '0' && toupper(cp[1]) == 'X')
|
|
+ cp += 2;
|
|
+ }
|
|
+ while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
|
|
+ ? toupper(*cp) : *cp)-'A'+10) < base) {
|
|
+ result = result*base + value;
|
|
+ cp++;
|
|
+ }
|
|
+ if (endp)
|
|
+ *endp = (char *)cp;
|
|
+ return result;
|
|
+}
|
|
+
|
|
+EXPORT_SYMBOL(simple_strtoull);
|
|
+
|
|
+/**
|
|
+ * simple_strtoll - convert a string to a signed long long
|
|
+ * @cp: The start of the string
|
|
+ * @endp: A pointer to the end of the parsed string will be placed here
|
|
+ * @base: The number base to use
|
|
+ */
|
|
+long long simple_strtoll(const char *cp,char **endp,unsigned int base)
|
|
+{
|
|
+ if(*cp=='-')
|
|
+ return -simple_strtoull(cp+1,endp,base);
|
|
+ return simple_strtoull(cp,endp,base);
|
|
+}
|
|
+
|
|
+static int skip_atoi(const char **s)
|
|
+{
|
|
+ int i=0;
|
|
+
|
|
+ while (isdigit(**s))
|
|
+ i = i*10 + *((*s)++) - '0';
|
|
+ return i;
|
|
+}
|
|
+
|
|
+#define ZEROPAD 1 /* pad with zero */
|
|
+#define SIGN 2 /* unsigned/signed long */
|
|
+#define PLUS 4 /* show plus */
|
|
+#define SPACE 8 /* space if plus */
|
|
+#define LEFT 16 /* left justified */
|
|
+#define SPECIAL 32 /* 0x */
|
|
+#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */
|
|
+
|
|
+static char * number(char * buf, char * end, unsigned long long num, int base, int size, int precision, int type)
|
|
+{
|
|
+ char c,sign,tmp[66];
|
|
+ const char *digits;
|
|
+ static const char small_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
|
|
+ static const char large_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
+ int i;
|
|
+
|
|
+ digits = (type & LARGE) ? large_digits : small_digits;
|
|
+ if (type & LEFT)
|
|
+ type &= ~ZEROPAD;
|
|
+ if (base < 2 || base > 36)
|
|
+ return NULL;
|
|
+ c = (type & ZEROPAD) ? '0' : ' ';
|
|
+ sign = 0;
|
|
+ if (type & SIGN) {
|
|
+ if ((signed long long) num < 0) {
|
|
+ sign = '-';
|
|
+ num = - (signed long long) num;
|
|
+ size--;
|
|
+ } else if (type & PLUS) {
|
|
+ sign = '+';
|
|
+ size--;
|
|
+ } else if (type & SPACE) {
|
|
+ sign = ' ';
|
|
+ size--;
|
|
+ }
|
|
+ }
|
|
+ if (type & SPECIAL) {
|
|
+ if (base == 16)
|
|
+ size -= 2;
|
|
+ else if (base == 8)
|
|
+ size--;
|
|
+ }
|
|
+ i = 0;
|
|
+ if (num == 0)
|
|
+ tmp[i++]='0';
|
|
+ else while (num != 0)
|
|
+ tmp[i++] = digits[do_div(num,base)];
|
|
+ if (i > precision)
|
|
+ precision = i;
|
|
+ size -= precision;
|
|
+ if (!(type&(ZEROPAD+LEFT))) {
|
|
+ while(size-->0) {
|
|
+ if (buf <= end)
|
|
+ *buf = ' ';
|
|
+ ++buf;
|
|
+ }
|
|
+ }
|
|
+ if (sign) {
|
|
+ if (buf <= end)
|
|
+ *buf = sign;
|
|
+ ++buf;
|
|
+ }
|
|
+ if (type & SPECIAL) {
|
|
+ if (base==8) {
|
|
+ if (buf <= end)
|
|
+ *buf = '0';
|
|
+ ++buf;
|
|
+ } else if (base==16) {
|
|
+ if (buf <= end)
|
|
+ *buf = '0';
|
|
+ ++buf;
|
|
+ if (buf <= end)
|
|
+ *buf = digits[33];
|
|
+ ++buf;
|
|
+ }
|
|
+ }
|
|
+ if (!(type & LEFT)) {
|
|
+ while (size-- > 0) {
|
|
+ if (buf <= end)
|
|
+ *buf = c;
|
|
+ ++buf;
|
|
+ }
|
|
+ }
|
|
+ while (i < precision--) {
|
|
+ if (buf <= end)
|
|
+ *buf = '0';
|
|
+ ++buf;
|
|
+ }
|
|
+ while (i-- > 0) {
|
|
+ if (buf <= end)
|
|
+ *buf = tmp[i];
|
|
+ ++buf;
|
|
+ }
|
|
+ while (size-- > 0) {
|
|
+ if (buf <= end)
|
|
+ *buf = ' ';
|
|
+ ++buf;
|
|
+ }
|
|
+ return buf;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * vsnprintf - Format a string and place it in a buffer
|
|
+ * @buf: The buffer to place the result into
|
|
+ * @size: The size of the buffer, including the trailing null space
|
|
+ * @fmt: The format string to use
|
|
+ * @args: Arguments for the format string
|
|
+ *
|
|
+ * The return value is the number of characters which would
|
|
+ * be generated for the given input, excluding the trailing
|
|
+ * '\0', as per ISO C99. If you want to have the exact
|
|
+ * number of characters written into @buf as return value
|
|
+ * (not including the trailing '\0'), use vscnprintf. If the
|
|
+ * return is greater than or equal to @size, the resulting
|
|
+ * string is truncated.
|
|
+ *
|
|
+ * Call this function if you are already dealing with a va_list.
|
|
+ * You probably want snprintf instead.
|
|
+ */
|
|
+int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
|
|
+{
|
|
+ int len;
|
|
+ unsigned long long num;
|
|
+ int i, base;
|
|
+ char *str, *end, c;
|
|
+ const char *s;
|
|
+
|
|
+ int flags; /* flags to number() */
|
|
+
|
|
+ int field_width; /* width of output field */
|
|
+ int precision; /* min. # of digits for integers; max
|
|
+ number of chars for from string */
|
|
+ int qualifier; /* 'h', 'l', or 'L' for integer fields */
|
|
+ /* 'z' support added 23/7/1999 S.H. */
|
|
+ /* 'z' changed to 'Z' --davidm 1/25/99 */
|
|
+ /* 't' added for ptrdiff_t */
|
|
+
|
|
+ /* Reject out-of-range values early */
|
|
+ if (unlikely((int) size < 0)) {
|
|
+ /* There can be only one.. */
|
|
+ static int warn = 1;
|
|
+ WARN_ON(warn);
|
|
+ warn = 0;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ str = buf;
|
|
+ end = buf + size - 1;
|
|
+
|
|
+ if (end < buf - 1) {
|
|
+ end = ((void *) -1);
|
|
+ size = end - buf + 1;
|
|
+ }
|
|
+
|
|
+ for (; *fmt ; ++fmt) {
|
|
+ if (*fmt != '%') {
|
|
+ if (str <= end)
|
|
+ *str = *fmt;
|
|
+ ++str;
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ /* process flags */
|
|
+ flags = 0;
|
|
+ repeat:
|
|
+ ++fmt; /* this also skips first '%' */
|
|
+ switch (*fmt) {
|
|
+ case '-': flags |= LEFT; goto repeat;
|
|
+ case '+': flags |= PLUS; goto repeat;
|
|
+ case ' ': flags |= SPACE; goto repeat;
|
|
+ case '#': flags |= SPECIAL; goto repeat;
|
|
+ case '0': flags |= ZEROPAD; goto repeat;
|
|
+ }
|
|
+
|
|
+ /* get field width */
|
|
+ field_width = -1;
|
|
+ if (isdigit(*fmt))
|
|
+ field_width = skip_atoi(&fmt);
|
|
+ else if (*fmt == '*') {
|
|
+ ++fmt;
|
|
+ /* it's the next argument */
|
|
+ field_width = va_arg(args, int);
|
|
+ if (field_width < 0) {
|
|
+ field_width = -field_width;
|
|
+ flags |= LEFT;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* get the precision */
|
|
+ precision = -1;
|
|
+ if (*fmt == '.') {
|
|
+ ++fmt;
|
|
+ if (isdigit(*fmt))
|
|
+ precision = skip_atoi(&fmt);
|
|
+ else if (*fmt == '*') {
|
|
+ ++fmt;
|
|
+ /* it's the next argument */
|
|
+ precision = va_arg(args, int);
|
|
+ }
|
|
+ if (precision < 0)
|
|
+ precision = 0;
|
|
+ }
|
|
+
|
|
+ /* get the conversion qualifier */
|
|
+ qualifier = -1;
|
|
+ if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
|
|
+ *fmt =='Z' || *fmt == 'z' || *fmt == 't') {
|
|
+ qualifier = *fmt;
|
|
+ ++fmt;
|
|
+ if (qualifier == 'l' && *fmt == 'l') {
|
|
+ qualifier = 'L';
|
|
+ ++fmt;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* default base */
|
|
+ base = 10;
|
|
+
|
|
+ switch (*fmt) {
|
|
+ case 'c':
|
|
+ if (!(flags & LEFT)) {
|
|
+ while (--field_width > 0) {
|
|
+ if (str <= end)
|
|
+ *str = ' ';
|
|
+ ++str;
|
|
+ }
|
|
+ }
|
|
+ c = (unsigned char) va_arg(args, int);
|
|
+ if (str <= end)
|
|
+ *str = c;
|
|
+ ++str;
|
|
+ while (--field_width > 0) {
|
|
+ if (str <= end)
|
|
+ *str = ' ';
|
|
+ ++str;
|
|
+ }
|
|
+ continue;
|
|
+
|
|
+ case 's':
|
|
+ s = va_arg(args, char *);
|
|
+ if ((unsigned long)s < PAGE_SIZE)
|
|
+ s = "<NULL>";
|
|
+
|
|
+ len = strnlen(s, precision);
|
|
+
|
|
+ if (!(flags & LEFT)) {
|
|
+ while (len < field_width--) {
|
|
+ if (str <= end)
|
|
+ *str = ' ';
|
|
+ ++str;
|
|
+ }
|
|
+ }
|
|
+ for (i = 0; i < len; ++i) {
|
|
+ if (str <= end)
|
|
+ *str = *s;
|
|
+ ++str; ++s;
|
|
+ }
|
|
+ while (len < field_width--) {
|
|
+ if (str <= end)
|
|
+ *str = ' ';
|
|
+ ++str;
|
|
+ }
|
|
+ continue;
|
|
+
|
|
+ case 'p':
|
|
+ if (field_width == -1) {
|
|
+ field_width = 2*sizeof(void *);
|
|
+ flags |= ZEROPAD;
|
|
+ }
|
|
+ str = number(str, end,
|
|
+ (unsigned long) va_arg(args, void *),
|
|
+ 16, field_width, precision, flags);
|
|
+ continue;
|
|
+
|
|
+
|
|
+ case 'n':
|
|
+ /* FIXME:
|
|
+ * What does C99 say about the overflow case here? */
|
|
+ if (qualifier == 'l') {
|
|
+ long * ip = va_arg(args, long *);
|
|
+ *ip = (str - buf);
|
|
+ } else if (qualifier == 'Z' || qualifier == 'z') {
|
|
+ size_t * ip = va_arg(args, size_t *);
|
|
+ *ip = (str - buf);
|
|
+ } else {
|
|
+ int * ip = va_arg(args, int *);
|
|
+ *ip = (str - buf);
|
|
+ }
|
|
+ continue;
|
|
+
|
|
+ case '%':
|
|
+ if (str <= end)
|
|
+ *str = '%';
|
|
+ ++str;
|
|
+ continue;
|
|
+
|
|
+ /* integer number formats - set up the flags and "break" */
|
|
+ case 'o':
|
|
+ base = 8;
|
|
+ break;
|
|
+
|
|
+ case 'X':
|
|
+ flags |= LARGE;
|
|
+ case 'x':
|
|
+ base = 16;
|
|
+ break;
|
|
+
|
|
+ case 'd':
|
|
+ case 'i':
|
|
+ flags |= SIGN;
|
|
+ case 'u':
|
|
+ break;
|
|
+
|
|
+ default:
|
|
+ if (str <= end)
|
|
+ *str = '%';
|
|
+ ++str;
|
|
+ if (*fmt) {
|
|
+ if (str <= end)
|
|
+ *str = *fmt;
|
|
+ ++str;
|
|
+ } else {
|
|
+ --fmt;
|
|
+ }
|
|
+ continue;
|
|
+ }
|
|
+ if (qualifier == 'L')
|
|
+ num = va_arg(args, long long);
|
|
+ else if (qualifier == 'l') {
|
|
+ num = va_arg(args, unsigned long);
|
|
+ if (flags & SIGN)
|
|
+ num = (signed long) num;
|
|
+ } else if (qualifier == 'Z' || qualifier == 'z') {
|
|
+ num = va_arg(args, size_t);
|
|
+ } else if (qualifier == 't') {
|
|
+ num = va_arg(args, ptrdiff_t);
|
|
+ } else if (qualifier == 'h') {
|
|
+ num = (unsigned short) va_arg(args, int);
|
|
+ if (flags & SIGN)
|
|
+ num = (signed short) num;
|
|
+ } else {
|
|
+ num = va_arg(args, unsigned int);
|
|
+ if (flags & SIGN)
|
|
+ num = (signed int) num;
|
|
+ }
|
|
+ str = number(str, end, num, base,
|
|
+ field_width, precision, flags);
|
|
+ }
|
|
+ if (str <= end)
|
|
+ *str = '\0';
|
|
+ else if (size > 0)
|
|
+ /* don't write out a null byte if the buf size is zero */
|
|
+ *end = '\0';
|
|
+ /* the trailing null byte doesn't count towards the total
|
|
+ * ++str;
|
|
+ */
|
|
+ return str-buf;
|
|
+}
|
|
+
|
|
+EXPORT_SYMBOL(vsnprintf);
|
|
+
|
|
+/**
|
|
+ * vscnprintf - Format a string and place it in a buffer
|
|
+ * @buf: The buffer to place the result into
|
|
+ * @size: The size of the buffer, including the trailing null space
|
|
+ * @fmt: The format string to use
|
|
+ * @args: Arguments for the format string
|
|
+ *
|
|
+ * The return value is the number of characters which have been written into
|
|
+ * the @buf not including the trailing '\0'. If @size is <= 0 the function
|
|
+ * returns 0.
|
|
+ *
|
|
+ * Call this function if you are already dealing with a va_list.
|
|
+ * You probably want scnprintf instead.
|
|
+ */
|
|
+int vscnprintf(char *buf, size_t size, const char *fmt, va_list args)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ i=vsnprintf(buf,size,fmt,args);
|
|
+ return (i >= size) ? (size - 1) : i;
|
|
+}
|
|
+
|
|
+EXPORT_SYMBOL(vscnprintf);
|
|
+
|
|
+/**
|
|
+ * snprintf - Format a string and place it in a buffer
|
|
+ * @buf: The buffer to place the result into
|
|
+ * @size: The size of the buffer, including the trailing null space
|
|
+ * @fmt: The format string to use
|
|
+ * @...: Arguments for the format string
|
|
+ *
|
|
+ * The return value is the number of characters which would be
|
|
+ * generated for the given input, excluding the trailing null,
|
|
+ * as per ISO C99. If the return is greater than or equal to
|
|
+ * @size, the resulting string is truncated.
|
|
+ */
|
|
+int snprintf(char * buf, size_t size, const char *fmt, ...)
|
|
+{
|
|
+ va_list args;
|
|
+ int i;
|
|
+
|
|
+ va_start(args, fmt);
|
|
+ i=vsnprintf(buf,size,fmt,args);
|
|
+ va_end(args);
|
|
+ return i;
|
|
+}
|
|
+
|
|
+EXPORT_SYMBOL(snprintf);
|
|
+
|
|
+/**
|
|
+ * scnprintf - Format a string and place it in a buffer
|
|
+ * @buf: The buffer to place the result into
|
|
+ * @size: The size of the buffer, including the trailing null space
|
|
+ * @fmt: The format string to use
|
|
+ * @...: Arguments for the format string
|
|
+ *
|
|
+ * The return value is the number of characters written into @buf not including
|
|
+ * the trailing '\0'. If @size is <= 0 the function returns 0. If the return is
|
|
+ * greater than or equal to @size, the resulting string is truncated.
|
|
+ */
|
|
+
|
|
+int scnprintf(char * buf, size_t size, const char *fmt, ...)
|
|
+{
|
|
+ va_list args;
|
|
+ int i;
|
|
+
|
|
+ va_start(args, fmt);
|
|
+ i = vsnprintf(buf, size, fmt, args);
|
|
+ va_end(args);
|
|
+ return (i >= size) ? (size - 1) : i;
|
|
+}
|
|
+EXPORT_SYMBOL(scnprintf);
|
|
+
|
|
+/**
|
|
+ * vsprintf - Format a string and place it in a buffer
|
|
+ * @buf: The buffer to place the result into
|
|
+ * @fmt: The format string to use
|
|
+ * @args: Arguments for the format string
|
|
+ *
|
|
+ * The function returns the number of characters written
|
|
+ * into @buf. Use vsnprintf or vscnprintf in order to avoid
|
|
+ * buffer overflows.
|
|
+ *
|
|
+ * Call this function if you are already dealing with a va_list.
|
|
+ * You probably want sprintf instead.
|
|
+ */
|
|
+int vsprintf(char *buf, const char *fmt, va_list args)
|
|
+{
|
|
+ return vsnprintf(buf, INT_MAX, fmt, args);
|
|
+}
|
|
+
|
|
+EXPORT_SYMBOL(vsprintf);
|
|
+
|
|
+/**
|
|
+ * sprintf - Format a string and place it in a buffer
|
|
+ * @buf: The buffer to place the result into
|
|
+ * @fmt: The format string to use
|
|
+ * @...: Arguments for the format string
|
|
+ *
|
|
+ * The function returns the number of characters written
|
|
+ * into @buf. Use snprintf or scnprintf in order to avoid
|
|
+ * buffer overflows.
|
|
+ */
|
|
+int sprintf(char * buf, const char *fmt, ...)
|
|
+{
|
|
+ va_list args;
|
|
+ int i;
|
|
+
|
|
+ va_start(args, fmt);
|
|
+ i=vsnprintf(buf, INT_MAX, fmt, args);
|
|
+ va_end(args);
|
|
+ return i;
|
|
+}
|
|
+
|
|
+EXPORT_SYMBOL(sprintf);
|
|
+
|
|
+/**
|
|
+ * vsscanf - Unformat a buffer into a list of arguments
|
|
+ * @buf: input buffer
|
|
+ * @fmt: format of buffer
|
|
+ * @args: arguments
|
|
+ */
|
|
+int vsscanf(const char * buf, const char * fmt, va_list args)
|
|
+{
|
|
+ const char *str = buf;
|
|
+ char *next;
|
|
+ char digit;
|
|
+ int num = 0;
|
|
+ int qualifier;
|
|
+ int base;
|
|
+ int field_width;
|
|
+ int is_sign = 0;
|
|
+
|
|
+ while(*fmt && *str) {
|
|
+ /* skip any white space in format */
|
|
+ /* white space in format matchs any amount of
|
|
+ * white space, including none, in the input.
|
|
+ */
|
|
+ if (isspace(*fmt)) {
|
|
+ while (isspace(*fmt))
|
|
+ ++fmt;
|
|
+ while (isspace(*str))
|
|
+ ++str;
|
|
+ }
|
|
+
|
|
+ /* anything that is not a conversion must match exactly */
|
|
+ if (*fmt != '%' && *fmt) {
|
|
+ if (*fmt++ != *str++)
|
|
+ break;
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (!*fmt)
|
|
+ break;
|
|
+ ++fmt;
|
|
+
|
|
+ /* skip this conversion.
|
|
+ * advance both strings to next white space
|
|
+ */
|
|
+ if (*fmt == '*') {
|
|
+ while (!isspace(*fmt) && *fmt)
|
|
+ fmt++;
|
|
+ while (!isspace(*str) && *str)
|
|
+ str++;
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ /* get field width */
|
|
+ field_width = -1;
|
|
+ if (isdigit(*fmt))
|
|
+ field_width = skip_atoi(&fmt);
|
|
+
|
|
+ /* get conversion qualifier */
|
|
+ qualifier = -1;
|
|
+ if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' ||
|
|
+ *fmt == 'Z' || *fmt == 'z') {
|
|
+ qualifier = *fmt++;
|
|
+ if (unlikely(qualifier == *fmt)) {
|
|
+ if (qualifier == 'h') {
|
|
+ qualifier = 'H';
|
|
+ fmt++;
|
|
+ } else if (qualifier == 'l') {
|
|
+ qualifier = 'L';
|
|
+ fmt++;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ base = 10;
|
|
+ is_sign = 0;
|
|
+
|
|
+ if (!*fmt || !*str)
|
|
+ break;
|
|
+
|
|
+ switch(*fmt++) {
|
|
+ case 'c':
|
|
+ {
|
|
+ char *s = (char *) va_arg(args,char*);
|
|
+ if (field_width == -1)
|
|
+ field_width = 1;
|
|
+ do {
|
|
+ *s++ = *str++;
|
|
+ } while (--field_width > 0 && *str);
|
|
+ num++;
|
|
+ }
|
|
+ continue;
|
|
+ case 's':
|
|
+ {
|
|
+ char *s = (char *) va_arg(args, char *);
|
|
+ if(field_width == -1)
|
|
+ field_width = INT_MAX;
|
|
+ /* first, skip leading white space in buffer */
|
|
+ while (isspace(*str))
|
|
+ str++;
|
|
+
|
|
+ /* now copy until next white space */
|
|
+ while (*str && !isspace(*str) && field_width--) {
|
|
+ *s++ = *str++;
|
|
+ }
|
|
+ *s = '\0';
|
|
+ num++;
|
|
+ }
|
|
+ continue;
|
|
+ case 'n':
|
|
+ /* return number of characters read so far */
|
|
+ {
|
|
+ int *i = (int *)va_arg(args,int*);
|
|
+ *i = str - buf;
|
|
+ }
|
|
+ continue;
|
|
+ case 'o':
|
|
+ base = 8;
|
|
+ break;
|
|
+ case 'x':
|
|
+ case 'X':
|
|
+ base = 16;
|
|
+ break;
|
|
+ case 'i':
|
|
+ base = 0;
|
|
+ case 'd':
|
|
+ is_sign = 1;
|
|
+ case 'u':
|
|
+ break;
|
|
+ case '%':
|
|
+ /* looking for '%' in str */
|
|
+ if (*str++ != '%')
|
|
+ return num;
|
|
+ continue;
|
|
+ default:
|
|
+ /* invalid format; stop here */
|
|
+ return num;
|
|
+ }
|
|
+
|
|
+ /* have some sort of integer conversion.
|
|
+ * first, skip white space in buffer.
|
|
+ */
|
|
+ while (isspace(*str))
|
|
+ str++;
|
|
+
|
|
+ digit = *str;
|
|
+ if (is_sign && digit == '-')
|
|
+ digit = *(str + 1);
|
|
+
|
|
+ if (!digit
|
|
+ || (base == 16 && !isxdigit(digit))
|
|
+ || (base == 10 && !isdigit(digit))
|
|
+ || (base == 8 && (!isdigit(digit) || digit > '7'))
|
|
+ || (base == 0 && !isdigit(digit)))
|
|
+ break;
|
|
+
|
|
+ switch(qualifier) {
|
|
+ case 'H': /* that's 'hh' in format */
|
|
+ if (is_sign) {
|
|
+ signed char *s = (signed char *) va_arg(args,signed char *);
|
|
+ *s = (signed char) simple_strtol(str,&next,base);
|
|
+ } else {
|
|
+ unsigned char *s = (unsigned char *) va_arg(args, unsigned char *);
|
|
+ *s = (unsigned char) simple_strtoul(str, &next, base);
|
|
+ }
|
|
+ break;
|
|
+ case 'h':
|
|
+ if (is_sign) {
|
|
+ short *s = (short *) va_arg(args,short *);
|
|
+ *s = (short) simple_strtol(str,&next,base);
|
|
+ } else {
|
|
+ unsigned short *s = (unsigned short *) va_arg(args, unsigned short *);
|
|
+ *s = (unsigned short) simple_strtoul(str, &next, base);
|
|
+ }
|
|
+ break;
|
|
+ case 'l':
|
|
+ if (is_sign) {
|
|
+ long *l = (long *) va_arg(args,long *);
|
|
+ *l = simple_strtol(str,&next,base);
|
|
+ } else {
|
|
+ unsigned long *l = (unsigned long*) va_arg(args,unsigned long*);
|
|
+ *l = simple_strtoul(str,&next,base);
|
|
+ }
|
|
+ break;
|
|
+ case 'L':
|
|
+ if (is_sign) {
|
|
+ long long *l = (long long*) va_arg(args,long long *);
|
|
+ *l = simple_strtoll(str,&next,base);
|
|
+ } else {
|
|
+ unsigned long long *l = (unsigned long long*) va_arg(args,unsigned long long*);
|
|
+ *l = simple_strtoull(str,&next,base);
|
|
+ }
|
|
+ break;
|
|
+ case 'Z':
|
|
+ case 'z':
|
|
+ {
|
|
+ size_t *s = (size_t*) va_arg(args,size_t*);
|
|
+ *s = (size_t) simple_strtoul(str,&next,base);
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ if (is_sign) {
|
|
+ int *i = (int *) va_arg(args, int*);
|
|
+ *i = (int) simple_strtol(str,&next,base);
|
|
+ } else {
|
|
+ unsigned int *i = (unsigned int*) va_arg(args, unsigned int*);
|
|
+ *i = (unsigned int) simple_strtoul(str,&next,base);
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ num++;
|
|
+
|
|
+ if (!next)
|
|
+ break;
|
|
+ str = next;
|
|
+ }
|
|
+ return num;
|
|
+}
|
|
+
|
|
+EXPORT_SYMBOL(vsscanf);
|
|
+
|
|
+/**
|
|
+ * sscanf - Unformat a buffer into a list of arguments
|
|
+ * @buf: input buffer
|
|
+ * @fmt: formatting of buffer
|
|
+ * @...: resulting arguments
|
|
+ */
|
|
+int sscanf(const char * buf, const char * fmt, ...)
|
|
+{
|
|
+ va_list args;
|
|
+ int i;
|
|
+
|
|
+ va_start(args,fmt);
|
|
+ i = vsscanf(buf,fmt,args);
|
|
+ va_end(args);
|
|
+ return i;
|
|
+}
|
|
+
|
|
+EXPORT_SYMBOL(sscanf);
|
|
Index: xen-3.3.1-testing/tools/xcutils/helper/x86_32/div64.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ xen-3.3.1-testing/tools/xcutils/helper/x86_32/div64.h
|
|
@@ -0,0 +1,48 @@
|
|
+#ifndef __I386_DIV64
|
|
+#define __I386_DIV64
|
|
+
|
|
+/*
|
|
+ * do_div() is NOT a C function. It wants to return
|
|
+ * two values (the quotient and the remainder), but
|
|
+ * since that doesn't work very well in C, what it
|
|
+ * does is:
|
|
+ *
|
|
+ * - modifies the 64-bit dividend _in_place_
|
|
+ * - returns the 32-bit remainder
|
|
+ *
|
|
+ * This ends up being the most efficient "calling
|
|
+ * convention" on x86.
|
|
+ */
|
|
+#define do_div(n,base) ({ \
|
|
+ unsigned long __upper, __low, __high, __mod, __base; \
|
|
+ __base = (base); \
|
|
+ asm("":"=a" (__low), "=d" (__high):"A" (n)); \
|
|
+ __upper = __high; \
|
|
+ if (__high) { \
|
|
+ __upper = __high % (__base); \
|
|
+ __high = __high / (__base); \
|
|
+ } \
|
|
+ asm("divl %2":"=a" (__low), "=d" (__mod):"rm" (__base), "0" (__low), "1" (__upper)); \
|
|
+ asm("":"=A" (n):"a" (__low),"d" (__high)); \
|
|
+ __mod; \
|
|
+})
|
|
+
|
|
+/*
|
|
+ * (long)X = ((long long)divs) / (long)div
|
|
+ * (long)rem = ((long long)divs) % (long)div
|
|
+ *
|
|
+ * Warning, this will do an exception if X overflows.
|
|
+ */
|
|
+#define div_long_long_rem(a,b,c) div_ll_X_l_rem(a,b,c)
|
|
+
|
|
+static inline long
|
|
+div_ll_X_l_rem(long long divs, long div, long *rem)
|
|
+{
|
|
+ long dum2;
|
|
+ __asm__("divl %2":"=a"(dum2), "=d"(*rem)
|
|
+ : "rm"(div), "A"(divs));
|
|
+
|
|
+ return dum2;
|
|
+
|
|
+}
|
|
+#endif
|
|
Index: xen-3.3.1-testing/tools/xcutils/helper/x86_32/entry.S
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ xen-3.3.1-testing/tools/xcutils/helper/x86_32/entry.S
|
|
@@ -0,0 +1,49 @@
|
|
+#include "offsets.h"
|
|
+
|
|
+/* --- stack -------------------------------------------------------- */
|
|
+
|
|
+ .data
|
|
+stack_bottom:
|
|
+ .fill 4096,1,0
|
|
+stack_top:
|
|
+
|
|
+/* --- text --------------------------------------------------------- */
|
|
+
|
|
+ .text
|
|
+ .globl _start,_start_kernel,BUG,hypercall_page
|
|
+
|
|
+_start:
|
|
+ movl %esi, old_info
|
|
+ lea stack_top,%esp
|
|
+ jmp start_helper
|
|
+
|
|
+_start_kernel:
|
|
+ movl vcpu+vcpu_ebx,%ebx /* load registers */
|
|
+ movl vcpu+vcpu_ecx,%ecx
|
|
+ movl vcpu+vcpu_edx,%edx
|
|
+ movl vcpu+vcpu_esi,%esi
|
|
+ movl vcpu+vcpu_edi,%edi
|
|
+ movl vcpu+vcpu_ebp,%ebp
|
|
+ movl vcpu+vcpu_esp,%esp /* using new kernels boot stack now */
|
|
+
|
|
+ movl vcpu+vcpu_eip,%eax /* push entry point */
|
|
+ push %eax
|
|
+ movl vcpu+vcpu_eax,%eax /* push eax */
|
|
+ push %eax
|
|
+ movl vcpu+vcpu_cr3,%eax /* activate new pagetables */
|
|
+ movl %eax,%cr3
|
|
+ pop %eax /* reload eax */
|
|
+ ret /* jump to new kernel */
|
|
+
|
|
+BUG:
|
|
+ /* crash the domain, make xen dump registers */
|
|
+ ud2
|
|
+ jmp BUG
|
|
+
|
|
+/* --- hypercall page ----------------------------------------------- */
|
|
+
|
|
+ .align 4096
|
|
+hypercall_page:
|
|
+ nop
|
|
+ .align 4096
|
|
+hypercall_end:
|
|
Index: xen-3.3.1-testing/tools/xcutils/helper/x86_32/hypercall.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ xen-3.3.1-testing/tools/xcutils/helper/x86_32/hypercall.h
|
|
@@ -0,0 +1,359 @@
|
|
+/******************************************************************************
|
|
+ * hypercall.h
|
|
+ *
|
|
+ * Linux-specific hypervisor handling.
|
|
+ *
|
|
+ * Copyright (c) 2002-2004, K A Fraser
|
|
+ *
|
|
+ * 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; or, when distributed
|
|
+ * separately from the Linux kernel or incorporated into other
|
|
+ * software packages, subject to the following license:
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
+ * of this source file (the "Software"), to deal in the Software without
|
|
+ * restriction, including without limitation the rights to use, copy, modify,
|
|
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
|
+ * and to permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice shall be included in
|
|
+ * all copies or substantial portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
+ * IN THE SOFTWARE.
|
|
+ */
|
|
+
|
|
+#ifndef __HYPERCALL_H__
|
|
+#define __HYPERCALL_H__
|
|
+
|
|
+
|
|
+#define __STR(x) #x
|
|
+#define STR(x) __STR(x)
|
|
+
|
|
+#define _hypercall0(type, name) \
|
|
+({ \
|
|
+ long __res; \
|
|
+ asm volatile ( \
|
|
+ "call hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)"\
|
|
+ : "=a" (__res) \
|
|
+ : \
|
|
+ : "memory" ); \
|
|
+ (type)__res; \
|
|
+})
|
|
+
|
|
+#define _hypercall1(type, name, a1) \
|
|
+({ \
|
|
+ long __res, __ign1; \
|
|
+ asm volatile ( \
|
|
+ "call hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)"\
|
|
+ : "=a" (__res), "=b" (__ign1) \
|
|
+ : "1" ((long)(a1)) \
|
|
+ : "memory" ); \
|
|
+ (type)__res; \
|
|
+})
|
|
+
|
|
+#define _hypercall2(type, name, a1, a2) \
|
|
+({ \
|
|
+ long __res, __ign1, __ign2; \
|
|
+ asm volatile ( \
|
|
+ "call hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)"\
|
|
+ : "=a" (__res), "=b" (__ign1), "=c" (__ign2) \
|
|
+ : "1" ((long)(a1)), "2" ((long)(a2)) \
|
|
+ : "memory" ); \
|
|
+ (type)__res; \
|
|
+})
|
|
+
|
|
+#define _hypercall3(type, name, a1, a2, a3) \
|
|
+({ \
|
|
+ long __res, __ign1, __ign2, __ign3; \
|
|
+ asm volatile ( \
|
|
+ "call hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)"\
|
|
+ : "=a" (__res), "=b" (__ign1), "=c" (__ign2), \
|
|
+ "=d" (__ign3) \
|
|
+ : "1" ((long)(a1)), "2" ((long)(a2)), \
|
|
+ "3" ((long)(a3)) \
|
|
+ : "memory" ); \
|
|
+ (type)__res; \
|
|
+})
|
|
+
|
|
+#define _hypercall4(type, name, a1, a2, a3, a4) \
|
|
+({ \
|
|
+ long __res, __ign1, __ign2, __ign3, __ign4; \
|
|
+ asm volatile ( \
|
|
+ "call hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)"\
|
|
+ : "=a" (__res), "=b" (__ign1), "=c" (__ign2), \
|
|
+ "=d" (__ign3), "=S" (__ign4) \
|
|
+ : "1" ((long)(a1)), "2" ((long)(a2)), \
|
|
+ "3" ((long)(a3)), "4" ((long)(a4)) \
|
|
+ : "memory" ); \
|
|
+ (type)__res; \
|
|
+})
|
|
+
|
|
+#define _hypercall5(type, name, a1, a2, a3, a4, a5) \
|
|
+({ \
|
|
+ long __res, __ign1, __ign2, __ign3, __ign4, __ign5; \
|
|
+ asm volatile ( \
|
|
+ "call hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)"\
|
|
+ : "=a" (__res), "=b" (__ign1), "=c" (__ign2), \
|
|
+ "=d" (__ign3), "=S" (__ign4), "=D" (__ign5) \
|
|
+ : "1" ((long)(a1)), "2" ((long)(a2)), \
|
|
+ "3" ((long)(a3)), "4" ((long)(a4)), \
|
|
+ "5" ((long)(a5)) \
|
|
+ : "memory" ); \
|
|
+ (type)__res; \
|
|
+})
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_set_trap_table(
|
|
+ trap_info_t *table)
|
|
+{
|
|
+ return _hypercall1(int, set_trap_table, table);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_mmu_update(
|
|
+ mmu_update_t *req, int count, int *success_count, domid_t domid)
|
|
+{
|
|
+ return _hypercall4(int, mmu_update, req, count, success_count, domid);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_mmuext_op(
|
|
+ struct mmuext_op *op, int count, int *success_count, domid_t domid)
|
|
+{
|
|
+ return _hypercall4(int, mmuext_op, op, count, success_count, domid);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_set_gdt(
|
|
+ unsigned long *frame_list, int entries)
|
|
+{
|
|
+ return _hypercall2(int, set_gdt, frame_list, entries);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_stack_switch(
|
|
+ unsigned long ss, unsigned long esp)
|
|
+{
|
|
+ return _hypercall2(int, stack_switch, ss, esp);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_set_callbacks(
|
|
+ unsigned long event_selector, unsigned long event_address,
|
|
+ unsigned long failsafe_selector, unsigned long failsafe_address)
|
|
+{
|
|
+ return _hypercall4(int, set_callbacks,
|
|
+ event_selector, event_address,
|
|
+ failsafe_selector, failsafe_address);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_fpu_taskswitch(
|
|
+ int set)
|
|
+{
|
|
+ return _hypercall1(int, fpu_taskswitch, set);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_sched_op_compat(
|
|
+ int cmd, unsigned long arg)
|
|
+{
|
|
+ return _hypercall2(int, sched_op_compat, cmd, arg);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_sched_op(
|
|
+ int cmd, void *arg)
|
|
+{
|
|
+ return _hypercall2(int, sched_op, cmd, arg);
|
|
+}
|
|
+
|
|
+#if 0
|
|
+static inline long
|
|
+HYPERVISOR_set_timer_op(
|
|
+ u64 timeout)
|
|
+{
|
|
+ unsigned long timeout_hi = (unsigned long)(timeout>>32);
|
|
+ unsigned long timeout_lo = (unsigned long)timeout;
|
|
+ return _hypercall2(long, set_timer_op, timeout_lo, timeout_hi);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_dom0_op(
|
|
+ dom0_op_t *dom0_op)
|
|
+{
|
|
+ dom0_op->interface_version = DOM0_INTERFACE_VERSION;
|
|
+ return _hypercall1(int, dom0_op, dom0_op);
|
|
+}
|
|
+#endif
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_set_debugreg(
|
|
+ int reg, unsigned long value)
|
|
+{
|
|
+ return _hypercall2(int, set_debugreg, reg, value);
|
|
+}
|
|
+
|
|
+static inline unsigned long
|
|
+HYPERVISOR_get_debugreg(
|
|
+ int reg)
|
|
+{
|
|
+ return _hypercall1(unsigned long, get_debugreg, reg);
|
|
+}
|
|
+
|
|
+#if 0
|
|
+static inline int
|
|
+HYPERVISOR_update_descriptor(
|
|
+ u64 ma, u64 desc)
|
|
+{
|
|
+ return _hypercall4(int, update_descriptor, ma, ma>>32, desc, desc>>32);
|
|
+}
|
|
+#endif
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_memory_op(
|
|
+ unsigned int cmd, void *arg)
|
|
+{
|
|
+ return _hypercall2(int, memory_op, cmd, arg);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_multicall(
|
|
+ void *call_list, int nr_calls)
|
|
+{
|
|
+ return _hypercall2(int, multicall, call_list, nr_calls);
|
|
+}
|
|
+
|
|
+#if 0
|
|
+static inline int
|
|
+HYPERVISOR_update_va_mapping(
|
|
+ unsigned long va, pte_t new_val, unsigned long flags)
|
|
+{
|
|
+ unsigned long pte_hi = 0;
|
|
+#ifdef CONFIG_X86_PAE
|
|
+ pte_hi = new_val.pte_high;
|
|
+#endif
|
|
+ return _hypercall4(int, update_va_mapping, va,
|
|
+ new_val.pte_low, pte_hi, flags);
|
|
+}
|
|
+#endif
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_event_channel_op(
|
|
+ int cmd, void *arg)
|
|
+{
|
|
+ int rc = _hypercall2(int, event_channel_op, cmd, arg);
|
|
+ /* FIXME: handle -ENOSYS */
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_acm_op(
|
|
+ int cmd, void *arg)
|
|
+{
|
|
+ return _hypercall2(int, acm_op, cmd, arg);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_xen_version(
|
|
+ int cmd, void *arg)
|
|
+{
|
|
+ return _hypercall2(int, xen_version, cmd, arg);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_console_io(
|
|
+ int cmd, int count, char *str)
|
|
+{
|
|
+ return _hypercall3(int, console_io, cmd, count, str);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_physdev_op(
|
|
+ int cmd, void *arg)
|
|
+{
|
|
+ int rc = _hypercall2(int, physdev_op, cmd, arg);
|
|
+ /* FIXME: handle -ENOSYS */
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_grant_table_op(
|
|
+ unsigned int cmd, void *uop, unsigned int count)
|
|
+{
|
|
+ return _hypercall3(int, grant_table_op, cmd, uop, count);
|
|
+}
|
|
+
|
|
+#if 0
|
|
+static inline int
|
|
+HYPERVISOR_update_va_mapping_otherdomain(
|
|
+ unsigned long va, pte_t new_val, unsigned long flags, domid_t domid)
|
|
+{
|
|
+ unsigned long pte_hi = 0;
|
|
+#ifdef CONFIG_X86_PAE
|
|
+ pte_hi = new_val.pte_high;
|
|
+#endif
|
|
+ return _hypercall5(int, update_va_mapping_otherdomain, va,
|
|
+ new_val.pte_low, pte_hi, flags, domid);
|
|
+}
|
|
+#endif
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_vm_assist(
|
|
+ unsigned int cmd, unsigned int type)
|
|
+{
|
|
+ return _hypercall2(int, vm_assist, cmd, type);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_vcpu_op(
|
|
+ int cmd, int vcpuid, void *extra_args)
|
|
+{
|
|
+ return _hypercall3(int, vcpu_op, cmd, vcpuid, extra_args);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_suspend(
|
|
+ unsigned long srec)
|
|
+{
|
|
+ struct sched_shutdown sched_shutdown = {
|
|
+ .reason = SHUTDOWN_suspend
|
|
+ };
|
|
+
|
|
+ int rc = _hypercall3(int, sched_op, SCHEDOP_shutdown,
|
|
+ &sched_shutdown, srec);
|
|
+ /* FIXME: handle -ENOSYS */
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_nmi_op(
|
|
+ unsigned long op, void *arg)
|
|
+{
|
|
+ return _hypercall2(int, nmi_op, op, arg);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_callback_op(
|
|
+ int cmd, void *arg)
|
|
+{
|
|
+ return _hypercall2(int, callback_op, cmd, arg);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_xenoprof_op(
|
|
+ int op, void *arg)
|
|
+{
|
|
+ return _hypercall2(int, xenoprof_op, op, arg);
|
|
+}
|
|
+
|
|
+
|
|
+#endif /* __HYPERCALL_H__ */
|
|
Index: xen-3.3.1-testing/tools/xcutils/helper/x86_64/div64.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ xen-3.3.1-testing/tools/xcutils/helper/x86_64/div64.h
|
|
@@ -0,0 +1,57 @@
|
|
+#ifndef _ASM_GENERIC_DIV64_H
|
|
+#define _ASM_GENERIC_DIV64_H
|
|
+/*
|
|
+ * Copyright (C) 2003 Bernardo Innocenti <bernie@develer.com>
|
|
+ * Based on former asm-ppc/div64.h and asm-m68knommu/div64.h
|
|
+ *
|
|
+ * The semantics of do_div() are:
|
|
+ *
|
|
+ * uint32_t do_div(uint64_t *n, uint32_t base)
|
|
+ * {
|
|
+ * uint32_t remainder = *n % base;
|
|
+ * *n = *n / base;
|
|
+ * return remainder;
|
|
+ * }
|
|
+ *
|
|
+ * NOTE: macro parameter n is evaluated multiple times,
|
|
+ * beware of side effects!
|
|
+ */
|
|
+
|
|
+#include <linux/types.h>
|
|
+
|
|
+#if BITS_PER_LONG == 64
|
|
+
|
|
+# define do_div(n,base) ({ \
|
|
+ uint32_t __base = (base); \
|
|
+ uint32_t __rem; \
|
|
+ __rem = ((uint64_t)(n)) % __base; \
|
|
+ (n) = ((uint64_t)(n)) / __base; \
|
|
+ __rem; \
|
|
+ })
|
|
+
|
|
+#elif BITS_PER_LONG == 32
|
|
+
|
|
+extern uint32_t __div64_32(uint64_t *dividend, uint32_t divisor);
|
|
+
|
|
+/* The unnecessary pointer compare is there
|
|
+ * to check for type safety (n must be 64bit)
|
|
+ */
|
|
+# define do_div(n,base) ({ \
|
|
+ uint32_t __base = (base); \
|
|
+ uint32_t __rem; \
|
|
+ (void)(((typeof((n)) *)0) == ((uint64_t *)0)); \
|
|
+ if (likely(((n) >> 32) == 0)) { \
|
|
+ __rem = (uint32_t)(n) % __base; \
|
|
+ (n) = (uint32_t)(n) / __base; \
|
|
+ } else \
|
|
+ __rem = __div64_32(&(n), __base); \
|
|
+ __rem; \
|
|
+ })
|
|
+
|
|
+#else /* BITS_PER_LONG == ?? */
|
|
+
|
|
+# error do_div() does not yet support the C64
|
|
+
|
|
+#endif /* BITS_PER_LONG */
|
|
+
|
|
+#endif /* _ASM_GENERIC_DIV64_H */
|
|
Index: xen-3.3.1-testing/tools/xcutils/helper/x86_64/entry.S
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ xen-3.3.1-testing/tools/xcutils/helper/x86_64/entry.S
|
|
@@ -0,0 +1,50 @@
|
|
+#include "offsets.h"
|
|
+
|
|
+/* --- stack -------------------------------------------------------- */
|
|
+
|
|
+ .data
|
|
+stack_bottom:
|
|
+ .fill 4096,1,0
|
|
+stack_top:
|
|
+
|
|
+/* --- text --------------------------------------------------------- */
|
|
+
|
|
+ .text
|
|
+ .globl _start,_start_kernel,hypercall_page,BUG
|
|
+
|
|
+_start:
|
|
+ /* setup stack */
|
|
+ movq %rsi, old_info
|
|
+ lea stack_top,%rsp
|
|
+ jmp start_helper
|
|
+
|
|
+_start_kernel:
|
|
+ movq vcpu+vcpu_ebx,%rbx /* load registers */
|
|
+ movq vcpu+vcpu_ecx,%rcx
|
|
+ movq vcpu+vcpu_edx,%rdx
|
|
+ movq vcpu+vcpu_esi,%rsi
|
|
+ movq vcpu+vcpu_edi,%rdi
|
|
+ movq vcpu+vcpu_ebp,%rbp
|
|
+ movq vcpu+vcpu_esp,%rsp /* using new kernels boot stack now */
|
|
+
|
|
+ movq vcpu+vcpu_eip,%rax
|
|
+ push %rax
|
|
+ movq vcpu+vcpu_eax,%rax
|
|
+ push %rax
|
|
+ movq vcpu+vcpu_cr3,%rax
|
|
+ movq %rax,%cr3 /* activate new pagetables */
|
|
+ pop %rax /* load rax */
|
|
+ ret /* jump to new kernel */
|
|
+
|
|
+BUG:
|
|
+ /* crash the domain, make xen dump registers */
|
|
+ ud2
|
|
+ jmp BUG
|
|
+
|
|
+/* --- hypercall page ----------------------------------------------- */
|
|
+
|
|
+ .align 4096
|
|
+hypercall_page:
|
|
+ nop
|
|
+ .align 4096
|
|
+hypercall_end:
|
|
Index: xen-3.3.1-testing/tools/xcutils/helper/x86_64/hypercall.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ xen-3.3.1-testing/tools/xcutils/helper/x86_64/hypercall.h
|
|
@@ -0,0 +1,354 @@
|
|
+/******************************************************************************
|
|
+ * hypercall.h
|
|
+ *
|
|
+ * Linux-specific hypervisor handling.
|
|
+ *
|
|
+ * Copyright (c) 2002-2004, K A Fraser
|
|
+ *
|
|
+ * 64-bit updates:
|
|
+ * Benjamin Liu <benjamin.liu@intel.com>
|
|
+ * Jun Nakajima <jun.nakajima@intel.com>
|
|
+ *
|
|
+ * 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; or, when distributed
|
|
+ * separately from the Linux kernel or incorporated into other
|
|
+ * software packages, subject to the following license:
|
|
+ *
|
|
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
+ * of this source file (the "Software"), to deal in the Software without
|
|
+ * restriction, including without limitation the rights to use, copy, modify,
|
|
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
|
+ * and to permit persons to whom the Software is furnished to do so, subject to
|
|
+ * the following conditions:
|
|
+ *
|
|
+ * The above copyright notice and this permission notice shall be included in
|
|
+ * all copies or substantial portions of the Software.
|
|
+ *
|
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
+ * IN THE SOFTWARE.
|
|
+ */
|
|
+
|
|
+#ifndef __HYPERCALL_H__
|
|
+#define __HYPERCALL_H__
|
|
+
|
|
+
|
|
+#define __STR(x) #x
|
|
+#define STR(x) __STR(x)
|
|
+
|
|
+#define _hypercall0(type, name) \
|
|
+({ \
|
|
+ long __res; \
|
|
+ asm volatile ( \
|
|
+ "call hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)"\
|
|
+ : "=a" (__res) \
|
|
+ : \
|
|
+ : "memory" ); \
|
|
+ (type)__res; \
|
|
+})
|
|
+
|
|
+#define _hypercall1(type, name, a1) \
|
|
+({ \
|
|
+ long __res, __ign1; \
|
|
+ asm volatile ( \
|
|
+ "call hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)"\
|
|
+ : "=a" (__res), "=D" (__ign1) \
|
|
+ : "1" ((long)(a1)) \
|
|
+ : "memory" ); \
|
|
+ (type)__res; \
|
|
+})
|
|
+
|
|
+#define _hypercall2(type, name, a1, a2) \
|
|
+({ \
|
|
+ long __res, __ign1, __ign2; \
|
|
+ asm volatile ( \
|
|
+ "call hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)"\
|
|
+ : "=a" (__res), "=D" (__ign1), "=S" (__ign2) \
|
|
+ : "1" ((long)(a1)), "2" ((long)(a2)) \
|
|
+ : "memory" ); \
|
|
+ (type)__res; \
|
|
+})
|
|
+
|
|
+#define _hypercall3(type, name, a1, a2, a3) \
|
|
+({ \
|
|
+ long __res, __ign1, __ign2, __ign3; \
|
|
+ asm volatile ( \
|
|
+ "call hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)"\
|
|
+ : "=a" (__res), "=D" (__ign1), "=S" (__ign2), \
|
|
+ "=d" (__ign3) \
|
|
+ : "1" ((long)(a1)), "2" ((long)(a2)), \
|
|
+ "3" ((long)(a3)) \
|
|
+ : "memory" ); \
|
|
+ (type)__res; \
|
|
+})
|
|
+
|
|
+#define _hypercall4(type, name, a1, a2, a3, a4) \
|
|
+({ \
|
|
+ long __res, __ign1, __ign2, __ign3; \
|
|
+ asm volatile ( \
|
|
+ "movq %7,%%r10; " \
|
|
+ "call hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)"\
|
|
+ : "=a" (__res), "=D" (__ign1), "=S" (__ign2), \
|
|
+ "=d" (__ign3) \
|
|
+ : "1" ((long)(a1)), "2" ((long)(a2)), \
|
|
+ "3" ((long)(a3)), "g" ((long)(a4)) \
|
|
+ : "memory", "r10" ); \
|
|
+ (type)__res; \
|
|
+})
|
|
+
|
|
+#define _hypercall5(type, name, a1, a2, a3, a4, a5) \
|
|
+({ \
|
|
+ long __res, __ign1, __ign2, __ign3; \
|
|
+ asm volatile ( \
|
|
+ "movq %7,%%r10; movq %8,%%r8; " \
|
|
+ "call hypercall_page + ("STR(__HYPERVISOR_##name)" * 32)"\
|
|
+ : "=a" (__res), "=D" (__ign1), "=S" (__ign2), \
|
|
+ "=d" (__ign3) \
|
|
+ : "1" ((long)(a1)), "2" ((long)(a2)), \
|
|
+ "3" ((long)(a3)), "g" ((long)(a4)), \
|
|
+ "g" ((long)(a5)) \
|
|
+ : "memory", "r10", "r8" ); \
|
|
+ (type)__res; \
|
|
+})
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_set_trap_table(
|
|
+ trap_info_t *table)
|
|
+{
|
|
+ return _hypercall1(int, set_trap_table, table);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_mmu_update(
|
|
+ mmu_update_t *req, int count, int *success_count, domid_t domid)
|
|
+{
|
|
+ return _hypercall4(int, mmu_update, req, count, success_count, domid);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_mmuext_op(
|
|
+ struct mmuext_op *op, int count, int *success_count, domid_t domid)
|
|
+{
|
|
+ return _hypercall4(int, mmuext_op, op, count, success_count, domid);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_set_gdt(
|
|
+ unsigned long *frame_list, int entries)
|
|
+{
|
|
+ return _hypercall2(int, set_gdt, frame_list, entries);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_stack_switch(
|
|
+ unsigned long ss, unsigned long esp)
|
|
+{
|
|
+ return _hypercall2(int, stack_switch, ss, esp);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_set_callbacks(
|
|
+ unsigned long event_address, unsigned long failsafe_address,
|
|
+ unsigned long syscall_address)
|
|
+{
|
|
+ return _hypercall3(int, set_callbacks,
|
|
+ event_address, failsafe_address, syscall_address);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_fpu_taskswitch(
|
|
+ int set)
|
|
+{
|
|
+ return _hypercall1(int, fpu_taskswitch, set);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_sched_op_compat(
|
|
+ int cmd, unsigned long arg)
|
|
+{
|
|
+ return _hypercall2(int, sched_op_compat, cmd, arg);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_sched_op(
|
|
+ int cmd, void *arg)
|
|
+{
|
|
+ return _hypercall2(int, sched_op, cmd, arg);
|
|
+}
|
|
+
|
|
+#if 0
|
|
+static inline long
|
|
+HYPERVISOR_set_timer_op(
|
|
+ u64 timeout)
|
|
+{
|
|
+ return _hypercall1(long, set_timer_op, timeout);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_dom0_op(
|
|
+ dom0_op_t *dom0_op)
|
|
+{
|
|
+ dom0_op->interface_version = DOM0_INTERFACE_VERSION;
|
|
+ return _hypercall1(int, dom0_op, dom0_op);
|
|
+}
|
|
+#endif
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_set_debugreg(
|
|
+ int reg, unsigned long value)
|
|
+{
|
|
+ return _hypercall2(int, set_debugreg, reg, value);
|
|
+}
|
|
+
|
|
+static inline unsigned long
|
|
+HYPERVISOR_get_debugreg(
|
|
+ int reg)
|
|
+{
|
|
+ return _hypercall1(unsigned long, get_debugreg, reg);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_update_descriptor(
|
|
+ unsigned long ma, unsigned long word)
|
|
+{
|
|
+ return _hypercall2(int, update_descriptor, ma, word);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_memory_op(
|
|
+ unsigned int cmd, void *arg)
|
|
+{
|
|
+ return _hypercall2(int, memory_op, cmd, arg);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_multicall(
|
|
+ void *call_list, int nr_calls)
|
|
+{
|
|
+ return _hypercall2(int, multicall, call_list, nr_calls);
|
|
+}
|
|
+
|
|
+#if 0
|
|
+static inline int
|
|
+HYPERVISOR_update_va_mapping(
|
|
+ unsigned long va, pte_t new_val, unsigned long flags)
|
|
+{
|
|
+ return _hypercall3(int, update_va_mapping, va, new_val.pte, flags);
|
|
+}
|
|
+#endif
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_event_channel_op(
|
|
+ int cmd, void *arg)
|
|
+{
|
|
+ int rc = _hypercall2(int, event_channel_op, cmd, arg);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_acm_op(
|
|
+ int cmd, void *arg)
|
|
+{
|
|
+ return _hypercall2(int, acm_op, cmd, arg);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_xen_version(
|
|
+ int cmd, void *arg)
|
|
+{
|
|
+ return _hypercall2(int, xen_version, cmd, arg);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_console_io(
|
|
+ int cmd, int count, char *str)
|
|
+{
|
|
+ return _hypercall3(int, console_io, cmd, count, str);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_physdev_op(
|
|
+ int cmd, void *arg)
|
|
+{
|
|
+ int rc = _hypercall2(int, physdev_op, cmd, arg);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_grant_table_op(
|
|
+ unsigned int cmd, void *uop, unsigned int count)
|
|
+{
|
|
+ return _hypercall3(int, grant_table_op, cmd, uop, count);
|
|
+}
|
|
+
|
|
+#if 0
|
|
+static inline int
|
|
+HYPERVISOR_update_va_mapping_otherdomain(
|
|
+ unsigned long va, pte_t new_val, unsigned long flags, domid_t domid)
|
|
+{
|
|
+ return _hypercall4(int, update_va_mapping_otherdomain, va,
|
|
+ new_val.pte, flags, domid);
|
|
+}
|
|
+#endif
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_vm_assist(
|
|
+ unsigned int cmd, unsigned int type)
|
|
+{
|
|
+ return _hypercall2(int, vm_assist, cmd, type);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_vcpu_op(
|
|
+ int cmd, int vcpuid, void *extra_args)
|
|
+{
|
|
+ return _hypercall3(int, vcpu_op, cmd, vcpuid, extra_args);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_set_segment_base(
|
|
+ int reg, unsigned long value)
|
|
+{
|
|
+ return _hypercall2(int, set_segment_base, reg, value);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_suspend(
|
|
+ unsigned long srec)
|
|
+{
|
|
+ struct sched_shutdown sched_shutdown = {
|
|
+ .reason = SHUTDOWN_suspend
|
|
+ };
|
|
+
|
|
+ int rc = _hypercall3(int, sched_op, SCHEDOP_shutdown,
|
|
+ &sched_shutdown, srec);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_nmi_op(
|
|
+ unsigned long op, void *arg)
|
|
+{
|
|
+ return _hypercall2(int, nmi_op, op, arg);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_callback_op(
|
|
+ int cmd, void *arg)
|
|
+{
|
|
+ return _hypercall2(int, callback_op, cmd, arg);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+HYPERVISOR_xenoprof_op(
|
|
+ int op, void *arg)
|
|
+{
|
|
+ return _hypercall2(int, xenoprof_op, op, arg);
|
|
+}
|
|
+
|
|
+#endif /* __HYPERCALL_H__ */
|
|
Index: xen-3.3.1-testing/tools/xcutils/kexec-syscall.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ xen-3.3.1-testing/tools/xcutils/kexec-syscall.h
|
|
@@ -0,0 +1,80 @@
|
|
+#ifndef KEXEC_SYSCALL_H
|
|
+#define KEXEC_SYSCALL_H
|
|
+
|
|
+#define __LIBRARY__
|
|
+#include <syscall.h>
|
|
+#include <sys/syscall.h>
|
|
+#include <unistd.h>
|
|
+
|
|
+#define LINUX_REBOOT_MAGIC1 0xfee1dead
|
|
+#define LINUX_REBOOT_MAGIC2 672274793
|
|
+#define LINUX_REBOOT_MAGIC2A 85072278
|
|
+#define LINUX_REBOOT_MAGIC2B 369367448
|
|
+
|
|
+#define LINUX_REBOOT_CMD_RESTART 0x01234567
|
|
+#define LINUX_REBOOT_CMD_HALT 0xCDEF0123
|
|
+#define LINUX_REBOOT_CMD_CAD_ON 0x89ABCDEF
|
|
+#define LINUX_REBOOT_CMD_CAD_OFF 0x00000000
|
|
+#define LINUX_REBOOT_CMD_POWER_OFF 0x4321FEDC
|
|
+#define LINUX_REBOOT_CMD_RESTART2 0xA1B2C3D4
|
|
+#define LINUX_REBOOT_CMD_EXEC_KERNEL 0x18273645
|
|
+#define LINUX_REBOOT_CMD_KEXEC_OLD 0x81726354
|
|
+#define LINUX_REBOOT_CMD_KEXEC_OLD2 0x18263645
|
|
+#define LINUX_REBOOT_CMD_KEXEC 0x45584543
|
|
+
|
|
+#ifdef __i386__
|
|
+#define __NR_kexec_load 283
|
|
+#endif
|
|
+#ifdef __ia64__
|
|
+#define __NR_kexec_load 1268
|
|
+#endif
|
|
+#ifdef __powerpc64__
|
|
+#define __NR_kexec_load 268
|
|
+#endif
|
|
+#ifdef __powerpc__
|
|
+#define __NR_kexec_load 268
|
|
+#endif
|
|
+#ifdef __x86_64__
|
|
+#define __NR_kexec_load 246
|
|
+#endif
|
|
+#ifdef __s390x__
|
|
+#define __NR_kexec_load 277
|
|
+#endif
|
|
+#ifdef __s390__
|
|
+#define __NR_kexec_load 277
|
|
+#endif
|
|
+#ifndef __NR_kexec_load
|
|
+#error Unknown processor architecture. Needs a kexec_load syscall number.
|
|
+#endif
|
|
+
|
|
+struct kexec_segment;
|
|
+
|
|
+static inline long kexec_load(void *entry, unsigned long nr_segments,
|
|
+ struct kexec_segment *segments, unsigned long flags)
|
|
+{
|
|
+ return (long) syscall(__NR_kexec_load, entry, nr_segments, segments, flags);
|
|
+}
|
|
+
|
|
+static inline long kexec_reboot(void)
|
|
+{
|
|
+ return (long) syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_KEXEC, 0);
|
|
+}
|
|
+
|
|
+
|
|
+#define KEXEC_ON_CRASH 0x00000001
|
|
+#define KEXEC_ARCH_MASK 0xffff0000
|
|
+
|
|
+/* These values match the ELF architecture values.
|
|
+ * Unless there is a good reason that should continue to be the case.
|
|
+ */
|
|
+#define KEXEC_ARCH_DEFAULT ( 0 << 16)
|
|
+#define KEXEC_ARCH_386 ( 3 << 16)
|
|
+#define KEXEC_ARCH_X86_64 (62 << 16)
|
|
+#define KEXEC_ARCH_PPC (20 << 16)
|
|
+#define KEXEC_ARCH_PPC64 (21 << 16)
|
|
+#define KEXEC_ARCH_IA_64 (50 << 16)
|
|
+#define KEXEC_ARCH_S390 (22 << 16)
|
|
+
|
|
+#define KEXEC_MAX_SEGMENTS 16
|
|
+
|
|
+#endif /* KEXEC_SYSCALL_H */
|
|
Index: xen-3.3.1-testing/tools/xcutils/xc_kexec.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ xen-3.3.1-testing/tools/xcutils/xc_kexec.c
|
|
@@ -0,0 +1,503 @@
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+#include <unistd.h>
|
|
+#include <string.h>
|
|
+#include <inttypes.h>
|
|
+#include <malloc.h>
|
|
+#include <sys/mman.h>
|
|
+
|
|
+#include <xg_private.h>
|
|
+#include <xc_dom.h>
|
|
+#include <xen/libelf.h>
|
|
+
|
|
+#include "kexec-syscall.h"
|
|
+
|
|
+/* ------------------------------------------------------------------ */
|
|
+
|
|
+static unsigned long debug_level;
|
|
+
|
|
+static char hypercall_page[PAGE_SIZE];
|
|
+static unsigned long max_pfn;
|
|
+static unsigned long xen_start_info;
|
|
+static start_info_t start_info;
|
|
+
|
|
+/* -------------------------------------------------------------------------------- */
|
|
+
|
|
+static unsigned long kernel_find_symbol(char *name)
|
|
+{
|
|
+ FILE *file;
|
|
+ char line[256], type, symbol[256];
|
|
+ unsigned long addr, retval = 0;
|
|
+
|
|
+ file = fopen("/proc/kallsyms", "r");
|
|
+ if (NULL == file) {
|
|
+ perror("open /proc/kallsyms");
|
|
+ exit(1);
|
|
+ }
|
|
+ while (NULL != fgets(line, sizeof(line), file)) {
|
|
+ if (3 != sscanf(line, "%lx %c %127s", &addr, &type, symbol)) {
|
|
+ fprintf(stderr,"parse error: %s\n", line);
|
|
+ exit(1);
|
|
+ }
|
|
+ if (0 == strcmp(name, symbol)) {
|
|
+ retval = addr;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ fclose(file);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+static int kernel_core_read(void *dest, off_t start, size_t size)
|
|
+{
|
|
+ FILE *file;
|
|
+ int rc;
|
|
+
|
|
+ file = fopen("/proc/kcore", "r");
|
|
+ if (NULL == file) {
|
|
+ perror("open /proc/kcore");
|
|
+ exit(1);
|
|
+ }
|
|
+ rc = fseek(file, start, SEEK_SET);
|
|
+ if (rc < 0)
|
|
+ goto out;
|
|
+ rc = fread(dest, 1, size, file);
|
|
+ fclose(file);
|
|
+
|
|
+ out:
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static off_t kernel_core_offset(unsigned long addr)
|
|
+{
|
|
+ static void *core_header = NULL;
|
|
+ static struct elf_binary elf;
|
|
+ const elf_phdr *phdr;
|
|
+ uint64_t p_type;
|
|
+ uint64_t p_vaddr;
|
|
+ uint64_t p_offset;
|
|
+ uint64_t p_filesz;
|
|
+ off_t offset = 0;
|
|
+ int h;
|
|
+
|
|
+ if (NULL == core_header) {
|
|
+ core_header = malloc(PAGE_SIZE);
|
|
+ if (PAGE_SIZE != kernel_core_read(core_header, 0, PAGE_SIZE))
|
|
+ goto out;
|
|
+ elf_init(&elf, core_header, PAGE_SIZE);
|
|
+ }
|
|
+
|
|
+ for (h = 0; h < elf_uval(&elf, elf.ehdr, e_phnum); h++) {
|
|
+ phdr = elf_phdr_by_index(&elf, h);
|
|
+ p_type = elf_uval(&elf, phdr, p_type);
|
|
+ p_vaddr = elf_uval(&elf, phdr, p_vaddr);
|
|
+ p_offset = elf_uval(&elf, phdr, p_offset);
|
|
+ p_filesz = elf_uval(&elf, phdr, p_filesz);
|
|
+ if (p_type != PT_LOAD)
|
|
+ continue;
|
|
+ if (addr > p_vaddr && addr < p_vaddr + p_filesz)
|
|
+ offset = p_offset + (addr - p_vaddr);
|
|
+ }
|
|
+
|
|
+ out:
|
|
+ return offset;
|
|
+}
|
|
+
|
|
+static int kernel_core_by_addr(void *dest, unsigned long addr, size_t size)
|
|
+{
|
|
+ off_t offset;
|
|
+ int rc = 0;
|
|
+
|
|
+ offset = kernel_core_offset(addr);
|
|
+ if (0 == addr) {
|
|
+ fprintf(stderr, "%s: addr 0x%lx not in /proc/kcore\n",
|
|
+ __FUNCTION__, addr);
|
|
+ goto out;
|
|
+ }
|
|
+ rc = kernel_core_read(dest, offset, size);
|
|
+ if (debug_level)
|
|
+ fprintf(stderr, "%s: vaddr %lx, offset %lx, %d bytes\n",
|
|
+ __FUNCTION__, addr, (unsigned long)offset, rc);
|
|
+
|
|
+ out:
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+static int kernel_core_by_name(void *dest, char *name, size_t size)
|
|
+{
|
|
+ unsigned long addr;
|
|
+ off_t offset;
|
|
+ int rc = 0;
|
|
+
|
|
+ addr = kernel_find_symbol(name);
|
|
+ if (0 == addr) {
|
|
+ fprintf(stderr, "%s: can't find symbol \"%s\" in /proc/kallsyms\n",
|
|
+ __FUNCTION__, name);
|
|
+ fprintf(stderr, "%s: try CONFIG_KALLSYMS_ALL=y\n", __FUNCTION__);
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ offset = kernel_core_offset(addr);
|
|
+ if (0 == addr) {
|
|
+ fprintf(stderr, "%s: symbol \"%s\" (0x%lx) not in /proc/kcore\n",
|
|
+ __FUNCTION__, name, addr);
|
|
+ goto out;
|
|
+ }
|
|
+ rc = kernel_core_read(dest, offset, size);
|
|
+ if (debug_level)
|
|
+ fprintf(stderr, "%s: vaddr %lx, offset %lx, %d bytes (%s)\n",
|
|
+ __FUNCTION__, addr, (unsigned long)offset, rc, name);
|
|
+
|
|
+ out:
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------------ */
|
|
+
|
|
+struct kexec_segment {
|
|
+ const void *buf;
|
|
+ size_t bufsz;
|
|
+ const void *mem;
|
|
+ size_t memsz;
|
|
+};
|
|
+
|
|
+struct kexec_segment segments[KEXEC_MAX_SEGMENTS];
|
|
+static unsigned int nsegments;
|
|
+static void *entry;
|
|
+
|
|
+static void kexec_add_segment(struct xc_dom_image *dom,
|
|
+ struct xc_dom_seg *xc_seg,
|
|
+ struct kexec_segment *kseg)
|
|
+{
|
|
+ if (0 == xc_seg->vend - xc_seg->vstart)
|
|
+ return;
|
|
+
|
|
+ kseg->buf = xc_dom_seg_to_ptr(dom, xc_seg);
|
|
+ kseg->bufsz = xc_seg->vend - xc_seg->vstart;
|
|
+ kseg->mem = (void*)(xc_seg->pfn << PAGE_SHIFT);
|
|
+ kseg->memsz = kseg->bufsz;
|
|
+ if (debug_level)
|
|
+ fprintf(stderr," kexec segment: 0x%08lx-0x%08lx (+0x%08lx)\n",
|
|
+ (unsigned long)kseg->mem,
|
|
+ (unsigned long)(kseg->mem+kseg->memsz-1),
|
|
+ (unsigned long)kseg->memsz);
|
|
+}
|
|
+
|
|
+static void kexec_add_page(struct xc_dom_image *dom, xen_pfn_t pfn,
|
|
+ struct kexec_segment *kseg)
|
|
+{
|
|
+ if (0 == pfn)
|
|
+ return;
|
|
+
|
|
+ kseg->buf = xc_dom_pfn_to_ptr(dom, pfn, 1);
|
|
+ kseg->bufsz = PAGE_SIZE;
|
|
+ kseg->mem = (void*)(pfn << PAGE_SHIFT);
|
|
+ kseg->memsz = kseg->bufsz;
|
|
+ if (debug_level)
|
|
+ fprintf(stderr," kexec page : 0x%08lx-0x%08lx\n",
|
|
+ (unsigned long)kseg->mem,
|
|
+ (unsigned long)(kseg->mem+kseg->memsz-1));
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------------ */
|
|
+
|
|
+static const char helper_blob[] = {
|
|
+#include "helper/blob.h"
|
|
+};
|
|
+
|
|
+static void helper_parse(struct xc_dom_image *dom,
|
|
+ struct elf_binary *elf)
|
|
+{
|
|
+ if (debug_level)
|
|
+ fprintf(stderr, "%s\n", __FUNCTION__);
|
|
+ elf_init(elf, helper_blob, sizeof(helper_blob));
|
|
+ if (debug_level)
|
|
+ elf_set_logfile(elf, stderr, debug_level);
|
|
+ elf_parse_binary(elf);
|
|
+ elf->pstart = elf->pstart & PAGE_MASK;
|
|
+ elf->pend = (elf->pend + PAGE_SIZE - 1) & PAGE_MASK;
|
|
+
|
|
+ dom->extra_pages = (elf->pend - elf->pstart) >> PAGE_SHIFT;
|
|
+}
|
|
+
|
|
+static void *helper_load(struct xc_dom_image *dom,
|
|
+ struct elf_binary *elf)
|
|
+{
|
|
+ struct xc_dom_seg seg;
|
|
+ uint64_t entry;
|
|
+
|
|
+ if (debug_level)
|
|
+ fprintf(stderr, "%s\n", __FUNCTION__);
|
|
+
|
|
+ xc_dom_alloc_segment(dom, &seg, "kexec", 0, elf->pend - elf->pstart);
|
|
+ elf->dest = xc_dom_seg_to_ptr(dom, &seg);
|
|
+ elf->reloc_offset = seg.vstart - dom->parms.virt_base - elf->pstart;
|
|
+ if (debug_level)
|
|
+ fprintf(stderr,"%s: relocation offset: 0x%" PRIx64 "\n",
|
|
+ __FUNCTION__, elf->reloc_offset);
|
|
+
|
|
+ if (NULL == elf->dest)
|
|
+ goto err;
|
|
+ elf_load_binary(elf);
|
|
+ if (-1 == elf_reloc(elf))
|
|
+ goto err;
|
|
+ kexec_add_segment(dom, &seg, &segments[nsegments++]);
|
|
+
|
|
+ entry = elf_uval(elf, elf->ehdr, e_entry);
|
|
+ return (void*)((intptr_t)(entry + elf->reloc_offset));
|
|
+
|
|
+ err:
|
|
+ exit(1);
|
|
+}
|
|
+
|
|
+static void* ptr_symbol(struct elf_binary *elf, const char *symbol)
|
|
+{
|
|
+ unsigned long addr;
|
|
+
|
|
+ addr = elf_lookup_addr(elf, symbol);
|
|
+ if (-1 == addr)
|
|
+ exit(1);
|
|
+ return elf_get_ptr(elf, addr);
|
|
+}
|
|
+
|
|
+static void helper_pass_info(struct xc_dom_image *dom, struct elf_binary *elf)
|
|
+{
|
|
+ struct vcpu_guest_context ctxt;
|
|
+ struct vcpu_guest_context *vcpu;
|
|
+ unsigned long *addr;
|
|
+ void *page;
|
|
+
|
|
+ if (debug_level)
|
|
+ fprintf(stderr, "%s\n", __FUNCTION__);
|
|
+ dom->arch_hooks->vcpu(dom, &ctxt);
|
|
+ vcpu = ptr_symbol(elf, "vcpu");
|
|
+ *vcpu = ctxt;
|
|
+ addr = ptr_symbol(elf, "virt_base");
|
|
+ *addr = dom->parms.virt_base;
|
|
+ addr = ptr_symbol(elf, "virt_hypercall");
|
|
+ *addr = dom->parms.virt_hypercall;
|
|
+#if defined(__i386__)
|
|
+ addr = ptr_symbol(elf, "pae_paging");
|
|
+ *addr = dom->parms.pae;
|
|
+#endif
|
|
+ addr = ptr_symbol(elf, "debug_level");
|
|
+ *addr = debug_level;
|
|
+ page = ptr_symbol(elf, "hypercall_page");
|
|
+ memcpy(page, hypercall_page, PAGE_SIZE);
|
|
+}
|
|
+
|
|
+/* ------------------------------------------------------------------ */
|
|
+
|
|
+static char *kernel_name = "/boot/vmlinuz-xen";
|
|
+static char *ramdisk_name;
|
|
+static char *cmdline;
|
|
+static char *features; // = "auto_translated_physmap";
|
|
+static int do_exec = 0;
|
|
+static int do_load = 1;
|
|
+static int do_type = 0;
|
|
+
|
|
+static void usage(FILE *fp)
|
|
+{
|
|
+ fprintf(fp,
|
|
+ "\n"
|
|
+ "This is a xen kexec loader.\n"
|
|
+ "\n"
|
|
+ "usage: xen-kexec [ options ]\n"
|
|
+ "options:\n"
|
|
+ " -h print this text\n"
|
|
+ " -d enable debug messages\n"
|
|
+ " -e boot loaded kernel [%s]\n"
|
|
+ " -l load kernel [%s]\n"
|
|
+ " -k <file> kernel image [%s]\n"
|
|
+ " -i <file> initrd [%s]\n"
|
|
+ " -c <args> kernel command line [%s]\n"
|
|
+ " -f <feat> xen features [%s]\n"
|
|
+ " -t print kernel type\n"
|
|
+ "\n"
|
|
+ "(c) 2006 Gerd Hoffmann <kraxel@suse.de>\n",
|
|
+ do_exec ? "yes" : "no",
|
|
+ do_load ? "yes" : "no",
|
|
+ kernel_name, ramdisk_name,
|
|
+ cmdline ? : "-",
|
|
+ features ? : "-");
|
|
+}
|
|
+
|
|
+static void print_type(char *filename)
|
|
+{
|
|
+ struct xc_dom_image *dom;
|
|
+
|
|
+ dom = xc_dom_allocate(cmdline, features);
|
|
+ if (0 != xc_dom_kernel_file(dom, filename)) {
|
|
+ fprintf(stderr, "can't open %s: %s\n", filename, strerror(errno));
|
|
+ goto done;
|
|
+ }
|
|
+ if (0 != xc_dom_parse_image(dom)) {
|
|
+ printf("unknown-kernel\n");
|
|
+ goto done;
|
|
+ }
|
|
+#if defined(__i386__) || defined(__x86_64__)
|
|
+ if (NULL == dom->parms.elf_note_start && NULL == dom->parms.guest_info) {
|
|
+ printf("other-elf-kernel\n");
|
|
+ goto done;
|
|
+ }
|
|
+#endif
|
|
+ printf("%s\n", dom->guest_type);
|
|
+
|
|
+ done:
|
|
+ xc_dom_release(dom);
|
|
+}
|
|
+
|
|
+int main(int argc, char *argv[])
|
|
+{
|
|
+ struct xc_dom_image *dom = NULL;
|
|
+ struct elf_binary helper;
|
|
+ int fd,rc,c,xc;
|
|
+
|
|
+ /* figure defaults */
|
|
+ fd = open("/proc/cmdline", O_RDONLY);
|
|
+ if (-1 != fd) {
|
|
+ cmdline = malloc(1024);
|
|
+ rc = read(fd,cmdline,1024);
|
|
+ cmdline[rc-1] = 0;
|
|
+ close(fd);
|
|
+ }
|
|
+
|
|
+ /* parse args */
|
|
+ for (;;) {
|
|
+ if (-1 == (c = getopt(argc, argv, "hdletk:i:c:f:")))
|
|
+ break;
|
|
+ switch (c) {
|
|
+ case 'k':
|
|
+ kernel_name = optarg;
|
|
+ break;
|
|
+ case 'i':
|
|
+ if (strlen(optarg))
|
|
+ ramdisk_name = optarg;
|
|
+ else
|
|
+ ramdisk_name = NULL;
|
|
+ break;
|
|
+ case 'c':
|
|
+ cmdline = optarg;
|
|
+ break;
|
|
+ case 'f':
|
|
+ features = optarg;
|
|
+ break;
|
|
+ case 'd':
|
|
+ debug_level++;
|
|
+ break;
|
|
+
|
|
+ case 'e':
|
|
+ do_exec = 1;
|
|
+ do_load = 0;
|
|
+ do_type = 0;
|
|
+ break;
|
|
+ case 'l':
|
|
+ do_exec = 0;
|
|
+ do_load = 1;
|
|
+ do_type = 0;
|
|
+ break;
|
|
+ case 't':
|
|
+ do_exec = 0;
|
|
+ do_load = 0;
|
|
+ do_type = 1;
|
|
+ break;
|
|
+
|
|
+ case 'h':
|
|
+ usage(stdout);
|
|
+ exit(0);
|
|
+ default:
|
|
+ usage(stderr);
|
|
+ exit(1);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (do_exec) {
|
|
+ kexec_reboot();
|
|
+ perror("kexec_reboot");
|
|
+ exit(1);
|
|
+ }
|
|
+
|
|
+ /* logging */
|
|
+ if (debug_level)
|
|
+ xc_dom_logfile = stderr;
|
|
+
|
|
+ if (do_type) {
|
|
+ print_type(kernel_name);
|
|
+ exit(0);
|
|
+ }
|
|
+
|
|
+ if (!do_load)
|
|
+ exit(0);
|
|
+
|
|
+ xc = xc_interface_open();
|
|
+ if (-1 == xc)
|
|
+ exit(1);
|
|
+
|
|
+ /* get info from kernel */
|
|
+ if (kernel_core_by_name(hypercall_page, "hypercall_page", PAGE_SIZE) <= 0)
|
|
+ goto out;
|
|
+ if (kernel_core_by_name(&max_pfn, "max_pfn", sizeof(max_pfn)) <= 0)
|
|
+ goto out;
|
|
+ if (kernel_core_by_name(&xen_start_info, "xen_start_info", sizeof(xen_start_info)) <= 0)
|
|
+ goto out;
|
|
+ if (kernel_core_by_addr(&start_info, xen_start_info, sizeof(start_info)) <= 0)
|
|
+ goto out;
|
|
+
|
|
+ /* build image */
|
|
+ dom = xc_dom_allocate(cmdline, features);
|
|
+ dom->xen_version = xc_version(xc, XENVER_version, NULL);
|
|
+ strcpy(dom->xen_caps, start_info.magic);
|
|
+
|
|
+ if (0 != (rc = xc_dom_kernel_file(dom, kernel_name)))
|
|
+ goto out;
|
|
+ if (ramdisk_name && strlen(ramdisk_name))
|
|
+ if (0 != (rc = xc_dom_ramdisk_file(dom, ramdisk_name)))
|
|
+ goto out;
|
|
+ if (0 != (rc = xc_dom_parse_image(dom)))
|
|
+ goto out;
|
|
+ if (0 != strcmp(start_info.magic, dom->guest_type)) {
|
|
+ fprintf(stderr,"incompatible kernel (need guest_type \"%s\")\n",
|
|
+ start_info.magic);
|
|
+ goto out;
|
|
+ }
|
|
+ helper_parse(dom, &helper);
|
|
+
|
|
+ if (0 != (rc = xc_dom_mem_init(dom, max_pfn >> (20 - PAGE_SHIFT))))
|
|
+ goto out;
|
|
+ if (0 != (rc = xc_dom_build_image(dom)))
|
|
+ goto out;
|
|
+ entry = helper_load(dom, &helper);
|
|
+ if (0 != (rc = dom->arch_hooks->setup_pgtables(dom)))
|
|
+ goto out;
|
|
+ dom->arch_hooks->start_info(dom);
|
|
+
|
|
+ /* setup kexec structs */
|
|
+ kexec_add_segment(dom, &dom->kernel_seg, &segments[nsegments++]);
|
|
+ kexec_add_segment(dom, &dom->ramdisk_seg, &segments[nsegments++]);
|
|
+ kexec_add_segment(dom, &dom->p2m_seg, &segments[nsegments++]);
|
|
+ kexec_add_page(dom, dom->start_info_pfn, &segments[nsegments++]);
|
|
+ kexec_add_segment(dom, &dom->pgtables_seg, &segments[nsegments++]);
|
|
+
|
|
+ dom->flags = start_info.flags;
|
|
+ dom->console_evtchn = start_info.console.domU.evtchn;
|
|
+ dom->xenstore_evtchn = start_info.store_evtchn;
|
|
+
|
|
+ /* setup helper code */
|
|
+ helper_pass_info(dom, &helper);
|
|
+
|
|
+ rc = kexec_load(entry, nsegments, segments, 0);
|
|
+ if (0 != rc) {
|
|
+ perror("kexec_load");
|
|
+ exit(1);
|
|
+ }
|
|
+ if (debug_level)
|
|
+ fprintf(stderr,"kexec_load ok [%p]\n", entry);
|
|
+
|
|
+ xc_dom_release(dom);
|
|
+ exit(0);
|
|
+
|
|
+ out:
|
|
+ if (dom)
|
|
+ xc_dom_release(dom);
|
|
+ exit(1);
|
|
+}
|