In my first comment I suggested that most people simply create their multiboot header (and bootstrap) code in a separate assembly module. Although that is normal and cleaner it is possible to do in
C as well using basic inline assembly statements outside a function. A basic
entry.c that should work and be compatible with legacy multiboot:
Code:
#include <multiboot/multiboot.h>
#include <stdint.h>
/* STRINGIZE is a C macro that allow us to convert an integer to a string
* for use by the C pre-processor */
#define STRINGIZE_INTERNAL(x) #x
#define STRINGIZE(x) STRINGIZE_INTERNAL(x)
/* 32k stack */
#define STACK_SIZE 32768
/* Define the multiboot structure that will be detectable by the multiboot
* loader. Request the loader to provide us a memory information */
#define MULTIBOOT_FLAGS MULTIBOOT_MEMORY_INFO
struct multiboot_header mb_header
__attribute__ ((aligned (4), section(".multiboot"))) = {
.magic = MULTIBOOT_HEADER_MAGIC,
.flags = MULTIBOOT_FLAGS,
.checksum = -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_FLAGS)
};
/* Allocate space for a stack */
uint8_t stack[STACK_SIZE];
/* Entry point set in linker script that the mulitboot loader will transfer control to */
extern void Kernel_entry(void);
__asm__ (".global Kernel_entry\n"
"Kernel_entry:\n\t"
/* Set stack pointer to end of stack variable.
Stack grows down. Align stack to 16 byte boundary */
"mov $stack + " STRINGIZE(STACK_SIZE) ", %esp\n\t"
"and $-16, %esp\n\t"
"cld\n\t" /* Ensure string instructions have forward movement */
"sub $8, %esp\n\t"/* For alignment on call to kmain */
"push %eax\n\t" /* Pass magicnum in EAX as 2nd parameter */
"push %ebx\n\t" /* Pass multiboot info struct in EBX as 1st parameter */
"call kmain\n\t" /* At this point stack 16 byte aligned, call kernel */
"add $16, %esp\n\t"
/* Infinite loop to end */
"cli\n"
".L0:\n\t"
"hlt\n\t"
"jmp .L0\n"
);
/* Text mode video pointer */
volatile uint16_t *const video_memory = (uint16_t *)0xb8000;
/* kmain is main C entry point */
void kmain(multiboot_info_t *mb_info, uint32_t magicnum)
{
(void)mb_info; /* Suppress warning */
/* Verify we were booted from multiboot loader and print MB to the display */
if (magicnum == MULTIBOOT_BOOTLOADER_MAGIC) {
video_memory[0] = 0x57 << 8 | 'M';
video_memory[1] = 0x57 << 8 | 'B';
}
}
As was mentioned by no92 using a cross compiler is much preferred to avoid potential problems. Using your host GCC may also present a particular problem if the build-id gets generated. This build id section may end up getting placed in such a way as to push your mulitboot header beyond the first 8k of the file where a multiboot loader may not detect it. I'd add the
-Wl,--build-id=none option to the GCC command that links and generated your final executable.