root/src/add-ons/kernel/partitioning_systems/sun/sun.cpp
/*
 * Copyright (C) 2020 Adrien Destugues <pulkomandy@pulkomandy.tk>
 *
 * Distributed under terms of the MIT license.
 */


#ifdef _BOOT_MODE
#       include <boot/partitions.h>
#else
#       include <DiskDeviceTypes.h>
#endif

#include <ByteOrder.h>
#include <ddm_modules.h>
#include <util/kernel_cpp.h>
#include <KernelExport.h>
#include <stdint.h>
#include <string.h>


//#define TRACE_SUN_PARTITION
#ifdef TRACE_SUN_PARTITION
#       define TRACE(x) dprintf x
#else
#       define TRACE(x) ;
#endif

#define SUN_PARTITION_MODULE_NAME "partitioning_systems/sun/v1"
#define SUN_PARTITION_NAME "Sun volume table of contents"


static uint16_t kMainSignature = 0xDABE;
static uint32_t kVtocSignature = 0x600DDEEE;
static uint32_t kVtocVersion = 1;
static off_t kSectorSize = 512;


struct sun_vtoc {
        char diskName[128];
        struct {
                uint32_t version;
                char volumeName[8];
                uint16_t partitionCount;
                struct {
                        uint16_t type;
                        uint16_t flags;
                } partitions[8];
                uint8_t bootinfo[12];
                uint8_t reserved[2];
                uint32_t signature;
                uint8_t reserved2[38];
                uint32_t timestamp[8];
        } __attribute__((packed)) vtoc;
        uint16_t write_skip;
        uint16_t read_skip;
        uint8_t reserved[154];
        uint16_t diskSpeed;
        uint16_t cylinders;
        uint16_t alternates;
        uint8_t reserved2[4];
        uint16_t interleave;
        uint16_t dataCylinders;
        uint16_t alternateCylinders;
        uint16_t heads;
        uint16_t sectorsPerTrack;
        uint8_t reserved3[4];
        struct {
                uint32_t startCylinder;
                uint32_t sectorCount;
        } partitions[8];
        uint16_t signature;
        uint16_t checksum;
} __attribute__((packed));


//      #pragma mark -
//      Sun VTOC public module interface


static status_t
sun_std_ops(int32 op, ...)
{
        switch (op) {
                case B_MODULE_INIT:
                case B_MODULE_UNINIT:
                        return B_OK;
        }

        return B_ERROR;
}


static float
sun_identify_partition(int fd, partition_data *partition, void **_cookie)
{
        uint8 buffer[512];
        ssize_t bytesRead = read_pos(fd, 0, buffer, sizeof(buffer));
        sun_vtoc *vtoc = (sun_vtoc *)buffer;
        if (bytesRead < (ssize_t)sizeof(buffer)) {
                TRACE(("%s: read error: %ld\n", __FUNCTION__, bytesRead));
                return B_ERROR;
        }

        if (vtoc->signature == B_HOST_TO_BENDIAN_INT16(kMainSignature)
                && vtoc->vtoc.signature == B_HOST_TO_BENDIAN_INT32(kVtocSignature)
                && vtoc->vtoc.version == B_HOST_TO_BENDIAN_INT32(kVtocVersion)) {
                // TODO verify the checksum

                uint16_t partitionCount
                        = B_BENDIAN_TO_HOST_INT16(vtoc->vtoc.partitionCount);
                TRACE(("Found %d partitions\n", partitionCount));

                if (partitionCount > 8)
                        return B_ERROR;

                vtoc = new sun_vtoc();
                memcpy(vtoc, buffer, sizeof(sun_vtoc));
                *_cookie = (void *)vtoc;

                bool hasParent = (get_parent_partition(partition->id) != NULL);
                if (!hasParent)
                        return 0.61; // Larger than iso9660
                else
                        return 0.3;
        }

        return B_ERROR;
}


static status_t
sun_scan_partition(int fd, partition_data *partition, void *cookie)
{
        sun_vtoc *vtoc = (sun_vtoc *)cookie;

        partition->status = B_PARTITION_VALID;
        partition->flags |= B_PARTITION_PARTITIONING_SYSTEM
                                                | B_PARTITION_READ_ONLY;
        partition->content_size = partition->size;

        off_t headCount = B_BENDIAN_TO_HOST_INT16(vtoc->heads);
        off_t sectorsPerTrack = B_BENDIAN_TO_HOST_INT16(vtoc->sectorsPerTrack);
        off_t cylinderSize = kSectorSize * headCount * sectorsPerTrack;

        TRACE(("%" B_PRIdOFF " heads, %" B_PRIdOFF " sectors, cylindersize %"
                B_PRIdOFF "\n", headCount, sectorsPerTrack, cylinderSize));

        for (int i = 0; i < B_BENDIAN_TO_HOST_INT16(vtoc->vtoc.partitionCount);
                i++) {
                uint16_t type = B_BENDIAN_TO_HOST_INT16(vtoc->vtoc.partitions[i].type);
                // Ignore unused and "whole disk" entries
                if (type == 0 || type == 5)
                        continue;

                off_t start = B_BENDIAN_TO_HOST_INT32(
                        vtoc->partitions[i].startCylinder) * cylinderSize;
                off_t size = B_BENDIAN_TO_HOST_INT32(vtoc->partitions[i].sectorCount)
                        * kSectorSize;
                TRACE(("Part %d type %d start %" B_PRIdOFF " size %" B_PRIdOFF "\n", i,
                        type, start, size));
                partition_data *child = create_child_partition(partition->id, i,
                        start, size, -1);
                if (child == NULL) {
                        TRACE(("sun: Creating child at index %d failed\n", i));
                        return B_ERROR;
                }
                child->block_size = partition->block_size;
        }

        return B_OK;
}


static void
sun_free_identify_partition_cookie(partition_data *partition, void *_cookie)
{
        delete (sun_vtoc *)_cookie;
}


#ifndef _BOOT_MODE
static partition_module_info sSunPartitionModule = {
#else
partition_module_info gSunPartitionModule = {
#endif
        {
                SUN_PARTITION_MODULE_NAME,
                0,
                sun_std_ops
        },
        "sun",                                                  // short_name
        SUN_PARTITION_NAME,                             // pretty_name
        0,                                                                      // flags

        // scanning
        sun_identify_partition,         // identify_partition
        sun_scan_partition,                     // scan_partition
        sun_free_identify_partition_cookie,     // free_identify_partition_cookie
        NULL,
//      atari_free_partition_content_cookie,    // free_partition_content_cookie
};

#ifndef _BOOT_MODE
partition_module_info *modules[] = {
        &sSunPartitionModule,
        NULL
};
#endif