/*
 * This file is part of the coreboot project.
 *
 * Copyright 2015 Google Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <boot_device.h>
#include <cbfs.h>
#include <endian.h>
#include <stdlib.h>

/* The ROM is memory mapped just below 4GiB. Form a pointer for the base. */
#define rom_base ((void *)(uintptr_t)(0x100000000ULL-CONFIG_ROM_SIZE))

static const struct mem_region_device boot_dev =
	MEM_REGION_DEV_RO_INIT(rom_base, CONFIG_ROM_SIZE);

const struct region_device *boot_device_ro(void)
{
	return &boot_dev.rdev;
}

static int cbfs_master_header_props(struct cbfs_props *props)
{
	struct cbfs_header header;
	int32_t offset;
	const struct region_device *bdev;

	bdev = boot_device_ro();

	rdev_readat(bdev, &offset, CONFIG_ROM_SIZE - sizeof(offset),
			sizeof(offset));

	/* The offset is relative to the end of the media. */
	offset += CONFIG_ROM_SIZE;

	rdev_readat(bdev, &header, offset, sizeof(header));

	header.magic = ntohl(header.magic);
	header.romsize = ntohl(header.romsize);
	header.bootblocksize = ntohl(header.bootblocksize);
	header.offset = ntohl(header.offset);

	if (header.magic != CBFS_HEADER_MAGIC)
		return -1;

	props->offset = header.offset;
	if (header.romsize != CONFIG_ROM_SIZE)
		props->size = CONFIG_ROM_SIZE;
	else
		props->size = header.romsize;
	props->size -= props->offset;
	props->size -= header.bootblocksize;
	props->size = ALIGN_DOWN(props->size, 64);

	return 0;
}

const struct cbfs_locator cbfs_master_header_locator = {
	.name = "Master Header Locator",
	.locate = cbfs_master_header_props,
};
