/* mbr.pk - MBR (Master Boot Record) partition table.  */

/* Copyright (C) 2021, 2022, 2023, 2024 The poke authors */

/* 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 3 of the License, or
 * (at your option) any later version.
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

type MBR_CHS =
  struct
  {
    uint<8> head;
    struct uint<8>
      {
        uint<2> cylinder_hi;
        uint<6> sector;
      } cs;
    uint<8> cylinder_lo;
    computed uint<10> cylinder;

    method get_cylinder = uint<10>:
      {
        return cs.cylinder_hi ::: cylinder_lo;
      }
    method set_cylinder = (uint<10> c) void:
      {
        cs.cylinder_hi = c .>> 8;
        cylinder_lo = c;
      }
    method _print = void:
      {
        printf ("#<head=%u8d,sector=%u6d,cylinder=%u10d>",
                head, cs.sector, get_cylinder);
      }
  };

var MBR_PT_Empty           = 0x00UB,
    MBR_PT_FAT12           = 0x01UB,
    MBR_PT_FAT16_32MB      = 0x04UB,
    MBR_PT_Extended        = 0x05UB,
    MBR_PT_FAT16           = 0x06UB,
    MBR_PT_NTFS            = 0x07UB,
    MBR_PT_FAT32           = 0x0BUB,
    MBR_PT_FAT32_LBA       = 0x0CUB,
    MBR_PT_Extended_LBA    = 0x0FUB,
    MBR_PT_LinuxSwap       = 0x82UB,
    MBR_PT_LinuxNative     = 0x83UB,
    MBR_PT_LinuxExtended   = 0x85UB,
    MBR_PT_LinuxLVM        = 0x8EUB,
    MBR_PT_FreeBSD         = 0xA5UB,
    MBR_PT_OpenBSD         = 0xA6UB,
    MBR_PT_NetBSD          = 0xA9UB,
    MBR_PT_HFS_HFSPlus     = 0xAFUB,
    MBR_PT_Solaris         = 0xBEUB,
    MBR_PT_SolarisBoot     = 0xBFUB,
    MBR_PT_ChromeOSKernel  = 0xC1UB,
    MBR_PT_ChromeOSRootFS  = 0xC2UB,
    MBR_PT_ChromeOSFuture  = 0xC3UB,
    MBR_PT_CP_M            = 0xDBUB,
    MBR_PT_CTOS            = 0xE1UB,
    MBR_PT_LinuxPlaintext  = 0xE2UB,
    MBR_PT_LinuxLVM2       = 0xE3UB,
    MBR_PT_DRDOS           = 0xE4UB,
    MBR_PT_SpeedStor       = 0xE5UB,
    MBR_PT_BeOS            = 0xEBUB,
    MBR_PT_GPT_PROTECTIVE  = 0xEEUB,
    MBR_PT_EFISystem       = 0xEFUB,
    MBR_PT_SkyOS           = 0xF1UB,
    MBR_PT_IBM_OS2         = 0xFBUB;

type MBR_PartitionType =
  struct uint<8>
  {
    uint<8> p_type;
    method _print = void:
      {
        var s = p_type == MBR_PT_Empty          ? "Empty"
              : p_type == MBR_PT_FAT12          ? "FAT12"
              : p_type == MBR_PT_FAT16_32MB     ? "FAT16 (32MB or smaller)"
              : p_type == MBR_PT_Extended       ? "Extended"
              : p_type == MBR_PT_FAT16          ? "FAT16"
              : p_type == MBR_PT_NTFS           ? "NTFS"
              : p_type == MBR_PT_FAT32          ? "FAT32"
              : p_type == MBR_PT_FAT32_LBA      ? "FAT32 (LBA)"
              : p_type == MBR_PT_Extended_LBA   ? "Extended (LBA)"
              : p_type == MBR_PT_LinuxSwap      ? "Linux Swap"
              : p_type == MBR_PT_LinuxNative    ? "Linux Native"
              : p_type == MBR_PT_LinuxExtended  ? "Linux Extended"
              : p_type == MBR_PT_LinuxLVM       ? "Linux LVM"
              : p_type == MBR_PT_FreeBSD        ? "FreeBSD"
              : p_type == MBR_PT_OpenBSD        ? "OpenBSD"
              : p_type == MBR_PT_NetBSD         ? "NetBSD"
              : p_type == MBR_PT_HFS_HFSPlus    ? "HFS / HFS+"
              : p_type == MBR_PT_Solaris        ? "Solaris"
              : p_type == MBR_PT_SolarisBoot    ? "Solaris Boot"
              : p_type == MBR_PT_ChromeOSKernel ? "ChromeOS Kernel"
              : p_type == MBR_PT_ChromeOSRootFS ? "ChromeOS Root FS"
              : p_type == MBR_PT_ChromeOSFuture ? "ChromeOS Future"
              : p_type == MBR_PT_CP_M           ? "CP/M"
              : p_type == MBR_PT_CTOS           ? "CTOS"
              : p_type == MBR_PT_LinuxPlaintext ? "Linux Plaintext"
              : p_type == MBR_PT_LinuxLVM2      ? "Linux LVM2"
              : p_type == MBR_PT_DRDOS          ? "DR-DOS"
              : p_type == MBR_PT_SpeedStor      ? "SpeedStor"
              : p_type == MBR_PT_BeOS           ? "BeOS"
              : p_type == MBR_PT_GPT_PROTECTIVE ? "GPT Protective Partition"
              : p_type == MBR_PT_EFISystem      ? "EFI System"
              : p_type == MBR_PT_SkyOS          ? "SkyOS"
              : p_type == MBR_PT_IBM_OS2        ? "IBM OS/2"
              : "UNRECOGNIZED";
        print "#<" + s + ">";
      }
  };

unit MBR_SectorSize = 512 * 8;  /* 512 bytes (by convention).  */

