FLATBOOT Barebones
The ESA S/390 architecture is one of the most interesting ones for kernels, since it involves concepts not seen on standard architectures, such as a dedicated processor for I/O or failure-prone checks.
Sources
The Flatboot bootloader is an easy starting point for starting OS-development on the ESA S/390. It partially-implements the stivale protocol (but it is not fully compliant). Supports ELF loading and BIN loading as well. In general; in the mainframe world having a general purpouse bootloader is not practical, since most IPL bootloaders are dependant on the device they loaded on. Flatboot wastes precious amounts of memory (64KB) which is a lot on the S/360 and the S/370. But it provides an easy starting point for newcomers.
kernel.c
The central controlling part of the system (runs in a LPAR when it's z/Arch):
// Source: https://github.com/limine-bootloader/limine-barebones/blob/master/stivale2-barebones/kernel/kernel.c
#include <stdint.h>
#include <stddef.h>
#define _STIVALE2_SPLIT_64_FORCE 1
#include <stivale2.h>
static uint8_t stack[8192];
static struct stivale2_header_tag_terminal terminal_hdr_tag = {
.tag = {
.identifier = STIVALE2_HEADER_TAG_TERMINAL_ID,
.next = 0
},
.flags = 0
};
__attribute__((section(".stivale2hdr"), used))
static struct stivale2_header stivale_hdr = {
.entry_point = 0,
.stack = (uintptr_t)stack + sizeof(stack),
.flags = (1 << 1) | (1 << 2),
.tags = (uintptr_t)&terminal_hdr_tag
};
// We will now write a helper function which will allow us to scan for tags
// that we want FROM the bootloader (structure tags).
void *stivale2_get_tag(struct stivale2_struct *stivale2_struct, uint64_t id) {
struct stivale2_tag *current_tag = (void *)stivale2_struct->tags;
for (;;) {
// If the tag pointer is NULL (end of linked list), we did not find
// the tag. Return NULL to signal this.
if (current_tag == NULL) {
return NULL;
}
// Check whether the identifier matches. If it does, return a pointer
// to the matching tag.
if (current_tag->identifier == id) {
return current_tag;
}
// Get a pointer to the next tag in the linked list and repeat.
current_tag = (void *)current_tag->next;
}
}
// The following will be our kernel's entry point.
void _start(struct stivale2_struct *stivale2_struct) {
// Let's get the terminal structure tag from the bootloader.
struct stivale2_struct_tag_terminal *term_str_tag;
term_str_tag = stivale2_get_tag(stivale2_struct, STIVALE2_STRUCT_TAG_TERMINAL_ID);
// Check if the tag was actually found.
if (term_str_tag == NULL) {
// It wasn't found, just hang...
for (;;);
}
// Let's get the address of the terminal write function.
void *term_write_ptr = (void *)term_str_tag->term_write;
// Now, let's assign this pointer to a function pointer which
// matches the prototype described in the stivale2 specification for
// the stivale2_term_write function.
void (*term_write)(const char *string, size_t length) = term_write_ptr;
term_write("Hello World\n", 13);
for (;;);
}
linker.ld
ENTRY(_start); SECTIONS { /* This is required since flatboot hates loading kernels under 64K */ . = 0x20000; .text : { *(.text); *(.text.*); *(.stivale2hdr*); } .rodata : { *(.rodata); *(.rodata.*); } .data : { *(.data); *(.data.*); } .bss : ALIGN(4K) { *(COMMON); *(.bss); *(.bss.*); } }
hercules.cnf
MAINSIZE 32 ARCHMODE ESA S/390 CPUSERIAL 000611 CPUMODEL 4381 DIAG8CMD ENABLE NUMCPU 1 PANRATE MEDIUM PGMPRDOS RESTRICTED CODEPAGE 819/1047 # This is the IPL disk and can be any address 01b9 3390 flat00.cckd
dasd.txt
Describe the files in the DASD disk:
FLAT00 3390-1 * stage1.txt STAGE2.BIN SEQ stage2.bin TRK 10 1 0 PS FB 1 18452 SYSVTOC VTOC CYL 2 KERNEL.ELF SEQ kernel CYL 1 1 0 PS FB 1 18452
Running the mainframe
#!/bin/bash
rm stivale2.h
wget https://raw.githubusercontent.com/stivale/stivale/master/stivale2.h
# Download the disk-dependant IPL
wget https://github.com/udos-project/flatboot/releases/download/0.1.2/stage1_S390.txt
mv stage1_S390.txt stage1.txt
# Download the second stage bootloader
wget https://github.com/udos-project/flatboot/releases/download/0.1.2/stage2_S390.bin
mv stage2_S390.bin stage2.bin
CC=s390-linux-gcc
CFLAGS="-fexec-charset=IBM-1047 -fno-stack-protector -pipe -m31 -ffreestanding -mpacked-stack -I. -O2 -g"
LDFLAGS="-Wl,-static,--no-dynamic-linker,-ztext -nostdlib -z max-page-size=0x1000 -static -fno-pic -fno-pie -g"
$CC $CFLAGS -c kernel.c -o kernel.o
$CC $LDFLAGS $CFLAGS -Tlinker.ld kernel.o -o kernel
dasdload -bz2 dasd.txt flat00.cckd
hercules -f hercules.cnf >hercules.log
What's next?
- Use MVS 3.8/tk4 in hercules to know how a mainframe operates
- Read this
- Catch program exceptions
- Get the CSS to be able to do read/write operations on devices
- There is no framebuffer to speak of, no GUI, only terminals - so implement a 3270 (Graphical terminal) driver or 2703 (Telnet)
- Implement multiprocessing