/* Support for ELF Boot Proposal as a boot image */
#include "config.h"
#include "arch/common/elf_boot.h"
#include "libopenbios/sys_info.h"
#include "asm/io.h"
#include "libopenbios/ipchecksum.h"
#include "openbios-version.h"
#define printf printk
#define debug  printk

/* ELF image notes provide information to the loader who boots us */

/* This compiles and generates correct PT_NOTE segment for me.
 * If it doesn't, use assembly version below. */

struct elf_image_note {
    Elf_Nhdr hdr0;
    char name0[sizeof(ELF_NOTE_BOOT)];
    char prog_name[sizeof(PROGRAM_NAME)];

    Elf_Nhdr hdr1;
    char name1[sizeof(ELF_NOTE_BOOT)];
    char version[sizeof(OPENBIOS_VERSION_STR)];

    Elf_Nhdr hdr2;
    char name2[sizeof(ELF_NOTE_BOOT)];
    unsigned short checksum;
};

const struct elf_image_note elf_image_notes
	__attribute__ ((section (".note.ELFBoot"))) =
{
    .hdr0 = {
	.n_namesz = sizeof(ELF_NOTE_BOOT),
	.n_descsz = sizeof(PROGRAM_NAME),
	.n_type = EIN_PROGRAM_NAME,
    },
    .name0 = ELF_NOTE_BOOT,
    .prog_name = PROGRAM_NAME,

    .hdr1 = {
	.n_namesz = sizeof(ELF_NOTE_BOOT),
        .n_descsz = sizeof(OPENBIOS_VERSION_STR),
	.n_type = EIN_PROGRAM_VERSION,
    },
    .name1 = ELF_NOTE_BOOT,
    .version = OPENBIOS_VERSION_STR,

    .hdr2 = {
	.n_namesz = sizeof(ELF_NOTE_BOOT),
	.n_descsz = sizeof(unsigned short),
	.n_type = EIN_PROGRAM_CHECKSUM,
    },
    .name2 = ELF_NOTE_BOOT,
    .checksum = 0, /* to be computed by external tool */
};

/* This is refered by other files */
const char *program_name = elf_image_notes.prog_name;
const char *program_version = elf_image_notes.version;

#if 0

	/* This tells the linker to make a PT_NOTE segment.
	 * If the section is named just ".note", it will be
	 * mixed up with useless .version notes generated by GCC.
	 */
	.section ".note.ELFBoot", "a"

	.align 4
	.int 2f - 1f
	.int 4f - 3f
	.int EIN_PROGRAM_NAME
1:	.asciz "ELFBoot"
2:	.align 4
3:	.asciz PROGRAM_NAME
4:

	.align 4
	.int 2f - 1f
	.int 4f - 3f
	.int EIN_PROGRAM_VERSION
1:	.asciz "ELFBoot"
2:	.align 4
3:      .asciz OPENBIOS_VERSION_STR
4:

	.align 4
	.int 2f - 1f
	.int 4f - 3f
	.int EIN_PROGRAM_CHECKSUM
1:	.asciz "ELFBoot"
2:	.align 4
3:	.short 0
4:
#endif

/* Collect information from the ELF bootloader
 * Note that we have to copy them to our own memory,
 * otherwise they might be overwritten afterward. */
void collect_elfboot_info(struct sys_info *info)
{
    Elf_Bhdr *hdr = NULL;
    char *addr, *end;
    Elf_Nhdr *nhdr;
    char *desc;

    if (info->boot_type == ELF_BHDR_MAGIC)
	hdr = phys_to_virt(info->boot_data);
    else
	hdr = phys_to_virt(info->boot_arg);

    if (hdr->b_signature != ELF_BHDR_MAGIC)
	return;

    if (ipchksum(hdr, hdr->b_size) != 0) {
	printf("Broken ELF boot notes\n");
	return;
    }

    addr = (char *) (hdr + 1);
    end = addr + hdr->b_size;
    while (addr <  end) {
	nhdr = (Elf_Nhdr *) addr;
	addr += sizeof(Elf_Nhdr);
	addr += (nhdr->n_namesz + 3) & ~3;
	desc = addr;
	addr += (nhdr->n_descsz + 3) & ~3;

	if (nhdr->n_namesz == 0) {
	    /* Standard notes */
	    switch (nhdr->n_type) {
	    case EBN_FIRMWARE_TYPE:
		info->firmware = strdup(desc);
		break;
	    case EBN_BOOTLOADER_NAME:
		debug("Bootloader: %s\n", desc);
		break;
	    case EBN_BOOTLOADER_VERSION:
		debug("Version: %s\n", desc);
		break;
	    case EBN_COMMAND_LINE:
		info->command_line = strdup(desc);
		break;
	    case EBN_LOADED_IMAGE:
		debug("Image name: %s\n", desc);
		break;
	    }
	}
    }
}