/* MBR Partition Table Entry (PTE)
 *
 *  Offset  Size (bytes)  Description
 *
 *  0x00    1             Drive attributes (bit 7 set = active or bootable)
 *  0x01    3             CHS Address of partition start
 *  0x04    1             Partition type
 *  0x05    3             CHS address of last partition sector
 *  0x08    4             LBA of partition start
 *  0x0C    4             Number of sectors in partition
 *
 * ref: https://wiki.osdev.org/MBR_(x86)
 */
type MBR_PTE =
  struct
  {
    struct uint<8>
    {
      uint<1> active;  /* bootable */
      uint<7>;

      method _print = void:
        {
          printf "#<active=%u1d>", active;
        }
    } attr;
    MBR_CHS start_chs;
    MBR_PartitionType part_type;
    MBR_CHS end_chs;
    little offset<uint<32>,MBR_SectorSize> lba;
    little offset<uint<32>,MBR_SectorSize> sector_count;

    method lba_end = offset<uint<32>,MBR_SectorSize>:
      { return lba + sector_count; }
  };

/*
 *  Offset  Size (bytes)  Description
 *
 *  0x000   440           MBR Bootstrap (flat binary executable code)
 *  0x1B8   4             Optional "Unique Disk ID / Signature"
 *  0x1BC   2             Optional, reserved 0x0000
 *  0x1BE   16            First partition table entry
 *  0x1CE   16            Second partition table entry
 *  0x1DE   16            Third partition table entry
 *  0x1EE   16            Fourth partition table entry
 *  0x1FE   2             (0x55, 0xAA) "Valid bootsector" signature bytes
 *
 * ref: https://wiki.osdev.org/MBR_(x86)
 */
type MBR =
  struct
  {
    uint<8>[440#B] bootstrap;
    little uint<32> signature;
    uint<8>[2] reserved;
    MBR_PTE[4] pte;
    little uint<16> magic == 0xaa55UH;
  };
