/*
 * lkcd_dump.cpp
 *  lkcd dump class
 * 
 *  Copyright (C) 2004 IBM Deutschland Entwicklung GmbH, IBM Corporation
 *   
 *  Author(s): Michael Holzheu
 */

#include <zlib.h>
#include <string.h>
#include "lkcd_dump.h"

LKCDDump::LKCDDump(Dump* dump, const char* arch){
	referenceDump = dump;
        dumpHeader.magic_number   = DUMP_MAGIC_NUMBER;
        dumpHeader.version        = DUMP_VERSION_NUMBER;
        dumpHeader.header_size    = sizeof(struct _lkcd_dump_header);
        dumpHeader.time.tv_sec    = dump->getDumpTime().tv_sec;
        dumpHeader.time.tv_usec   = dump->getDumpTime().tv_usec;
	strcpy(dumpHeader.utsname_machine, arch);
        dumpHeader.memory_size    = dump->getMemSize();
        dumpHeader.memory_start   = 0;
        dumpHeader.memory_end     = dump->getMemSize();
        dumpHeader.num_dump_pages = dump->getMemSize()/0x1000;
        dumpHeader.page_size      = 0x1000; 
        dumpHeader.dump_compress  = DUMP_COMPRESS_GZIP;
        dumpHeader.dump_level     = DUMP_LEVEL_ALL;
}

int
LKCDDump::compressGZIP(const char *old, uint32_t old_size, char *n, uint32_t new_size)
{
        int rc;
        unsigned long len = old_size;
        rc = compress((Bytef*)n, &len, (const Bytef*)old, new_size);
        switch(rc){
                case Z_OK:
                        rc = len;
                        break;
                case Z_MEM_ERROR:
                        fprintf(stderr,"Z_MEM_ERROR (not enough memory)!\n");
                        rc = -1;
                        break;
                case Z_BUF_ERROR:
                        /* In this case the compressed output is bigger than the uncompressed */
                        rc = -1;
                        break;
                case Z_DATA_ERROR:
                        fprintf(stderr, "Z_DATA_ERROR (input data corrupted)!\n");
                        rc = -1;
                        break;
                default:
                        fprintf(stderr, "Z_UNKNOWN_ERROR (rc 0x%x unknown)!\n",rc);
                        rc = -1;
                        break;
        }
        return rc;
}

               
void 
LKCDDump::writeStorageDump(FILE* fh)
{
	ProgressBar progressBar;
	char dump_page_buf[DUMP_BUFFER_SIZE];
	char dpcpage[DUMP_PAGE_SIZE];
	char buf[DUMP_PAGE_SIZE];
	uint32_t dp_size,dp_flags;
	uint64_t mem_loc = 0;
	uint64_t buf_loc = 0;
	struct _dump_page dp;
	int size;
	
        memset(dump_page_buf, 0, DUMP_BUFFER_SIZE);
        memcpy((void *)dump_page_buf, (const void *)&dumpHeader, sizeof(dumpHeader));
	/* write dump header */

        if(fseek(fh, 0L, SEEK_SET) < 0) {
		throw(DumpErrnoException("LKCDDump::writeStorageDump - seek failed"));
        }
        if (fwrite((char *)dump_page_buf, DUMP_BUFFER_SIZE, 1,fh) != 1) {
		throw(DumpErrnoException("write failed"));
        }

	/* write memory */

	referenceDump->seekMem(0);

	mem_loc = 0;
        while (mem_loc < dumpHeader.memory_size) {
		// fprintf(stderr,"mem: %llx/%llx\n",mem_loc,dumpHeader.memory_size);
                referenceDump->readMem(buf, DUMP_PAGE_SIZE);
		copyRegsToPage(mem_loc,buf);

                memset(dpcpage, 0, DUMP_PAGE_SIZE);
                /* get the new compressed page size
                 */

                size = compressGZIP((char *)buf, DUMP_PAGE_SIZE, (char *)dpcpage, DUMP_PAGE_SIZE);

                /* if compression failed or compressed was ineffective,
                 * we write an uncompressed page
                 */
                if (size < 0) {
                        dp_flags = DUMP_DH_RAW;
                        dp_size  = DUMP_PAGE_SIZE;
                } else {
                        dp_flags = DUMP_DH_COMPRESSED;
                        dp_size  = size;
                }
                dp.address = mem_loc;
                dp.size    = dp_size;
                dp.flags   = dp_flags;
                memcpy((void *)(dump_page_buf + buf_loc), (const void *)&dp, sizeof(dp));
                buf_loc += sizeof(dp);
                /* copy the page of memory
                 */
                if (dp_flags & DUMP_DH_COMPRESSED) {
                        /* copy the compressed page
                         */
                        memcpy((void *)(dump_page_buf + buf_loc), (const void *)dpcpage, dp_size);
                } else {
                        /* copy directly from memory
                         */
                        memcpy((void *)(dump_page_buf + buf_loc), (const void *)buf, dp_size);
                }
                buf_loc += dp_size;
                if(fwrite(dump_page_buf, buf_loc, 1,fh) != 1){
			throw(DumpErrnoException("write failed"));
                }
                buf_loc = 0;
                mem_loc += DUMP_PAGE_SIZE;
                progressBar.displayProgress(mem_loc/(1024*1024),dumpHeader.memory_size/(1024*1024));
        }

        /* write end marker
         */

        dp.address = 0x0;
        dp.size    = DUMP_DH_END;
        dp.flags   = 0x0;
        if(fwrite(dump_page_buf, sizeof(dp),1,fh) != 1){
		throw(DumpErrnoException("write failed"));
	}
	printf("\n\n");
	fflush(stdout);
}

