ZXFL Barebones
|
WAIT! Have you read Getting Started, Beginner Mistakes, and some of the related OS theory? |
| Difficulty level |
|---|
Medium |
About
A minimal kernel targeting IBM z/Architecture (s390x), using ZXFoundationLoader (ZXFL) dual-stage loader.
Note: This is a minimal barebones intended to get you to a booting kernel as quickly as possible. It deliberately omits explanation of z/Architecture concepts (IPL, DASD, PSW, boot protocol internals, etc.). See the IBM Principle Of Operations or ZXFoundation API documentation for a thorough treatment of all of these.
- ZXFoundation source: GitHub
- Documentation: assembler-0.github.io/ZXFoundation
Why ZXFoundationLoader?
Realistically, this is one of the few and only hobbyist IPL loader for the s390x architecture. There are some existing projects like FLATBOOT here are its limitation:
- Adapts (and awkwardly implements) x86 protocol stivale2 on a mainframe architecture
- Full EBCDIC end to end (which not all compiler installation supports)
- Doesn't even support ELF64, 32-bit ELFs only
- Abandoned/Inactive (?)
Prerequisites
s390x-ibm-linux-gnu-gcccross-compiler (or anys390x-*-gcc)s390x-ibm-linux-gnu-lddasdload(Hercules tools)- ZXFoundation binaries:
core.zxfoundationloader00.sys,core.zxfoundationloader01.sysandzxsign
Compiling ZXFoundation
git clone https://github.com/assembler-0/ZXFoundation.git
cd ZXFoundation
mkdir build && cd build
cmake .. -DZX_TOOLCHAIN_FILE=../cmake/toolchain/zxfoundation-gcc.cmake -DZX_CONFIG_FILE=../cmake/configs/base.cmake -GNinja
ninja
ls -l core.zxfoundationloader00.sys core.zxfoundationloader01.sys zxsign
# now, you can copy the listed files to the barebones sources (i.e. ZXFoundation/docs/barebones/)
Source Files
- main.c - kernel main entry point
#include <stdint.h>
#include "zxfl.h"
[[noreturn]] void zxfoundation_global_initialize(zxfl_boot_protocol_t *boot) {
while (1) __asm__ volatile("nop");
}
- head64.S - assembly stub
// arch/s390x/init/head64.S (ZXFoundation)
#include <arch/s390x/init/zxfl/zxvl.h>
#include <arch/s390x/init/zxfl/zxfl.h>
#define KERNEL_BOOT_STACK_SIZE 0x4000
.section .text.handshake, "ax"
.globl __zxfl_handshake_stub
__zxfl_handshake_stub:
llihf %r0, ZXVL_SEED_HI
iilf %r0, ZXVL_SEED_LO
xgr %r2, %r0
lgr %r0, %r2
sllg %r0, %r0, 17
srlg %r1, %r2, 47
ogr %r0, %r1
llihf %r1, ZXVL_HS_RESPONSE_HI
iilf %r1, ZXVL_HS_RESPONSE_LO
lgr %r2, %r0
agr %r2, %r1
br %r14
.section .text.__zx_start_kernel64, "ax", @progbits
.globl __zx_start_kernel64
__zx_start_kernel64:
lgr %r11, %r2
larl %r2, __bss_start
larl %r3, __bss_end
sgr %r3, %r2 // R3 = BSS size in bytes
jz .Lbss_done
lgr %r4, %r2 // R4 = source (same as dest)
sgr %r5, %r5 // R5 = 0 (source length 0 + pad byte 0x00)
.Lmvcl_retry:
mvcl %r2, %r4
jo .Lmvcl_retry
.Lbss_done:
lgr %r2, %r11
larl %r15, __kernel_boot_stack_top
aghi %r15, -160 // 160-byte register save area
// Enable AFP (bit 45) and Vector (bit 46) for BSP before entering C++ (you can remove this if you dont need these features. though compilers may assume this feature)
stctg %c0, %c0, 0(%r15)
lg %r1, 0(%r15)
iilf %r0, 0x00060000
ogr %r1, %r0
stg %r1, 0(%r15)
lctlg %c0, %c0, 0(%r15)
// Zero out backchain and r14 save area to terminate unwinder gracefully
sgr %r0, %r0
stg %r0, 0(%r15)
stg %r0, 112(%r15)
lgr %r2, %r11
brasl %r14, zxfoundation_global_initialize
.Lhalt:
larl %r1, .Ldisabled_wait_psw
lpswe 0(%r1)
j .Lhalt
.align 8
.Ldisabled_wait_psw:
.quad PSW_MASK_DISABLED_WAIT, 0x0000000000000000
// BSP boot stack: kernel-owned, in .bss, zeroed by the entry path above.
.section .bss.boot_stack, "aw", @nobits
.align 16
.globl __kernel_boot_stack
__kernel_boot_stack:
.skip KERNEL_BOOT_STACK_SIZE
.globl __kernel_boot_stack_top
__kernel_boot_stack_top:
.section .note.GNU-stack, "", @progbits
- link.ld - linker script
/* arch/s390x/init/link.ld — ZXFoundation Kernel Linker Script */
ENTRY(__zx_start_kernel64)
PHDRS {
hs_seg PT_LOAD FLAGS(0x00400005); /* ZXVL_PFLAGS_HS: PF_X|PF_R + bit22 */
entry_seg PT_LOAD FLAGS(0x00800005); /* ZXVL_PFLAGS_ENTRY: PF_X|PF_R + bit23 */
nucleus_text PT_LOAD FLAGS(0x00000005); /* PF_X|PF_R — kernel .text */
nucleus_ro PT_LOAD FLAGS(0x00000004); /* PF_R — kernel .rodata */
nucleus_data PT_LOAD FLAGS(0x00000006); /* PF_R|PF_W — kernel .data */
nucleus_bss PT_LOAD FLAGS(0x00000006); /* PF_R|PF_W — kernel .bss */
lock_seg PT_LOAD FLAGS(0x00100006); /* ZXVL_PFLAGS_LOCK: PF_R|PF_W + bit20 */
checksums_seg PT_LOAD FLAGS(0x00200004); /* ZXVL_PFLAGS_CKSUM: PF_R + bit21 */
}
nucleus_vma_base = 0xFFFF800000100000;
SECTIONS {
. = nucleus_vma_base;
__zx_vma_start = .;
.text.handshake : ALIGN(8) {
KEEP(*(.text.handshake))
} :hs_seg
.text.entry : ALIGN(8) {
KEEP(*(.text.__zx_start_kernel64))
} :entry_seg
.text : ALIGN(8) {
*(.text .text.*)
} :nucleus_text
.rodata : ALIGN(4096) {
__rodata_start = .;
*(.rodata .rodata.*)
__rodata_end = .;
} :nucleus_ro
.data : ALIGN(4096) {
__data_start = .;
*(.data .data.*)
__data_end = .;
} :nucleus_data
.bss : ALIGN(4096) {
__bss_start = .;
*(COMMON)
*(.bss .bss.*)
__bss_end = .;
} :nucleus_bss
.zxfl_lock : ALIGN(4096) {
LONG(0xCCBBCC35)
LONG(0x5A58464C)
. += 0x1000 - 8;
LONG(0xE5664311)
} :lock_seg
.zxvl_checksums : ALIGN(4096) {
KEEP(*(.zxvl_checksums))
} :checksums_seg
__zx_vma_end = .;
/DISCARD/ : { *(.comment) *(.eh_frame) *(.note.GNU-stack) }
}
- zxvl_cksum.c - SHA256 kernel checksum
#include "zxvl.h"
__attribute__((section(".zxvl_checksums"), used))
zxvl_checksum_table_t zxvl_kernel_checksum_table = { 0 };
- sysres.conf - dasdload control file
sysres 3390-2 * core.zxfoundationloader00.sys
core.zxfoundationloader01.sys SEQ core.zxfoundationloader01.sys TRK 5 0 0 PS FB 4096 4096 0
core.zxfoundation.nucleus SEQ core.zxfoundation.nucleus TRK 300 0 0 PS FB 4096 4096 0
etc.zxfoundation.parm SEQ etc.zxfoundation.parm TRK 2 0 0 PS FB 4096 4096 0- hercules.conf - hercules script
ARCHLVL z/Arch
CPUSERIAL 000666
CPUMODEL $(z16)
LPARNUM 1
LPARNAME ZXF
PLANT ZZ
MANUFACTURER IBM
MAINSIZE 512M
XPNDSIZE 0
NUMCPU 4
MAXCPU 8
DIAG8CMD ENABLE
CNSLPORT 3270
0009 3215-C /
001C SYSG
001F 3270
0100 3390 sysres.3390
IPL 0100- etc.zxfoundation.parm - parameter file (cmdline)
syssize=512M sysmodule=Using with C++ (modules)
ZXFL is capable of loading a C++ kernel (like most other loader). With traditional C++, ZXFL protocol (the C header) can easily be adapted. For C++ modules, the protocol module is not entirely freestanding-compatible, and depends on several simple dependencies:
- ZXFoundation fundamental types (rust-like) (can be trivially replaced by standard C/C++ types)
- STFLE_MAX_DWORDS (just a number)
- CONFIG_ZX_MAX_CPUS (your configuration)
- SHA256_DIGEST_SIZE (also a number)
- string_view-like implementation (can be swapped out for
const char*)
Though, is possible to adapt/hard-code for your kernel.
Environment and Compilation (from scratch)
# navigate to your source dir
# copy your ZXFoundation binaries here
# get header files
wget https://raw.githubusercontent.com/assembler-0/ZXFoundation/refs/heads/master/arch/s390x/init/zxfl/include/arch/s390x/init/zxfl/zxfl.h
wget https://raw.githubusercontent.com/assembler-0/ZXFoundation/refs/heads/master/arch/s390x/init/zxfl/include/arch/s390x/init/zxfl/zxvl.h
# make sure your sources are here
export CFLAGS="-ffreestanding -nostdlib -msoft-float -mno-vx -m64 -march=z13 -mtune=z13 \
-pipe -fno-stack-protector -std=c23 -mzarch -static-libgcc -O2 -g0 -I."
export LDFLAGS="-nostdlib -static --no-dynamic-linker -ztext -zmax-page-size=0x1000 \
--no-pie -g -melf64_s390"
# It is possible to use clang with target triple s390x-{unknown-none,linux-gnu}-elf
s390x-ibm-linux-gnu-gcc $CFLAGS -c head64.S -o head64.o
s390x-ibm-linux-gnu-gcc $CFLAGS -c main.c -o main.o
s390x-ibm-linux-gnu-gcc $CFLAGS -c zxvl_cksum.c -o zxvl_cksum.o
s390x-ibm-linux-gnu-ld -T link.ld $LDFLAGS head64.o main.o zxvl_cksum.o \
-o core.zxfoundation.nucleus
# Embed checksums required by the ZXFL ZXVerifiedLoad protocol
./zxsign core.zxfoundation.nucleus
# Build the DASD image
dasdload -z sysres.conf sysres.3390
The output is sysres.3390, a CKD 3390 disk image bootable under Hercules.
Running
$ hercules -f hercules.cnfFurther Reading
For loader internals, boot protocol layout, memory map, and kernel subsystems see the full documentation.