Files
s390-tools/zipl/boot/eckd2dump_zlib.c
Marc Hartmayer 86ce85b3a6 zipl/boot: Fix typos
Reviewed-by: Mikhail Zaslonko <zaslonko@linux.ibm.com>
Reviewed-by: Stefan Haberland <sth@linux.ibm.com>
Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2025-11-07 14:34:48 +01:00

184 lines
6.1 KiB
C

/*
* zipl - zSeries Initial Program Loader tool
*
* Common ECKD dump I/O functions
*
* Copyright IBM Corp. 2013, 2023
*
* 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 "cio.h"
#include "eckd2dump.h"
#include "eckd2dump_zlib.h"
#include "boot/linux_layout.h"
#include "boot/s390.h"
#include "stage2dump.h"
#include "dump/s390_dump.h"
/*
* The first megabyte of memory is used for zlib workspace and
* the output buffer for compressed data.
*/
static void *zlib_out_buf; // zlib output buffer
static unsigned long zlib_buf_size; // zlib output buffer size
/* Zlib workspace memory offset (skipping first 64K for the dumper itself) */
#define ZLIB_WORKSPACE_OFFSET IMAGE_ENTRY
/*
* Zlib workspace initialization.
* Return -1 if zlib deflate workspace requires more than half of the
* reserved memory area. Otherwise return 0.
*/
int zlib_workarea_init(unsigned long addr, z_stream *strm)
{
strm->workspace = (void *)MAX(addr, ZLIB_WORKSPACE_OFFSET);
zlib_out_buf = (void *)ROUND_UP((unsigned long)strm->workspace +
zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL),
PAGE_SIZE);
/* Make sure there is enough space left for zlib output buffer (at least half) */
if ((unsigned long)(zlib_out_buf - strm->workspace) > ZLIB_WORKSPACE_LIMIT / 2)
return -1;
/* Memory left for zlib output buffer (currently 692K) */
zlib_buf_size = addr + ZLIB_WORKSPACE_LIMIT - (unsigned long)zlib_out_buf;
return 0;
}
#define COMPRESSED 0
#define UNCOMPRESSED 1
#define COMPRESSION_ERROR -1
/*
* Compress chunk of data of given length at the given address using zlib
* deflate compression and write it starting from the given block number.
* Variable *blk is updated to the next free block number.
* Return 0 if memory chunk is successfully compressed and written.
* Return 1 if compression is ineffective and the entire chunk is written
* uncompressed.
* In case of deflate error, write the chunk uncompressed and return -1
*/
static int compress_write_next_chunk(unsigned long addr, unsigned long len,
unsigned long *blk, z_stream *strm)
{
unsigned long start_blk;
int rc;
/* Reset the stream for each new chunk */
zlib_deflateReset(strm);
strm->next_in = (void *)addr;
strm->avail_in = len;
strm->next_out = zlib_out_buf;
strm->avail_out = zlib_buf_size;
start_blk = *blk;
while (strm->avail_in != 0) {
rc = zlib_deflate(strm, Z_NO_FLUSH);
/*
* Compression error. Write this data chunk uncompressed and
* notify the caller.
*/
if (rc != Z_OK) {
*blk = write_addr_range(start_blk, addr, len, NO_PROGRESS);
return COMPRESSION_ERROR;
}
if (strm->avail_out == 0) {
/*
* If compressed output is larger than the input, write this chunk of
* data uncompressed and notify the caller with the return code.
* Do it only if no compressed output has been written yet.
*/
if (strm->total_out >= strm->total_in &&
*blk == start_blk) {
*blk = write_addr_range(start_blk, addr, len, NO_PROGRESS);
return UNCOMPRESSED;
}
*blk = write_addr_range(*blk, (unsigned long)zlib_out_buf,
zlib_buf_size, NO_PROGRESS);
strm->next_out = (void *)zlib_out_buf;
strm->avail_out = zlib_buf_size;
}
}
while (rc == Z_OK) {
strm->next_in = NULL;
strm->avail_in = 0;
rc = zlib_deflate(strm, Z_FINISH);
len = ROUND_UP(zlib_buf_size - strm->avail_out, PAGE_SIZE);
*blk = write_addr_range(*blk, (unsigned long)zlib_out_buf, len, NO_PROGRESS);
if (strm->avail_out == 0) {
strm->next_out = zlib_out_buf;
strm->avail_out = zlib_buf_size;
rc = Z_OK;
} else {
break;
}
}
return COMPRESSED;
}
/*
* Compress and write memory dump segment with the uncompressed header to DASD
* and return the next free block number.
* The compression takes place in chunks of data of equal size (currently 1MB)
* and the offset of each compressed chunk is stored in the dump segment
* header. Due to limited size of header, the maximum size of compressed dump
* segment is limited to DUMP_SEGM_ZLIB_MAXLEN.
* If compression of a memory chunk leads to the data expansion (due to
* incompressible input), the chunk of data is written uncompressed.
* Thus every chunk of data is compressed separately and can be
* decompressed independently. The main reason is to enable zgetdump to make
* fast read seeks. Otherwise, zgetdump would need to uncompress a big dump
* segment in the worst case to extract a single piece of data.
*/
unsigned long write_compressed_dump_segment(unsigned long blk,
struct df_s390_dump_segm_hdr *segm,
z_stream *strm)
{
unsigned long head_blk, start_blk, zero_page, len, offset = 0;
unsigned long chunk_size;
int rc;
head_blk = blk;
/* Skip one block for the header (written later on) */
blk += m2b(sizeof(struct df_s390_dump_segm_hdr));
/* Compress each data chunk of the dump segment separately */
chunk_size = dump_hdr->zlib_entry_size;
for (unsigned int i = 0; i <= segm->len / chunk_size; i++) {
/* Save starting block number */
start_blk = blk;
len = i < segm->len / chunk_size ?
chunk_size : segm->len % chunk_size;
if (len == 0)
break;
rc = compress_write_next_chunk(segm->start + i * chunk_size,
len, &blk, strm);
/*
* Store the offset to the compressed chunk of data written
* to disk in blocks.
*/
segm->entry_count++;
segm->entry_offset[i] = (uint32_t)offset;
/*
* Compression was ineffective or compression error occurred,
* data chunk has benn written uncompressed.
*/
if (rc == UNCOMPRESSED || rc == COMPRESSION_ERROR)
segm->entry_offset[i] |= DUMP_SEGM_ENTRY_UNCOMPRESSED;
offset += blk - start_blk;
/* Print progress after each compressed chunk written */
progress_print(segm->start + i * chunk_size + len);
}
/* Compression successful, store compressed size in the segment header */
segm->size_on_disk = (uint32_t)offset;
/*
* Write the dump segment header itself (1 page, uncompressed) to
* the predefined location.
*/
zero_page = get_zeroed_page();
writeblock(head_blk, (unsigned long)segm,
m2b(sizeof(struct df_s390_dump_segm_hdr)), zero_page);
free_page(zero_page);
return blk;
}