summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRemy Noel <mocramis@gmail.com>2018-04-14 10:00:10 +0200
committerRemy Noel <mocramis@gmail.com>2018-04-14 10:00:10 +0200
commitf3bdc3f165ccb4bcd608f3f3725e990b44de44f9 (patch)
tree4714c20dd154fd1dd4a40faef2e454148c873136
parent2efc39da83bf826186497a8fe3208fcc1afee2a4 (diff)
parent8088131a4e0165938593577a9cfa87de7ffd22bc (diff)
Merge branch 'master' into perfmon
-rw-r--r--Makefile13
-rw-r--r--arch/x86/Makefile9
-rw-r--r--arch/x86/machine/lapic.c6
-rw-r--r--arch/x86/x15.lds.S21
-rw-r--r--doc/intro.9.txt2
-rw-r--r--kern/Makefile1
-rw-r--r--kern/bulletin.c83
-rw-r--r--kern/bulletin.h75
-rw-r--r--kern/bulletin_i.h35
-rw-r--r--kern/hash.h23
-rw-r--r--kern/list.h27
-rw-r--r--kern/rcu.c2
-rw-r--r--kern/xcall.c2
-rw-r--r--test/Kconfig3
-rw-r--r--test/Makefile1
-rw-r--r--test/test_bulletin.c83
-rwxr-xr-xtools/build_configs.py1
-rwxr-xr-xtools/qemu.sh16
18 files changed, 361 insertions, 42 deletions
diff --git a/Makefile b/Makefile
index c4381bf..17de695 100644
--- a/Makefile
+++ b/Makefile
@@ -9,6 +9,8 @@ all: x15 docs
VERSION = 0.1
export VERSION
+COMMA := ,
+
ifndef V
V := 0
endif
@@ -254,6 +256,10 @@ endif
XBUILD_CFLAGS += -fsigned-char
XBUILD_CFLAGS += -fno-common
+# XXX Some assemblers consider the / symbol to denote comments. The --divide
+# option suppresses that behavior.
+XBUILD_CFLAGS += $(call xbuild_check_cc_option,-Wa$(COMMA)--divide)
+
XBUILD_CFLAGS += -Wall
XBUILD_CFLAGS += -Wextra
XBUILD_CFLAGS += -Wshadow
@@ -266,7 +272,10 @@ XBUILD_CFLAGS += -Wno-unneeded-internal-declaration
XBUILD_CFLAGS += $(call xbuild_check_cc_option,-fno-PIE)
XBUILD_CFLAGS += $(call xbuild_check_cc_option,-Qunused-arguments)
-XBUILD_LDFLAGS += -static -nostdlib -lgcc
+XBUILD_LDFLAGS += -static -nostdlib
+
+# Disable the build ID feature of the linker
+XBUILD_LDFLAGS += -Wl,--build-id=none
x15_SOURCES-y :=
x15_LDS_S := arch/$(ARCH)/x15.lds.S
@@ -294,7 +303,7 @@ x15_OBJECTS := $(call xbuild_replace_source_suffix,o,$(x15_SOURCES))
x15_LDS_D := $(call xbuild_gen_linker_script_depfile,$(x15_LDS_S))
x15_LDS := $(basename $(x15_LDS_S))
-XBUILD_LDFLAGS += -Xlinker -T $(x15_LDS)
+XBUILD_LDFLAGS += -Wl,--script=$(x15_LDS)
define gen_sorted_init_ops
$(call xbuild_action,GEN,$@) \
diff --git a/arch/x86/Makefile b/arch/x86/Makefile
index e20a711..002ed44 100644
--- a/arch/x86/Makefile
+++ b/arch/x86/Makefile
@@ -20,11 +20,20 @@ XBUILD_LDFLAGS += -Wl,-z,max-page-size=4096 -Wl,-z,common-page-size=4096
ifeq ($(CONFIG_X86_32),y)
biarch := $(call cc-option,-m32)
+
XBUILD_CPPFLAGS += -m32
+
+ XBUILD_LDFLAGS += -lgcc
else
biarch := -m64
+
XBUILD_CPPFLAGS += -m64
+ # XXX The kernel isn't linked with libgcc on amd64, because libgcc
+ # uses red zones. But since there are actually very few functions
+ # provided by libgcc on amd64, it was decided to just not link with
+ # it. If it turns out that libgcc is really needed some day, the
+ # kernel can somewhat easily be changed to support red zones.
XBUILD_CFLAGS += -mno-red-zone
XBUILD_CFLAGS += -mcmodel=kernel
endif
diff --git a/arch/x86/machine/lapic.c b/arch/x86/machine/lapic.c
index 180a1a2..3f6d0c2 100644
--- a/arch/x86/machine/lapic.c
+++ b/arch/x86/machine/lapic.c
@@ -271,11 +271,17 @@ lapic_ap_setup(void)
static void
lapic_ipi(uint32_t apic_id, uint32_t icr)
{
+ unsigned long flags;
+
+ cpu_intr_save(&flags);
+
if ((icr & LAPIC_ICR_DEST_MASK) == 0) {
lapic_write(&lapic_map->icr_high, apic_id << LAPIC_DEST_SHIFT);
}
lapic_write(&lapic_map->icr_low, icr & ~LAPIC_ICR_RESERVED);
+
+ cpu_intr_restore(flags);
}
static void
diff --git a/arch/x86/x15.lds.S b/arch/x86/x15.lds.S
index 80b1e65..b896a5d 100644
--- a/arch/x86/x15.lds.S
+++ b/arch/x86/x15.lds.S
@@ -58,7 +58,7 @@ SECTIONS
_percpu = .;
.percpu 0 : AT(BOOT_VTOP(_percpu)) {
- *(.percpu)
+ *(.percpu*)
} : percpu
. = _percpu + SIZEOF(.percpu);
@@ -67,18 +67,14 @@ SECTIONS
_text = .;
.text ALIGN(PAGE_SIZE) : AT(BOOT_VTOP(ADDR(.text))) {
- *(.text)
+ *(.text*)
} : text
. = ALIGN(PAGE_SIZE);
_rodata = .;
.rodata ALIGN(PAGE_SIZE) : AT(BOOT_VTOP(ADDR(.rodata))) {
- *(.rodata)
- } : rodata
-
- .notes ALIGN(CPU_DATA_ALIGN) : AT(BOOT_VTOP(ADDR(.notes))) {
- *(.note.*)
+ *(.rodata*)
} : rodata
. = ALIGN(PAGE_SIZE);
@@ -88,21 +84,18 @@ SECTIONS
. = ALIGN(CPU_L1_SIZE);
*(.data.read_mostly)
. = ALIGN(CPU_L1_SIZE);
- *(.data)
+ *(.data*)
} : data
.bss ALIGN(CPU_DATA_ALIGN) : AT(BOOT_VTOP(ADDR(.bss))) {
- *(.bss)
+ *(.bss*)
} : data
. = ALIGN(PAGE_SIZE);
_end = .;
- /*
- * XXX A global offset section is created because of linking with libgcc.
- * Is it safe to discard it ?
- */
/DISCARD/ : {
- *(.eh_frame)
+ *(.eh_frame*)
+ *(.note*)
}
}
diff --git a/doc/intro.9.txt b/doc/intro.9.txt
index 45c0a3f..d4532cb 100644
--- a/doc/intro.9.txt
+++ b/doc/intro.9.txt
@@ -135,6 +135,8 @@ other development tools :
module:kern/bitmap::
Arbitrary-length bit array.
+module:kern/bulletin::
+ Publish-subscribe mechanism.
module:kern/cbuf::
Circular byte buffer.
module:kern/clock::
diff --git a/kern/Makefile b/kern/Makefile
index 3779380..5b04fcb 100644
--- a/kern/Makefile
+++ b/kern/Makefile
@@ -1,6 +1,7 @@
x15_SOURCES-y += \
kern/arg.c \
kern/bitmap.c \
+ kern/bulletin.c \
kern/cbuf.c \
kern/clock.c \
kern/condition.c \
diff --git a/kern/bulletin.c b/kern/bulletin.c
new file mode 100644
index 0000000..bbb1bfc
--- /dev/null
+++ b/kern/bulletin.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2017-2018 Richard Braun.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+
+#include <kern/bulletin.h>
+#include <kern/list.h>
+#include <kern/rcu.h>
+#include <kern/spinlock.h>
+#include <kern/thread.h>
+
+static void
+bulletin_sub_init(struct bulletin_sub *sub,
+ bulletin_notif_fn_t notif_fn, void *arg)
+{
+ sub->notif_fn = notif_fn;
+ sub->arg = arg;
+}
+
+static void
+bulletin_sub_notify(const struct bulletin_sub *sub, uintptr_t value)
+{
+ sub->notif_fn(value, sub->arg);
+}
+
+void
+bulletin_init(struct bulletin *bulletin)
+{
+ spinlock_init(&bulletin->lock);
+ list_init(&bulletin->subs);
+}
+
+void
+bulletin_subscribe(struct bulletin *bulletin, struct bulletin_sub *sub,
+ bulletin_notif_fn_t notif_fn, void *arg)
+{
+ bulletin_sub_init(sub, notif_fn, arg);
+
+ spinlock_lock(&bulletin->lock);
+ list_rcu_insert_tail(&bulletin->subs, &sub->node);
+ spinlock_unlock(&bulletin->lock);
+}
+
+void
+bulletin_unsubscribe(struct bulletin *bulletin, struct bulletin_sub *sub)
+{
+ spinlock_lock(&bulletin->lock);
+ list_rcu_remove(&sub->node);
+ spinlock_unlock(&bulletin->lock);
+
+ rcu_wait();
+}
+
+void
+bulletin_publish(struct bulletin *bulletin, uintptr_t value)
+{
+ struct bulletin_sub *sub;
+
+ assert(!thread_interrupted());
+
+ rcu_read_enter();
+
+ list_rcu_for_each_entry(&bulletin->subs, sub, node) {
+ bulletin_sub_notify(sub, value);
+ }
+
+ rcu_read_leave();
+}
diff --git a/kern/bulletin.h b/kern/bulletin.h
new file mode 100644
index 0000000..8ce9d9e
--- /dev/null
+++ b/kern/bulletin.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2017-2018 Richard Braun.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Minimalist publish-subscribe mechanism.
+ */
+
+#ifndef KERN_BULLETIN_H
+#define KERN_BULLETIN_H
+
+#include <stdint.h>
+
+#include <kern/macros.h>
+#include <kern/work.h>
+
+/*
+ * Type for bulletin notification functions.
+ *
+ * The value is passed from the publisher unmodified, and can safely be
+ * cast into a pointer. Notification functions run in the context of the
+ * publisher.
+ */
+typedef void (*bulletin_notif_fn_t)(uintptr_t value, void *arg);
+
+#include <kern/bulletin_i.h>
+
+struct bulletin;
+
+/*
+ * Bulletin subscriber.
+ */
+struct bulletin_sub;
+
+void bulletin_init(struct bulletin *bulletin);
+
+/*
+ * Subscribe to a bulletin.
+ *
+ * Once subscribed, the notification function is called with its argument
+ * each time the bulletin is published.
+ */
+void bulletin_subscribe(struct bulletin *bulletin, struct bulletin_sub *sub,
+ bulletin_notif_fn_t notif_fn, void *arg);
+
+/*
+ * Unsubscribe from a bulletin.
+ *
+ * On return, the subscriber notification function may not be called any more.
+ *
+ * This function synchronizes with RCU.
+ */
+void bulletin_unsubscribe(struct bulletin *bulletin, struct bulletin_sub *sub);
+
+/*
+ * Publish a bulletin.
+ *
+ * All subscribers are notified by calling their notification function, with
+ * the given value passed unmodified.
+ */
+void bulletin_publish(struct bulletin *bulletin, uintptr_t value);
+
+#endif /* KERN_BULLETIN_H */
diff --git a/kern/bulletin_i.h b/kern/bulletin_i.h
new file mode 100644
index 0000000..ea0940a
--- /dev/null
+++ b/kern/bulletin_i.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017-2018 Richard Braun.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef KERN_BULLETIN_I_H
+#define KERN_BULLETIN_I_H
+
+#include <kern/list.h>
+#include <kern/spinlock.h>
+
+struct bulletin_sub {
+ struct list node;
+ bulletin_notif_fn_t notif_fn;
+ void *arg;
+};
+
+struct bulletin {
+ struct spinlock lock;
+ struct list subs;
+};
+
+#endif /* KERN_BULLETIN_I_H */
diff --git a/kern/hash.h b/kern/hash.h
index 9d7778e..19803eb 100644
--- a/kern/hash.h
+++ b/kern/hash.h
@@ -43,6 +43,7 @@
#define KERN_HASH_H
#include <assert.h>
+#include <stdbool.h>
#include <stdint.h>
#ifdef __LP64__
@@ -54,11 +55,19 @@ static_assert(sizeof(long) == 4, "unsupported data model");
#define hash_long(n, bits) hash_int32(n, bits)
#endif
+static inline bool
+hash_bits_valid(unsigned int bits)
+{
+ return (bits != 0) && (bits <= HASH_ALLBITS);
+}
+
static inline uint32_t
hash_int32(uint32_t n, unsigned int bits)
{
uint32_t hash;
+ assert(hash_bits_valid(bits));
+
hash = n;
hash = ~hash + (hash << 15);
hash ^= (hash >> 12);
@@ -75,6 +84,8 @@ hash_int64(uint64_t n, unsigned int bits)
{
uint64_t hash;
+ assert(hash_bits_valid(bits));
+
hash = n;
hash = ~hash + (hash << 21);
hash ^= (hash >> 24);
@@ -100,9 +111,11 @@ hash_ptr(const void *ptr, unsigned int bits)
static inline unsigned long
hash_str(const char *str, unsigned int bits)
{
- unsigned long hash;
+ unsigned long hash, mask;
char c;
+ assert(hash_bits_valid(bits));
+
for (hash = 0; /* no condition */; str++) {
c = *str;
@@ -113,7 +126,13 @@ hash_str(const char *str, unsigned int bits)
hash = ((hash << 5) - hash) + c;
}
- return hash & ((1UL << bits) - 1);
+ /*
+ * This mask construction avoids the undefined behavior that would
+ * result from directly shifting by the number of bits, if that number
+ * is equal to the width of the hash.
+ */
+ mask = (~0UL >> (HASH_ALLBITS - bits));
+ return hash & mask;
}
#endif /* KERN_HASH_H */
diff --git a/kern/list.h b/kern/list.h
index 832d94f..8163176 100644
--- a/kern/list.h
+++ b/kern/list.h
@@ -478,12 +478,12 @@ list_rcu_remove(struct list *node)
* the node pointer can only be read once, preventing the combination
* of lockless list_empty()/list_first_entry() variants.
*/
-#define list_rcu_first_entry(list, type, member) \
+#define list_rcu_first_entry(head, type, member) \
MACRO_BEGIN \
struct list *list___; \
struct list *first___; \
\
- list___ = (list); \
+ list___ = (head); \
first___ = list_rcu_first(list___); \
list_end(list___, first___) \
? NULL \
@@ -497,8 +497,17 @@ MACRO_END
* the node pointer can only be read once, preventing the combination
* of lockless list_empty()/list_next_entry() variants.
*/
-#define list_rcu_next_entry(entry, member) \
- list_rcu_first_entry(&entry->member, typeof(*entry), member)
+#define list_rcu_next_entry(head, entry, member) \
+MACRO_BEGIN \
+ struct list *list___; \
+ struct list *next___; \
+ \
+ list___ = (head); \
+ next___ = list_rcu_next(&entry->member); \
+ list_end(list___, next___) \
+ ? NULL \
+ : list_entry(next___, typeof(*entry), member); \
+MACRO_END
/*
* Forge a loop to process all nodes of a list.
@@ -515,11 +524,9 @@ for (node = list_rcu_first(list); \
*
* The entry node must not be altered during the loop.
*/
-#define list_rcu_for_each_entry(list, entry, member) \
-for (entry = list_rcu_entry(list_first(list), \
- typeof(*entry), member); \
- !list_end(list, &entry->member); \
- entry = list_rcu_entry(list_next(&entry->member), \
- typeof(*entry), member))
+#define list_rcu_for_each_entry(list, entry, member) \
+for (entry = list_rcu_first_entry(list, typeof(*entry), member); \
+ entry != NULL; \
+ entry = list_rcu_next_entry(list, entry, member))
#endif /* KERN_LIST_H */
diff --git a/kern/rcu.c b/kern/rcu.c
index 973d6d5..1903dcf 100644
--- a/kern/rcu.c
+++ b/kern/rcu.c
@@ -706,6 +706,8 @@ rcu_defer(struct work *work)
struct rcu_cpu_data *cpu_data;
unsigned long flags;
+ assert(!rcu_reader_in_cs(thread_rcu_reader(thread_self())));
+
thread_preempt_disable_intr_save(&flags);
cpu_data = rcu_get_cpu_data();
rcu_cpu_data_queue(cpu_data, work);
diff --git a/kern/xcall.c b/kern/xcall.c
index 2096f61..74a5bcb 100644
--- a/kern/xcall.c
+++ b/kern/xcall.c
@@ -177,7 +177,7 @@ xcall_intr(void)
if (call) {
xcall_process(call);
} else {
- log_warning("xcall: spurious interrupt on cpu%u", cpu_id());
+ log_err("xcall: spurious interrupt on cpu%u", cpu_id());
}
syscnt_inc(&cpu_data->sc_received);
diff --git a/test/Kconfig b/test/Kconfig
index 1355c1a..df2272e 100644
--- a/test/Kconfig
+++ b/test/Kconfig
@@ -9,6 +9,9 @@ if TEST_MODULE
choice
prompt "Select test module"
+config TEST_MODULE_BULLETIN
+ bool "bulletin"
+
config TEST_MODULE_MUTEX
bool "mutex"
select MUTEX_DEBUG
diff --git a/test/Makefile b/test/Makefile
index e6526be..b6f2260 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -1,3 +1,4 @@
+x15_SOURCES-$(CONFIG_TEST_MODULE_BULLETIN) += test/test_bulletin.c
x15_SOURCES-$(CONFIG_TEST_MODULE_MUTEX) += test/test_mutex.c
x15_SOURCES-$(CONFIG_TEST_MODULE_MUTEX_PI) += test/test_mutex_pi.c
x15_SOURCES-$(CONFIG_TEST_MODULE_PERFMON_CPU) += test/test_perfmon_cpu.c
diff --git a/test/test_bulletin.c b/test/test_bulletin.c
new file mode 100644
index 0000000..4a2fdb5
--- /dev/null
+++ b/test/test_bulletin.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2018 Richard Braun.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * This test makes a bulletin subscriber subscribe to a bulletin, and uses
+ * a timer to periodically publish, unsubscribe, and resubscribe to the
+ * bulletin. A counter is used to test passing values to the notification
+ * callback function.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+
+#include <kern/bulletin.h>
+#include <kern/clock.h>
+#include <kern/error.h>
+#include <kern/log.h>
+#include <kern/timer.h>
+#include <test/test.h>
+
+#define TEST_INTERVAL 1
+#define TEST_NR_LOOPS 4
+
+static struct bulletin test_bulletin;
+static struct bulletin_sub test_bulletin_sub;
+static struct timer test_timer;
+static unsigned int test_counter;
+
+static void
+test_notify(uintptr_t value, void *arg)
+{
+ log_info("test: notify: value:%lu arg:%p", value, arg);
+}
+
+static void
+test_tick(struct timer *timer)
+{
+ uint64_t ticks;
+
+ test_counter++;
+ bulletin_publish(&test_bulletin, test_counter);
+ bulletin_unsubscribe(&test_bulletin, &test_bulletin_sub);
+
+ if (test_counter == TEST_NR_LOOPS) {
+ log_info("test: done");
+ return;
+ }
+
+ bulletin_subscribe(&test_bulletin, &test_bulletin_sub,
+ test_notify, (void *)0x123);
+
+ ticks = timer_get_time(timer) + clock_ticks_from_ms(TEST_INTERVAL * 1000);
+ timer_schedule(&test_timer, ticks);
+}
+
+void __init
+test_setup(void)
+{
+ uint64_t ticks;
+
+ bulletin_init(&test_bulletin);
+ bulletin_subscribe(&test_bulletin, &test_bulletin_sub,
+ test_notify, (void *)0x123);
+
+ timer_init(&test_timer, test_tick, TIMER_DETACHED);
+ ticks = clock_get_time() + clock_ticks_from_ms(TEST_INTERVAL * 1000);
+ timer_schedule(&test_timer, ticks);
+}
diff --git a/tools/build_configs.py b/tools/build_configs.py
index 95a5905..dab95b7 100755
--- a/tools/build_configs.py
+++ b/tools/build_configs.py
@@ -109,6 +109,7 @@ large_options_dict.update({
# TODO Generate this list from test/test_*.c
test_list = [
+ 'CONFIG_TEST_MODULE_BULLETIN',
'CONFIG_TEST_MODULE_MUTEX',
'CONFIG_TEST_MODULE_MUTEX_PI',
'CONFIG_TEST_MODULE_PMAP_UPDATE_MP',
diff --git a/tools/qemu.sh b/tools/qemu.sh
index 1e1990b..3aef63e 100755
--- a/tools/qemu.sh
+++ b/tools/qemu.sh
@@ -22,18 +22,8 @@ KVM="-enable-kvm -cpu host"
X15=$PWD/x15
TMPDIR=$(mktemp -d)
-CDROOT=$TMPDIR/cdroot
-mkdir -p $CDROOT/boot/grub
-cp $X15 $CDROOT/boot
-cat > $CDROOT/boot/grub/grub.cfg << EOF
-set timeout=1
-
-menuentry "X15" --class os {
- multiboot (hd96)/boot/x15 root=device:hd1s8
-}
-EOF
-grub-mkrescue -o $TMPDIR/grub.iso $CDROOT
+objcopy -O elf32-i386 $X15 $TMPDIR/x15
$QEMU_EXE $KVM \
-ctrl-grab \
@@ -41,7 +31,7 @@ $QEMU_EXE $KVM \
-m $RAM \
-smp $NR_CPUS \
-monitor stdio \
- -cdrom $TMPDIR/grub.iso \
- -boot d
+ -kernel $TMPDIR/x15 \
+ -append "console=atcons"
rm -rf $TMPDIR