struct timeval 
LKCDDump::getDumpTime(void) const
{
	struct timeval rc;
	rc.tv_sec = dumpHeader.time.tv_sec;
	rc.tv_usec = dumpHeader.time.tv_usec;
	return rc;
}

LKCDDump32::LKCDDump32(Dump* dump, const RegisterContent32& r) 
	: LKCDDump(dump,"s390")
{
	registerContent = r;
}

void 
LKCDDump32::copyRegsToPage(uint64_t offset, char *buf){
	int cpu;
	for(cpu = 0; cpu < registerContent.getNumCpus(); cpu++){
		if(offset == registerContent.regSets[cpu].prefix){
			memcpy(buf + 216, &registerContent.regSets[cpu].cpuTimer, sizeof(registerContent.regSets[cpu].cpuTimer));
			memcpy(buf + 224, &registerContent.regSets[cpu].clkCmp, sizeof(registerContent.regSets[cpu].clkCmp));
			memcpy(buf + 256, &registerContent.regSets[cpu].psw, sizeof(registerContent.regSets[cpu].psw));
			memcpy(buf + 264, &registerContent.regSets[cpu].prefix, sizeof(registerContent.regSets[cpu].prefix));
			memcpy(buf + 288, &registerContent.regSets[cpu].acrs, sizeof(registerContent.regSets[cpu].acrs));
			memcpy(buf + 352, &registerContent.regSets[cpu].fprs, sizeof(registerContent.regSets[cpu].fprs));
			memcpy(buf + 384, &registerContent.regSets[cpu].gprs, sizeof(registerContent.regSets[cpu].gprs));
			memcpy(buf + 448, &registerContent.regSets[cpu].crs, sizeof(registerContent.regSets[cpu].crs));
		}
	}
}

void 
LKCDDump32::writeDump(const char* filename)
{
	FILE* fh = NULL;
	fh = fopen(filename,"w+");
	if(!fh){
		char msg[1024];
		sprintf(msg, "Open of dump '%s' failed",filename);
		throw(DumpErrnoException(msg));
	}
	writeStorageDump(fh);
	if(fh)
		fclose(fh);
}


LKCDDump64::LKCDDump64(Dump* dump, const RegisterContent64& r) 
	: LKCDDump(dump,"s390x")
{
	registerContent = r;
}

void 
LKCDDump64::copyRegsToPage(uint64_t offset, char *buf){
	int cpu;
	for(cpu = 0; cpu < registerContent.getNumCpus(); cpu++){
		if(offset == (registerContent.regSets[cpu].prefix + 0x1000)){
			memcpy(buf + 0x328, &registerContent.regSets[cpu].cpuTimer, sizeof(registerContent.regSets[cpu].cpuTimer));
			memcpy(buf + 0x330, &registerContent.regSets[cpu].clkCmp, sizeof(registerContent.regSets[cpu].clkCmp));
			memcpy(buf + 0x300, &registerContent.regSets[cpu].psw, sizeof(registerContent.regSets[cpu].psw));
			memcpy(buf + 0x318, &registerContent.regSets[cpu].prefix, sizeof(registerContent.regSets[cpu].prefix));
			memcpy(buf + 0x340, &registerContent.regSets[cpu].acrs, sizeof(registerContent.regSets[cpu].acrs));
			memcpy(buf + 0x200, &registerContent.regSets[cpu].fprs, sizeof(registerContent.regSets[cpu].fprs));
			memcpy(buf + 0x280, &registerContent.regSets[cpu].gprs, sizeof(registerContent.regSets[cpu].gprs));
			memcpy(buf + 0x380, &registerContent.regSets[cpu].crs, sizeof(registerContent.regSets[cpu].crs));
			memcpy(buf + 0x31c, &registerContent.regSets[cpu].fpCr, sizeof(registerContent.regSets[cpu].fpCr));			
		}
	}
}

void
LKCDDump64::writeDump(const char* filename)
{
	FILE* fh = NULL;
	fh = fopen(filename,"w+");
	if(!fh){
		char msg[1024];
		sprintf(msg, "Open of dump '%s' failed",filename);
		throw(DumpErrnoException(msg));
	}
	writeStorageDump(fh);
	if(fh)
		fclose(fh);
}
