Newer
Older
vvd / src / fs / mbr.h
#pragma once

#include <stdio.h>
#include <stdint.h>
#include "vdisk.h"
#include "platform.h"

#ifdef ENDIAN_LITTLE
enum {
	MBR_SIG = 0xAA55,	// MBR signature
};
#else
enum {
	MBR_SIG = 0x55AA,	// MBR signature
};
#endif

#define MBR_LBA_A_504_MB	0x1F800000
#define MBR_LBA_A_1008_MB	0x3F000000
#define MBR_LBA_A_2016_MB	0x7E000000
#define MBR_LBA_A_4032_MB	0xFC000000
// 8032.5 MiB
#define MBR_LBA_A_8032_MB	0x1F6080000ULL

typedef struct { // Cylinder-Head-Sector
	uint8_t head;	// HEAD[7:0]
	uint8_t sector;	// SECTOR[5:0], bits[7:6] for CYLINDER[9:8]
	uint8_t cylinder;	// CYLINDER[7:0]
} CHS;

typedef struct {
	uint8_t status;	// Partition status
	CHS chsfirst;	// CHS first absolute address
	uint8_t type;	// partition type
	CHS chslast;	// CHS last absolute address
	uint32_t lba;	// LBA of first absolute sector in partition
	uint32_t sectors;	// number of sectors for parition
} MBR_PARTITION;

typedef struct {
	union {
		uint8_t data[512];
		struct {
			uint8_t pad[440];
			uint32_t serial;	// WindowsNT 3.5+, Linux 2.6+
			// Usually 0000H
			// (Windows) 5A5AH if protected
			// (UEFI) AA55H if protective MBR
			uint16_t type;
			MBR_PARTITION pe[4];
			uint16_t sig;
		};
	};
} MBR;

/**
 * Obtain a LBA (sector index) from a CHS geometry. This is based on
 * `LBA = (C × HPC + H) × SPT + (S − 1)`.
 * 
 * \param chs CHS geometry structure
 * 
 * \returns LBA index
 */
uint32_t mbr_lba(CHS *chs);

/**
 * BIOS-assisted LBA translation from a CHS geometry with a disk size. Useful
 * for disks up to 8032.5 MiB.
 * 
 * ---
 * SIZE (MiB)	S/T	H	C
 * < 504	63	16	63 * H * 512
 * 504-1008	63	32	63 * H * 512
 * 1008-2016	63	64	63 * H * 512
 * 2016-4032	63	128	63 * H * 512
 * 4032-8032.5	63	255	63 * H * 512
 * ---
 * 
 * \param chs CHS geometry structure
 * \param dsize Disk size
 * 
 * \returns LBA index
 */
uint32_t mbr_lba_a(CHS *chs, uint64_t dsize);

/**
 * Get partition type string
 */
const char *mbr_part_type_str(uint8_t);