/* Copyright (c) 2018 SiFive, Inc */
/* SPDX-License-Identifier: Apache-2.0 */
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* See the file LICENSE for further information */

#include "comdef.h"
#include "gpt.h"

#define _ASSERT_SIZEOF(type, size) \
  _Static_assert(sizeof(type) == (size), #type " must be " #size " bytes wide")
#define _ASSERT_OFFSETOF(type, member, offset) \
  _Static_assert(offsetof(type, member) == (offset), #type "." #member " must be at offset " #offset)


typedef struct
{
  gpt_guid partition_type_guid;
  gpt_guid partition_guid;
  uint64_t first_lba;
  uint64_t last_lba;
  uint64_t attributes;
  uint16_t name[36];  // UTF-16
} gpt_partition_entry;
_ASSERT_SIZEOF(gpt_partition_entry, 128);


// GPT represents GUIDs with the first three blocks as little-endian
#if 0
// c12a7328-f81f-11d2-ba4b-00a0c93ec93b
const gpt_guid gpt_guid_efi = {{
  0x28, 0x73, 0x2a, 0xc1, 0x1f, 0xf8, 0xd2, 0x11, 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b
}};

// 2e54b353-1271-4842-806f-e436d6af6985
const gpt_guid gpt_guid_sifive_bare_metal = {{
  0x53, 0xb3, 0x54, 0x2e, 0x71, 0x12, 0x42, 0x48, 0x80, 0x6f, 0xe4, 0x36, 0xd6, 0xaf, 0x69, 0x85
}};
#endif

// 5b193300-fc78-40cd-8002-e86c45580b47
const gpt_guid gpt_guid_sifive_uboot = {{
  0x00, 0x33, 0x19, 0x5b, 0x78, 0xfc, 0xcd, 0x40, 0x80, 0x02, 0xe8, 0x6c, 0x45, 0x58, 0x0b, 0x47
}};

// EBD0A0A2-B9E5-4433-87C0-68B6B72699C7
const gpt_guid gpt_guid_sifive_kernel = {{
  0xa2,0xa0,0xd0,0xeb,0xe5,0xb9,0x33,0x44,0x87,0xc0,0x68,0xb6,0xb7,0x26,0x99,0xc7
}};

static inline bool guid_equal(const gpt_guid* a, const gpt_guid* b)
{
  for (int i = 0; i < GPT_GUID_SIZE; i++) {
    if (a->bytes[i] != b->bytes[i]) {
      return false;
    }
  }
  return true;
}


// Public functions

/**
 * Search the given block of partition entries for a partition with the given
 * GUID. Return a range of [0, 0] to indicate that the partition was not found.
 */
gpt_partition_range gpt_find_partition_by_guid(const void* entries, const gpt_guid* guid, uint32_t num_entries)
{
  gpt_partition_entry* gpt_entries = (gpt_partition_entry*) entries;
  for (uint32_t i = 0; i < num_entries; i++) {
    if (guid_equal(&gpt_entries[i].partition_type_guid, guid)) {
      return (gpt_partition_range) {
        .first_lba = gpt_entries[i].first_lba,
        .last_lba = gpt_entries[i].last_lba,
      };
    }
  }
  return (gpt_partition_range) { .first_lba = 0, .last_lba = 0 };
}
