/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 *
 * Copyright (C) 2011 Amaury Pouly
 *
 * 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; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/
#ifndef __SB_H__
#define __SB_H__

#include <stdint.h>
#include <stdbool.h>

#include "misc.h"

#define BLOCK_SIZE      16

/* All fields are in big-endian BCD */
struct sb_version_t
{
    uint16_t major;
    uint16_t pad0;
    uint16_t minor;
    uint16_t pad1;
    uint16_t revision;
    uint16_t pad2;
} __attribute__((packed));

struct sb_header_t
{
    uint8_t sha1_header[20]; /* SHA-1 of the rest of the header */
    uint8_t signature[4]; /* Signature "STMP" */
    uint8_t major_ver; /* Should be 1 */
    uint8_t minor_ver; /* Should be 1 */
    uint16_t flags;
    uint32_t image_size; /* In blocks (=16bytes) */
    uint32_t first_boot_tag_off; /* Offset in blocks */
    uint32_t first_boot_sec_id; /* First bootable section ID */
    uint16_t nr_keys; /* Number of encryption keys */
    uint16_t key_dict_off; /* Offset to key dictionary (in blocks) */
    uint16_t header_size; /* In blocks */
    uint16_t nr_sections; /* Number of sections */
    uint16_t sec_hdr_size; /* Section header size (in blocks) */
    uint8_t rand_pad0[6]; /* Random padding */
    uint64_t timestamp; /* In microseconds since 2000/1/1 00:00:00 */
    struct sb_version_t product_ver;
    struct sb_version_t component_ver;
    uint16_t drive_tag; /* first tag to boot ? */
    uint8_t rand_pad1[6]; /* Random padding */
} __attribute__((packed));

struct sb_section_header_t
{
    uint32_t identifier;
    uint32_t offset; /* In blocks */
    uint32_t size; /* In blocks */
    uint32_t flags;
} __attribute__((packed));

struct sb_key_dictionary_entry_t
{
    uint8_t hdr_cbc_mac[16]; /* CBC-MAC of the header */
    uint8_t key[16]; /* Actual AES Key (encrypted by the global key) */
} __attribute__((packed));

#define IMAGE_MAJOR_VERSION     1
#define IMAGE_MINOR_VERSION     1

#define SECTION_BOOTABLE        (1 << 0)
#define SECTION_CLEARTEXT       (1 << 1)
#define SECTION_STD_MASK        (3 << 0)

#define SB_INST_NOP     0x0
#define SB_INST_TAG     0x1
#define SB_INST_LOAD    0x2
#define SB_INST_FILL    0x3
#define SB_INST_JUMP    0x4
#define SB_INST_CALL    0x5
#define SB_INST_MODE    0x6

/* flags */
#define SB_INST_LAST_TAG    1 /* for TAG */
#define SB_INST_LOAD_DCD    1 /* for LOAD */
#define SB_INST_FILL_BYTE   0 /* for FILL */
#define SB_INST_FILL_HWORD  1 /* for FILL */
#define SB_INST_FILL_WORD   2 /* for FILL */
#define SB_INST_HAB_EXEC    1 /* for JUMP/CALL */

struct sb_instruction_header_t
{
    uint8_t checksum;
    uint8_t opcode;
    uint16_t flags;
} __attribute__((packed));

struct sb_instruction_common_t
{
    struct sb_instruction_header_t hdr;
    uint32_t addr;
    uint32_t len;
    uint32_t data;
} __attribute__((packed));

struct sb_instruction_load_t
{
    struct sb_instruction_header_t hdr;
    uint32_t addr;
    uint32_t len;
    uint32_t crc;
} __attribute__((packed));

struct sb_instruction_fill_t
{
    struct sb_instruction_header_t hdr;
    uint32_t addr;
    uint32_t len;
    uint32_t pattern;
} __attribute__((packed));

struct sb_instruction_mode_t
{
    struct sb_instruction_header_t hdr;
    uint32_t zero1;
    uint32_t zero2;
    uint32_t mode;
} __attribute__((packed));

