This patch enhances OSv multiboot mode in following ways:
- the command line is now properly passed all the way through
  and now we can now execute apps like so: ./script/run.py -k
- the multiboot info structure passed in by multiboot loader
  is now copied into lower memory so that it does not get
  overwritten later by boot64
- OSv can be now booted on HyperKit (still needs minor hpet-related hack)

Please note that booting OSv on QEMU in multiboot mode
is actually slower than using regular non-multiboot mode.
However booting on Hyperkit, where multiboot is the only
way to run OSv, OSv can boot and execute in around 50ms.

This is an example of how to execute OSv on hyperkit:

hyperkit -A -m 512M \
  -s 0:0,hostbridge \
  -s 31,lpc \
  -l com1,stdio \
  -s 4,virtio-blk,build/release/usr.img \
  -f multiboot,build/release/loader.bin,,'/hello.so'

Signed-off-by: Waldemar Kozaczuk <jwkozac...@gmail.com>
---
 Makefile               |  8 +++--
 arch/x64/arch-setup.cc | 32 +-----------------
 arch/x64/boot32.S      | 75 +++++++++++++++++++++++++++++++++++++----
 arch/x64/loader32.ld   |  1 +
 arch/x64/multiboot.cc  | 76 ++++++++++++++++++++++++++++++++++++++++++
 arch/x64/multiboot.h   | 50 +++++++++++++++++++++++++++
 6 files changed, 203 insertions(+), 39 deletions(-)
 create mode 100644 arch/x64/multiboot.cc
 create mode 100644 arch/x64/multiboot.h

diff --git a/Makefile b/Makefile
index 7f0a06fa..b69e3fca 100644
--- a/Makefile
+++ b/Makefile
@@ -441,12 +441,16 @@ $(out)/loader.img: $(out)/boot.bin $(out)/lzloader.elf
        $(call quiet, scripts/imgedit.py setsize "-f raw $@" $(image-size), 
IMGEDIT $@)
        $(call quiet, scripts/imgedit.py setargs "-f raw $@" $(cmdline), 
IMGEDIT $@)
 
-$(out)/loader.bin: $(out)/arch/x64/boot32.o arch/x64/loader32.ld
+$(out)/loader.bin: $(out)/arch/x64/boot32.o $(out)/arch/x64/multiboot.o 
arch/x64/loader32.ld
        $(call quiet, $(LD) -nostartfiles -static -nodefaultlibs -o $@ \
                        $(filter-out %.bin, $(^:%.ld=-T %.ld)), LD $@)
 
 $(out)/arch/x64/boot32.o: $(out)/loader-stripped.elf
-$(out)/arch/x64/boot32.o: ASFLAGS += -I$(out)
+$(out)/arch/x64/boot32.o: ASFLAGS += -I$(out) -m32
+
+$(out)/arch/x64/multiboot.o: arch/x64/multiboot.cc | generated-headers
+       $(makedir)
+       $(call quiet, $(CXX) $(CXXFLAGS) -O0 -m32 -fno-instrument-functions -o 
$@ -c arch/x64/multiboot.cc, CXX $<)
 
 $(out)/fastlz/fastlz.o:
        $(makedir)
diff --git a/arch/x64/arch-setup.cc b/arch/x64/arch-setup.cc
index 6e4833cf..cbb31464 100644
--- a/arch/x64/arch-setup.cc
+++ b/arch/x64/arch-setup.cc
@@ -21,30 +21,7 @@
 #include <osv/boot.hh>
 #include <osv/commands.hh>
 #include "dmi.hh"
-
-struct multiboot_info_type {
-    u32 flags;
-    u32 mem_lower;
-    u32 mem_upper;
-    u32 boot_device;
-    u32 cmdline;
-    u32 mods_count;
-    u32 mods_addr;
-    u32 syms[4];
-    u32 mmap_length;
-    u32 mmap_addr;
-    u32 drives_length;
-    u32 drives_addr;
-    u32 config_table;
-    u32 boot_loader_name;
-    u32 apm_table;
-    u32 vbe_control_info;
-    u32 vbe_mode_info;
-    u16 vbe_mode;
-    u16 vbe_interface_seg;
-    u16 vbe_interface_off;
-    u16 vbe_interface_len;
-} __attribute__((packed));
+#include "multiboot.h"
 
 struct osv_multiboot_info_type {
     struct multiboot_info_type mb;
@@ -54,13 +31,6 @@ struct osv_multiboot_info_type {
     u8 disk_err;
 } __attribute__((packed));
 
-struct e820ent {
-    u32 ent_size;
-    u64 addr;
-    u64 size;
-    u32 type;
-} __attribute__((packed));
-
 osv_multiboot_info_type* osv_multiboot_info;
 
 void parse_cmdline(multiboot_info_type& mb)
diff --git a/arch/x64/boot32.S b/arch/x64/boot32.S
index 33597e6d..57a23d83 100644
--- a/arch/x64/boot32.S
+++ b/arch/x64/boot32.S
@@ -1,21 +1,84 @@
 # Copyright (C) 2013 Cloudius Systems, Ltd.
+# Copyright (C) 2018 Waldemar Kozaczuk
 #
 # This work is open source software, licensed under the terms of the
 # BSD license as described in the LICENSE file in the top-level directory.
 
+#define MULTIBOOT_HEADER_MAGIC  0x1badb002
+
+/* Align all boot modules on i386 page (4KB) boundaries. */
+#define MULTIBOOT_PAGE_ALIGN    0x00000001
+
+/* Must pass memory information to OS. */
+#define MULTIBOOT_MEMORY_INFO   0x00000002
+
+/* This flag indicates the use of the address fields in the header. */
+#define MULTIBOOT_AOUT_KLUDGE   0x00010000
+
+#define MULTIBOOT_HEADER_FLAGS  (MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO 
| MULTIBOOT_AOUT_KLUDGE)
+
+#define OSV_MBKERNEL_BASE (OSV_KERNEL_BASE-0x2000)
 .text
 .code32
