pf2: implement much of Attach, structurally

This commit is contained in:
Michael Scire 2020-11-26 04:24:38 -08:00
parent 7b01d59b3b
commit ceef76c428
5 changed files with 318 additions and 5 deletions

View file

@ -117,6 +117,34 @@ namespace ams::prfile2::pf {
HandleType partition_handle;
DriveCharacter drive_char;
u8 status;
template<size_t Ix>
constexpr ALWAYS_INLINE bool GetStatusBit() const {
constexpr u32 Mask = (1u << Ix);
return (this->status & Mask) != 0;
}
template<size_t Ix>
constexpr ALWAYS_INLINE void SetStatusBit(bool en) {
constexpr u32 Mask = (1u << Ix);
if (en) {
this->status |= Mask;
} else {
this->status &= ~Mask;
}
}
constexpr bool IsAttached() const { return this->GetStatusBit<0>(); }
constexpr void SetAttached(bool en) { this->SetStatusBit<0>(en); }
constexpr bool IsMounted() const { return this->GetStatusBit<1>(); }
constexpr void SetMounted(bool en) { this->SetStatusBit<1>(en); }
constexpr bool IsDiskInserted() const { return this->GetStatusBit<2>(); }
constexpr void SetDiskInserted(bool en) { this->SetStatusBit<2>(en); }
constexpr bool IsFlag12() const { return this->GetStatusBit<12>(); }
constexpr void SetFlag12(bool en) { this->SetStatusBit<12>(en); }
};
using TailBuf = u32;

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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/>.
*/
#pragma once
#include <vapours/prfile2/prfile2_common.hpp>
namespace ams::prfile2 {
constexpr inline const auto MinimumFatBufferSize = 1;
constexpr inline const auto MaximumFatBufferSize = (1 << 16) - 1;
constexpr inline const auto MinimumDataBufferSize = 1;
constexpr inline const auto MaximumDataBufferSize = (1 << 15) - 1;
constexpr inline const auto MinimumFatPages = 1;
constexpr inline const auto MinimumDataPages = 4;
struct SectorCache {
u32 mode;
u16 num_fat_pages;
u16 num_data_pages;
pf::CachePage *current_fat;
pf::CachePage *current_data;
pf::SectorBuffer *buffers;
u32 fat_buf_size;
u32 data_buf_size;
void *signature;
};
}
namespace ams::prfile2::cache {
/* ... */
}

View file

