mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 20:31:14 +00:00
pf2: implement much of Attach, structurally
This commit is contained in:
parent
7b01d59b3b
commit
ceef76c428
5 changed files with 318 additions and 5 deletions
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
||||
/* ... */
|
||||
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in a new issue