+//
+// Define data that needs to be found in the beginning of the mbloader.elf
+// by multiboot loader to properly load kernel as defined by multiboot
+// specification at 
https://www.gnu.org/software/grub/manual/multiboot/multiboot.html.
+multiboot_header_start:
+    .long MULTIBOOT_HEADER_MAGIC
+    .long MULTIBOOT_HEADER_FLAGS
+    .long -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
+    .long multiboot_header_start        # header_addr
+    .long OSV_MBKERNEL_BASE             # load_addr
+    .long 0                             # load_end_addr
+    .long 0                             # bss_end_addr
+    .long start32                       # entry_addr
+
+mb_copy_address = 0x4000
+mb_cmd_line_copy_address = 0x3000
+mb_mmap_info_copy_address = 0x2000
 
-mb_magic = 0x1BADB002
-mb_flags = 0x00000001    # flags: 4k module alignment
+mb_cmdline_offset = 16
+mb_mmap_addr_offset = 48
 
-multiboot:
-    .long mb_magic
-    .long mb_flags
-    .long 0-mb_magic-mb_flags
+mb_tsc1_lo_offset = 88
+mb_tsc1_hi_offset = (88 + 4)
+mb_tsc_disk_lo_offset = (88 + 8)
+mb_tsc_disk_hi_offset = (88 + 12)
+mb_uncompress_lo_offset = (88 + 16)
+mb_uncompress_hi_offset = (88 + 20)
 
 .global start32
 start32:
+    //
+    // The original multiboot info address gets passed in by the multiboot 
loader
+    // in the ebx register. We need to copy the relevant parts of the data to
+    // the other place in memory (mb_copy_address) in case the original data
+    // gets overwritten by our code.
+    //
+    // First set target mmap_address and cmdline in the target 
multiboot_info_type struct.
+    movl $mb_copy_address, %eax
+    movl $mb_mmap_info_copy_address, mb_mmap_addr_offset(%eax)
+    movl $mb_cmd_line_copy_address, mb_cmdline_offset(%eax)
+    //
+    // Call utils function written in C to copy/transform
+    // memory information and command line
+    mov $0x7c00, %esp // Needs stack
+    pushl $mb_copy_address  // Pass multiboot info source and target address 
on stack as parameters
+    pushl %ebx
+    call copy_multiboot_info
+    subl $8, %esp // Restore stack
+    //
+    // Set bootchart fields
+    mov $mb_copy_address, %ebx
+    rdtsc
+    mov %eax, mb_tsc1_lo_offset(%ebx)
+    mov %edx, mb_tsc1_hi_offset(%ebx)
+    mov %eax, mb_tsc_disk_lo_offset(%ebx)
+    mov %edx, mb_tsc_disk_hi_offset(%ebx)
+    mov %eax, mb_uncompress_lo_offset(%ebx)
+    mov %edx, mb_uncompress_hi_offset(%ebx)
+
        lea elf64_header, %eax
        jmp *24+elf64_header
 
diff --git a/arch/x64/loader32.ld b/arch/x64/loader32.ld
index 8da03a84..03a9109c 100644
--- a/arch/x64/loader32.ld
+++ b/arch/x64/loader32.ld
@@ -5,6 +5,7 @@
  * BSD license as described in the LICENSE file in the top-level directory.
  */
 
+OUTPUT_ARCH(i386)
 OUTPUT_FORMAT(elf32-i386)
 
 SECTIONS