@ -64,7 +64,8 @@ namespace ams::prfile2 {
Error_SYSTEM = -1,
};
constexpr inline const u32 InvalidSector = std::numeric_limits<u32>::max();
constexpr inline const u32 InvalidSector = std::numeric_limits<u32>::max();
constexpr inline const u32 InvalidCluster = std::numeric_limits<u32>::max();
enum OpenMode {
OpenMode_None = 0,

View file

@ -15,6 +15,7 @@
*/
#pragma once
#include <vapours/prfile2/prfile2_common.hpp>
#include <vapours/prfile2/prfile2_cache.hpp>
#include <vapours/prfile2/prfile2_critical_section.hpp>
#include <vapours/prfile2/prfile2_entry.hpp>
#include <vapours/prfile2/prfile2_fat.hpp>
@ -129,6 +130,12 @@ namespace ams::prfile2 {
};
};
namespace pdm {
struct Partition;
}
struct Volume {
BiosParameterBlock bpb;
u32 num_free_clusters;
@ -142,7 +149,7 @@ namespace ams::prfile2 {
u32 num_open_files;
u32 num_open_directories;
/* ... */
/* TODO: SectorCache cache; */
SectorCache cache;
VolumeContext *context;
u64 context_id;
DirectoryTail tail_entry;
@ -156,13 +163,41 @@ namespace ams::prfile2 {
pf::Error last_error;
pf::Error last_driver_error;
/* ... */
void *partition;
HandleType partition_handle;
VolumeCallback callback;
const u8 *format_param;
/* ... */
/* TODO: ExtensionTable extension_table; */
/* TODO: ExtensionData ext; */
/* ... */
template<size_t Ix>
constexpr ALWAYS_INLINE bool GetFlagsBit() const {
constexpr u32 Mask = (1u << Ix);
return (this->flags & Mask) != 0;
}
template<size_t Ix>
constexpr ALWAYS_INLINE void SetFlagsBit(bool en) {
constexpr u32 Mask = (1u << Ix);
if (en) {
this->flags |= Mask;
} else {
this->flags &= ~Mask;
}
}
constexpr bool IsAttached() const { return this->GetFlagsBit<0>(); }
constexpr void SetAttached(bool en) { this->SetFlagsBit<0>(en); }
constexpr bool IsMounted() const { return this->GetFlagsBit<1>(); }
constexpr void SetMounted(bool en) { this->SetFlagsBit<1>(en); }
constexpr bool IsDiskInserted() const { return this->GetFlagsBit<2>(); }
constexpr void SetDiskInserted(bool en) { this->SetFlagsBit<2>(en); }
constexpr bool IsFlag12() const { return this->GetFlagsBit<12>(); }
constexpr void SetFlag12(bool en) { this->SetFlagsBit<12>(en); }
};
struct VolumeSet {

View file

@ -55,6 +55,48 @@ namespace ams::prfile2::vol {
return nullptr;
}
VolumeContext *GetCurrentVolumeContext(u64 *out_context_id) {
/* Get the volume set. */
auto &vol_set = GetVolumeSet();
/* Acquire exclusive access to the volume set. */
ScopedCriticalSection lk(vol_set.critical_section);
/* Get the current context id. */
u64 context_id = 0;
system::GetCurrentContextId(std::addressof(context_id));
if (out_context_id != nullptr) {
*out_context_id = context_id;
}
if (auto *ctx = GetVolumeContextById(context_id); ctx != nullptr) {
return ctx;
} else {
return std::addressof(vol_set.default_context);
}
}
bool IsValidDriveCharacter(pf::DriveCharacter drive_char) {
return static_cast<u8>((drive_char & 0xDF) - 'A') < 26;
}
Volume *GetVolumeByDriveCharacter(pf::DriveCharacter drive_char) {
/* Get the volume set. */
auto &vol_set = GetVolumeSet();
/* Calculate the volume index. */
const auto index = std::toupper(static_cast<unsigned char>(drive_char)) - 'A';
/* Acquire exclusive access to the volume set. */
ScopedCriticalSection lk(vol_set.critical_section);
if (index < MaxVolumes) {
return std::addressof(vol_set.volumes[index]);
} else {
return nullptr;
}
}
}
pf::Error Initialize(u32 config, void *param) {
@ -120,8 +162,168 @@ namespace ams::prfile2::vol {
}
pf::Error Attach(pf::DriveTable *drive_table) {
AMS_UNUSED(drive_table);
AMS_ABORT("vol::Attach");
/* Get the volume set. */
auto &vol_set = GetVolumeSet();
/* Get the volume context for error tracking. */
u64 context_id = 0;
auto *vol_ctx = GetCurrentVolumeContext(std::addressof(context_id));
auto SetLastErrorAndReturn = [&] ALWAYS_INLINE_LAMBDA (pf::Error err) -> pf::Error { vol_ctx->last_error = err; return err; };
/* Check the drive table. */
if (drive_table == nullptr) {
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
}
/* Clear the drive table's character/status. */
const auto drive_char = drive_table->drive_char;
drive_table->drive_char = 0;
drive_table->status = 0;
/* Check that we can attach. */
if (vol_set.num_attached_drives >= MaxVolumes) {
return SetLastErrorAndReturn(pf::Error_TooManyVolumesAttached);
}
/* Check the cache setting. */
auto *cache_setting = drive_table->cache;
if (cache_setting == nullptr) {
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
}
if (cache_setting->fat_buf_size > MaximumFatBufferSize) {
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
}
if (cache_setting->data_buf_size > MaximumDataBufferSize) {
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
}
if (cache_setting->num_fat_pages < MinimumFatPages) {
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
}
if (cache_setting->num_data_pages < MinimumDataPages) {
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
}
if (cache_setting->pages == nullptr) {
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
}
if (cache_setting->buffers == nullptr) {
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
}
if (!util::IsAligned(reinterpret_cast<uintptr_t>(cache_setting->pages), alignof(u32))) {
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
}
if (!util::IsAligned(reinterpret_cast<uintptr_t>(cache_setting->buffers), alignof(u32))) {
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
}
/* Adjust the cache setting. */
cache_setting->fat_buf_size = std::max<u32>(cache_setting->fat_buf_size, MinimumFatBufferSize);
cache_setting->data_buf_size = std::max<u32>(cache_setting->data_buf_size, MinimumDataBufferSize);
/* Check the adjusted setting. */
if (cache_setting->fat_buf_size > cache_setting->num_fat_pages) {
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
}
if ((cache_setting->num_data_pages / cache_setting->data_buf_size) < MinimumDataPages) {
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
}
/* Validate the drive character. */
if (drive_char != 0) {
if (!IsValidDriveCharacter(drive_char)) {
return SetLastErrorAndReturn(pf::Error_InvalidParameter);
}
if (auto *vol = GetVolumeByDriveCharacter(drive_char); vol == nullptr || vol->IsAttached()) {
return SetLastErrorAndReturn(pf::Error_InvalidVolumeLabel);
}
}
/* Perform the bulk of the attach. */
Volume *vol = nullptr;
{
/* Lock the volume set. */
ScopedCriticalSection lk(vol_set.critical_section);
/* Find a free volume. */
for (auto &v : vol_set.volumes) {
if (!v.IsAttached()) {
vol = std::addressof(v);
break;
}
}
if (vol == nullptr) {
return SetLastErrorAndReturn(pf::Error_TooManyVolumesAttached);
}
const auto vol_id = vol - vol_set.volumes;
/* Clear the volume. */
std::memset(vol, 0, sizeof(*vol));
/* Initialize the volume. */
vol->num_free_clusters = InvalidCluster;
vol->num_free_clusters_ = InvalidCluster;
vol->last_free_cluster = InvalidCluster;
vol->partition_handle = drive_table->partition_handle;
InitializeCriticalSection(std::addressof(vol->critical_section));
vol->drive_char = 'A' + vol_id;
/* Setup directory tail. */
vol->tail_entry.tracker_size = util::size(vol->tail_entry.tracker_buf);
vol->tail_entry.tracker_bits = vol->tail_entry.tracker_buf;
/* Initialize driver for volume. */
/* TODO: drv::Initialize(vol); + error checking */
/* Setup the cache. */
/* TODO: cache::SetCache(vol, drive_table->cache->pages, drive_table->cache->buffers, drive_table->cache->num_fat_pages, drive_table->cache->num_data_pages); */
/* TODO: cache::SetFatBufferSize(vol, drive_table->cache->fat_buf_size); */
/* TODO: cache::SetDataBufferSize(vol, drive_table->cache->data_buf_size); */
/* Set flags. */
vol->SetAttached(true);
vol->SetFlag12(true);
/* Update the drive table. */
drive_table->SetAttached(true);
drive_table->drive_char = vol->drive_char;
/* Update the number of attached drives. */
if ((vol_set.num_attached_drives++) == 0) {
vol_set.default_context.volume_id = vol_id;
}
}
/* Acquire exclusive access to the volume set. */
ScopedCriticalSection lk(vol_set.critical_section);
/* Associate the volume to our context while we operate on it. */
vol->context = vol_ctx;
vol->context_id = context_id;
ON_SCOPE_EXIT { vol->context_id = 0; };
/* TODO: Copy volume root dir entry to all contexts. */
/* TODO: Clear tracking fields at the end of the volume. */
/* Perform mount as appropriate. */
const auto check_mount_err = /* TODO vol::CheckMediaInsertForAttachMount(vol) */ pf::Error_Ok;
const bool inserted = /* TODO: drv::IsInserted(vol) */ false;
if (check_mount_err != pf::Error_Ok) {
if (inserted) {
drive_table->SetDiskInserted(true);
}
vol_ctx->last_error = check_mount_err;
} else if (inserted) {
drive_table->SetDiskInserted(true);
if (auto mount_err = /* TODO vol::MountImpl(vol, 0x1B, false) */pf::Error_InternalError; mount_err != pf::Error_Ok) {
vol_ctx->last_error = mount_err;
} else {
drive_table->SetMounted(true);
}
}
return pf::Error_Ok;
}
VolumeContext *RegisterContext(u64 *out_context_id) {