grub2-dev: Porting
1
1 5 Porting
1 *********
1
1 GRUB2 is designed to be easily portable accross platforms. But because
1 of the nature of bootloader every new port must be done separately.
1 Here is how I did MIPS (loongson and ARC) and Xen ports. Note than this
1 is more of suggestions, not absolute truth.
1
1 First of all grab any architecture specifications you can find in
1 public (please avoid NDA).
1
1 First stage is "Hello world". I've done it outside of GRUB for
1 simplicity. Your task is to have a small program which is loadable as
1 bootloader and clearly shows its presence to you. If you have easily
1 accessible console you can just print a message. If you have a mapped
1 framebuffer you know address of, you can draw a square. If you have a
1 debug facility, just hanging without crashing might be enough. For the
1 first stage you can choose to load the bootloader across the network
1 since format for network image is often easier than for local boot and
1 it skips the need of small intermediary stages and nvram handling.
1 Additionally you can often have a good idea of the needed format by
1 running "file" on any netbootable executable for given platform.
1
1 This program should probably have 2 parts: an assembler and C one.
1 Assembler one handles BSS cleaning and other needed setup (on some
1 platforms you may need to switch modes or copy the executable to its
1 definitive position). So your code may look like (x86 assembly for
1 illustration purposes)
1
1 .globl _start
1 _start:
1 movl $_bss_start, %edi
1 movl $_end, %ecx
1 subl %edi, %ecx
1 xorl %eax, %eax
1 cld
1 rep
1 stosb
1 call main
1
1
1 static const char msg[] = "Hello, world";
1
1 void
1 putchar (int c)
1 {
1 ...
1 }
1
1 void
1 main (void)
1 {
1 const char *ptr = msg;
1 while (*ptr)
1 putchar (*ptr++);
1 while (1);
1 }
1
1 Sometimes you need a third file: assembly stubs for
1 ABI-compatibility.
1
1 Once this file is functional it's time to move it into GRUB2. The
1 startup assembly file goes to grub-core/kern/$cpu/$platform/startup.S.
1 You should also include grub/symbol.h and replace call to entry point
1 with call to EXT_C(grub_main). The C file goes to
1 grub-core/kern/$cpu/$platform/init.c and its entry point is renamed to
1 void grub_machine_init (void). Keep final infinite loop for now. Stubs
1 file if any goes to grub-core/kern/$cpu/$platform/callwrap.S. Sometimes
1 either $cpu or $platform is dropped if file is used on several cpus
1 respectivelyplatforms. Check those locations if they already have what
1 you're looking for.
1
1 Then modify in configure.ac the following parts:
1
1 CPU names:
1
1 case "$target_cpu" in
1 i[[3456]]86) target_cpu=i386 ;;
1 amd64) target_cpu=x86_64 ;;
1 sparc) target_cpu=sparc64 ;;
1 s390x) target_cpu=s390 ;;
1 ...
1 esac
1
1 Sometimes CPU have additional architecture names which don't
1 influence booting. You might want to have some canonical name to avoid
1 having bunch of identical platforms with different names.
1
1 NOTE: it doesn't influence compile optimisations which depend solely
1 on chosen compiler and compile options.
1
1 if test "x$with_platform" = x; then
1 case "$target_cpu"-"$target_vendor" in
1 i386-apple) platform=efi ;;
1 i386-*) platform=pc ;;
1 x86_64-apple) platform=efi ;;
1 x86_64-*) platform=pc ;;
1 powerpc-*) platform=ieee1275 ;;
1 ...
1 esac
1 else
1 ...
1 fi
1
1 This part deals with guessing the platform from CPU and vendor.
1 Sometimes you need to use 32-bit mode for booting even if OS runs in
1 64-bit one. If so add your platform to:
1
1 case "$target_cpu"-"$platform" in
1 x86_64-efi) ;;
1 x86_64-emu) ;;
1 x86_64-*) target_cpu=i386 ;;
1 powerpc64-ieee1275) target_cpu=powerpc ;;
1 esac
1
1 Add your platform to the list of supported ones:
1
1 case "$target_cpu"-"$platform" in
1 i386-efi) ;;
1 x86_64-efi) ;;
1 i386-pc) ;;
1 i386-multiboot) ;;
1 i386-coreboot) ;;
1 ...
1 esac
1
1 If explicit -m32 or -m64 is needed add it to:
1
1 case "$target_cpu" in
1 i386 | powerpc) target_m32=1 ;;
1 x86_64 | sparc64) target_m64=1 ;;
1 esac
1
1 Finally you need to add a conditional to the following block:
1
1 AM_CONDITIONAL([COND_mips_arc], [test x$target_cpu = xmips -a x$platform = xarc])
1 AM_CONDITIONAL([COND_sparc64_ieee1275], [test x$target_cpu = xsparc64 -a x$platform = xieee1275])
1 AM_CONDITIONAL([COND_powerpc_ieee1275], [test x$target_cpu = xpowerpc -a x$platform = xieee1275])
1
1 Next stop is gentpl.py. You need to add your platform to the list of
1 supported ones (sorry that this list is duplicated):
1
1 GRUB_PLATFORMS = [ "emu", "i386_pc", "i386_efi", "i386_qemu", "i386_coreboot",
1 "i386_multiboot", "i386_ieee1275", "x86_64_efi",
1 "mips_loongson", "sparc64_ieee1275",
1 "powerpc_ieee1275", "mips_arc", "ia64_efi",
1 "mips_qemu_mips", "s390_mainframe" ]
1
1 You may also want already to add new platform to one or several of
1 available groups. In particular we always have a group for each CPU
1 even when only one platform for given CPU is available.
1
1 Then comes grub-core/Makefile.core.def. In the block "kernel" you'll
1 need to define ldflags for your platform ($cpu_$platform_ldflags). You
1 also need to declare startup asm file ($cpu_$platform_startup) as well
1 as any other files (e.g. init.c and callwrap.S) (e.g. $cpu_$platform =
1 kern/$cpu/$platform/init.c). At this stage you will also need to add
1 dummy dl.c and cache.S with functions grub_err_t
1 grub_arch_dl_check_header (void *ehdr), grub_err_t
1 grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) (dl.c) and
1 void grub_arch_sync_caches (void *address, grub_size_t len) (cache.S).
1 They won't be used for now.
1
1 You will need to create directory include/$cpu/$platform and a file
1 include/$cpu/types.h. The later folowing this template:
1
1 #ifndef GRUB_TYPES_CPU_HEADER
1 #define GRUB_TYPES_CPU_HEADER 1
1
1 /* The size of void *. */
1 #define GRUB_TARGET_SIZEOF_VOID_P 4
1
1 /* The size of long. */
1 #define GRUB_TARGET_SIZEOF_LONG 4
1
1 /* mycpu is big-endian. */
1 #define GRUB_TARGET_WORDS_BIGENDIAN 1
1 /* Alternatively: mycpu is little-endian. */
1 #undef GRUB_TARGET_WORDS_BIGENDIAN
1
1 #endif /* ! GRUB_TYPES_CPU_HEADER */
1
1 You will also need to add a dummy file to datetime and setjmp modules
1 to avoid any of it having no files. It can be just completely empty at
1 this stage.
1
1 You'll need to make grub-mkimage.c (util/grub_mkimage.c) aware of the
1 needed format. For most commonly used formats like ELF, PE, aout or raw
1 the support is already present and you'll need to make it follow the
1 existant code paths for your platform adding adjustments if necessary.
1 When done compile:
1
1 ./autogen.sh
1 ./configure --target=$cpu --with-platform=$platform TARGET_CC=.. OBJCOPY=... STRIP=...
1 make > /dev/null
1
1 And create image
1
1 ./grub-mkimage -d grub-core -O $format_id -o test.img
1
1 And it's time to test your test.img.
1
1 If it works next stage is to have heap, console and timer.
1
1 To have the heap working you need to determine which regions are
1 suitable for heap usage, allocate them from firmware and map (if
1 applicable). Then call grub_mm_init_region (vois *start, grub_size_t s)
1 for every of this region. As a shortcut for early port you can allocate
1 right after _end or have a big static array for heap. If you do you'll
1 probably need to come back to this later. As for output console you
1 should distinguish between an array of text, terminfo or graphics-based
1 console. Many of real-world examples don't fit perfectly into any of
1 these categories but one of the models is easier to be used as base. In
1 second and third case you should add your platform to terminfokernel
1 respectively videoinkernel group. A good example of array of text is
1 i386-pc (kern/i386/pc/init.c and term/i386/pc/console.c). Of terminfo
1 is ieee1275 (kern/ieee1275/init.c and term/ieee1275/console.c). Of
1 video is loongson (kern/mips/loongson/init.c). Note that terminfo has
1 to be inited in 2 stages: one before (to get at least rudimentary
1 console as early as possible) and another after the heap (to get
1 full-featured console). For the input there are string of keys,
1 terminfo and direct hardware. For string of keys look at i386-pc (same
1 files), for termino ieee1275 (same files) and for hardware loongson
1 (kern/mips/loongson/init.c and term/at_keyboard.c).
1
1 For the timer you'll need to call grub_install_get_time_ms (...)
1 with as sole argument a function returning a grub_uint64_t of a number
1 of milliseconds elapsed since arbitrary point in the past.
1
1 Once these steps accomplished you can remove the inifinite loop and
1 you should be able to get to the minimal console. Next step is to have
1 module loading working. For this you'll need to fill kern/$cpu/dl.c and
1 kern/$cpu/cache.S with real handling of relocations and respectively the
1 real sync of I and D caches. Also you'll need to decide where in the
1 image to store the modules. Usual way is to have it concatenated at the
1 end. In this case you'll need to modify startup.S to copy modules out
1 of bss to let's say ALIGN_UP (_end, 8) before cleaning out bss. You'll
1 probably find useful to add total_module_size field to startup.S. In
1 init.c you need to set grub_modbase to the address where modules can be
1 found. You may need grub_modules_get_end () to avoid declaring the
1 space occupied by modules as usable for heap. You can test modules
1 with:
1
1 ./grub-mkimage -d grub-core -O $format_id -o test.img hello
1
1 and then running "hello" in the shell.
1
1 Once this works, you should think of implementing disk access. Look
1 around disk/ for examples.
1
1 Then, very importantly, you probably need to implement the actual
1 loader (examples available in loader/)
1
1 Last step to have minimally usable port is to add support to
1 grub-install to put GRUB in a place where firmware or platform will pick
1 it up.
1
1 Next steps are: filling datetime.c, setjmp.S, network (net/drivers),
1 video (video/), halt (lib/), reboot (lib/).
1
1 Please add your platform to Platform limitations and Supported
1 kernels chapter in user documentation and mention any steps you skipped
1 which result in reduced features or performance. Here is the quick
1 checklist of features. Some of them are less important than others and
1 skipping them is completely ok, just needs to be mentioned in user
1 documentation.
1
1 Checklist:
1 * Is heap big enough?
1 * Which charset is supported by console?
1 * Does platform have disk driver?
1 * Do you have network card support?
1 * Are you able to retrieve datetime (with date)?
1 * Are you able to set datetime (with date)?
1 * Is serial supported?
1 * Do you have direct disk support?
1 * Do you have direct keyboard support?
1 * Do you have USB support?
1 * Do you support loading through network?
1 * Do you support loading from disk?
1 * Do you support chainloading?
1 * Do you support network chainloading?
1 * Does cpuid command supports checking all CPU features that the user
1 might want conditionalise on (64-bit mode, hypervisor,...)
1 * Do you support hints? How reliable are they?
1 * Does platform have ACPI? If so do "acpi" and "lsacpi" modules work?
1 * Do any of platform-specific operations mentioned in the relevant
1 section of user manual makes sense on your platform?
1 * Does your platform support PCI? If so is there an appropriate
1 driver for GRUB?
1 * Do you support badram?
1