Linux kernel development using Qemu?
Download Linux kernel source code from kernel.org, then modify, configure, build your kernel. After building you'll get a kernel image usually located at arch/x86/boot/bzImage (if your arch is x86, look at appropriate location for your arch), and this is what you need to test your modified kernel on Qemu. You'll also need a rootfs (use busybox) to run it properly. Use qemu's '-kernel' parameter to pass the bzImage. An example:
`qemu-system-x86_64 -m 1024 -smp 4 -kernel arch/x86/boot/bzImage -initrd initramfs.gz -append "rdinit=/ root=/dev/ram`
it tells qemu to use 1024 of ram, use 4 cpu.
Playing/Learning -- QEMU (for ARM), Angstrom Linux (or Debian)
The beagleboard, hawkboard, open-rd sites all tend to lead to their distros being built on qemu (arm), and from there there is no reason why you cannot just continue to keep running on the simulation instead of heading for hardware.
This is an example of how to do it with ubuntu.
https://wiki.edubuntu.org/ARM/RootfsFromScratch
Yes it is also possible to cross compile everything as well, I would start with wiki pages that hand hold you through all of the steps. Or as with the hawkboard or beagleboard get a pre-built binary (kernel and root file system) and just boot it and run on that environment and not mess with building everything.
x86 linux kernel development in qemu
WordPress typography features strike again! You're using the wrong kind of quotation marks:
-append “root=/dev/ram rdinit=/sbin/init”
^ ^
The indicated characters are, respectively, U+201C LEFT DOUBLE QUOTATION MARK and U+201D RIGHT DOUBLE QUOTATION MARK (i.e, "curly quotes"), not normal quotation marks.
Retype the command line by hand (or, at least, those two characters) and it'll work correctly.
How to compile baremetal hello_world.c and run it on qemu-system-aarch64?
You are right that you can use qemu-system-aarch64 for achieving your goal.
Depending on what you exactly want to do, you have multiple options:
use the semihosting mode of qemu, along with the gcc
--specs=rdimon.specs
withnewlib,
or using another semihosting library, such as the one available in the Arm Trusted Firmware source code - the example hereafter uses this approach.provide your own
syscalls.c
, and use the--specs=nosys.specs
ld
option, so that you can use newlib in baremetal programs: I would suggest to read the excellent article from Francesco Balducci on the Balau blog - the example hereafter uses this approach.use a more baremetal-like approach, such as the one described hereafter: it does use
sprintf()
and thepl011
UART of theqemu-virt
machine for displaying the resulting string.
gcc_arm64_ram.ld
:
/******************************************************************************
* @file gcc_arm32.ld
* @brief GNU Linker Script for Cortex-M based device
* @version V2.0.0
* @date 21. May 2019
******************************************************************************/
/*
* Copyright (c) 2009-2019 Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
MEMORY
{
RAM (rwx) : ORIGIN = __RAM_BASE, LENGTH = __RAM_SIZE
}
/* Linker script to place sections and symbol values. Should be used together
* with other linker script that defines memory regions FLASH and RAM.
* It references following symbols, which must be defined in code:
* Reset_Handler : Entry of reset handler
*
* It defines following symbols, which code can use without definition:
* __exidx_start
* __exidx_end
* __copy_table_start__
* __copy_table_end__
* __zero_table_start__
* __zero_table_end__
* __etext
* __data_start__
* __preinit_array_start
* __preinit_array_end
* __init_array_start
* __init_array_end
* __fini_array_start
* __fini_array_end
* __data_end__
* __bss_start__
* __bss_end__
* __end__
* end
* __HeapLimit
* __StackLimit
* __StackTop
* __stack
*/
ENTRY(Reset_Handler)
SECTIONS
{
.text :
{
KEEP(*(.vectors))
*(.text*)
KEEP(*(.init))
KEEP(*(.fini))
/* .ctors */
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
*(SORT(.ctors.*))
*(.ctors)
/* .dtors */
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
*(SORT(.dtors.*))
*(.dtors)
*(.rodata*)
KEEP(*(.eh_frame*))
} > RAM
/*
* SG veneers:
* All SG veneers are placed in the special output section .gnu.sgstubs. Its start address
* must be set, either with the command line option �--section-start� or in a linker script,
* to indicate where to place these veneers in memory.
*/
/*
.gnu.sgstubs :
{
. = ALIGN(32);
} > RAM
*/
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > RAM
__exidx_start = .;
.ARM.exidx :
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > RAM
__exidx_end = .;
.copy.table :
{
. = ALIGN(16);
__copy_table_start__ = .;
LONG (__etext)
LONG (__data_start__)
LONG (__data_end__ - __data_start__)
/* Add each additional data section here */
/*
LONG (__etext2)
LONG (__data2_start__)
LONG (__data2_end__ - __data2_start__)
*/
__copy_table_end__ = .;
} > RAM
.zero.table :
{
. = ALIGN(16);
__zero_table_start__ = .;
/* Add each additional bss section here */
/*
LONG (__bss2_start__)
LONG (__bss2_end__ - __bss2_start__)
*/
__zero_table_end__ = .;
} > RAM
/**
* Location counter can end up 2byte aligned with narrow Thumb code but
* __etext is assumed by startup code to be the LMA of a section in RAM
* which must be 4byte aligned
*/
__etext = ALIGN(16);
.data : AT (__etext)
{
__data_start__ = .;
*(vtable)
*(.data)
*(.data.*)
. = ALIGN(16);
/* preinit data */
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP(*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(16);
/* init data */
PROVIDE_HIDDEN (__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(16);
/* finit data */
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP(*(SORT(.fini_array.*)))
KEEP(*(.fini_array))
PROVIDE_HIDDEN (__fini_array_end = .);
KEEP(*(.jcr*))
. = ALIGN(16);
/* All data end */
__data_end__ = .;
} > RAM
/*
* Secondary data section, optional
*
* Remember to add each additional data section
* to the .copy.table above to asure proper
* initialization during startup.
*/
/*
__etext2 = ALIGN(16);
.data2 : AT (__etext2)
{
. = ALIGN(16);
__data2_start__ = .;
*(.data2)
*(.data2.*)
. = ALIGN(16);
__data2_end__ = .;
} > RAM2
*/
.bss :
{
. = ALIGN(16);
__bss_start__ = .;
*(.bss)
*(.bss.*)
*(COMMON)
. = ALIGN(16);
__bss_end__ = .;
} > RAM AT > RAM
/*
* Secondary bss section, optional
*
* Remember to add each additional bss section
* to the .zero.table above to asure proper
* initialization during startup.
*/
/*
.bss2 :
{
. = ALIGN(16);
__bss2_start__ = .;
*(.bss2)
*(.bss2.*)
. = ALIGN(16);
__bss2_end__ = .;
} > RAM2 AT > RAM2
*/
.heap (COPY) :
{
. = ALIGN(16);
__end__ = .;
PROVIDE(end = .);
. = . + __HEAP_SIZE;
. = ALIGN(16);
__HeapLimit = .;
} > RAM
.stack (ORIGIN(RAM) + LENGTH(RAM) - __STACK_SIZE) (COPY) :
{
. = ALIGN(16);
__StackLimit = .;
. = . + __STACK_SIZE;
. = ALIGN(16);
__StackTop = .;
} > RAM
PROVIDE(__stack = __StackTop);
/* Check if data + heap + stack exceeds RAM limit */
ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack")
}
qemu-virt-aarch64.ld
:
__RAM_BASE = 0x40000000;
__RAM_SIZE = 0x08000000;
__STACK_SIZE = 0x00100000;
__HEAP_SIZE = 0x00100000;
INCLUDE gcc_arm64_ram.ld
startup.s
:
.title startup64.s
.arch armv8-a
.text
.section .text.startup,"ax"
.globl Reset_Handler
Reset_Handler:
ldr x0, =__StackTop
mov sp, x0
bl main
wait: wfe
b wait
.end
pl011.c
:
#include <stdint.h>
static volatile unsigned int * const UART0DR = ( unsigned int * ) ( uintptr_t * ) 0x9000000;
int putchar(int c)
{
*UART0DR = c; /* Transmit char */
return c;
}
void putchar_uart0( int c )
{
*UART0DR = c; /* Transmit char */
}
void putc_uart0( int c )
{
*UART0DR = c; /* Transmit char */
}
void print_uart0( const char * s )
{
while( *s != '\0' ) /* Loop until end of string */
{
*UART0DR = ( unsigned int ) ( *s ); /* Transmit char */
s++; /* Next char */
}
}
void puts_uart0( const char * s )
{
while( *s != '\0' ) /* Loop until end of string */
{
*UART0DR = ( unsigned int ) ( *s ); /* Transmit char */
if (*s == '\n') {
*UART0DR = ( unsigned int ) ( '\r' );
}
s++; /* Next char */
}
}
pl011.h
:
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void putchar_uart0( int c );
void print_uart0( const char * s );
void putc_uart0( int c );
void puts_uart0( const char * s );
#ifdef __cplusplus
}
#endif
qemu-virt-aarch64.c
:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include "pl011.h"
// angel/semihosting interface
#define SYS_WRITE0 0x04
static uint64_t semihosting_call(uint32_t operation, uint64_t parameter)
{
__asm("HLT #0xF000");
}
// syscall stubs
int _close (int fd)
{
errno = EBADF;
return -1;
}
int _isatty (int fd)
{
return 1;
}
int _fstat (int fd, struct stat * st)
{
errno = EBADF;
return -1;
}
off_t _lseek (int fd, off_t ptr, int dir)
{
errno = EBADF;
return (off_t) -1;
}
int _read (int fd, void *ptr, size_t len)
{
errno = EBADF;
return -1;
}
int _write (int fd, const char *ptr, size_t len)
{
for (size_t i = 0; i < len; i++) {
putchar_uart0(ptr[i]);
}
return len;
}
void main()
{
char buffer[BUFSIZ];
uint64_t regCurrentEL;
__asm volatile ("mrs %0, CurrentEL" : "=r" (regCurrentEL));
// UART0
sprintf(buffer, "Hello EL%d World!\n", (regCurrentEL >> 2) & 0b11);
puts_uart0(buffer);
// angel/semihosting interface
sprintf(buffer, "Hello semi-hosted EL%d World!\n", (regCurrentEL >> 2) & 0b11);
semihosting_call(SYS_WRITE0, (uint64_t) (uintptr_t) buffer);
// newlib - custom syscalls.c, with _write() using UART0
printf("Hello EL%d World! (syscalls version)\n", (regCurrentEL >> 2) & 0b11);
}
Please note that the code responsible for initializing the .bss
section was omitted.
Compiling:
/opt/arm/9/gcc-arm-9.2-2019.12-x86_64-aarch64-none-elf/bin/aarch64-none-elf-gcc -I. -O0 -ggdb -mtune=cortex-a53 -nostartfiles -ffreestanding --specs=nosys.specs -L. -Wl,-T,qemu-virt-aarch64.ld -o virt.elf startup.s pl011.c qemu-virt-aarch64.c
Running:
/opt/qemu-5.2.0/bin/qemu-system-aarch64 -semihosting -m 128M -nographic -monitor none -serial stdio -machine virt,gic-version=2,secure=on,virtualization=on -cpu cortex-a53 -kernel virt.elf
Hello EL3 World!
Hello semi-hosted EL3 World!
Hello EL3 World! (syscalls version)
Qemu arm Linux kernel boot debug, no source code
The Linux kernel uses a 2-step booting processing (and this does not include any boot loader like u-Boot ...). You can better understand this especially by looking into 2 .lds files (detailed below) for linking:
arch/arm/boot/compressed/vmlinux.lds.in
, which generatesarch/arm/boot/compressed/vmlinux.lds
.Along with other .o files in
arch/arm/boot/compressed
, a vmlinux is generated inside this folder.You can use
arm-none-eabi-nm -a -n arch/arm/boot/compressed/vmlinux
to see the symbols for this stage. All addresses are physical addresses.These symbols are NOT included in System.map
The second vmlinux is generated by kernel .o files and
arch/arm/kernel/vmlinux.lds
(note: the path is different)
I hope this explains why you can not see the booting source code in Eclipse.
Related Topics
Get Canonical Path from Pathname
Difference Between ./Executable and . Executable
Using Symbolic Link Inside Web Server Root
Getting Kernel Version from The Compressed Kernel Image
How to Open The Default Text Editor in Linux
Jmeter:Difference Between Jmeter.Sh and Jmeter Without Extension File in Jmeter
Use Stdin from Within R Studio
How to Write Own Package for Recipe in Arago Project Build
Open Website from Within Eclipse's Internal Browser
Symbol Lookup Error Undefined Symbol, But All Symbols Seem to Be Present
When Compiling Programs to Run Inside a Vm, What Should March and Mtune Be Set To
Shell Script to Shutdown/Restart Linux System
How to Finding All Runnable Processes
Sharing Executable Memory Pages in Linux