struct sb_instruction_nop_t
{
    struct sb_instruction_header_t hdr;
    uint32_t zero1;
    uint32_t zero2;
    uint32_t zero3;
} __attribute__((packed));

struct sb_instruction_call_t
{
    struct sb_instruction_header_t hdr;
    uint32_t addr;
    uint32_t zero;
    uint32_t arg;
} __attribute__((packed));

struct sb_instruction_tag_t
{
    struct sb_instruction_header_t hdr;
    uint32_t identifier; /* section identifier */
    uint32_t len; /* length of the section */
    uint32_t flags; /* section flags */
} __attribute__((packed));

/*******
 * API *
 *******/

#define SB_INST_DATA    0xff

struct sb_inst_t
{
    uint8_t inst; /* SB_INST_* */
    uint32_t size;
    uint32_t addr;
    // <union>
    void *data;
    uint32_t pattern;
    // </union>
    uint32_t argument; // for call, jump and mode
    /* for production use */
    uint32_t padding_size;
    uint8_t *padding;
};

struct sb_section_t
{
    uint32_t identifier;
    bool is_data;
    bool is_cleartext;
    uint32_t other_flags;
    uint32_t alignment;
    // data sections are handled as one or more SB_INST_DATA virtual instruction
    int nr_insts;
    struct sb_inst_t *insts;
    /* for production use */
    uint32_t file_offset; /* in blocks */
    uint32_t sec_size; /* in blocks, without padding */
    uint32_t pad_size; /* padding size after the section until next section */
};

struct sb_file_t
{
    /* override real, otherwise it is randomly generated */
    bool override_real_key;
    uint8_t real_key[16];
    /* override crypto IV, use with caution ! Use NULL to generate it */
    bool override_crypto_iv;
    uint8_t crypto_iv[16];
    uint64_t timestamp; /* In microseconds since 2000/1/1 00:00:00 */
    uint8_t minor_version;

    int nr_sections;
    uint16_t drive_tag;
    uint32_t first_boot_sec_id;
    uint16_t flags;
    struct sb_section_t *sections;
    struct sb_version_t product_ver;
    struct sb_version_t component_ver;
    /* for production use */
    int first_boot_sec; /* index in sections[] */
    uint32_t image_size; /* in blocks */
};

enum sb_error_t
{
    SB_SUCCESS = 0,
    SB_ERROR = -1,
    SB_OPEN_ERROR = -2,
    SB_READ_ERROR = -3,
    SB_WRITE_ERROR = -4,
    SB_FORMAT_ERROR = -5,
    SB_CHECKSUM_ERROR = -6,
    SB_NO_VALID_KEY = -7,
    SB_CRYPTO_ERROR = -8,
};

#define SB_RAW_MODE     (1 << 0) /* read image in raw mode (aka bootloader-like) */
#define SB_IGNORE_SHA1  (1 << 1) /* ignore SHA-1 mismatch */

enum sb_error_t sb_write_file(struct sb_file_t *sb, const char *filename, void *u,
    generic_printf_t printf);
struct sb_file_t *sb_read_file(const char *filename, unsigned flags, void *u,
    generic_printf_t printf, enum sb_error_t *err);
/* use size_t(-1) to use maximum size */
struct sb_file_t *sb_read_file_ex(const char *filename, size_t offset, size_t size,
    unsigned flags, void *u, generic_printf_t printf, enum sb_error_t *err);
struct sb_file_t *sb_read_memory(void *buffer, size_t size, unsigned flags, void *u,
    generic_printf_t printf, enum sb_error_t *err);

uint64_t sb_generate_timestamp(void);
void sb_generate_default_version(struct sb_version_t *ver);
void sb_build_default_image(struct sb_file_t *file);
void sb_fill_section_name(char name[5], uint32_t identifier);
void sb_dump(struct sb_file_t *file, void *u, generic_printf_t printf);
void sb_free_instruction(struct sb_inst_t inst);
void sb_free_section(struct sb_section_t file);
void sb_free(struct sb_file_t *file);
void sb_get_zero_key(struct crypto_key_t *key);

#endif /* __SB_H__ */
