Viewing: partition.c
📄 partition.c (Read Only) ⬅ To go back
// SPDX-License-Identifier: BSD-3-Clause
/*
 * Copyright (c) 2018, Tulio A M Mendes <[email protected]>
 * All rights reserved.
 * See LICENSE for details.
 *
 * Source: https://github.com/tadryanom/AdrOS
 */

#include "partition.h"
#include "blockdev.h"
#include "spinlock.h"
#include "console.h"
#include "errno.h"

#include <string.h>

static partition_t g_partitions[PARTITION_MAX];
static int g_partition_count = 0;
static spinlock_t g_partition_lock;

void partition_init_lock(void) {
    spinlock_init(&g_partition_lock);
}

int partition_register(const partition_t* part) {
    if (!part) return -EINVAL;
    if (!part->parent) return -EINVAL;

    uintptr_t flags = spin_lock_irqsave(&g_partition_lock);

    if (g_partition_count >= PARTITION_MAX) {
        spin_unlock_irqrestore(&g_partition_lock, flags);
        return -ENOSPC;
    }

    /* Check for duplicate name */
    for (int i = 0; i < g_partition_count; i++) {
        if (strcmp(g_partitions[i].name, part->name) == 0) {
            /* Update existing entry */
            g_partitions[i] = *part;
            spin_unlock_irqrestore(&g_partition_lock, flags);
            return 0;
        }
    }

    g_partitions[g_partition_count++] = *part;
    spin_unlock_irqrestore(&g_partition_lock, flags);
    return 0;
}

partition_t* partition_find(const char* name) {
    if (!name) return NULL;

    uintptr_t flags = spin_lock_irqsave(&g_partition_lock);
    partition_t* result = NULL;
    for (int i = 0; i < g_partition_count; i++) {
        if (strcmp(g_partitions[i].name, name) == 0) {
            result = &g_partitions[i];
            break;
        }
    }
    spin_unlock_irqrestore(&g_partition_lock, flags);
    return result;
}

partition_t* partition_find_by_device(block_device_t* parent, uint8_t partition_number) {
    if (!parent) return NULL;

    uintptr_t flags = spin_lock_irqsave(&g_partition_lock);
    partition_t* result = NULL;
    for (int i = 0; i < g_partition_count; i++) {
        if (g_partitions[i].parent == parent && 
            g_partitions[i].partition_number == partition_number) {
            result = &g_partitions[i];
            break;
        }
    }
    spin_unlock_irqrestore(&g_partition_lock, flags);
    return result;
}

void partition_claim(partition_t* part) {
    if (!part) return;
    uintptr_t flags = spin_lock_irqsave(&g_partition_lock);
    part->refcount++;
    spin_unlock_irqrestore(&g_partition_lock, flags);
}

void partition_release(partition_t* part) {
    if (!part) return;
    uintptr_t flags = spin_lock_irqsave(&g_partition_lock);
    if (part->refcount > 0)
        part->refcount--;
    spin_unlock_irqrestore(&g_partition_lock, flags);
}

/* MBR signature at offset 510-511 */
#define MBR_SIGNATURE 0xAA55

int partition_scan_mbr(block_device_t* bdev) {
    if (!bdev) return -EINVAL;

    /* Skip devices with unknown sector count (likely not present) */
    if (bdev->sector_count == 0) {
        return 0;
    }

    /* Read MBR sector (LBA 0) */
    uint8_t mbr_sector[512];
    int rc = blockdev_read(bdev, 0, mbr_sector);
    if (rc < 0) {
        return 0;
    }

    /* Check MBR signature */
    uint16_t signature = *(uint16_t*)(mbr_sector + 510);
    if (signature != MBR_SIGNATURE) {
        return 0;
    }

    /* Parse 4 primary partition entries (starting at offset 446) */
    mbr_partition_entry_t* entries = (mbr_partition_entry_t*)(mbr_sector + 446);
    int partitions_found = 0;

    for (int i = 0; i < 4; i++) {
        mbr_partition_entry_t* entry = &entries[i];

        /* Skip empty partitions (type 0) */
        if (entry->partition_type == PART_TYPE_EMPTY)
            continue;

        /* Create partition structure */
        partition_t part;
        memset(&part, 0, sizeof(part));

        part.parent = bdev;
        part.start_lba = entry->start_lba;
        part.sector_count = entry->sector_count;
        part.partition_type = entry->partition_type;
        part.partition_number = i + 1; /* 1-4 for primary partitions */
        part.refcount = 0;

        /* Generate partition name (e.g. "hda1", "vda2") */
        strcpy(part.name, bdev->name);
        int name_len = strlen(part.name);
        if (name_len < (int)sizeof(part.name) - 2) {
            part.name[name_len] = '0' + (i + 1);
            part.name[name_len + 1] = '\0';
        }

        /* Register the partition */
        rc = partition_register(&part);
        if (rc < 0) {
            continue;
        }

        kprintf("[PARTITION] %s: type=0x%02X, start_lba=%u, sectors=%u\n",
                part.name, part.partition_type, part.start_lba, part.sector_count);
        partitions_found++;
    }

    if (partitions_found > 0) {
        kprintf("[PARTITION] Scanned %s: found %d partition(s)\n", bdev->name, partitions_found);
    }
    return partitions_found;
}