FLATBOOT Barebones

From OSDev.wiki
Revision as of 02:33, 19 October 2021 by Superleaf1995 (talk | contribs) (Created page with "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/...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

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