diff --git a/arch/x64/multiboot.cc b/arch/x64/multiboot.cc
new file mode 100644
index 00000000..d6aceeb9
--- /dev/null
+++ b/arch/x64/multiboot.cc
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2018 Waldemar Kozaczuk, Ltd.
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+
+#include <osv/types.h>
+#include "multiboot.h"
+
+#define MULTIBOOT_MMAP_PRESENT_FLAG (0x1 << 6)
+#define MULTIBOOT_MEM_PRESENT_FLAG  (0x1 << 0)
+#define SINGLE_KB_IN_BYTES (1024)
+#define SINGLE_MB_ADDRESS (1024 * SINGLE_KB_IN_BYTES)
+#define SPACE (' ')
+//
+// The copy_multiboot_info is called from assembly code in multiboot.S
+// where source and target multiboot_info_type are passed in.
+extern "C" void copy_multiboot_info(void *source, void *target) {
+    //
+    // Copy the relevant fields from original multiboot
+    // info struct to the target one
+    multiboot_info_type* multiboot_info_source = (multiboot_info_type*)source;
+    multiboot_info_type* multiboot_info_target = (multiboot_info_type*)target;
+    //
+    // Copy the flags just in case it will be useful in future
+    multiboot_info_target->flags = multiboot_info_source->flags;
+    //
+    // Most multiboot loaders (including QEMU) pass memory information
+    // in both mmap_* and mem_upper/mem_lower fields but we prefer the former
+    // as we do not need to change anything in arch-setup.cc. However some 
multiboot
+    // loaders like HyperKit pass information in mem_upper/mem_lower fields 
only
+    // which we need to translate to mmap_* structures.
+    //
+    // The arch_setup_free_memory() expects memory information
+    // in the mmap format. So either copy the mmap_* data if passed in ...
+    if( multiboot_info_source->flags & MULTIBOOT_MMAP_PRESENT_FLAG) {
+        char *mmap_source = (char *)multiboot_info_source->mmap_addr;
+        char *mmap_target = (char *)multiboot_info_target->mmap_addr;
+        auto mmap_bytes_to_copy = multiboot_info_source->mmap_length;
+        multiboot_info_target->mmap_length = mmap_bytes_to_copy;
+
+        while( mmap_bytes_to_copy > 0 ) {
+            *(mmap_target++) = *(mmap_source++);
+            mmap_bytes_to_copy--;
+        }
+    }
+    // ... or translate mem_* data into mmap_*
+    else if( multiboot_info_source->flags & MULTIBOOT_MEM_PRESENT_FLAG) {
+        multiboot_info_target->mmap_length = 2 * sizeof(struct e820ent);
+
+        e820ent *lower = (e820ent*)(multiboot_info_target->mmap_addr);
+        lower->ent_size = sizeof(struct e820ent) - 4;
+        lower->addr = 0;
+        lower->size = multiboot_info_source->mem_lower * SINGLE_KB_IN_BYTES;
+        lower->type = 1;
+
+        e820ent *upper = ((e820ent*)multiboot_info_target->mmap_addr) + 1;
+        upper->ent_size = sizeof(struct e820ent) - 4;
+        upper->addr = SINGLE_MB_ADDRESS;
+        upper->size = multiboot_info_source->mem_upper * SINGLE_KB_IN_BYTES;
+        upper->type = 1;
+    }
+    //
+    // Multiboot loaders concatenate path of the kernel with extra arguments 
(OSv cmdline)
+    // with a space in between. So we need to find first space as the 
beginning of
+    // the real cmdline
+    char *source_cmdline = (char*)multiboot_info_source->cmdline;
+    while (*source_cmdline && *source_cmdline != SPACE)
+        source_cmdline++;
+    //
+    // Copy command line
+    char *target_cmdline = (char*)multiboot_info_target->cmdline;
+    while (*source_cmdline)
+        *(target_cmdline++) = *(source_cmdline++);
+}
diff --git a/arch/x64/multiboot.h b/arch/x64/multiboot.h
new file mode 100644
index 00000000..1de948ca
--- /dev/null
+++ b/arch/x64/multiboot.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 Waldemar Kozaczuk, Ltd.
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+#ifndef MULTIBOOT_H
+#define MULTIBOOT_H
+
+// This structure and e820ent below is defined per multiboot specification
+// described at 
https://www.gnu.org/software/grub/manual/multiboot/multiboot.html
+struct multiboot_info_type {
+    u32 flags;
+    u32 mem_lower;
+    u32 mem_upper;
+    u32 boot_device;
+    u32 cmdline;
+    u32 mods_count;
+    u32 mods_addr;
+    u32 syms[4];
+    u32 mmap_length;
+    u32 mmap_addr;
+    u32 drives_length;
+    u32 drives_addr;
+    u32 config_table;
+    u32 boot_loader_name;
+    u32 apm_table;
+    u32 vbe_control_info;
+    u32 vbe_mode_info;
+    u16 vbe_mode;
+    u16 vbe_interface_seg;
+    u16 vbe_interface_off;
+    u16 vbe_interface_len;
+} __attribute__((packed));
+
+//
+// This structure e820ent is referenced when compiling to 32-bit and 64-bit 
machine code.
+// Therefore we need to define this type explicitly instead of using standard 
u64
+// so that in both situations the compiler calculates offsets of relevant 
fields
+// and size of e820ent struct correctly.
+typedef unsigned long long multiboot_uint64_t;
+
+struct e820ent {
+    u32 ent_size;
+    multiboot_uint64_t addr;
+    multiboot_uint64_t size;
+    u32 type;
+} __attribute__((packed));
+
+#endif //MULTIBOOT_H
-- 
2.17.1

-- 
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to osv-dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to