/* * zipl - zSeries Initial Program Loader tool * * Main program for stage2 bootloader * * Copyright IBM Corp. 2013, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include "error.h" #include "libc.h" #include "menu.h" #include "s390.h" #include "stage2.h" static int is_null_descriptor(disk_blockptr_t *address) { unsigned long long *value = (unsigned long long *)address; return *value == 0; } static void *load_blocklist(struct component_entry *descriptor_address, struct subchannel_id subchannel_id, void *load_address) { disk_blockptr_t *indirect_blocks, *indirect_blockspace, *start_addr; unsigned long length; long long nr_descr; /* start address to load first indirect blocks */ start_addr = (disk_blockptr_t *)&descriptor_address->data; /* get a free page to store indirect blocks in */ indirect_blockspace = (void *)get_zeroed_page(); do { length = extract_length(start_addr); /* calculate number of descriptors to be processed and base 0 */ nr_descr = length/DESCR_PER_BLOCK - 1; indirect_blocks = indirect_blockspace; /* load indirect blocks */ load_direct(start_addr, subchannel_id, indirect_blocks); /* process indirect blocks */ while (nr_descr > 0) { if (is_null_descriptor(indirect_blocks)) goto out_free_page; /* for special zero block simply clear mem */ if (!is_zero_block(indirect_blocks)) { length = extract_length(indirect_blocks); memset(load_address, 0, length); load_address += length; } else load_address = load_direct(indirect_blocks, subchannel_id, load_address); indirect_blocks++; nr_descr--; } /* update pointer for next round */ start_addr = indirect_blocks; /* as long as another indirect block needs to be loaded */ } while (!is_null_descriptor(indirect_blocks)); out_free_page: free_page((unsigned long)indirect_blockspace); return load_address; } static void execute(uint64_t psw) { asm volatile( " lpsw %[psw]\n" : : [psw] "Q" (psw) : "cc" ); } void start(void) { struct stage2_descr stage2_descr; struct subchannel_id subchannel_id; void *load_address; struct component_entry *entry; disk_blockptr_t *blockptr; void *load_page; int config_nr; subchannel_id = *(struct subchannel_id *)&S390_lowcore.subchannel_id; stage2_descr = *(struct stage2_descr*)STAGE2_DESC; /* print menu and get configuration number */ config_nr = menu(); kdump_stage2(config_nr); io_irq_enable(); set_device(subchannel_id, ENABLED); load_page = (void *)get_zeroed_page(); load_address = (disk_blockptr_t *) load_page; /* load blockpointer list to load address */ load_direct((disk_blockptr_t *)&stage2_descr, subchannel_id, load_address); blockptr = (disk_blockptr_t *)(load_address + sizeof(disk_blockptr_t)); load_direct(&blockptr[config_nr], subchannel_id, load_address); /* skip header */ entry = (struct component_entry *) (load_address + sizeof(struct component_header)); while (entry->type == COMPONENT_LOAD || entry->type == COMPONENT_SIGNATURE) { if (entry->type == COMPONENT_SIGNATURE) { /* Skip unhandled signature components */ entry++; continue; } load_address = (void *)(unsigned long) entry->address.load_address[1]; load_blocklist(entry, subchannel_id, load_address); entry++; } if (entry->type != COMPONENT_EXECUTE) panic(EWRONGTYPE, ""); free_page((unsigned long)load_page); io_irq_disable(); set_device(subchannel_id, DISABLED); execute(entry->address.load_psw); } void panic_notify(unsigned long UNUSED(reason)) { } uint64_t stage2_head __attribute__((section(".stage2.head")));