Revert "hoc-clk: add live vdd2, live boost clock and basic pwm dimming"
This reverts commit 15b7df8ef1.
This commit is contained in:
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::crypto {
|
||||
|
||||
size_t EncryptAes128Cbc(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size) {
|
||||
Aes128CbcEncryptor aes;
|
||||
aes.Initialize(key, key_size, iv, iv_size);
|
||||
return aes.Update(dst, dst_size, src, src_size);
|
||||
}
|
||||
|
||||
size_t EncryptAes192Cbc(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size) {
|
||||
Aes192CbcEncryptor aes;
|
||||
aes.Initialize(key, key_size, iv, iv_size);
|
||||
return aes.Update(dst, dst_size, src, src_size);
|
||||
}
|
||||
|
||||
size_t EncryptAes256Cbc(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size) {
|
||||
Aes256CbcEncryptor aes;
|
||||
aes.Initialize(key, key_size, iv, iv_size);
|
||||
return aes.Update(dst, dst_size, src, src_size);
|
||||
}
|
||||
|
||||
size_t DecryptAes128Cbc(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size) {
|
||||
Aes128CbcDecryptor aes;
|
||||
aes.Initialize(key, key_size, iv, iv_size);
|
||||
return aes.Update(dst, dst_size, src, src_size);
|
||||
}
|
||||
|
||||
size_t DecryptAes192Cbc(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size) {
|
||||
Aes192CbcDecryptor aes;
|
||||
aes.Initialize(key, key_size, iv, iv_size);
|
||||
return aes.Update(dst, dst_size, src, src_size);
|
||||
}
|
||||
|
||||
size_t DecryptAes256Cbc(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size) {
|
||||
Aes256CbcDecryptor aes;
|
||||
aes.Initialize(key, key_size, iv, iv_size);
|
||||
return aes.Update(dst, dst_size, src, src_size);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::crypto {
|
||||
|
||||
size_t EncryptAes128Ctr(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size) {
|
||||
Aes128CtrEncryptor aes;
|
||||
aes.Initialize(key, key_size, iv, iv_size);
|
||||
return aes.Update(dst, dst_size, src, src_size);
|
||||
}
|
||||
|
||||
size_t EncryptAes192Ctr(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size) {
|
||||
Aes192CtrEncryptor aes;
|
||||
aes.Initialize(key, key_size, iv, iv_size);
|
||||
return aes.Update(dst, dst_size, src, src_size);
|
||||
}
|
||||
|
||||
size_t EncryptAes256Ctr(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size) {
|
||||
Aes256CtrEncryptor aes;
|
||||
aes.Initialize(key, key_size, iv, iv_size);
|
||||
return aes.Update(dst, dst_size, src, src_size);
|
||||
}
|
||||
|
||||
size_t DecryptAes128Ctr(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size) {
|
||||
Aes128CtrDecryptor aes;
|
||||
aes.Initialize(key, key_size, iv, iv_size);
|
||||
return aes.Update(dst, dst_size, src, src_size);
|
||||
}
|
||||
|
||||
size_t DecryptAes192Ctr(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size) {
|
||||
Aes192CtrDecryptor aes;
|
||||
aes.Initialize(key, key_size, iv, iv_size);
|
||||
return aes.Update(dst, dst_size, src, src_size);
|
||||
}
|
||||
|
||||
size_t DecryptAes256Ctr(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, const void *src, size_t src_size) {
|
||||
Aes256CtrDecryptor aes;
|
||||
aes.Initialize(key, key_size, iv, iv_size);
|
||||
return aes.Update(dst, dst_size, src, src_size);
|
||||
}
|
||||
|
||||
size_t EncryptAes128CtrPartial(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, s64 offset, const void *src, size_t src_size) {
|
||||
Aes128CtrEncryptor aes;
|
||||
aes.Initialize(key, key_size, iv, iv_size, offset);
|
||||
return aes.Update(dst, dst_size, src, src_size);
|
||||
}
|
||||
|
||||
size_t EncryptAes192CtrPartial(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, s64 offset, const void *src, size_t src_size) {
|
||||
Aes192CtrEncryptor aes;
|
||||
aes.Initialize(key, key_size, iv, iv_size, offset);
|
||||
return aes.Update(dst, dst_size, src, src_size);
|
||||
}
|
||||
|
||||
size_t EncryptAes256CtrPartial(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, s64 offset, const void *src, size_t src_size) {
|
||||
Aes256CtrEncryptor aes;
|
||||
aes.Initialize(key, key_size, iv, iv_size, offset);
|
||||
return aes.Update(dst, dst_size, src, src_size);
|
||||
}
|
||||
|
||||
size_t DecryptAes128CtrPartial(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, s64 offset, const void *src, size_t src_size) {
|
||||
Aes128CtrDecryptor aes;
|
||||
aes.Initialize(key, key_size, iv, iv_size, offset);
|
||||
return aes.Update(dst, dst_size, src, src_size);
|
||||
}
|
||||
|
||||
size_t DecryptAes192CtrPartial(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, s64 offset, const void *src, size_t src_size) {
|
||||
Aes192CtrDecryptor aes;
|
||||
aes.Initialize(key, key_size, iv, iv_size, offset);
|
||||
return aes.Update(dst, dst_size, src, src_size);
|
||||
}
|
||||
|
||||
size_t DecryptAes256CtrPartial(void *dst, size_t dst_size, const void *key, size_t key_size, const void *iv, size_t iv_size, s64 offset, const void *src, size_t src_size) {
|
||||
Aes256CtrDecryptor aes;
|
||||
aes.Initialize(key, key_size, iv, iv_size, offset);
|
||||
return aes.Update(dst, dst_size, src, src_size);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::crypto {
|
||||
|
||||
void GenerateHmacSha1(void *dst, size_t dst_size, const void *data, size_t data_size, const void *key, size_t key_size) {
|
||||
HmacSha1Generator hmac;
|
||||
|
||||
hmac.Initialize(key, key_size);
|
||||
hmac.Update(data, data_size);
|
||||
hmac.GetMac(dst, dst_size);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::crypto {
|
||||
|
||||
void GenerateHmacSha256(void *dst, size_t dst_size, const void *data, size_t data_size, const void *key, size_t key_size) {
|
||||
HmacSha256Generator hmac;
|
||||
|
||||
hmac.Initialize(key, key_size);
|
||||
hmac.Update(data, data_size);
|
||||
hmac.GetMac(dst, dst_size);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::crypto {
|
||||
|
||||
void GenerateMd5(void *dst, size_t dst_size, const void *src, size_t src_size) {
|
||||
Md5Generator gen;
|
||||
|
||||
gen.Initialize();
|
||||
gen.Update(src, src_size);
|
||||
gen.GetHash(dst, dst_size);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::crypto {
|
||||
|
||||
void ClearMemory(void *_mem, size_t size) {
|
||||
volatile u8 *mem = reinterpret_cast<volatile u8 *>(_mem);
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
mem[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::crypto {
|
||||
|
||||
bool IsSameBytes(const void *lhs, const void *rhs, size_t size) {
|
||||
bool result;
|
||||
u8 xor_acc, ltmp, rtmp;
|
||||
size_t index;
|
||||
|
||||
__asm__ __volatile__(
|
||||
/* Clear registers and prepare for comparison. */
|
||||
" movs %[xor_acc], #0\n"
|
||||
" movs %[index], #0\n"
|
||||
" b 1f\n"
|
||||
|
||||
/* Compare one byte in constant time. */
|
||||
"0:\n"
|
||||
" ldrb %[ltmp], [%[lhs]]\n"
|
||||
" ldrb %[rtmp], [%[rhs]]\n"
|
||||
" adds %[lhs], #1\n"
|
||||
" adds %[rhs], #1\n"
|
||||
" eors %[ltmp], %[ltmp], %[rtmp]\n"
|
||||
" orrs %[xor_acc], %[xor_acc], %[ltmp]\n"
|
||||
" adds %[index], #1\n"
|
||||
|
||||
/* Check if there is still data to compare. */
|
||||
"1:\n"
|
||||
" cmp %[index], %[size]\n"
|
||||
" bcc 0b\n"
|
||||
|
||||
/* We're done, set result. */
|
||||
" cmp %[xor_acc], #0\n"
|
||||
" moveq %[result], #1\n"
|
||||
" movne %[result], #0\n"
|
||||
: [result]"=r"(result), [lhs]"+r"(lhs), [rhs]"+r"(rhs), [xor_acc]"=&r"(xor_acc), [index]"=&r"(index), [ltmp]"=&r"(ltmp), [rtmp]"=&r"(rtmp)
|
||||
: "m"(*(const u8 (*)[size])lhs), "m"(*(const u8 (*)[size])rhs), [size]"r"(size)
|
||||
: "cc"
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::crypto {
|
||||
|
||||
bool IsSameBytes(const void *lhs, const void *rhs, size_t size) {
|
||||
bool result;
|
||||
u8 xor_acc, ltmp, rtmp;
|
||||
size_t index;
|
||||
|
||||
__asm__ __volatile__(
|
||||
/* Clear registers and prepare for comparison. */
|
||||
" mov %w[xor_acc], #0\n"
|
||||
" mov %w[index], #0\n"
|
||||
" b 1f\n"
|
||||
|
||||
/* Compare one byte in constant time. */
|
||||
"0:\n"
|
||||
" ldrb %w[ltmp], [%[lhs]]\n"
|
||||
" ldrb %w[rtmp], [%[rhs]]\n"
|
||||
" adds %[lhs], %[lhs], #1\n"
|
||||
" adds %[rhs], %[rhs], #1\n"
|
||||
" eor %w[ltmp], %w[ltmp], %w[rtmp]\n"
|
||||
" orr %w[xor_acc], %w[xor_acc], %w[ltmp]\n"
|
||||
" adds %[index], %[index], #1\n"
|
||||
|
||||
/* Check if there is still data to compare. */
|
||||
"1:\n"
|
||||
" cmp %[index], %[size]\n"
|
||||
" bcc 0b\n"
|
||||
|
||||
/* We're done, set result. */
|
||||
" cmp %w[xor_acc], #0\n"
|
||||
" cset %w[result], eq\n"
|
||||
: [result]"=r"(result), [lhs]"+r"(lhs), [rhs]"+r"(rhs), [xor_acc]"=&r"(xor_acc), [index]"=&r"(index), [ltmp]"=&r"(ltmp), [rtmp]"=&r"(rtmp)
|
||||
: "m"(*(const u8 (*)[size])lhs), "m"(*(const u8 (*)[size])rhs), [size]"r"(size)
|
||||
: "cc"
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::crypto {
|
||||
|
||||
bool IsSameBytes(const void *lhs, const void *rhs, size_t size) {
|
||||
/* TODO: Should the generic impl be constant time? */
|
||||
volatile u8 diff = 0;
|
||||
|
||||
const volatile u8 *lhs8 = static_cast<const volatile u8 *>(lhs);
|
||||
const volatile u8 *rhs8 = static_cast<const volatile u8 *>(rhs);
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
diff = diff | (lhs8[i] ^ rhs8[i]);
|
||||
}
|
||||
|
||||
return diff == 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::crypto {
|
||||
|
||||
void GenerateSha1(void *dst, size_t dst_size, const void *src, size_t src_size) {
|
||||
Sha1Generator gen;
|
||||
|
||||
gen.Initialize();
|
||||
gen.Update(src, src_size);
|
||||
gen.GetHash(dst, dst_size);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::crypto {
|
||||
|
||||
void GenerateSha256(void *dst, size_t dst_size, const void *src, size_t src_size) {
|
||||
Sha256Generator gen{};
|
||||
|
||||
gen.Initialize();
|
||||
gen.Update(src, src_size);
|
||||
gen.GetHash(dst, dst_size);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,501 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
/* Helper macros to setup for inline AES asm */
|
||||
#define AES_ENC_DEC_SETUP_VARS() \
|
||||
const auto *ctx = reinterpret_cast<const RoundKeyHelper<KeySize> *>(m_round_keys); \
|
||||
static_assert(sizeof(*ctx) == sizeof(m_round_keys)); \
|
||||
\
|
||||
uint8x16_t tmp = vld1q_u8((const uint8_t *)src); \
|
||||
uint8x16_t tmp2
|
||||
|
||||
#define AES_ENC_DEC_OUTPUT_VARS() \
|
||||
[tmp]"+w"(tmp), [tmp2]"=w"(tmp2)
|
||||
|
||||
#define AES_ENC_DEC_STORE_RESULT() \
|
||||
vst1q_u8((uint8_t *)dst, tmp)
|
||||
|
||||
/* Helper macros to do AES encryption, via inline asm. */
|
||||
#define AES_ENC_ROUND(n) \
|
||||
"ldr %q[tmp2], %[round_key_" #n "]\n" \
|
||||
"aese %[tmp].16b, %[tmp2].16b\n" \
|
||||
"aesmc %[tmp].16b, %[tmp].16b\n"
|
||||
|
||||
#define AES_ENC_FINAL_ROUND() \
|
||||
"ldr %q[tmp2], %[round_key_second_last]\n" \
|
||||
"aese %[tmp].16b, %[tmp2].16b\n" \
|
||||
"ldr %q[tmp2], %[round_key_last]\n" \
|
||||
"eor %[tmp].16b, %[tmp].16b, %[tmp2].16b"
|
||||
|
||||
#define AES_ENC_INPUT_ROUND_KEY(num_rounds, n) \
|
||||
[round_key_##n]"m"(ctx->round_keys[(n-1)])
|
||||
|
||||
#define AES_ENC_INPUT_LAST_ROUND_KEYS(num_rounds) \
|
||||
[round_key_second_last]"m"(ctx->round_keys[(num_rounds - 1)]), \
|
||||
[round_key_last]"m"(ctx->round_keys[(num_rounds)])
|
||||
|
||||
/* Helper macros to do AES decryption, via inline asm. */
|
||||
#define AES_DEC_ROUND(n) \
|
||||
"ldr %q[tmp2], %[round_key_" #n "]\n" \
|
||||
"aesd %[tmp].16b, %[tmp2].16b\n" \
|
||||
"aesimc %[tmp].16b, %[tmp].16b\n"
|
||||
|
||||
#define AES_DEC_FINAL_ROUND() \
|
||||
"ldr %q[tmp2], %[round_key_second_last]\n" \
|
||||
"aesd %[tmp].16b, %[tmp2].16b\n" \
|
||||
"ldr %q[tmp2], %[round_key_last]\n" \
|
||||
"eor %[tmp].16b, %[tmp].16b, %[tmp2].16b"
|
||||
|
||||
#define AES_DEC_INPUT_ROUND_KEY(num_rounds, n) \
|
||||
[round_key_##n]"m"(ctx->round_keys[(num_rounds + 1 - n)])
|
||||
|
||||
#define AES_DEC_INPUT_LAST_ROUND_KEYS(num_rounds) \
|
||||
[round_key_second_last]"m"(ctx->round_keys[1]), \
|
||||
[round_key_last]"m"(ctx->round_keys[0])
|
||||
|
||||
|
||||
constexpr const u8 RoundKeyRcon0[] = {
|
||||
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36, 0x6C, 0xD8, 0xAB, 0x4D, 0x9A, 0x2F,
|
||||
0x5E, 0xBC, 0x63, 0xC6, 0x97, 0x35, 0x6A, 0xD4, 0xB3, 0x7D, 0xFA, 0xEF, 0xC5, 0x91,
|
||||
};
|
||||
|
||||
constexpr const u8 SubBytesTable[0x100] = {
|
||||
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
|
||||
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
|
||||
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
|
||||
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
|
||||
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
|
||||
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
|
||||
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
|
||||
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
|
||||
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
|
||||
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
|
||||
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
|
||||
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
|
||||
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
|
||||
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
|
||||
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
|
||||
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16,
|
||||
};
|
||||
|
||||
constexpr auto AesWordByte0Shift = 0 * BITSIZEOF(u8);
|
||||
constexpr auto AesWordByte1Shift = 1 * BITSIZEOF(u8);
|
||||
constexpr auto AesWordByte2Shift = 2 * BITSIZEOF(u8);
|
||||
constexpr auto AesWordByte3Shift = 3 * BITSIZEOF(u8);
|
||||
|
||||
constexpr u32 SubBytesAndRotate(u32 v) {
|
||||
return (static_cast<u32>(SubBytesTable[(v >> AesWordByte0Shift) & 0xFFu]) << AesWordByte3Shift) ^
|
||||
(static_cast<u32>(SubBytesTable[(v >> AesWordByte1Shift) & 0xFFu]) << AesWordByte0Shift) ^
|
||||
(static_cast<u32>(SubBytesTable[(v >> AesWordByte2Shift) & 0xFFu]) << AesWordByte1Shift) ^
|
||||
(static_cast<u32>(SubBytesTable[(v >> AesWordByte3Shift) & 0xFFu]) << AesWordByte2Shift);
|
||||
}
|
||||
|
||||
constexpr u32 SubBytes(u32 v) {
|
||||
return (static_cast<u32>(SubBytesTable[(v >> AesWordByte0Shift) & 0xFFu]) << AesWordByte0Shift) ^
|
||||
(static_cast<u32>(SubBytesTable[(v >> AesWordByte1Shift) & 0xFFu]) << AesWordByte1Shift) ^
|
||||
(static_cast<u32>(SubBytesTable[(v >> AesWordByte2Shift) & 0xFFu]) << AesWordByte2Shift) ^
|
||||
(static_cast<u32>(SubBytesTable[(v >> AesWordByte3Shift) & 0xFFu]) << AesWordByte3Shift);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<size_t KeySize>
|
||||
AesImpl<KeySize>::~AesImpl() {
|
||||
ClearMemory(this, sizeof(*this));
|
||||
}
|
||||
|
||||
template<size_t KeySize>
|
||||
void AesImpl<KeySize>::Initialize(const void *key, size_t key_size, bool is_encrypt) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(key_size == KeySize);
|
||||
AMS_UNUSED(key_size);
|
||||
|
||||
/* Set up key. */
|
||||
u32 *dst = m_round_keys;
|
||||
std::memcpy(dst, key, KeySize);
|
||||
|
||||
/* Perform key scheduling. */
|
||||
constexpr auto InitialKeyWords = KeySize / sizeof(u32);
|
||||
u32 tmp = dst[InitialKeyWords - 1];
|
||||
|
||||
for (auto i = InitialKeyWords; i < (RoundCount + 1) * 4; ++i) {
|
||||
const auto idx_in_key = i % InitialKeyWords;
|
||||
if (idx_in_key == 0) {
|
||||
/* At start of key word, we need to handle sub/rotate/rcon. */
|
||||
tmp = SubBytesAndRotate(tmp);
|
||||
tmp ^= (RoundKeyRcon0[i / InitialKeyWords - 1] << AesWordByte0Shift);
|
||||
} else if ((InitialKeyWords > 6) && idx_in_key == 4) {
|
||||
/* Halfway into a 256-bit key word, we need to do an additional subbytes. */
|
||||
tmp = SubBytes(tmp);
|
||||
}
|
||||
|
||||
/* Set the key word. */
|
||||
tmp ^= dst[i - InitialKeyWords];
|
||||
dst[i] = tmp;
|
||||
}
|
||||
|
||||
/* If decrypting, perform inverse mix columns on all round keys. */
|
||||
if (!is_encrypt) {
|
||||
auto *key8 = reinterpret_cast<u8 *>(m_round_keys) + BlockSize;
|
||||
|
||||
for (auto i = 1; i < RoundCount; ++i) {
|
||||
vst1q_u8(key8, vaesimcq_u8(vld1q_u8(key8)));
|
||||
key8 += BlockSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t KeySize>
|
||||
void AesImpl<KeySize>::EncryptBlock(void *dst, size_t dst_size, const void *src, size_t src_size) const {
|
||||
AMS_UNUSED(src_size, dst_size);
|
||||
|
||||
/* Get the key. */
|
||||
const u8 *key8 = reinterpret_cast<const u8 *>(m_round_keys);
|
||||
|
||||
/* Read the block. */
|
||||
uint8x16_t block = vld1q_u8(static_cast<const u8 *>(src));
|
||||
|
||||
/* Encrypt block. */
|
||||
for (auto round = 1; round < RoundCount; ++round) {
|
||||
/* Do aes round. */
|
||||
block = vaeseq_u8(block, vld1q_u8(key8));
|
||||
key8 += BlockSize;
|
||||
|
||||
/* Do mix columns. */
|
||||
block = vaesmcq_u8(block);
|
||||
}
|
||||
|
||||
/* Do last aes round. */
|
||||
block = vaeseq_u8(block, vld1q_u8(key8));
|
||||
key8 += BlockSize;
|
||||
|
||||
/* Add the final round key. */
|
||||
block = veorq_u8(block, vld1q_u8(key8));
|
||||
|
||||
/* Store the block. */
|
||||
vst1q_u8(static_cast<u8 *>(dst), block);
|
||||
}
|
||||
|
||||
template<size_t KeySize>
|
||||
void AesImpl<KeySize>::DecryptBlock(void *dst, size_t dst_size, const void *src, size_t src_size) const {
|
||||
AMS_UNUSED(src_size, dst_size);
|
||||
|
||||
/* Get the key. */
|
||||
const u8 *key8 = reinterpret_cast<const u8 *>(m_round_keys) + (RoundCount * BlockSize);
|
||||
|
||||
/* Read the block. */
|
||||
uint8x16_t block = vld1q_u8(static_cast<const u8 *>(src));
|
||||
|
||||
/* Encrypt block. */
|
||||
for (auto round = RoundCount; round > 1; --round) {
|
||||
/* Do aes round. */
|
||||
block = vaesdq_u8(block, vld1q_u8(key8));
|
||||
key8 -= BlockSize;
|
||||
|
||||
/* Do mix columns. */
|
||||
block = vaesimcq_u8(block);
|
||||
}
|
||||
|
||||
/* Do last aes round. */
|
||||
block = vaesdq_u8(block, vld1q_u8(key8));
|
||||
key8 -= BlockSize;
|
||||
|
||||
/* Add the first round key. */
|
||||
block = veorq_u8(block, vld1q_u8(key8));
|
||||
|
||||
/* Store the block. */
|
||||
vst1q_u8(static_cast<u8 *>(dst), block);
|
||||
}
|
||||
|
||||
/* Specializations when building specifically for cortex-a57 (or for apple M* processors). */
|
||||
#if defined(ATMOSPHERE_CPU_ARM_CORTEX_A57) || defined(ATMOSPHERE_OS_MACOS)
|
||||
|
||||
namespace {
|
||||
|
||||
template<size_t KeySize>
|
||||
struct RoundKeyHelper {
|
||||
u8 round_keys[AesImpl<KeySize>::RoundCount + 1][AesImpl<KeySize>::BlockSize];
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<>
|
||||
void AesImpl<16>::EncryptBlock(void *dst, size_t dst_size, const void *src, size_t src_size) const {
|
||||
AMS_UNUSED(src_size, dst_size);
|
||||
|
||||
/* Setup for asm */
|
||||
AES_ENC_DEC_SETUP_VARS();
|
||||
|
||||
/* Use optimized assembly to do all rounds. */
|
||||
__asm__ __volatile__ (
|
||||
AES_ENC_ROUND(1)
|
||||
AES_ENC_ROUND(2)
|
||||
AES_ENC_ROUND(3)
|
||||
AES_ENC_ROUND(4)
|
||||
AES_ENC_ROUND(5)
|
||||
AES_ENC_ROUND(6)
|
||||
AES_ENC_ROUND(7)
|
||||
AES_ENC_ROUND(8)
|
||||
AES_ENC_ROUND(9)
|
||||
AES_ENC_FINAL_ROUND()
|
||||
: AES_ENC_DEC_OUTPUT_VARS()
|
||||
: AES_ENC_INPUT_ROUND_KEY(RoundCount, 1),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 2),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 3),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 4),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 5),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 6),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 7),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 8),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 9),
|
||||
AES_ENC_INPUT_LAST_ROUND_KEYS(RoundCount)
|
||||
);
|
||||
|
||||
/* Store result. */
|
||||
AES_ENC_DEC_STORE_RESULT();
|
||||
}
|
||||
|
||||
template<>
|
||||
void AesImpl<24>::EncryptBlock(void *dst, size_t dst_size, const void *src, size_t src_size) const {
|
||||
AMS_UNUSED(src_size, dst_size);
|
||||
|
||||
/* Setup for asm */
|
||||
AES_ENC_DEC_SETUP_VARS();
|
||||
|
||||
/* Use optimized assembly to do all rounds. */
|
||||
__asm__ __volatile__ (
|
||||
AES_ENC_ROUND(1)
|
||||
AES_ENC_ROUND(2)
|
||||
AES_ENC_ROUND(3)
|
||||
AES_ENC_ROUND(4)
|
||||
AES_ENC_ROUND(5)
|
||||
AES_ENC_ROUND(6)
|
||||
AES_ENC_ROUND(7)
|
||||
AES_ENC_ROUND(8)
|
||||
AES_ENC_ROUND(9)
|
||||
AES_ENC_ROUND(10)
|
||||
AES_ENC_ROUND(11)
|
||||
AES_ENC_FINAL_ROUND()
|
||||
: AES_ENC_DEC_OUTPUT_VARS()
|
||||
: AES_ENC_INPUT_ROUND_KEY(RoundCount, 1),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 2),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 3),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 4),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 5),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 6),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 7),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 8),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 9),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 10),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 11),
|
||||
AES_ENC_INPUT_LAST_ROUND_KEYS(RoundCount)
|
||||
);
|
||||
|
||||
/* Store result. */
|
||||
AES_ENC_DEC_STORE_RESULT();
|
||||
}
|
||||
|
||||
template<>
|
||||
void AesImpl<32>::EncryptBlock(void *dst, size_t dst_size, const void *src, size_t src_size) const {
|
||||
AMS_UNUSED(src_size, dst_size);
|
||||
|
||||
/* Setup for asm */
|
||||
AES_ENC_DEC_SETUP_VARS();
|
||||
|
||||
/* Use optimized assembly to do all rounds. */
|
||||
__asm__ __volatile__ (
|
||||
AES_ENC_ROUND(1)
|
||||
AES_ENC_ROUND(2)
|
||||
AES_ENC_ROUND(3)
|
||||
AES_ENC_ROUND(4)
|
||||
AES_ENC_ROUND(5)
|
||||
AES_ENC_ROUND(6)
|
||||
AES_ENC_ROUND(7)
|
||||
AES_ENC_ROUND(8)
|
||||
AES_ENC_ROUND(9)
|
||||
AES_ENC_ROUND(10)
|
||||
AES_ENC_ROUND(11)
|
||||
AES_ENC_ROUND(12)
|
||||
AES_ENC_ROUND(13)
|
||||
AES_ENC_FINAL_ROUND()
|
||||
: AES_ENC_DEC_OUTPUT_VARS()
|
||||
: AES_ENC_INPUT_ROUND_KEY(RoundCount, 1),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 2),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 3),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 4),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 5),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 6),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 7),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 8),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 9),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 10),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 11),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 12),
|
||||
AES_ENC_INPUT_ROUND_KEY(RoundCount, 13),
|
||||
AES_ENC_INPUT_LAST_ROUND_KEYS(RoundCount)
|
||||
);
|
||||
|
||||
/* Store result. */
|
||||
AES_ENC_DEC_STORE_RESULT();
|
||||
}
|
||||
|
||||
template<>
|
||||
void AesImpl<16>::DecryptBlock(void *dst, size_t dst_size, const void *src, size_t src_size) const {
|
||||
AMS_UNUSED(src_size, dst_size);
|
||||
|
||||
/* Setup for asm */
|
||||
AES_ENC_DEC_SETUP_VARS();
|
||||
|
||||
/* Use optimized assembly to do all rounds. */
|
||||
__asm__ __volatile__ (
|
||||
AES_DEC_ROUND(1)
|
||||
AES_DEC_ROUND(2)
|
||||
AES_DEC_ROUND(3)
|
||||
AES_DEC_ROUND(4)
|
||||
AES_DEC_ROUND(5)
|
||||
AES_DEC_ROUND(6)
|
||||
AES_DEC_ROUND(7)
|
||||
AES_DEC_ROUND(8)
|
||||
AES_DEC_ROUND(9)
|
||||
AES_DEC_FINAL_ROUND()
|
||||
: AES_ENC_DEC_OUTPUT_VARS()
|
||||
: AES_DEC_INPUT_ROUND_KEY(RoundCount, 1),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 2),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 3),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 4),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 5),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 6),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 7),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 8),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 9),
|
||||
AES_DEC_INPUT_LAST_ROUND_KEYS(RoundCount)
|
||||
);
|
||||
|
||||
/* Store result. */
|
||||
AES_ENC_DEC_STORE_RESULT();
|
||||
}
|
||||
|
||||
template<>
|
||||
void AesImpl<24>::DecryptBlock(void *dst, size_t dst_size, const void *src, size_t src_size) const {
|
||||
AMS_UNUSED(src_size, dst_size);
|
||||
|
||||
/* Setup for asm */
|
||||
AES_ENC_DEC_SETUP_VARS();
|
||||
|
||||
/* Use optimized assembly to do all rounds. */
|
||||
__asm__ __volatile__ (
|
||||
AES_DEC_ROUND(1)
|
||||
AES_DEC_ROUND(2)
|
||||
AES_DEC_ROUND(3)
|
||||
AES_DEC_ROUND(4)
|
||||
AES_DEC_ROUND(5)
|
||||
AES_DEC_ROUND(6)
|
||||
AES_DEC_ROUND(7)
|
||||
AES_DEC_ROUND(8)
|
||||
AES_DEC_ROUND(9)
|
||||
AES_DEC_ROUND(10)
|
||||
AES_DEC_ROUND(11)
|
||||
AES_DEC_FINAL_ROUND()
|
||||
: AES_ENC_DEC_OUTPUT_VARS()
|
||||
: AES_DEC_INPUT_ROUND_KEY(RoundCount, 1),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 2),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 3),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 4),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 5),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 6),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 7),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 8),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 9),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 10),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 11),
|
||||
AES_DEC_INPUT_LAST_ROUND_KEYS(RoundCount)
|
||||
);
|
||||
|
||||
/* Store result. */
|
||||
AES_ENC_DEC_STORE_RESULT();
|
||||
}
|
||||
|
||||
template<>
|
||||
void AesImpl<32>::DecryptBlock(void *dst, size_t dst_size, const void *src, size_t src_size) const {
|
||||
AMS_UNUSED(src_size, dst_size);
|
||||
|
||||
/* Setup for asm */
|
||||
AES_ENC_DEC_SETUP_VARS();
|
||||
|
||||
/* Use optimized assembly to do all rounds. */
|
||||
__asm__ __volatile__ (
|
||||
AES_DEC_ROUND(1)
|
||||
AES_DEC_ROUND(2)
|
||||
AES_DEC_ROUND(3)
|
||||
AES_DEC_ROUND(4)
|
||||
AES_DEC_ROUND(5)
|
||||
AES_DEC_ROUND(6)
|
||||
AES_DEC_ROUND(7)
|
||||
AES_DEC_ROUND(8)
|
||||
AES_DEC_ROUND(9)
|
||||
AES_DEC_ROUND(10)
|
||||
AES_DEC_ROUND(11)
|
||||
AES_DEC_ROUND(12)
|
||||
AES_DEC_ROUND(13)
|
||||
AES_DEC_FINAL_ROUND()
|
||||
: AES_ENC_DEC_OUTPUT_VARS()
|
||||
: AES_DEC_INPUT_ROUND_KEY(RoundCount, 1),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 2),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 3),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 4),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 5),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 6),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 7),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 8),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 9),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 10),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 11),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 12),
|
||||
AES_DEC_INPUT_ROUND_KEY(RoundCount, 13),
|
||||
AES_DEC_INPUT_LAST_ROUND_KEYS(RoundCount)
|
||||
);
|
||||
|
||||
/* Store result. */
|
||||
AES_ENC_DEC_STORE_RESULT();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* Explicitly instantiate the three supported key sizes. */
|
||||
template class AesImpl<16>;
|
||||
template class AesImpl<24>;
|
||||
template class AesImpl<32>;
|
||||
|
||||
#else
|
||||
|
||||
/* NOTE: Exosphere defines this in libexosphere. */
|
||||
|
||||
/* TODO: Non-EL0 implementation. */
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -1,435 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
#include "crypto_aes_impl.arch.x64.hpp"
|
||||
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr bool IsSupportedKeySize(size_t size) {
|
||||
return size == 16 || size == 24 || size == 32;
|
||||
}
|
||||
|
||||
constexpr int WordsPerBlock = AesImpl<16>::BlockSize / sizeof(u32);
|
||||
static_assert(AesImpl<16>::BlockSize == AesImpl<24>::BlockSize);
|
||||
static_assert(AesImpl<16>::BlockSize == AesImpl<32>::BlockSize);
|
||||
|
||||
bool GetAesNiAvailabilityImpl() {
|
||||
/* Call cpu id. */
|
||||
int a = 0, b = 0, c = 0, d = 0;
|
||||
__asm__ __volatile__("cpuid" : "=a"(a), "=b"(b), "=c"(c), "=d"(d) : "0"(1) : "memory");
|
||||
|
||||
/* Check for AES-NI and SSE2. */
|
||||
return (c & (1 << 25)) && (d & (1 << 26));
|
||||
}
|
||||
|
||||
static_assert(util::IsLittleEndian());
|
||||
|
||||
constexpr const u8 RoundKeyRcon0[] = {
|
||||
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36, 0x6C, 0xD8, 0xAB, 0x4D, 0x9A, 0x2F,
|
||||
0x5E, 0xBC, 0x63, 0xC6, 0x97, 0x35, 0x6A, 0xD4, 0xB3, 0x7D, 0xFA, 0xEF, 0xC5, 0x91,
|
||||
};
|
||||
|
||||
constexpr const u8 SubBytesTable[0x100] = {
|
||||
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
|
||||
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
|
||||
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
|
||||
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
|
||||
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
|
||||
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
|
||||
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
|
||||
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
|
||||
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
|
||||
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
|
||||
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
|
||||
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
|
||||
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
|
||||
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
|
||||
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
|
||||
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16,
|
||||
};
|
||||
|
||||
constexpr const u8 InvSubBytesTable[0x100] = {
|
||||
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
|
||||
0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
|
||||
0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
|
||||
0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
|
||||
0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
|
||||
0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
|
||||
0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
|
||||
0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
|
||||
0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
|
||||
0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
|
||||
0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
|
||||
0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
|
||||
0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
|
||||
0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
|
||||
0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
|
||||
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D,
|
||||
};
|
||||
|
||||
constexpr bool IsSubBytesTableValid() {
|
||||
for (size_t i = 0; i < 0x100; ++i) {
|
||||
if (SubBytesTable[InvSubBytesTable[i]] != i) {
|
||||
return false;
|
||||
}
|
||||
if (InvSubBytesTable[SubBytesTable[i]] != i) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static_assert(IsSubBytesTableValid());
|
||||
|
||||
constexpr const u32 EncryptTable[0x100] = {
|
||||
0xA56363C6, 0x847C7CF8, 0x997777EE, 0x8D7B7BF6, 0x0DF2F2FF, 0xBD6B6BD6, 0xB16F6FDE, 0x54C5C591,
|
||||
0x50303060, 0x03010102, 0xA96767CE, 0x7D2B2B56, 0x19FEFEE7, 0x62D7D7B5, 0xE6ABAB4D, 0x9A7676EC,
|
||||
0x45CACA8F, 0x9D82821F, 0x40C9C989, 0x877D7DFA, 0x15FAFAEF, 0xEB5959B2, 0xC947478E, 0x0BF0F0FB,
|
||||
0xECADAD41, 0x67D4D4B3, 0xFDA2A25F, 0xEAAFAF45, 0xBF9C9C23, 0xF7A4A453, 0x967272E4, 0x5BC0C09B,
|
||||
0xC2B7B775, 0x1CFDFDE1, 0xAE93933D, 0x6A26264C, 0x5A36366C, 0x413F3F7E, 0x02F7F7F5, 0x4FCCCC83,
|
||||
0x5C343468, 0xF4A5A551, 0x34E5E5D1, 0x08F1F1F9, 0x937171E2, 0x73D8D8AB, 0x53313162, 0x3F15152A,
|
||||
0x0C040408, 0x52C7C795, 0x65232346, 0x5EC3C39D, 0x28181830, 0xA1969637, 0x0F05050A, 0xB59A9A2F,
|
||||
0x0907070E, 0x36121224, 0x9B80801B, 0x3DE2E2DF, 0x26EBEBCD, 0x6927274E, 0xCDB2B27F, 0x9F7575EA,
|
||||
0x1B090912, 0x9E83831D, 0x742C2C58, 0x2E1A1A34, 0x2D1B1B36, 0xB26E6EDC, 0xEE5A5AB4, 0xFBA0A05B,
|
||||
0xF65252A4, 0x4D3B3B76, 0x61D6D6B7, 0xCEB3B37D, 0x7B292952, 0x3EE3E3DD, 0x712F2F5E, 0x97848413,
|
||||
0xF55353A6, 0x68D1D1B9, 0x00000000, 0x2CEDEDC1, 0x60202040, 0x1FFCFCE3, 0xC8B1B179, 0xED5B5BB6,
|
||||
0xBE6A6AD4, 0x46CBCB8D, 0xD9BEBE67, 0x4B393972, 0xDE4A4A94, 0xD44C4C98, 0xE85858B0, 0x4ACFCF85,
|
||||
0x6BD0D0BB, 0x2AEFEFC5, 0xE5AAAA4F, 0x16FBFBED, 0xC5434386, 0xD74D4D9A, 0x55333366, 0x94858511,
|
||||
0xCF45458A, 0x10F9F9E9, 0x06020204, 0x817F7FFE, 0xF05050A0, 0x443C3C78, 0xBA9F9F25, 0xE3A8A84B,
|
||||
0xF35151A2, 0xFEA3A35D, 0xC0404080, 0x8A8F8F05, 0xAD92923F, 0xBC9D9D21, 0x48383870, 0x04F5F5F1,
|
||||
0xDFBCBC63, 0xC1B6B677, 0x75DADAAF, 0x63212142, 0x30101020, 0x1AFFFFE5, 0x0EF3F3FD, 0x6DD2D2BF,
|
||||
0x4CCDCD81, 0x140C0C18, 0x35131326, 0x2FECECC3, 0xE15F5FBE, 0xA2979735, 0xCC444488, 0x3917172E,
|
||||
0x57C4C493, 0xF2A7A755, 0x827E7EFC, 0x473D3D7A, 0xAC6464C8, 0xE75D5DBA, 0x2B191932, 0x957373E6,
|
||||
0xA06060C0, 0x98818119, 0xD14F4F9E, 0x7FDCDCA3, 0x66222244, 0x7E2A2A54, 0xAB90903B, 0x8388880B,
|
||||
0xCA46468C, 0x29EEEEC7, 0xD3B8B86B, 0x3C141428, 0x79DEDEA7, 0xE25E5EBC, 0x1D0B0B16, 0x76DBDBAD,
|
||||
0x3BE0E0DB, 0x56323264, 0x4E3A3A74, 0x1E0A0A14, 0xDB494992, 0x0A06060C, 0x6C242448, 0xE45C5CB8,
|
||||
0x5DC2C29F, 0x6ED3D3BD, 0xEFACAC43, 0xA66262C4, 0xA8919139, 0xA4959531, 0x37E4E4D3, 0x8B7979F2,
|
||||
0x32E7E7D5, 0x43C8C88B, 0x5937376E, 0xB76D6DDA, 0x8C8D8D01, 0x64D5D5B1, 0xD24E4E9C, 0xE0A9A949,
|
||||
0xB46C6CD8, 0xFA5656AC, 0x07F4F4F3, 0x25EAEACF, 0xAF6565CA, 0x8E7A7AF4, 0xE9AEAE47, 0x18080810,
|
||||
0xD5BABA6F, 0x887878F0, 0x6F25254A, 0x722E2E5C, 0x241C1C38, 0xF1A6A657, 0xC7B4B473, 0x51C6C697,
|
||||
0x23E8E8CB, 0x7CDDDDA1, 0x9C7474E8, 0x211F1F3E, 0xDD4B4B96, 0xDCBDBD61, 0x868B8B0D, 0x858A8A0F,
|
||||
0x907070E0, 0x423E3E7C, 0xC4B5B571, 0xAA6666CC, 0xD8484890, 0x05030306, 0x01F6F6F7, 0x120E0E1C,
|
||||
0xA36161C2, 0x5F35356A, 0xF95757AE, 0xD0B9B969, 0x91868617, 0x58C1C199, 0x271D1D3A, 0xB99E9E27,
|
||||
0x38E1E1D9, 0x13F8F8EB, 0xB398982B, 0x33111122, 0xBB6969D2, 0x70D9D9A9, 0x898E8E07, 0xA7949433,
|
||||
0xB69B9B2D, 0x221E1E3C, 0x92878715, 0x20E9E9C9, 0x49CECE87, 0xFF5555AA, 0x78282850, 0x7ADFDFA5,
|
||||
0x8F8C8C03, 0xF8A1A159, 0x80898909, 0x170D0D1A, 0xDABFBF65, 0x31E6E6D7, 0xC6424284, 0xB86868D0,
|
||||
0xC3414182, 0xB0999929, 0x772D2D5A, 0x110F0F1E, 0xCBB0B07B, 0xFC5454A8, 0xD6BBBB6D, 0x3A16162C,
|
||||
};
|
||||
|
||||
constexpr const u32 DecryptTable[0x100] = {
|
||||
0x50A7F451, 0x5365417E, 0xC3A4171A, 0x965E273A, 0xCB6BAB3B, 0xF1459D1F, 0xAB58FAAC, 0x9303E34B,
|
||||
0x55FA3020, 0xF66D76AD, 0x9176CC88, 0x254C02F5, 0xFCD7E54F, 0xD7CB2AC5, 0x80443526, 0x8FA362B5,
|
||||
0x495AB1DE, 0x671BBA25, 0x980EEA45, 0xE1C0FE5D, 0x02752FC3, 0x12F04C81, 0xA397468D, 0xC6F9D36B,
|
||||
0xE75F8F03, 0x959C9215, 0xEB7A6DBF, 0xDA595295, 0x2D83BED4, 0xD3217458, 0x2969E049, 0x44C8C98E,
|
||||
0x6A89C275, 0x78798EF4, 0x6B3E5899, 0xDD71B927, 0xB64FE1BE, 0x17AD88F0, 0x66AC20C9, 0xB43ACE7D,
|
||||
0x184ADF63, 0x82311AE5, 0x60335197, 0x457F5362, 0xE07764B1, 0x84AE6BBB, 0x1CA081FE, 0x942B08F9,
|
||||
0x58684870, 0x19FD458F, 0x876CDE94, 0xB7F87B52, 0x23D373AB, 0xE2024B72, 0x578F1FE3, 0x2AAB5566,
|
||||
0x0728EBB2, 0x03C2B52F, 0x9A7BC586, 0xA50837D3, 0xF2872830, 0xB2A5BF23, 0xBA6A0302, 0x5C8216ED,
|
||||
0x2B1CCF8A, 0x92B479A7, 0xF0F207F3, 0xA1E2694E, 0xCDF4DA65, 0xD5BE0506, 0x1F6234D1, 0x8AFEA6C4,
|
||||
0x9D532E34, 0xA055F3A2, 0x32E18A05, 0x75EBF6A4, 0x39EC830B, 0xAAEF6040, 0x069F715E, 0x51106EBD,
|
||||
0xF98A213E, 0x3D06DD96, 0xAE053EDD, 0x46BDE64D, 0xB58D5491, 0x055DC471, 0x6FD40604, 0xFF155060,
|
||||
0x24FB9819, 0x97E9BDD6, 0xCC434089, 0x779ED967, 0xBD42E8B0, 0x888B8907, 0x385B19E7, 0xDBEEC879,
|
||||
0x470A7CA1, 0xE90F427C, 0xC91E84F8, 0x00000000, 0x83868009, 0x48ED2B32, 0xAC70111E, 0x4E725A6C,
|
||||
0xFBFF0EFD, 0x5638850F, 0x1ED5AE3D, 0x27392D36, 0x64D90F0A, 0x21A65C68, 0xD1545B9B, 0x3A2E3624,
|
||||
0xB1670A0C, 0x0FE75793, 0xD296EEB4, 0x9E919B1B, 0x4FC5C080, 0xA220DC61, 0x694B775A, 0x161A121C,
|
||||
0x0ABA93E2, 0xE52AA0C0, 0x43E0223C, 0x1D171B12, 0x0B0D090E, 0xADC78BF2, 0xB9A8B62D, 0xC8A91E14,
|
||||
0x8519F157, 0x4C0775AF, 0xBBDD99EE, 0xFD607FA3, 0x9F2601F7, 0xBCF5725C, 0xC53B6644, 0x347EFB5B,
|
||||
0x7629438B, 0xDCC623CB, 0x68FCEDB6, 0x63F1E4B8, 0xCADC31D7, 0x10856342, 0x40229713, 0x2011C684,
|
||||
0x7D244A85, 0xF83DBBD2, 0x1132F9AE, 0x6DA129C7, 0x4B2F9E1D, 0xF330B2DC, 0xEC52860D, 0xD0E3C177,
|
||||
0x6C16B32B, 0x99B970A9, 0xFA489411, 0x2264E947, 0xC48CFCA8, 0x1A3FF0A0, 0xD82C7D56, 0xEF903322,
|
||||
0xC74E4987, 0xC1D138D9, 0xFEA2CA8C, 0x360BD498, 0xCF81F5A6, 0x28DE7AA5, 0x268EB7DA, 0xA4BFAD3F,
|
||||
0xE49D3A2C, 0x0D927850, 0x9BCC5F6A, 0x62467E54, 0xC2138DF6, 0xE8B8D890, 0x5EF7392E, 0xF5AFC382,
|
||||
0xBE805D9F, 0x7C93D069, 0xA92DD56F, 0xB31225CF, 0x3B99ACC8, 0xA77D1810, 0x6E639CE8, 0x7BBB3BDB,
|
||||
0x097826CD, 0xF418596E, 0x01B79AEC, 0xA89A4F83, 0x656E95E6, 0x7EE6FFAA, 0x08CFBC21, 0xE6E815EF,
|
||||
0xD99BE7BA, 0xCE366F4A, 0xD4099FEA, 0xD67CB029, 0xAFB2A431, 0x31233F2A, 0x3094A5C6, 0xC066A235,
|
||||
0x37BC4E74, 0xA6CA82FC, 0xB0D090E0, 0x15D8A733, 0x4A9804F1, 0xF7DAEC41, 0x0E50CD7F, 0x2FF69117,
|
||||
0x8DD64D76, 0x4DB0EF43, 0x544DAACC, 0xDF0496E4, 0xE3B5D19E, 0x1B886A4C, 0xB81F2CC1, 0x7F516546,
|
||||
0x04EA5E9D, 0x5D358C01, 0x737487FA, 0x2E410BFB, 0x5A1D67B3, 0x52D2DB92, 0x335610E9, 0x1347D66D,
|
||||
0x8C61D79A, 0x7A0CA137, 0x8E14F859, 0x893C13EB, 0xEE27A9CE, 0x35C961B7, 0xEDE51CE1, 0x3CB1477A,
|
||||
0x59DFD29C, 0x3F73F255, 0x79CE1418, 0xBF37C773, 0xEACDF753, 0x5BAAFD5F, 0x146F3DDF, 0x86DB4478,
|
||||
0x81F3AFCA, 0x3EC468B9, 0x2C342438, 0x5F40A3C2, 0x72C31D16, 0x0C25E2BC, 0x8B493C28, 0x41950DFF,
|
||||
0x7101A839, 0xDEB30C08, 0x9CE4B4D8, 0x90C15664, 0x6184CB7B, 0x70B632D5, 0x745C6C48, 0x4257B8D0,
|
||||
};
|
||||
|
||||
constexpr auto AesWordByte0Shift = 0 * BITSIZEOF(u8);
|
||||
constexpr auto AesWordByte1Shift = 1 * BITSIZEOF(u8);
|
||||
constexpr auto AesWordByte2Shift = 2 * BITSIZEOF(u8);
|
||||
constexpr auto AesWordByte3Shift = 3 * BITSIZEOF(u8);
|
||||
|
||||
constexpr auto AesMixShift = 3 * BITSIZEOF(u8);
|
||||
|
||||
constexpr void InverseMixColumns(u32 *dst, const u32 *src) {
|
||||
for (auto i = 0; i < WordsPerBlock; ++i) {
|
||||
const u32 v0 = src[i];
|
||||
const u32 v1 = (((v0 & 0x7F7F7F7Fu) << 1) ^ (((v0 & 0x80808080) >> 7) * 0x1B));
|
||||
const u32 v2 = (((v1 & 0x7F7F7F7Fu) << 1) ^ (((v1 & 0x80808080) >> 7) * 0x1B));
|
||||
const u32 v3 = (((v2 & 0x7F7F7F7Fu) << 1) ^ (((v2 & 0x80808080) >> 7) * 0x1B));
|
||||
|
||||
u32 v = v0 ^ v3;
|
||||
v ^= util::RotateLeft(v, AesMixShift) ^ v2;
|
||||
v ^= util::RotateLeft(v, AesMixShift) ^ v1;
|
||||
v ^= util::RotateLeft(v, AesMixShift) ^ v0;
|
||||
|
||||
dst[i] = v;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr u32 SubBytesAndRotate(u32 v) {
|
||||
return (static_cast<u32>(SubBytesTable[(v >> AesWordByte0Shift) & 0xFFu]) << AesWordByte3Shift) ^
|
||||
(static_cast<u32>(SubBytesTable[(v >> AesWordByte1Shift) & 0xFFu]) << AesWordByte0Shift) ^
|
||||
(static_cast<u32>(SubBytesTable[(v >> AesWordByte2Shift) & 0xFFu]) << AesWordByte1Shift) ^
|
||||
(static_cast<u32>(SubBytesTable[(v >> AesWordByte3Shift) & 0xFFu]) << AesWordByte2Shift);
|
||||
}
|
||||
|
||||
constexpr u32 SubBytes(u32 v) {
|
||||
return (static_cast<u32>(SubBytesTable[(v >> AesWordByte0Shift) & 0xFFu]) << AesWordByte0Shift) ^
|
||||
(static_cast<u32>(SubBytesTable[(v >> AesWordByte1Shift) & 0xFFu]) << AesWordByte1Shift) ^
|
||||
(static_cast<u32>(SubBytesTable[(v >> AesWordByte2Shift) & 0xFFu]) << AesWordByte2Shift) ^
|
||||
(static_cast<u32>(SubBytesTable[(v >> AesWordByte3Shift) & 0xFFu]) << AesWordByte3Shift);
|
||||
}
|
||||
|
||||
constexpr u32 ShiftSubMix(u32 v0, u32 v1, u32 v2, u32 v3) {
|
||||
return (util::RotateLeft(static_cast<u32>(EncryptTable[(v0 >> AesWordByte0Shift) & 0xFFu]), AesWordByte0Shift)) ^
|
||||
(util::RotateLeft(static_cast<u32>(EncryptTable[(v1 >> AesWordByte1Shift) & 0xFFu]), AesWordByte1Shift)) ^
|
||||
(util::RotateLeft(static_cast<u32>(EncryptTable[(v2 >> AesWordByte2Shift) & 0xFFu]), AesWordByte2Shift)) ^
|
||||
(util::RotateLeft(static_cast<u32>(EncryptTable[(v3 >> AesWordByte3Shift) & 0xFFu]), AesWordByte3Shift));
|
||||
}
|
||||
|
||||
constexpr u32 ShiftSub(u32 v0, u32 v1, u32 v2, u32 v3) {
|
||||
return (static_cast<u32>(SubBytesTable[(v0 >> AesWordByte0Shift) & 0xFFu]) << AesWordByte0Shift) ^
|
||||
(static_cast<u32>(SubBytesTable[(v1 >> AesWordByte1Shift) & 0xFFu]) << AesWordByte1Shift) ^
|
||||
(static_cast<u32>(SubBytesTable[(v2 >> AesWordByte2Shift) & 0xFFu]) << AesWordByte2Shift) ^
|
||||
(static_cast<u32>(SubBytesTable[(v3 >> AesWordByte3Shift) & 0xFFu]) << AesWordByte3Shift);
|
||||
}
|
||||
|
||||
constexpr u32 InvShiftSubMix(u32 v0, u32 v1, u32 v2, u32 v3) {
|
||||
return (util::RotateLeft(static_cast<u32>(DecryptTable[(v0 >> AesWordByte0Shift) & 0xFFu]), AesWordByte0Shift)) ^
|
||||
(util::RotateLeft(static_cast<u32>(DecryptTable[(v1 >> AesWordByte1Shift) & 0xFFu]), AesWordByte1Shift)) ^
|
||||
(util::RotateLeft(static_cast<u32>(DecryptTable[(v2 >> AesWordByte2Shift) & 0xFFu]), AesWordByte2Shift)) ^
|
||||
(util::RotateLeft(static_cast<u32>(DecryptTable[(v3 >> AesWordByte3Shift) & 0xFFu]), AesWordByte3Shift));
|
||||
}
|
||||
|
||||
constexpr u32 InvShiftSub(u32 v0, u32 v1, u32 v2, u32 v3) {
|
||||
return (static_cast<u32>(InvSubBytesTable[(v0 >> AesWordByte0Shift) & 0xFFu]) << AesWordByte0Shift) ^
|
||||
(static_cast<u32>(InvSubBytesTable[(v1 >> AesWordByte1Shift) & 0xFFu]) << AesWordByte1Shift) ^
|
||||
(static_cast<u32>(InvSubBytesTable[(v2 >> AesWordByte2Shift) & 0xFFu]) << AesWordByte2Shift) ^
|
||||
(static_cast<u32>(InvSubBytesTable[(v3 >> AesWordByte3Shift) & 0xFFu]) << AesWordByte3Shift);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const bool g_is_aes_ni_available = GetAesNiAvailabilityImpl();
|
||||
|
||||
template<size_t KeySize>
|
||||
AesImpl<KeySize>::~AesImpl() {
|
||||
ClearMemory(this, sizeof(*this));
|
||||
}
|
||||
|
||||
template<size_t KeySize>
|
||||
void AesImpl<KeySize>::Initialize(const void *key, size_t key_size, bool is_encrypt) {
|
||||
/* Check pre-conditions. */
|
||||
static_assert(IsSupportedKeySize(KeySize));
|
||||
AMS_ASSERT(key != nullptr);
|
||||
AMS_ASSERT(key_size == KeySize);
|
||||
AMS_UNUSED(key_size);
|
||||
|
||||
/* Set up key. */
|
||||
u32 *dst = m_round_keys;
|
||||
std::memcpy(dst, key, KeySize);
|
||||
|
||||
/* Perform key scheduling. */
|
||||
constexpr auto InitialKeyWords = KeySize / sizeof(u32);
|
||||
u32 tmp = dst[InitialKeyWords - 1];
|
||||
|
||||
for (auto i = InitialKeyWords; i < (RoundCount + 1) * 4; ++i) {
|
||||
const auto idx_in_key = i % InitialKeyWords;
|
||||
if (idx_in_key == 0) {
|
||||
/* At start of key word, we need to handle sub/rotate/rcon. */
|
||||
tmp = SubBytesAndRotate(tmp);
|
||||
tmp ^= (RoundKeyRcon0[i / InitialKeyWords - 1] << AesWordByte0Shift);
|
||||
} else if ((InitialKeyWords > 6) && idx_in_key == 4) {
|
||||
/* Halfway into a 256-bit key word, we need to do an additional subbytes. */
|
||||
tmp = SubBytes(tmp);
|
||||
}
|
||||
|
||||
/* Set the key word. */
|
||||
tmp ^= dst[i - InitialKeyWords];
|
||||
dst[i] = tmp;
|
||||
}
|
||||
|
||||
/* If decrypting, perform inverse mix columns on all round keys. */
|
||||
if (!is_encrypt) {
|
||||
if (IsAesNiAvailable()) {
|
||||
auto *key8 = reinterpret_cast<u8 *>(m_round_keys) + BlockSize;
|
||||
|
||||
for (auto i = 1; i < RoundCount; ++i) {
|
||||
auto * const key128 = reinterpret_cast<__m128i *>(key8);
|
||||
_mm_storeu_si128(key128, _mm_aesimc_si128(_mm_loadu_si128(key128)));
|
||||
key8 += BlockSize;
|
||||
}
|
||||
} else {
|
||||
for (auto i = 1; i < RoundCount; ++i) {
|
||||
InverseMixColumns(m_round_keys + WordsPerBlock * i, m_round_keys + WordsPerBlock * i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t KeySize>
|
||||
void AesImpl<KeySize>::EncryptBlock(void *dst, size_t dst_size, const void *src, size_t src_size) const {
|
||||
static_assert(IsSupportedKeySize(KeySize));
|
||||
AMS_ASSERT(dst_size == BlockSize && src_size == BlockSize);
|
||||
AMS_UNUSED(dst_size, src_size);
|
||||
|
||||
/* Perform block encryption. */
|
||||
if (IsAesNiAvailable()) {
|
||||
const auto *key8 = reinterpret_cast<const u8 *>(m_round_keys);
|
||||
|
||||
/* Load the block. */
|
||||
auto block = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src));
|
||||
|
||||
/* Add the first round key. */
|
||||
block = _mm_xor_si128(block, _mm_loadu_si128(reinterpret_cast<const __m128i *>(key8)));
|
||||
key8 += BlockSize;
|
||||
|
||||
/* Perform aes round on remaining round keys. */
|
||||
for (auto i = 1; i < RoundCount; ++i) {
|
||||
block = _mm_aesenc_si128(block, _mm_loadu_si128(reinterpret_cast<const __m128i *>(key8)));
|
||||
key8 += BlockSize;
|
||||
}
|
||||
|
||||
/* Do final update. */
|
||||
block = _mm_aesenclast_si128(block, _mm_loadu_si128(reinterpret_cast<const __m128i *>(key8)));
|
||||
|
||||
/* Store the output. */
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(dst), block);
|
||||
} else {
|
||||
static_assert(WordsPerBlock == 4);
|
||||
|
||||
/* Without AES-NI, we'll operate on words. */
|
||||
const u32 *key32 = m_round_keys;
|
||||
const u32 *src32 = static_cast<const u32 *>(src);
|
||||
u32 *dst32 = static_cast< u32 *>(dst);
|
||||
|
||||
/* Add the first round key. */
|
||||
u32 v0 = src32[0] ^ key32[0];
|
||||
u32 v1 = src32[1] ^ key32[1];
|
||||
u32 v2 = src32[2] ^ key32[2];
|
||||
u32 v3 = src32[3] ^ key32[3];
|
||||
key32 += 4;
|
||||
|
||||
/* Perform each round. */
|
||||
auto round = RoundCount;
|
||||
while (--round > 0) {
|
||||
/* Perform aes round. */
|
||||
const u32 e0 = ShiftSubMix(v0, v1, v2, v3);
|
||||
const u32 e1 = ShiftSubMix(v1, v2, v3, v0);
|
||||
const u32 e2 = ShiftSubMix(v2, v3, v0, v1);
|
||||
const u32 e3 = ShiftSubMix(v3, v0, v1, v2);
|
||||
|
||||
/* Add the round key. */
|
||||
v0 = e0 ^ key32[0];
|
||||
v1 = e1 ^ key32[1];
|
||||
v2 = e2 ^ key32[2];
|
||||
v3 = e3 ^ key32[3];
|
||||
key32 += 4;
|
||||
}
|
||||
|
||||
/* Perform the final round. */
|
||||
dst32[0] = key32[0] ^ ShiftSub(v0, v1, v2, v3);
|
||||
dst32[1] = key32[1] ^ ShiftSub(v1, v2, v3, v0);
|
||||
dst32[2] = key32[2] ^ ShiftSub(v2, v3, v0, v1);
|
||||
dst32[3] = key32[3] ^ ShiftSub(v3, v0, v1, v2);
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t KeySize>
|
||||
void AesImpl<KeySize>::DecryptBlock(void *dst, size_t dst_size, const void *src, size_t src_size) const {
|
||||
static_assert(IsSupportedKeySize(KeySize));
|
||||
AMS_ASSERT(dst_size == BlockSize && src_size == BlockSize);
|
||||
AMS_UNUSED(dst_size, src_size);
|
||||
|
||||
/* Perform block decryption. */
|
||||
if (IsAesNiAvailable()) {
|
||||
const auto *key8 = reinterpret_cast<const u8 *>(m_round_keys) + (RoundCount * BlockSize);
|
||||
|
||||
/* Load the block. */
|
||||
auto block = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src));
|
||||
|
||||
/* Add the final round key. */
|
||||
block = _mm_xor_si128(block, _mm_loadu_si128(reinterpret_cast<const __m128i *>(key8)));
|
||||
key8 -= BlockSize;
|
||||
|
||||
/* Perform aes invround on remaining round keys. */
|
||||
for (auto i = RoundCount; i > 1; --i) {
|
||||
block = _mm_aesdec_si128(block, _mm_loadu_si128(reinterpret_cast<const __m128i *>(key8)));
|
||||
key8 -= BlockSize;
|
||||
}
|
||||
|
||||
/* Do final update. */
|
||||
block = _mm_aesdeclast_si128(block, _mm_loadu_si128(reinterpret_cast<const __m128i *>(key8)));
|
||||
|
||||
/* Store the output. */
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(dst), block);
|
||||
} else {
|
||||
static_assert(WordsPerBlock == 4);
|
||||
|
||||
/* Without AES-NI, we'll operate on words. */
|
||||
const u32 *key32 = m_round_keys + WordsPerBlock * RoundCount;
|
||||
const u32 *src32 = static_cast<const u32 *>(src);
|
||||
u32 *dst32 = static_cast< u32 *>(dst);
|
||||
|
||||
/* Add the final round key. */
|
||||
u32 v0 = src32[0] ^ key32[0];
|
||||
u32 v1 = src32[1] ^ key32[1];
|
||||
u32 v2 = src32[2] ^ key32[2];
|
||||
u32 v3 = src32[3] ^ key32[3];
|
||||
key32 -= 4;
|
||||
|
||||
/* Perform each round. */
|
||||
auto round = RoundCount;
|
||||
while (--round > 0) {
|
||||
/* Perform aes inv round. */
|
||||
const u32 e0 = InvShiftSubMix(v0, v3, v2, v1);
|
||||
const u32 e1 = InvShiftSubMix(v1, v0, v3, v2);
|
||||
const u32 e2 = InvShiftSubMix(v2, v1, v0, v3);
|
||||
const u32 e3 = InvShiftSubMix(v3, v2, v1, v0);
|
||||
|
||||
/* Add the round key. */
|
||||
v0 = e0 ^ key32[0];
|
||||
v1 = e1 ^ key32[1];
|
||||
v2 = e2 ^ key32[2];
|
||||
v3 = e3 ^ key32[3];
|
||||
key32 -= 4;
|
||||
}
|
||||
|
||||
/* Perform the final round. */
|
||||
dst32[0] = key32[0] ^ InvShiftSub(v0, v3, v2, v1);
|
||||
dst32[1] = key32[1] ^ InvShiftSub(v1, v0, v3, v2);
|
||||
dst32[2] = key32[2] ^ InvShiftSub(v2, v1, v0, v3);
|
||||
dst32[3] = key32[3] ^ InvShiftSub(v3, v2, v1, v0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Explicitly instantiate the three supported key sizes. */
|
||||
template class AesImpl<16>;
|
||||
template class AesImpl<24>;
|
||||
template class AesImpl<32>;
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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.hpp>
|
||||
#include <x86intrin.h>
|
||||
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
extern const bool g_is_aes_ni_available;
|
||||
|
||||
ALWAYS_INLINE bool IsAesNiAvailable() {
|
||||
return g_is_aes_ni_available;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
void BigNum::ImportImpl(Word *out, size_t out_size, const u8 *src, size_t src_size) {
|
||||
size_t octet_ofs = src_size;
|
||||
size_t word_ofs = 0;
|
||||
|
||||
/* Parse octets into words. */
|
||||
while (word_ofs < out_size && octet_ofs > 0) {
|
||||
Word w = 0;
|
||||
for (size_t shift = 0; octet_ofs > 0 && shift < BITSIZEOF(Word); shift += BITSIZEOF(u8)) {
|
||||
w |= static_cast<Word>(src[--octet_ofs]) << shift;
|
||||
}
|
||||
out[word_ofs++] = w;
|
||||
}
|
||||
|
||||
/* Zero-fill upper words. */
|
||||
while (word_ofs < out_size) {
|
||||
out[word_ofs++] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void BigNum::ExportImpl(u8 *out, size_t out_size, const Word *src, size_t src_size) {
|
||||
size_t octet_ofs = out_size;
|
||||
|
||||
/* Parse words into octets. */
|
||||
for (size_t word_ofs = 0; word_ofs < src_size && octet_ofs > 0; word_ofs++) {
|
||||
const Word w = src[word_ofs];
|
||||
for (size_t shift = 0; octet_ofs > 0 && shift < BITSIZEOF(Word); shift += BITSIZEOF(u8)) {
|
||||
out[--octet_ofs] = static_cast<u8>(w >> shift);
|
||||
}
|
||||
}
|
||||
|
||||
/* Zero-clear remaining octets. */
|
||||
while (octet_ofs > 0) {
|
||||
out[--octet_ofs] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t BigNum::GetSize() const {
|
||||
if (m_num_words == 0) {
|
||||
return 0;
|
||||
}
|
||||
static_assert(sizeof(Word) == 4);
|
||||
|
||||
size_t size = m_num_words * sizeof(Word);
|
||||
const Word last = m_words[m_num_words - 1];
|
||||
AMS_ASSERT(last != 0);
|
||||
if (last >= 0x01000000u) {
|
||||
return size - 0;
|
||||
} else if (last >= 0x00010000u) {
|
||||
return size - 1;
|
||||
} else if (last >= 0x00000100u) {
|
||||
return size - 2;
|
||||
} else {
|
||||
return size - 3;
|
||||
}
|
||||
}
|
||||
|
||||
bool BigNum::Import(const void *src, size_t src_size) {
|
||||
AMS_ASSERT((src != nullptr) || (src_size != 0));
|
||||
|
||||
/* Ignore leading zeroes. */
|
||||
const u8 *data = static_cast<const u8 *>(src);
|
||||
while (src_size > 0 && *data == 0) {
|
||||
++data;
|
||||
--src_size;
|
||||
}
|
||||
|
||||
/* Ensure we have space for the number. */
|
||||
AMS_ASSERT(src_size <= m_max_words * sizeof(Word));
|
||||
if (AMS_UNLIKELY(!(src_size <= m_max_words * sizeof(Word)))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Import. */
|
||||
m_num_words = util::AlignUp(src_size, sizeof(Word)) / sizeof(Word);
|
||||
|
||||
ImportImpl(m_words, m_max_words, data, src_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
void BigNum::Export(void *dst, size_t dst_size) {
|
||||
AMS_ASSERT(dst_size >= this->GetSize());
|
||||
ExportImpl(static_cast<u8 *>(dst), dst_size, m_words, m_num_words);
|
||||
}
|
||||
|
||||
bool BigNum::ExpMod(void *dst, const void *src, size_t size, const BigNum &exp, u32 *work_buf, size_t work_buf_size) const {
|
||||
/* Can't exponentiate with or about zero. */
|
||||
if (this->IsZero() || exp.IsZero()) {
|
||||
return false;
|
||||
}
|
||||
AMS_ASSERT(size == this->GetSize());
|
||||
|
||||
/* Create an allocator. */
|
||||
WordAllocator allocator(work_buf, work_buf_size / sizeof(Word));
|
||||
ON_SCOPE_EXIT { ClearMemory(work_buf, allocator.GetMaxUsedSize()); };
|
||||
|
||||
/* Create a BigNum for the signature. */
|
||||
BigNum signature;
|
||||
auto signature_words = allocator.Allocate(size / sizeof(Word));
|
||||
if (!signature_words.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Import data for the signature. */
|
||||
signature.ReserveStatic(signature_words.GetBuffer(), signature_words.GetCount());
|
||||
if (!signature.Import(src, size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Perform the exponentiation. */
|
||||
if (!ExpMod(signature.m_words, signature.m_words, exp.m_words, exp.m_num_words, m_words, m_num_words, std::addressof(allocator))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* We succeeded, so export. */
|
||||
signature.UpdateCount();
|
||||
signature.Export(dst, size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void BigNum::ClearToZero() {
|
||||
std::memset(m_words, 0, m_num_words * sizeof(Word));
|
||||
}
|
||||
|
||||
void BigNum::UpdateCount() {
|
||||
m_num_words = CountWords(m_words, m_max_words);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,490 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr ALWAYS_INLINE BigNum::Word GetTop2Bits(BigNum::Word w) {
|
||||
return (w >> (BigNum::BitsPerWord - 2)) & 0x3u;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE void MultWord(BigNum::Word *dst, BigNum::Word lhs, BigNum::Word rhs) {
|
||||
static_assert(sizeof(BigNum::DoubleWord) == sizeof(BigNum::Word) * 2);
|
||||
BigNum::DoubleWord result = static_cast<BigNum::DoubleWord>(lhs) * static_cast<BigNum::DoubleWord>(rhs);
|
||||
dst[0] = static_cast<BigNum::Word>(result & ~BigNum::Word());
|
||||
dst[1] = static_cast<BigNum::Word>(result >> BITSIZEOF(BigNum::Word));
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE BigNum::HalfWord GetUpperHalf(BigNum::Word word) {
|
||||
static_assert(sizeof(BigNum::Word) == sizeof(BigNum::HalfWord) * 2);
|
||||
return static_cast<BigNum::HalfWord>((word >> BITSIZEOF(BigNum::HalfWord)) & ~BigNum::HalfWord());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE BigNum::HalfWord GetLowerHalf(BigNum::Word word) {
|
||||
static_assert(sizeof(BigNum::Word) == sizeof(BigNum::HalfWord) * 2);
|
||||
return static_cast<BigNum::HalfWord>(word & ~BigNum::HalfWord());
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE BigNum::Word ToUpperHalf(BigNum::HalfWord half) {
|
||||
static_assert(sizeof(BigNum::Word) == sizeof(BigNum::HalfWord) * 2);
|
||||
return static_cast<BigNum::Word>(half) << BITSIZEOF(BigNum::HalfWord);
|
||||
}
|
||||
|
||||
[[maybe_unused]] constexpr ALWAYS_INLINE BigNum::Word ToLowerHalf(BigNum::HalfWord half) {
|
||||
static_assert(sizeof(BigNum::Word) == sizeof(BigNum::HalfWord) * 2);
|
||||
return static_cast<BigNum::Word>(half);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE BigNum::Word DivWord(const BigNum::Word *w, BigNum::Word div) {
|
||||
using Word = BigNum::Word;
|
||||
using HalfWord = BigNum::HalfWord;
|
||||
|
||||
Word work[2] = { w[0], w[1] };
|
||||
HalfWord r_hi = 0, r_lo = 0;
|
||||
|
||||
HalfWord d_hi = GetUpperHalf(div);
|
||||
HalfWord d_lo = GetLowerHalf(div);
|
||||
|
||||
if (d_hi == BigNum::MaxHalfWord) {
|
||||
r_hi = GetUpperHalf(work[1]);
|
||||
} else {
|
||||
r_hi = GetLowerHalf(work[1] / (d_hi + 1));
|
||||
}
|
||||
|
||||
{
|
||||
const Word hh = static_cast<Word>(r_hi) * static_cast<Word>(d_hi);
|
||||
const Word hl = static_cast<Word>(r_hi) * static_cast<Word>(d_lo);
|
||||
|
||||
const Word uhl = ToUpperHalf(static_cast<HalfWord>(hl));
|
||||
if ((work[0] -= uhl) > (BigNum::MaxWord - uhl)) {
|
||||
work[1]--;
|
||||
}
|
||||
work[1] -= GetUpperHalf(hl);
|
||||
work[1] -= hh;
|
||||
|
||||
const Word udl = ToUpperHalf(d_lo);
|
||||
while (work[1] > d_hi || (work[1] == d_hi && work[0] >= udl)) {
|
||||
if ((work[0] -= udl) > (BigNum::MaxWord - udl)) {
|
||||
work[1]--;
|
||||
}
|
||||
work[1] -= d_hi;
|
||||
r_hi++;
|
||||
}
|
||||
}
|
||||
|
||||
if (d_hi == BigNum::MaxHalfWord) {
|
||||
r_lo = GetLowerHalf(work[1]);
|
||||
} else {
|
||||
r_lo = GetLowerHalf((ToUpperHalf(static_cast<HalfWord>(work[1])) + GetUpperHalf(work[0])) / (d_hi + 1));
|
||||
}
|
||||
|
||||
{
|
||||
const Word ll = static_cast<Word>(r_lo) * static_cast<Word>(d_lo);
|
||||
const Word lh = static_cast<Word>(r_lo) * static_cast<Word>(d_hi);
|
||||
|
||||
if ((work[0] -= ll) > (BigNum::MaxWord - ll)) {
|
||||
work[1]--;
|
||||
}
|
||||
|
||||
const Word ulh = ToUpperHalf(static_cast<HalfWord>(lh));
|
||||
if ((work[0] -= ulh) > (BigNum::MaxWord - ulh)) {
|
||||
work[1]--;
|
||||
}
|
||||
work[1] -= GetUpperHalf(lh);
|
||||
|
||||
while ((work[1] > 0) || (work[1] == 0 && work[0] >= div)) {
|
||||
if ((work[0] -= div) > (BigNum::MaxWord - div)) {
|
||||
work[1]--;
|
||||
}
|
||||
r_lo++;
|
||||
}
|
||||
}
|
||||
|
||||
return ToUpperHalf(r_hi) + r_lo;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool BigNum::IsZero(const Word *w, size_t num_words) {
|
||||
for (size_t i = 0; i < num_words; i++) {
|
||||
if (w[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int BigNum::Compare(const Word *lhs, const Word *rhs, size_t num_words) {
|
||||
for (s32 i = static_cast<s32>(num_words) - 1; i >= 0; i--) {
|
||||
if (lhs[i] > rhs[i]) {
|
||||
return 1;
|
||||
} else if (lhs[i] < rhs[i]) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t BigNum::CountWords(const Word *w, size_t num_words) {
|
||||
s32 i = static_cast<s32>(num_words) - 1;
|
||||
while (i >= 0 && !w[i]) {
|
||||
i--;
|
||||
}
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
size_t BigNum::CountSignificantBits(Word w) {
|
||||
size_t i;
|
||||
for (i = 0; i < BitsPerWord && w != 0; i++) {
|
||||
w >>= 1;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
void BigNum::ClearToZero(Word *w, size_t num_words) {
|
||||
for (size_t i = 0; i < num_words; i++) {
|
||||
w[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void BigNum::SetToWord(Word *w, size_t num_words, Word v) {
|
||||
ClearToZero(w, num_words);
|
||||
w[0] = v;
|
||||
}
|
||||
|
||||
void BigNum::Copy(Word *dst, const Word *src, size_t num_words) {
|
||||
for (size_t i = 0; i < num_words; i++) {
|
||||
dst[i] = src[i];
|
||||
}
|
||||
}
|
||||
|
||||
BigNum::Word BigNum::LeftShift(Word *dst, const Word *w, size_t num_words, const size_t shift) {
|
||||
if (shift >= BitsPerWord) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const size_t invshift = BitsPerWord - shift;
|
||||
Word carry = 0;
|
||||
for (size_t i = 0; i < num_words; i++) {
|
||||
const Word cur = w[i];
|
||||
dst[i] = (cur << shift) | carry;
|
||||
carry = shift ? (cur >> invshift) : 0;
|
||||
}
|
||||
|
||||
return carry;
|
||||
}
|
||||
|
||||
BigNum::Word BigNum::RightShift(Word *dst, const Word *w, size_t num_words, const size_t shift) {
|
||||
if (shift >= BitsPerWord) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const size_t invshift = BitsPerWord - shift;
|
||||
Word carry = 0;
|
||||
for (s32 i = static_cast<s32>(num_words) - 1; i >= 0; i--) {
|
||||
const Word cur = w[i];
|
||||
dst[i] = (cur >> shift) | carry;
|
||||
carry = shift ? (cur << invshift) : 0;
|
||||
}
|
||||
|
||||
return carry;
|
||||
}
|
||||
|
||||
BigNum::Word BigNum::MultSub(Word *dst, const Word *w, const Word *v, size_t num_words, Word mult) {
|
||||
/* If multiplying by zero, nothing to do. */
|
||||
if (mult == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Word borrow = 0, work[2];
|
||||
for (size_t i = 0; i < num_words; i++) {
|
||||
/* Multiply, calculate borrow for next. */
|
||||
MultWord(work, mult, v[i]);
|
||||
if ((dst[i] = (w[i] - borrow)) > (MaxWord - borrow)) {
|
||||
borrow = 1;
|
||||
} else {
|
||||
borrow = 0;
|
||||
}
|
||||
|
||||
if ((dst[i] -= work[0]) > (MaxWord - work[0])) {
|
||||
borrow++;
|
||||
}
|
||||
borrow += work[1];
|
||||
}
|
||||
|
||||
return borrow;
|
||||
}
|
||||
|
||||
bool BigNum::ExpMod(Word *dst, const Word *src, const Word *exp, size_t exp_words, const Word *mod, size_t mod_words, WordAllocator *allocator) {
|
||||
/* Nintendo uses an algorithm that relies on powers of exp. */
|
||||
bool needs_exp[4] = {};
|
||||
if (exp_words > 1) {
|
||||
needs_exp[2] = true;
|
||||
needs_exp[3] = true;
|
||||
} else {
|
||||
Word exp_w = exp[0];
|
||||
|
||||
for (size_t i = 0; i < BitsPerWord / 2; i++) {
|
||||
/* Nintendo at each step determines needed exponent from a pair of two bits. */
|
||||
needs_exp[exp_w & 0x3u] = true;
|
||||
exp_w >>= 2;
|
||||
}
|
||||
|
||||
if (needs_exp[3]) {
|
||||
needs_exp[2] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate space for powers 1, 2, 3. */
|
||||
auto power_1 = allocator->Allocate(mod_words);
|
||||
auto power_2 = allocator->Allocate(mod_words);
|
||||
auto power_3 = allocator->Allocate(mod_words);
|
||||
if (!(power_1.IsValid() && power_2.IsValid() && power_3.IsValid())) {
|
||||
return false;
|
||||
}
|
||||
decltype(power_1)* powers[3] = { std::addressof(power_1), std::addressof(power_2), std::addressof(power_3) };
|
||||
|
||||
/* Set the powers of src. */
|
||||
Copy(power_1.GetBuffer(), src, mod_words);
|
||||
if (needs_exp[2]) {
|
||||
if (!MultMod(power_2.GetBuffer(), power_1.GetBuffer(), src, mod, mod_words, allocator)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (needs_exp[3]) {
|
||||
if (!MultMod(power_3.GetBuffer(), power_2.GetBuffer(), src, mod, mod_words, allocator)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate space to work. */
|
||||
auto work = allocator->Allocate(mod_words);
|
||||
if (!work.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
SetToWord(work.GetBuffer(), work.GetCount(), 1);
|
||||
|
||||
/* Ensure we're working with the correct exponent word count. */
|
||||
exp_words = CountWords(exp, exp_words);
|
||||
|
||||
for (s32 i = static_cast<s32>(exp_words - 1); i >= 0; i--) {
|
||||
Word cur_word = exp[i];
|
||||
size_t cur_bits = BitsPerWord;
|
||||
|
||||
/* Remove leading zeroes in first word. */
|
||||
if (i == static_cast<s32>(exp_words - 1)) {
|
||||
while (!GetTop2Bits(cur_word)) {
|
||||
cur_word <<= 2;
|
||||
cur_bits -= 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Compute current modular multiplicative step. */
|
||||
for (size_t j = 0; j < cur_bits; j += 2, cur_word <<= 2) {
|
||||
/* Exponentiate current work to the 4th power. */
|
||||
if (!MultMod(work.GetBuffer(), work.GetBuffer(), work.GetBuffer(), mod, mod_words, allocator)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!MultMod(work.GetBuffer(), work.GetBuffer(), work.GetBuffer(), mod, mod_words, allocator)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (const Word top = GetTop2Bits(cur_word)) {
|
||||
if (!MultMod(work.GetBuffer(), work.GetBuffer(), powers[top - 1]->GetBuffer(), mod, mod_words, allocator)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy work to output. */
|
||||
Copy(dst, work.GetBuffer(), mod_words);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BigNum::MultMod(Word *dst, const Word *src, const Word *mult, const Word *mod, size_t num_words, WordAllocator *allocator) {
|
||||
/* Allocate work. */
|
||||
auto work = allocator->Allocate(2 * num_words);
|
||||
if (!work.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Multiply. */
|
||||
if (!Mult(work.GetBuffer(), src, mult, num_words, allocator)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Mod. */
|
||||
if (!Mod(dst, work.GetBuffer(), 2 * num_words, mod, num_words, allocator)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BigNum::Mod(Word *dst, const Word *src, size_t src_words, const Word *mod, size_t mod_words, WordAllocator *allocator) {
|
||||
/* Allocate work. */
|
||||
auto work = allocator->Allocate(src_words);
|
||||
if (!work.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DivMod(work.GetBuffer(), dst, src, src_words, mod, mod_words, allocator)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BigNum::DivMod(Word *quot, Word *rem, const Word *top, size_t top_words, const Word *bot, size_t bot_words, WordAllocator *allocator) {
|
||||
/* Allocate work. */
|
||||
auto top_work = allocator->Allocate(top_words + 1);
|
||||
auto bot_work = allocator->Allocate(bot_words);
|
||||
if (!(top_work.IsValid() && bot_work.IsValid())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Prevent division by zero. */
|
||||
size_t bot_work_words = CountWords(bot, bot_words);
|
||||
if (bot_work_words == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ClearToZero(quot, top_words);
|
||||
ClearToZero(top_work.GetBuffer(), bot_work_words);
|
||||
|
||||
/* Align to edges. */
|
||||
const size_t shift = BitsPerWord - CountSignificantBits(bot[bot_work_words - 1]);
|
||||
top_work.GetBuffer()[top_words] = LeftShift(top_work.GetBuffer(), top, top_words, shift);
|
||||
LeftShift(bot_work.GetBuffer(), bot, bot_work_words, shift);
|
||||
const Word tb = bot_work.GetBuffer()[bot_work_words - 1];
|
||||
|
||||
/* Repeatedly div + sub. */
|
||||
for (s32 i = (top_words - bot_work_words); i >= 0; i--) {
|
||||
Word cur_word;
|
||||
if (tb == MaxWord) {
|
||||
cur_word = top_work.GetBuffer()[i + bot_work_words];
|
||||
} else {
|
||||
cur_word = DivWord(top_work.GetBuffer() + i + bot_work_words - 1, tb + 1);
|
||||
}
|
||||
top_work.GetBuffer()[i + bot_work_words] -= MultSub(top_work.GetBuffer() + i, top_work.GetBuffer() + i, bot_work.GetBuffer(), bot_work_words, cur_word);
|
||||
|
||||
while (top_work.GetBuffer()[i + bot_work_words] || Compare(top_work.GetBuffer() + i, bot_work.GetBuffer(), bot_work_words) >= 0) {
|
||||
cur_word++;
|
||||
top_work.GetBuffer()[i + bot_work_words] -= Sub(top_work.GetBuffer() + i, top_work.GetBuffer() + i, bot_work.GetBuffer(), bot_work_words);
|
||||
}
|
||||
quot[i] = cur_word;
|
||||
}
|
||||
|
||||
/* Calculate remainder. */
|
||||
ClearToZero(rem, bot_words);
|
||||
RightShift(rem, top_work.GetBuffer(), bot_work_words, shift);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BigNum::Mult(Word *dst, const Word *lhs, const Word *rhs, size_t num_words, WordAllocator *allocator) {
|
||||
/* Allocate work. */
|
||||
auto work = allocator->Allocate(2 * num_words);
|
||||
if (!work.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
ClearToZero(work.GetBuffer(), work.GetCount());
|
||||
|
||||
/* Repeatedly add and multiply. */
|
||||
const size_t lhs_words = CountWords(lhs, num_words);
|
||||
const size_t rhs_words = CountWords(rhs, num_words);
|
||||
|
||||
for (size_t i = 0; i < lhs_words; i++) {
|
||||
work.GetBuffer()[i + rhs_words] += MultAdd(work.GetBuffer() + i, rhs, rhs_words, lhs[i]);
|
||||
}
|
||||
|
||||
/* Copy to output. */
|
||||
Copy(dst, work.GetBuffer(), work.GetCount());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if !defined(ATMOSPHERE_ARCH_ARM64)
|
||||
BigNum::Word BigNum::Add(Word *dst, const Word *lhs, const Word *rhs, size_t num_words) {
|
||||
Word carry = 0;
|
||||
|
||||
for (size_t i = 0; i < num_words; ++i) {
|
||||
Word v;
|
||||
if ((v = lhs[i] + carry) < carry) {
|
||||
v = rhs[i];
|
||||
} else if ((v += rhs[i]) < rhs[i]) {
|
||||
carry = 1;
|
||||
} else {
|
||||
carry = 0;
|
||||
}
|
||||
|
||||
dst[i] = v;
|
||||
}
|
||||
|
||||
return carry;
|
||||
}
|
||||
|
||||
BigNum::Word BigNum::Sub(Word *dst, const Word *lhs, const Word *rhs, size_t num_words) {
|
||||
Word borrow = 0;
|
||||
|
||||
for (size_t i = 0; i < num_words; ++i) {
|
||||
Word v;
|
||||
if ((v = lhs[i] - borrow) > (BigNum::MaxWord - borrow)) {
|
||||
v = BigNum::MaxWord - rhs[i];
|
||||
} else if ((v -= rhs[i]) > (BigNum::MaxWord - rhs[i])) {
|
||||
borrow = 1;
|
||||
} else {
|
||||
borrow = 0;
|
||||
}
|
||||
|
||||
dst[i] = v;
|
||||
}
|
||||
|
||||
return borrow;
|
||||
}
|
||||
|
||||
BigNum::Word BigNum::MultAdd(Word *dst, const Word *w, size_t num_words, Word mult) {
|
||||
/* If multiplying by zero, nothing to do. */
|
||||
if (mult == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Word carry = 0, work[2];
|
||||
for (size_t i = 0; i < num_words; i++) {
|
||||
/* Multiply, calculate carry for next. */
|
||||
MultWord(work, mult, w[i]);
|
||||
if ((dst[i] += carry) < carry) {
|
||||
carry = 1;
|
||||
} else {
|
||||
carry = 0;
|
||||
}
|
||||
|
||||
if ((dst[i] += work[0]) < work[0]) {
|
||||
carry++;
|
||||
}
|
||||
carry += work[1];
|
||||
}
|
||||
|
||||
return carry;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -1,294 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
|
||||
/* ams::crypto::impl::BigNum::Add(Word *dst, const Word *lhs, const Word *rhs, size_t num_words) */
|
||||
#if !defined(ATMOSPHERE_OS_MACOS)
|
||||
.section .text._ZN3ams6crypto4impl6BigNum3AddEPjPKjS5_m, "ax", %progbits
|
||||
.global _ZN3ams6crypto4impl6BigNum3AddEPjPKjS5_m
|
||||
.type _ZN3ams6crypto4impl6BigNum3AddEPjPKjS5_m, %function
|
||||
#else
|
||||
.text
|
||||
.global _ZN3ams6crypto4impl6BigNum3AddEPjPKjS5_m
|
||||
#endif
|
||||
.balign 0x10
|
||||
_ZN3ams6crypto4impl6BigNum3AddEPjPKjPKjm:
|
||||
/* Check if we have anything to do at all. */
|
||||
msr nzcv, xzr
|
||||
cbz x3, 7f
|
||||
|
||||
/* Save registers. */
|
||||
stp x16, x17, [sp, #-16]!
|
||||
stp xzr, x19, [sp, #-16]!
|
||||
stp x20, x21, [sp, #-16]!
|
||||
|
||||
/* Check if we have less than 16 words to process. */
|
||||
lsr x20, x3, #4
|
||||
cbz x20, 2f
|
||||
|
||||
sub x3, x3, x20, lsl #4
|
||||
|
||||
1: /* Process 16 words at a time. */
|
||||
/* NOTE: Nintendo uses X18 here, we will use X21 for EL1+ compat. */
|
||||
ldp x4, x5, [x1], #16
|
||||
ldp x12, x13, [x2], #16
|
||||
ldp x6, x7, [x1], #16
|
||||
ldp x14, x15, [x2], #16
|
||||
ldp x8, x9, [x1], #16
|
||||
ldp x16, x17, [x2], #16
|
||||
ldp x10, x11, [x1], #16
|
||||
ldp x21, x19, [x2], #16
|
||||
|
||||
adcs x4, x4, x12
|
||||
adcs x5, x5, x13
|
||||
stp x4, x5, [x0], #16
|
||||
|
||||
adcs x6, x6, x14
|
||||
adcs x7, x7, x15
|
||||
stp x6, x7, [x0], #16
|
||||
|
||||
adcs x8, x8, x16
|
||||
adcs x9, x9, x17
|
||||
stp x8, x9, [x0], #16
|
||||
|
||||
adcs x10, x10, x21
|
||||
adcs x11, x11, x19
|
||||
stp x10, x11, [x0], #16
|
||||
|
||||
sub x20, x20, #1
|
||||
cbnz x20, 1b
|
||||
|
||||
2: /* We have less than 16 words to process. */
|
||||
lsr x15, x3, #2
|
||||
cbz x15, 4f
|
||||
|
||||
sub x3, x3, x15, lsl #2
|
||||
|
||||
3: /* Process 4 words at a time. */
|
||||
ldp x4, x5, [x1], #16
|
||||
ldp x8, x9, [x2], #16
|
||||
|
||||
sub x15, x15, #1
|
||||
|
||||
adcs x4, x4, x8
|
||||
adcs x5, x5, x9
|
||||
|
||||
stp x4, x5, [x0], #16
|
||||
|
||||
cbnz x15, 3b
|
||||
|
||||
4: /* We have less than 4 words to process. */
|
||||
cbz x3, 6f
|
||||
|
||||
5: /* Process 1 word at a time. */
|
||||
ldr w4, [x1], #4
|
||||
ldr w8, [x2], #4
|
||||
adcs w4, w4, w8
|
||||
str w4, [x0], #4
|
||||
|
||||
sub x3, x3, #1
|
||||
cbnz x3, 5b
|
||||
|
||||
6: /* Restore registers we used while adding. */
|
||||
ldp x20, x21, [sp], #16
|
||||
ldp xzr, x19, [sp], #16
|
||||
ldp x16, x17, [sp], #16
|
||||
|
||||
7: /* We're done. */
|
||||
adc x0, xzr, xzr
|
||||
ret
|
||||
|
||||
/* ams::crypto::impl::BigNum::Sub(Word *dst, const Word *lhs, const Word *rhs, size_t num_words) */
|
||||
#if !defined(ATMOSPHERE_OS_MACOS)
|
||||
.section .text._ZN3ams6crypto4impl6BigNum3SubEPjPKjS5_m, "ax", %progbits
|
||||
.global _ZN3ams6crypto4impl6BigNum3SubEPjPKjS5_m
|
||||
.type _ZN3ams6crypto4impl6BigNum3SubEPjPKjS5_m, %function
|
||||
#else
|
||||
.text
|
||||
.global _ZN3ams6crypto4impl6BigNum3SubEPjPKjS5_m
|
||||
#endif
|
||||
.balign 0x10
|
||||
_ZN3ams6crypto4impl6BigNum3SubEPjPKjS5_m:
|
||||
/* Check if we have anything to do at all. */
|
||||
mov x4, #0x20000000
|
||||
msr nzcv, x4
|
||||
cbz x3, 7f
|
||||
|
||||
/* Save registers. */
|
||||
stp x16, x17, [sp, #-16]!
|
||||
stp xzr, x19, [sp, #-16]!
|
||||
stp x20, x21, [sp, #-16]!
|
||||
|
||||
/* Check if we have less than 16 words to process. */
|
||||
lsr x20, x3, #4
|
||||
cbz x20, 2f
|
||||
|
||||
sub x3, x3, x20, lsl #4
|
||||
|
||||
1: /* Process 16 words at a time. */
|
||||
/* NOTE: Nintendo uses X18 here, we will use X21 for EL1+ compat. */
|
||||
ldp x4, x5, [x1], #16
|
||||
ldp x12, x13, [x2], #16
|
||||
ldp x6, x7, [x1], #16
|
||||
ldp x14, x15, [x2], #16
|
||||
ldp x8, x9, [x1], #16
|
||||
ldp x16, x17, [x2], #16
|
||||
ldp x10, x11, [x1], #16
|
||||
ldp x21, x19, [x2], #16
|
||||
|
||||
sbcs x4, x4, x12
|
||||
sbcs x5, x5, x13
|
||||
stp x4, x5, [x0], #16
|
||||
|
||||
sbcs x6, x6, x14
|
||||
sbcs x7, x7, x15
|
||||
stp x6, x7, [x0], #16
|
||||
|
||||
sbcs x8, x8, x16
|
||||
sbcs x9, x9, x17
|
||||
stp x8, x9, [x0], #16
|
||||
|
||||
sbcs x10, x10, x21
|
||||
sbcs x11, x11, x19
|
||||
stp x10, x11, [x0], #16
|
||||
|
||||
sub x20, x20, #1
|
||||
cbnz x20, 1b
|
||||
|
||||
2: /* We have less than 16 words to process. */
|
||||
lsr x15, x3, #2
|
||||
cbz x15, 4f
|
||||
|
||||
sub x3, x3, x15, lsl #2
|
||||
|
||||
3: /* Process 4 words at a time. */
|
||||
ldp x4, x5, [x1], #16
|
||||
ldp x8, x9, [x2], #16
|
||||
|
||||
sub x15, x15, #1
|
||||
|
||||
sbcs x4, x4, x8
|
||||
sbcs x5, x5, x9
|
||||
|
||||
stp x4, x5, [x0], #16
|
||||
|
||||
cbnz x15, 3b
|
||||
|
||||
4: /* We have less than 4 words to process. */
|
||||
cbz x3, 6f
|
||||
|
||||
5: /* Process 1 word at a time. */
|
||||
ldr w4, [x1], #4
|
||||
ldr w8, [x2], #4
|
||||
sbcs w4, w4, w8
|
||||
str w4, [x0], #4
|
||||
|
||||
sub x3, x3, #1
|
||||
cbnz x3, 5b
|
||||
|
||||
6: /* Restore registers we used while adding. */
|
||||
ldp x20, x21, [sp], #16
|
||||
ldp xzr, x19, [sp], #16
|
||||
ldp x16, x17, [sp], #16
|
||||
|
||||
7: /* We're done. */
|
||||
cinc x0, xzr, cc
|
||||
ret
|
||||
|
||||
/* ams::crypto::impl::BigNum::MultAdd(Word *dst, const Word *w, size_t num_words, Word mult) */
|
||||
#if !defined(ATMOSPHERE_OS_MACOS)
|
||||
.section .text._ZN3ams6crypto4impl6BigNum7MultAddEPjPKjmj, "ax", %progbits
|
||||
.global _ZN3ams6crypto4impl6BigNum7MultAddEPjPKjmj
|
||||
.type _ZN3ams6crypto4impl6BigNum7MultAddEPjPKjmj, %function
|
||||
#else
|
||||
.text
|
||||
.global _ZN3ams6crypto4impl6BigNum7MultAddEPjPKjmj
|
||||
#endif
|
||||
.balign 0x10
|
||||
_ZN3ams6crypto4impl6BigNum7MultAddEPjPKjmj:
|
||||
/* Check if we have anything to do at all. */
|
||||
mov x15, xzr
|
||||
cbz x2, 5f
|
||||
|
||||
/* Check if we have less than four words to process. */
|
||||
lsr x6, x2, #2
|
||||
cbz x6, 2f
|
||||
|
||||
/* We have more than four words to process. */
|
||||
sub x2, x2, x6, lsl #2
|
||||
stp x16, x17, [sp, #-16]!
|
||||
|
||||
1: /* Loop processing four words at a time. */
|
||||
ldp w4, w5, [x1], #8
|
||||
ldp w16, w7, [x1], #8
|
||||
ldp w8, w9, [x0]
|
||||
ldp w10, w11, [x0, #8]
|
||||
|
||||
umaddl x4, w3, w4, x8
|
||||
umaddl x5, w3, w5, x9
|
||||
umaddl x16, w3, w16, x10
|
||||
umaddl x7, w3, w7, x11
|
||||
|
||||
add x12, x4, x15, lsr #32
|
||||
add x13, x5, x12, lsr #32
|
||||
stp w12, w13, [x0], #8
|
||||
|
||||
add x14, x16, x13, lsr #32
|
||||
add x15, x7, x14, lsr #32
|
||||
stp w14, w15, [x0], #8
|
||||
|
||||
sub x6, x6, #1
|
||||
cbnz x6, 1b
|
||||
|
||||
ldp x16, x17, [sp], #16
|
||||
|
||||
2: /* We have less than four words. Check if we have less than two. */
|
||||
lsr x6, x2, #1
|
||||
cbz x6, 4f
|
||||
|
||||
/* We have more than two words to process. */
|
||||
sub x2, x2, x6, lsl #1
|
||||
|
||||
3: /* Loop processing two words at a time. */
|
||||
ldp w4, w5, [x1], #8
|
||||
ldp w8, w9, [x0]
|
||||
|
||||
umaddl x4, w3, w4, x8
|
||||
umaddl x5, w3, w5, x9
|
||||
|
||||
sub x6, x6, #1
|
||||
|
||||
add x14, x4, x15, lsr #32
|
||||
add x15, x5, x14, lsr #32
|
||||
|
||||
stp w14, w15, [x0], #8
|
||||
|
||||
cbnz x6, 3b
|
||||
|
||||
4: /* We have less than two words to process. */
|
||||
cbz x2, 5f
|
||||
|
||||
/* We have one word to process. */
|
||||
ldr w4, [x1], #4
|
||||
ldr w8, [x0]
|
||||
|
||||
umaddl x4, w3, w4, x8
|
||||
add x15, x4, x15, lsr #32
|
||||
|
||||
str w15, [x0], #4
|
||||
|
||||
5: /* We're done. */
|
||||
lsr x0, x15, #32
|
||||
ret
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
#include "crypto_update_impl.hpp"
|
||||
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
void CbcMacImpl::UpdateGeneric(const void *data, size_t size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(m_state == State_Initialized);
|
||||
|
||||
/* Update. */
|
||||
UpdateImpl<void>(this, data, size);
|
||||
}
|
||||
|
||||
void CbcMacImpl::ProcessBlocksGeneric(const void *data, size_t num_blocks) {
|
||||
/* If we have a block remaining, process it. */
|
||||
if (m_buffered_bytes == BlockSize) {
|
||||
this->ProcessBlock(m_buffer);
|
||||
m_buffered_bytes = 0;
|
||||
}
|
||||
|
||||
/* Process blocks. */
|
||||
const u8 *data8 = static_cast<const u8 *>(data);
|
||||
|
||||
u8 block[BlockSize];
|
||||
while ((--num_blocks) > 0) {
|
||||
for (size_t i = 0; i < BlockSize; ++i) {
|
||||
block[i] = data8[i] ^ m_mac[i];
|
||||
}
|
||||
|
||||
m_cipher_function(m_mac, block, m_cipher_context);
|
||||
|
||||
data8 += BlockSize;
|
||||
}
|
||||
|
||||
/* Process the last block. */
|
||||
std::memcpy(m_buffer, data8, BlockSize);
|
||||
m_buffered_bytes = BlockSize;
|
||||
}
|
||||
|
||||
template<>
|
||||
void CbcMacImpl::Update<AesEncryptor128>(const void *data, size_t size) {
|
||||
this->UpdateGeneric(data, size);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
void CbcMacImpl::ProcessBlock(const void *data) {
|
||||
/* Procses the block. */
|
||||
const u8 *data8 = static_cast<const u8 *>(data);
|
||||
|
||||
u8 block[BlockSize];
|
||||
for (size_t i = 0; i < BlockSize; ++i) {
|
||||
block[i] = data8[i] ^ m_mac[i];
|
||||
}
|
||||
|
||||
m_cipher_function(m_mac, block, m_cipher_context);
|
||||
}
|
||||
|
||||
void CbcMacImpl::ProcessPartialData(const void *data, size_t size) {
|
||||
/* Copy in the data. */
|
||||
std::memcpy(m_buffer + m_buffered_bytes, data, size);
|
||||
m_buffered_bytes += size;
|
||||
}
|
||||
|
||||
void CbcMacImpl::ProcessRemainingData(const void *data, size_t size) {
|
||||
/* If we have a block remaining, process it. */
|
||||
if (m_buffered_bytes == BlockSize) {
|
||||
this->ProcessBlock(m_buffer);
|
||||
m_buffered_bytes = 0;
|
||||
}
|
||||
|
||||
/* Copy the remaining data. */
|
||||
std::memcpy(m_buffer, data, size);
|
||||
m_buffered_bytes = size;
|
||||
}
|
||||
|
||||
void CbcMacImpl::GetMac(void *mac, size_t mac_size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(m_state == State_Initialized || m_state == State_Done);
|
||||
AMS_ASSERT(mac_size >= BlockSize);
|
||||
AMS_UNUSED(mac_size);
|
||||
|
||||
/* Ensure we're done. */
|
||||
if (m_state == State_Initialized) {
|
||||
if (m_buffered_bytes == BlockSize) {
|
||||
this->ProcessBlock(m_buffer);
|
||||
m_buffered_bytes = 0;
|
||||
}
|
||||
m_state = State_Done;
|
||||
}
|
||||
|
||||
/* Copy out the mac. */
|
||||
std::memcpy(mac, m_mac, sizeof(m_mac));
|
||||
}
|
||||
|
||||
void CbcMacImpl::MaskBufferedData(const void *data, size_t size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(m_buffered_bytes == BlockSize);
|
||||
AMS_ASSERT(size == BlockSize);
|
||||
AMS_UNUSED(size);
|
||||
|
||||
/* Mask the data. */
|
||||
for (size_t i = 0; i < BlockSize; ++i) {
|
||||
m_buffer[i] ^= static_cast<const u8 *>(data)[i];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,591 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
|
||||
#ifdef ATMOSPHERE_IS_STRATOSPHERE
|
||||
#include <arm_neon.h>
|
||||
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
/* Variable management macros. */
|
||||
#define DECLARE_ROUND_KEY_VAR(n) \
|
||||
const uint8x16_t round_key_##n = vld1q_u8(keys + (BlockSize * n))
|
||||
|
||||
#define AES_ENC_DEC_OUTPUT_THREE_BLOCKS() \
|
||||
[tmp0]"+w"(tmp0), [tmp1]"+w"(tmp1), [tmp2]"+w"(tmp2)
|
||||
|
||||
#define AES_ENC_DEC_OUTPUT_THREE_CTRS() \
|
||||
[ctr0]"+w"(ctr0), [ctr1]"+w"(ctr1), [ctr2]"+w"(ctr2)
|
||||
|
||||
#define AES_ENC_DEC_OUTPUT_ONE_BLOCK() \
|
||||
[tmp0]"+w"(tmp0)
|
||||
|
||||
#define AES_ENC_DEC_OUTPUT_ONE_CTR() \
|
||||
[ctr0]"+w"(ctr0)
|
||||
|
||||
#define CTR_INCREMENT_OUTPUT_HIGH_LOW() \
|
||||
[high]"=&r"(high), [low]"=&r"(low)
|
||||
|
||||
#define CTR_INCREMENT_OUTPUT_HIGH_LOW_TMP() \
|
||||
[high_tmp]"=&r"(high_tmp), [low_tmp]"=&r"(low_tmp)
|
||||
|
||||
#define CTR_INCREMENT_OUTPUT_HL_SINGLE_TMP() \
|
||||
[hl_tmp]"=&r"(hl_tmp)
|
||||
|
||||
#define AES_ENC_DEC_INPUT_ROUND_KEY(n) \
|
||||
[round_key_##n]"w"(round_key_##n)
|
||||
|
||||
/* AES Encryption macros. */
|
||||
#define AES_ENC_ROUND(n, i) \
|
||||
"aese %[tmp" #i "].16b, %[round_key_" #n "].16b\n" \
|
||||
"aesmc %[tmp" #i "].16b, %[tmp" #i "].16b\n"
|
||||
|
||||
#define AES_ENC_SECOND_LAST_ROUND(n, i) \
|
||||
"aese %[tmp" #i "].16b, %[round_key_" #n "].16b\n"
|
||||
|
||||
#define AES_ENC_LAST_ROUND(n, i) \
|
||||
"eor %[tmp" #i "].16b, %[tmp" #i "].16b, %[round_key_" #n "].16b\n"
|
||||
|
||||
namespace {
|
||||
|
||||
ALWAYS_INLINE uint8x16_t IncrementCounterOptimized(const uint8x16_t ctr) {
|
||||
uint8x16_t inc;
|
||||
uint64_t high, low;
|
||||
/* Use ASM. TODO: Better than using intrinsics? */
|
||||
__asm__ __volatile__ (
|
||||
"mov %[high], %[ctr].d[0]\n"
|
||||
"mov %[low], %[ctr].d[1]\n"
|
||||
"rev %[high], %[high]\n"
|
||||
"rev %[low], %[low]\n"
|
||||
"adds %[low], %[low], 1\n"
|
||||
"cinc %[high], %[high], cs\n"
|
||||
"rev %[high], %[high]\n"
|
||||
"rev %[low], %[low]\n"
|
||||
"mov %[inc].d[0], %[high]\n"
|
||||
"mov %[inc].d[1], %[low]\n"
|
||||
: [inc]"=w"(inc),
|
||||
CTR_INCREMENT_OUTPUT_HIGH_LOW()
|
||||
: [ctr]"w"(ctr)
|
||||
: "cc"
|
||||
);
|
||||
return inc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<>
|
||||
void CtrModeImpl<AesEncryptor128>::ProcessBlocks(u8 *dst, const u8 *src, size_t num_blocks) {
|
||||
/* Preload all round keys + iv into neon registers. */
|
||||
const u8 *keys = m_block_cipher->GetRoundKey();
|
||||
DECLARE_ROUND_KEY_VAR(0);
|
||||
DECLARE_ROUND_KEY_VAR(1);
|
||||
DECLARE_ROUND_KEY_VAR(2);
|
||||
DECLARE_ROUND_KEY_VAR(3);
|
||||
DECLARE_ROUND_KEY_VAR(4);
|
||||
DECLARE_ROUND_KEY_VAR(5);
|
||||
DECLARE_ROUND_KEY_VAR(6);
|
||||
DECLARE_ROUND_KEY_VAR(7);
|
||||
DECLARE_ROUND_KEY_VAR(8);
|
||||
DECLARE_ROUND_KEY_VAR(9);
|
||||
DECLARE_ROUND_KEY_VAR(10);
|
||||
uint8x16_t ctr0 = vld1q_u8(m_counter);
|
||||
uint64_t high, low;
|
||||
|
||||
/* Process three blocks at a time, when possible. */
|
||||
if (num_blocks >= 3) {
|
||||
/* Increment CTR twice. */
|
||||
uint8x16_t ctr1 = IncrementCounterOptimized(ctr0);
|
||||
uint8x16_t ctr2 = IncrementCounterOptimized(ctr1);
|
||||
uint64_t high_tmp, low_tmp;
|
||||
|
||||
while (num_blocks >= 3) {
|
||||
/* Read blocks in. Keep them in registers for XOR later. */
|
||||
const uint8x16_t block0 = vld1q_u8(src);
|
||||
src += AesEncryptor128::BlockSize;
|
||||
const uint8x16_t block1 = vld1q_u8(src);
|
||||
src += AesEncryptor128::BlockSize;
|
||||
const uint8x16_t block2 = vld1q_u8(src);
|
||||
src += AesEncryptor128::BlockSize;
|
||||
|
||||
/* We'll be encrypting the three CTRs. */
|
||||
uint8x16_t tmp0 = ctr0, tmp1 = ctr1, tmp2 = ctr2;
|
||||
|
||||
/* Actually do encryption, use optimized asm. */
|
||||
/* Interleave CTR calculations with AES ones, to mask latencies. */
|
||||
__asm__ __volatile__ (
|
||||
AES_ENC_ROUND(0, 0) "mov %[high], %[ctr2].d[0]\n"
|
||||
AES_ENC_ROUND(0, 1) "mov %[low], %[ctr2].d[1]\n"
|
||||
AES_ENC_ROUND(0, 2) "rev %[high], %[high]\n"
|
||||
AES_ENC_ROUND(1, 0) "rev %[low], %[low]\n"
|
||||
AES_ENC_ROUND(1, 1) "adds %[low], %[low], 1\n"
|
||||
AES_ENC_ROUND(1, 2) "cinc %[high], %[high], cs\n"
|
||||
AES_ENC_ROUND(2, 0) "rev %[high_tmp], %[high]\n"
|
||||
AES_ENC_ROUND(2, 1) "rev %[low_tmp], %[low]\n"
|
||||
AES_ENC_ROUND(2, 2) "mov %[ctr0].d[0], %[high_tmp]\n"
|
||||
AES_ENC_ROUND(3, 0) "mov %[ctr0].d[1], %[low_tmp]\n"
|
||||
AES_ENC_ROUND(3, 1) "adds %[low], %[low], 1\n"
|
||||
AES_ENC_ROUND(3, 2) "cinc %[high], %[high], cs\n"
|
||||
AES_ENC_ROUND(4, 0) "rev %[high_tmp], %[high]\n"
|
||||
AES_ENC_ROUND(4, 1) "rev %[low_tmp], %[low]\n"
|
||||
AES_ENC_ROUND(4, 2) "mov %[ctr1].d[0], %[high_tmp]\n"
|
||||
AES_ENC_ROUND(5, 0) "mov %[ctr1].d[1], %[low_tmp]\n"
|
||||
AES_ENC_ROUND(5, 1) "adds %[low], %[low], 1\n"
|
||||
AES_ENC_ROUND(5, 2) "cinc %[high], %[high], cs\n"
|
||||
AES_ENC_ROUND(6, 0) "rev %[high_tmp], %[high]\n"
|
||||
AES_ENC_ROUND(6, 1) "rev %[low_tmp], %[low]\n"
|
||||
AES_ENC_ROUND(6, 2) "mov %[ctr2].d[0], %[high_tmp]\n"
|
||||
AES_ENC_ROUND(7, 0) "mov %[ctr2].d[1], %[low_tmp]\n"
|
||||
AES_ENC_ROUND(7, 1)
|
||||
AES_ENC_ROUND(7, 2)
|
||||
AES_ENC_ROUND(8, 0) AES_ENC_ROUND(8, 1) AES_ENC_ROUND(8, 2)
|
||||
AES_ENC_SECOND_LAST_ROUND(9, 0) AES_ENC_SECOND_LAST_ROUND(9, 1) AES_ENC_SECOND_LAST_ROUND(9, 2)
|
||||
AES_ENC_LAST_ROUND(10, 0) AES_ENC_LAST_ROUND(10, 1) AES_ENC_LAST_ROUND(10, 2)
|
||||
: AES_ENC_DEC_OUTPUT_THREE_BLOCKS(),
|
||||
AES_ENC_DEC_OUTPUT_THREE_CTRS(),
|
||||
CTR_INCREMENT_OUTPUT_HIGH_LOW(),
|
||||
CTR_INCREMENT_OUTPUT_HIGH_LOW_TMP()
|
||||
: AES_ENC_DEC_INPUT_ROUND_KEY(0),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(1),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(2),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(3),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(4),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(5),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(6),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(7),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(8),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(9),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(10)
|
||||
: "cc"
|
||||
);
|
||||
|
||||
/* XOR blocks. */
|
||||
tmp0 = veorq_u8(block0, tmp0);
|
||||
tmp1 = veorq_u8(block1, tmp1);
|
||||
tmp2 = veorq_u8(block2, tmp2);
|
||||
|
||||
/* Store to output. */
|
||||
vst1q_u8(dst, tmp0);
|
||||
dst += AesEncryptor128::BlockSize;
|
||||
vst1q_u8(dst, tmp1);
|
||||
dst += AesEncryptor128::BlockSize;
|
||||
vst1q_u8(dst, tmp2);
|
||||
dst += AesEncryptor128::BlockSize;
|
||||
|
||||
num_blocks -= 3;
|
||||
}
|
||||
}
|
||||
|
||||
while (num_blocks >= 1) {
|
||||
/* Read block in, keep in register for XOR. */
|
||||
const uint8x16_t block0 = vld1q_u8(src);
|
||||
src += AesEncryptor128::BlockSize;
|
||||
|
||||
/* We'll be encrypting the CTR. */
|
||||
uint8x16_t tmp0 = ctr0;
|
||||
|
||||
/* Actually do encryption, use optimized asm. */
|
||||
/* Interleave CTR calculations with AES ones, to mask latencies. */
|
||||
__asm__ __volatile__ (
|
||||
AES_ENC_ROUND(0, 0) "mov %[high], %[ctr0].d[0]\n"
|
||||
AES_ENC_ROUND(1, 0) "mov %[low], %[ctr0].d[1]\n"
|
||||
AES_ENC_ROUND(2, 0) "rev %[high], %[high]\n"
|
||||
AES_ENC_ROUND(3, 0) "rev %[low], %[low]\n"
|
||||
AES_ENC_ROUND(4, 0) "adds %[low], %[low], 1\n"
|
||||
AES_ENC_ROUND(5, 0) "cinc %[high], %[high], cs\n"
|
||||
AES_ENC_ROUND(6, 0) "rev %[high], %[high]\n"
|
||||
AES_ENC_ROUND(7, 0) "rev %[low], %[low]\n"
|
||||
AES_ENC_ROUND(8, 0) "mov %[ctr0].d[0], %[high]\n"
|
||||
AES_ENC_SECOND_LAST_ROUND(9, 0) "mov %[ctr0].d[1], %[low]\n"
|
||||
AES_ENC_LAST_ROUND(10, 0)
|
||||
: AES_ENC_DEC_OUTPUT_ONE_BLOCK(),
|
||||
AES_ENC_DEC_OUTPUT_ONE_CTR(),
|
||||
CTR_INCREMENT_OUTPUT_HIGH_LOW()
|
||||
: AES_ENC_DEC_INPUT_ROUND_KEY(0),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(1),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(2),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(3),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(4),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(5),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(6),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(7),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(8),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(9),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(10)
|
||||
: "cc"
|
||||
);
|
||||
|
||||
/* XOR blocks. */
|
||||
tmp0 = veorq_u8(block0, tmp0);
|
||||
|
||||
/* Store to output. */
|
||||
vst1q_u8(dst, tmp0);
|
||||
dst += AesEncryptor128::BlockSize;
|
||||
|
||||
num_blocks--;
|
||||
}
|
||||
|
||||
vst1q_u8(m_counter, ctr0);
|
||||
}
|
||||
|
||||
template<>
|
||||
void CtrModeImpl<AesEncryptor192>::ProcessBlocks(u8 *dst, const u8 *src, size_t num_blocks) {
|
||||
/* Preload all round keys + iv into neon registers. */
|
||||
const u8 *keys = m_block_cipher->GetRoundKey();
|
||||
DECLARE_ROUND_KEY_VAR(0);
|
||||
DECLARE_ROUND_KEY_VAR(1);
|
||||
DECLARE_ROUND_KEY_VAR(2);
|
||||
DECLARE_ROUND_KEY_VAR(3);
|
||||
DECLARE_ROUND_KEY_VAR(4);
|
||||
DECLARE_ROUND_KEY_VAR(5);
|
||||
DECLARE_ROUND_KEY_VAR(6);
|
||||
DECLARE_ROUND_KEY_VAR(7);
|
||||
DECLARE_ROUND_KEY_VAR(8);
|
||||
DECLARE_ROUND_KEY_VAR(9);
|
||||
DECLARE_ROUND_KEY_VAR(10);
|
||||
DECLARE_ROUND_KEY_VAR(11);
|
||||
DECLARE_ROUND_KEY_VAR(12);
|
||||
uint8x16_t ctr0 = vld1q_u8(m_counter);
|
||||
uint64_t high, low;
|
||||
|
||||
/* Process three blocks at a time, when possible. */
|
||||
if (num_blocks >= 3) {
|
||||
/* Increment CTR twice. */
|
||||
uint8x16_t ctr1 = IncrementCounterOptimized(ctr0);
|
||||
uint8x16_t ctr2 = IncrementCounterOptimized(ctr1);
|
||||
uint64_t high_tmp, low_tmp;
|
||||
|
||||
while (num_blocks >= 3) {
|
||||
/* Read blocks in. Keep them in registers for XOR later. */
|
||||
const uint8x16_t block0 = vld1q_u8(src);
|
||||
src += AesEncryptor192::BlockSize;
|
||||
const uint8x16_t block1 = vld1q_u8(src);
|
||||
src += AesEncryptor192::BlockSize;
|
||||
const uint8x16_t block2 = vld1q_u8(src);
|
||||
src += AesEncryptor192::BlockSize;
|
||||
|
||||
/* We'll be encrypting the three CTRs. */
|
||||
uint8x16_t tmp0 = ctr0, tmp1 = ctr1, tmp2 = ctr2;
|
||||
|
||||
/* Actually do encryption, use optimized asm. */
|
||||
/* Interleave CTR calculations with AES ones, to mask latencies. */
|
||||
__asm__ __volatile__ (
|
||||
AES_ENC_ROUND(0, 0) "mov %[high], %[ctr2].d[0]\n"
|
||||
AES_ENC_ROUND(0, 1) "mov %[low], %[ctr2].d[1]\n"
|
||||
AES_ENC_ROUND(0, 2) "rev %[high], %[high]\n"
|
||||
AES_ENC_ROUND(1, 0) "rev %[low], %[low]\n"
|
||||
AES_ENC_ROUND(1, 1) "adds %[low], %[low], 1\n"
|
||||
AES_ENC_ROUND(1, 2) "cinc %[high], %[high], cs\n"
|
||||
AES_ENC_ROUND(2, 0) "rev %[high_tmp], %[high]\n"
|
||||
AES_ENC_ROUND(2, 1) "rev %[low_tmp], %[low]\n"
|
||||
AES_ENC_ROUND(2, 2) "mov %[ctr0].d[0], %[high_tmp]\n"
|
||||
AES_ENC_ROUND(3, 0) "mov %[ctr0].d[1], %[low_tmp]\n"
|
||||
AES_ENC_ROUND(3, 1) "adds %[low], %[low], 1\n"
|
||||
AES_ENC_ROUND(3, 2) "cinc %[high], %[high], cs\n"
|
||||
AES_ENC_ROUND(4, 0) "rev %[high_tmp], %[high]\n"
|
||||
AES_ENC_ROUND(4, 1) "rev %[low_tmp], %[low]\n"
|
||||
AES_ENC_ROUND(4, 2) "mov %[ctr1].d[0], %[high_tmp]\n"
|
||||
AES_ENC_ROUND(5, 0) "mov %[ctr1].d[1], %[low_tmp]\n"
|
||||
AES_ENC_ROUND(5, 1) "adds %[low], %[low], 1\n"
|
||||
AES_ENC_ROUND(5, 2) "cinc %[high], %[high], cs\n"
|
||||
AES_ENC_ROUND(6, 0) "rev %[high_tmp], %[high]\n"
|
||||
AES_ENC_ROUND(6, 1) "rev %[low_tmp], %[low]\n"
|
||||
AES_ENC_ROUND(6, 2) "mov %[ctr2].d[0], %[high_tmp]\n"
|
||||
AES_ENC_ROUND(7, 0) "mov %[ctr2].d[1], %[low_tmp]\n"
|
||||
AES_ENC_ROUND(7, 1)
|
||||
AES_ENC_ROUND(7, 2)
|
||||
AES_ENC_ROUND(8, 0) AES_ENC_ROUND(8, 1) AES_ENC_ROUND(8, 2)
|
||||
AES_ENC_ROUND(9, 0) AES_ENC_ROUND(9, 1) AES_ENC_ROUND(9, 2)
|
||||
AES_ENC_ROUND(10, 0) AES_ENC_ROUND(10, 1) AES_ENC_ROUND(10, 2)
|
||||
AES_ENC_SECOND_LAST_ROUND(11, 0) AES_ENC_SECOND_LAST_ROUND(11, 1) AES_ENC_SECOND_LAST_ROUND(11, 2)
|
||||
AES_ENC_LAST_ROUND(12, 0) AES_ENC_LAST_ROUND(12, 1) AES_ENC_LAST_ROUND(12, 2)
|
||||
: AES_ENC_DEC_OUTPUT_THREE_BLOCKS(),
|
||||
AES_ENC_DEC_OUTPUT_THREE_CTRS(),
|
||||
CTR_INCREMENT_OUTPUT_HIGH_LOW(),
|
||||
CTR_INCREMENT_OUTPUT_HIGH_LOW_TMP()
|
||||
: AES_ENC_DEC_INPUT_ROUND_KEY(0),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(1),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(2),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(3),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(4),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(5),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(6),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(7),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(8),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(9),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(10),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(11),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(12)
|
||||
: "cc"
|
||||
);
|
||||
|
||||
/* XOR blocks. */
|
||||
tmp0 = veorq_u8(block0, tmp0);
|
||||
tmp1 = veorq_u8(block1, tmp1);
|
||||
tmp2 = veorq_u8(block2, tmp2);
|
||||
|
||||
/* Store to output. */
|
||||
vst1q_u8(dst, tmp0);
|
||||
dst += AesEncryptor192::BlockSize;
|
||||
vst1q_u8(dst, tmp1);
|
||||
dst += AesEncryptor192::BlockSize;
|
||||
vst1q_u8(dst, tmp2);
|
||||
dst += AesEncryptor192::BlockSize;
|
||||
|
||||
num_blocks -= 3;
|
||||
}
|
||||
}
|
||||
|
||||
while (num_blocks >= 1) {
|
||||
/* Read block in, keep in register for XOR. */
|
||||
const uint8x16_t block0 = vld1q_u8(src);
|
||||
src += AesEncryptor192::BlockSize;
|
||||
|
||||
/* We'll be encrypting the CTR. */
|
||||
uint8x16_t tmp0 = ctr0;
|
||||
|
||||
/* Actually do encryption, use optimized asm. */
|
||||
/* Interleave CTR calculations with AES ones, to mask latencies. */
|
||||
__asm__ __volatile__ (
|
||||
AES_ENC_ROUND(0, 0) "mov %[high], %[ctr0].d[0]\n"
|
||||
AES_ENC_ROUND(1, 0) "mov %[low], %[ctr0].d[1]\n"
|
||||
AES_ENC_ROUND(2, 0) "rev %[high], %[high]\n"
|
||||
AES_ENC_ROUND(3, 0) "rev %[low], %[low]\n"
|
||||
AES_ENC_ROUND(4, 0) "adds %[low], %[low], 1\n"
|
||||
AES_ENC_ROUND(5, 0) "cinc %[high], %[high], cs\n"
|
||||
AES_ENC_ROUND(6, 0) "rev %[high], %[high]\n"
|
||||
AES_ENC_ROUND(7, 0) "rev %[low], %[low]\n"
|
||||
AES_ENC_ROUND(8, 0) "mov %[ctr0].d[0], %[high]\n"
|
||||
AES_ENC_ROUND(9, 0) "mov %[ctr0].d[1], %[low]\n"
|
||||
AES_ENC_ROUND(10, 0)
|
||||
AES_ENC_SECOND_LAST_ROUND(11, 0)
|
||||
AES_ENC_LAST_ROUND(12, 0)
|
||||
: AES_ENC_DEC_OUTPUT_ONE_BLOCK(),
|
||||
AES_ENC_DEC_OUTPUT_ONE_CTR(),
|
||||
CTR_INCREMENT_OUTPUT_HIGH_LOW()
|
||||
: AES_ENC_DEC_INPUT_ROUND_KEY(0),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(1),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(2),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(3),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(4),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(5),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(6),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(7),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(8),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(9),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(10),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(11),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(12)
|
||||
: "cc"
|
||||
);
|
||||
|
||||
/* XOR blocks. */
|
||||
tmp0 = veorq_u8(block0, tmp0);
|
||||
|
||||
/* Store to output. */
|
||||
vst1q_u8(dst, tmp0);
|
||||
dst += AesEncryptor192::BlockSize;
|
||||
|
||||
num_blocks--;
|
||||
}
|
||||
|
||||
vst1q_u8(m_counter, ctr0);
|
||||
}
|
||||
|
||||
template<>
|
||||
void CtrModeImpl<AesEncryptor256>::ProcessBlocks(u8 *dst, const u8 *src, size_t num_blocks) {
|
||||
/* Preload all round keys + iv into neon registers. */
|
||||
const u8 *keys = m_block_cipher->GetRoundKey();
|
||||
DECLARE_ROUND_KEY_VAR(0);
|
||||
DECLARE_ROUND_KEY_VAR(1);
|
||||
DECLARE_ROUND_KEY_VAR(2);
|
||||
DECLARE_ROUND_KEY_VAR(3);
|
||||
DECLARE_ROUND_KEY_VAR(4);
|
||||
DECLARE_ROUND_KEY_VAR(5);
|
||||
DECLARE_ROUND_KEY_VAR(6);
|
||||
DECLARE_ROUND_KEY_VAR(7);
|
||||
DECLARE_ROUND_KEY_VAR(8);
|
||||
DECLARE_ROUND_KEY_VAR(9);
|
||||
DECLARE_ROUND_KEY_VAR(10);
|
||||
DECLARE_ROUND_KEY_VAR(11);
|
||||
DECLARE_ROUND_KEY_VAR(12);
|
||||
DECLARE_ROUND_KEY_VAR(13);
|
||||
DECLARE_ROUND_KEY_VAR(14);
|
||||
uint8x16_t ctr0 = vld1q_u8(m_counter);
|
||||
uint64_t high, low;
|
||||
|
||||
/* Process three blocks at a time, when possible. */
|
||||
if (num_blocks >= 3) {
|
||||
/* Increment CTR twice. */
|
||||
uint8x16_t ctr1 = IncrementCounterOptimized(ctr0);
|
||||
uint8x16_t ctr2 = IncrementCounterOptimized(ctr1);
|
||||
uint64_t hl_tmp;
|
||||
|
||||
while (num_blocks >= 3) {
|
||||
/* Read blocks in. Keep them in registers for XOR later. */
|
||||
const uint8x16_t block0 = vld1q_u8(src);
|
||||
src += AesEncryptor256::BlockSize;
|
||||
const uint8x16_t block1 = vld1q_u8(src);
|
||||
src += AesEncryptor256::BlockSize;
|
||||
const uint8x16_t block2 = vld1q_u8(src);
|
||||
src += AesEncryptor256::BlockSize;
|
||||
|
||||
/* We'll be encrypting the three CTRs. */
|
||||
uint8x16_t tmp0 = ctr0, tmp1 = ctr1, tmp2 = ctr2;
|
||||
|
||||
/* Actually do encryption, use optimized asm. */
|
||||
/* Interleave CTR calculations with AES ones, to mask latencies. */
|
||||
/* Note: ASM here only uses one temporary u64 instead of two, due to 30 operand limit. */
|
||||
__asm__ __volatile__ (
|
||||
AES_ENC_ROUND(0, 0) "mov %[high], %[ctr2].d[0]\n"
|
||||
AES_ENC_ROUND(0, 1) "mov %[low], %[ctr2].d[1]\n"
|
||||
AES_ENC_ROUND(0, 2) "rev %[high], %[high]\n"
|
||||
AES_ENC_ROUND(1, 0) "rev %[low], %[low]\n"
|
||||
AES_ENC_ROUND(1, 1) "adds %[low], %[low], 1\n"
|
||||
AES_ENC_ROUND(1, 2) "cinc %[high], %[high], cs\n"
|
||||
AES_ENC_ROUND(2, 0) "rev %[hl_tmp], %[high]\n"
|
||||
AES_ENC_ROUND(2, 1) "mov %[ctr0].d[0], %[hl_tmp]\n"
|
||||
AES_ENC_ROUND(2, 2) "rev %[hl_tmp], %[low]\n"
|
||||
AES_ENC_ROUND(3, 0) "mov %[ctr0].d[1], %[hl_tmp]\n"
|
||||
AES_ENC_ROUND(3, 1) "adds %[low], %[low], 1\n"
|
||||
AES_ENC_ROUND(3, 2) "cinc %[high], %[high], cs\n"
|
||||
AES_ENC_ROUND(4, 0) "rev %[hl_tmp], %[high]\n"
|
||||
AES_ENC_ROUND(4, 1) "mov %[ctr1].d[0], %[hl_tmp]\n"
|
||||
AES_ENC_ROUND(4, 2) "rev %[hl_tmp], %[low]\n"
|
||||
AES_ENC_ROUND(5, 0) "mov %[ctr1].d[1], %[hl_tmp]\n"
|
||||
AES_ENC_ROUND(5, 1) "adds %[low], %[low], 1\n"
|
||||
AES_ENC_ROUND(5, 2) "cinc %[high], %[high], cs\n"
|
||||
AES_ENC_ROUND(6, 0) "rev %[hl_tmp], %[high]\n"
|
||||
AES_ENC_ROUND(6, 1) "mov %[ctr2].d[0], %[hl_tmp]\n"
|
||||
AES_ENC_ROUND(6, 2) "rev %[hl_tmp], %[low]\n"
|
||||
AES_ENC_ROUND(7, 0) "mov %[ctr2].d[1], %[hl_tmp]\n"
|
||||
AES_ENC_ROUND(7, 1)
|
||||
AES_ENC_ROUND(7, 2)
|
||||
AES_ENC_ROUND(8, 0) AES_ENC_ROUND(8, 1) AES_ENC_ROUND(8, 2)
|
||||
AES_ENC_ROUND(9, 0) AES_ENC_ROUND(9, 1) AES_ENC_ROUND(9, 2)
|
||||
AES_ENC_ROUND(10, 0) AES_ENC_ROUND(10, 1) AES_ENC_ROUND(10, 2)
|
||||
AES_ENC_ROUND(11, 0) AES_ENC_ROUND(11, 1) AES_ENC_ROUND(11, 2)
|
||||
AES_ENC_ROUND(12, 0) AES_ENC_ROUND(12, 1) AES_ENC_ROUND(12, 2)
|
||||
AES_ENC_SECOND_LAST_ROUND(13, 0) AES_ENC_SECOND_LAST_ROUND(13, 1) AES_ENC_SECOND_LAST_ROUND(13, 2)
|
||||
AES_ENC_LAST_ROUND(14, 0) AES_ENC_LAST_ROUND(14, 1) AES_ENC_LAST_ROUND(14, 2)
|
||||
: AES_ENC_DEC_OUTPUT_THREE_BLOCKS(),
|
||||
AES_ENC_DEC_OUTPUT_THREE_CTRS(),
|
||||
CTR_INCREMENT_OUTPUT_HIGH_LOW(),
|
||||
CTR_INCREMENT_OUTPUT_HL_SINGLE_TMP()
|
||||
: AES_ENC_DEC_INPUT_ROUND_KEY(0),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(1),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(2),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(3),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(4),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(5),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(6),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(7),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(8),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(9),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(10),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(11),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(12),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(13),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(14)
|
||||
: "cc"
|
||||
);
|
||||
|
||||
/* XOR blocks. */
|
||||
tmp0 = veorq_u8(block0, tmp0);
|
||||
tmp1 = veorq_u8(block1, tmp1);
|
||||
tmp2 = veorq_u8(block2, tmp2);
|
||||
|
||||
/* Store to output. */
|
||||
vst1q_u8(dst, tmp0);
|
||||
dst += AesEncryptor256::BlockSize;
|
||||
vst1q_u8(dst, tmp1);
|
||||
dst += AesEncryptor256::BlockSize;
|
||||
vst1q_u8(dst, tmp2);
|
||||
dst += AesEncryptor256::BlockSize;
|
||||
|
||||
num_blocks -= 3;
|
||||
}
|
||||
}
|
||||
|
||||
while (num_blocks >= 1) {
|
||||
/* Read block in, keep in register for XOR. */
|
||||
const uint8x16_t block0 = vld1q_u8(src);
|
||||
src += AesEncryptor256::BlockSize;
|
||||
|
||||
/* We'll be encrypting the CTR. */
|
||||
uint8x16_t tmp0 = ctr0;
|
||||
|
||||
/* Actually do encryption, use optimized asm. */
|
||||
/* Interleave CTR calculations with AES ones, to mask latencies. */
|
||||
__asm__ __volatile__ (
|
||||
AES_ENC_ROUND(0, 0) "mov %[high], %[ctr0].d[0]\n"
|
||||
AES_ENC_ROUND(1, 0) "mov %[low], %[ctr0].d[1]\n"
|
||||
AES_ENC_ROUND(2, 0) "rev %[high], %[high]\n"
|
||||
AES_ENC_ROUND(3, 0) "rev %[low], %[low]\n"
|
||||
AES_ENC_ROUND(4, 0) "adds %[low], %[low], 1\n"
|
||||
AES_ENC_ROUND(5, 0) "cinc %[high], %[high], cs\n"
|
||||
AES_ENC_ROUND(6, 0) "rev %[high], %[high]\n"
|
||||
AES_ENC_ROUND(7, 0) "rev %[low], %[low]\n"
|
||||
AES_ENC_ROUND(8, 0) "mov %[ctr0].d[0], %[high]\n"
|
||||
AES_ENC_ROUND(9, 0) "mov %[ctr0].d[1], %[low]\n"
|
||||
AES_ENC_ROUND(10, 0)
|
||||
AES_ENC_ROUND(11, 0)
|
||||
AES_ENC_ROUND(12, 0)
|
||||
AES_ENC_SECOND_LAST_ROUND(13, 0)
|
||||
AES_ENC_LAST_ROUND(14, 0)
|
||||
: AES_ENC_DEC_OUTPUT_ONE_BLOCK(),
|
||||
AES_ENC_DEC_OUTPUT_ONE_CTR(),
|
||||
CTR_INCREMENT_OUTPUT_HIGH_LOW()
|
||||
: AES_ENC_DEC_INPUT_ROUND_KEY(0),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(1),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(2),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(3),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(4),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(5),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(6),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(7),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(8),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(9),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(10),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(11),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(12),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(13),
|
||||
AES_ENC_DEC_INPUT_ROUND_KEY(14)
|
||||
: "cc"
|
||||
);
|
||||
|
||||
/* XOR blocks. */
|
||||
tmp0 = veorq_u8(block0, tmp0);
|
||||
|
||||
/* Store to output. */
|
||||
vst1q_u8(dst, tmp0);
|
||||
dst += AesEncryptor256::BlockSize;
|
||||
|
||||
num_blocks--;
|
||||
}
|
||||
|
||||
vst1q_u8(m_counter, ctr0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* TODO: Non-EL0 implementation. */
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,269 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
#include "crypto_aes_impl.arch.x64.hpp"
|
||||
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
template<> void CtrModeImpl<AesEncryptor128>::ProcessBlocks(u8 *dst, const u8 *src, size_t num_blocks) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(src != nullptr);
|
||||
AMS_ASSERT(dst != nullptr);
|
||||
|
||||
/* If we have aes-ni, use an optimized impl. */
|
||||
if (IsAesNiAvailable()) {
|
||||
/* Load all keys into sse2 registers. */
|
||||
const u8 *raw_round_keys = m_block_cipher->GetRoundKey();
|
||||
const __m128i round_keys[AesEncryptor128::RoundKeySize / BlockSize] = {
|
||||
_mm_loadu_si128(reinterpret_cast<const __m128i *>(raw_round_keys + BlockSize * 0)),
|
||||
_mm_loadu_si128(reinterpret_cast<const __m128i *>(raw_round_keys + BlockSize * 1)),
|
||||
_mm_loadu_si128(reinterpret_cast<const __m128i *>(raw_round_keys + BlockSize * 2)),
|
||||
_mm_loadu_si128(reinterpret_cast<const __m128i *>(raw_round_keys + BlockSize * 3)),
|
||||
_mm_loadu_si128(reinterpret_cast<const __m128i *>(raw_round_keys + BlockSize * 4)),
|
||||
_mm_loadu_si128(reinterpret_cast<const __m128i *>(raw_round_keys + BlockSize * 5)),
|
||||
_mm_loadu_si128(reinterpret_cast<const __m128i *>(raw_round_keys + BlockSize * 6)),
|
||||
_mm_loadu_si128(reinterpret_cast<const __m128i *>(raw_round_keys + BlockSize * 7)),
|
||||
_mm_loadu_si128(reinterpret_cast<const __m128i *>(raw_round_keys + BlockSize * 8)),
|
||||
_mm_loadu_si128(reinterpret_cast<const __m128i *>(raw_round_keys + BlockSize * 9)),
|
||||
_mm_loadu_si128(reinterpret_cast<const __m128i *>(raw_round_keys + BlockSize * 10)),
|
||||
};
|
||||
static_assert(AesEncryptor128::RoundKeySize / BlockSize == 11);
|
||||
|
||||
/* Declare constant for counter math. */
|
||||
const __m128i One = _mm_setr_epi8(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1);
|
||||
|
||||
/* Process eight blocks at a time, while we can. */
|
||||
constexpr const auto UnrolledBlockCount = 8;
|
||||
constexpr const auto CounterThreshold = static_cast<u8>(0x100 - UnrolledBlockCount);
|
||||
|
||||
/* Load the counter. */
|
||||
auto counter = _mm_loadu_si128(reinterpret_cast<const __m128i *>(m_counter));
|
||||
|
||||
size_t cur_blocks;
|
||||
for (cur_blocks = 0; cur_blocks + UnrolledBlockCount <= num_blocks; cur_blocks += UnrolledBlockCount) {
|
||||
__m128i b0;
|
||||
__m128i b1;
|
||||
__m128i b2;
|
||||
__m128i b3;
|
||||
__m128i b4;
|
||||
__m128i b5;
|
||||
__m128i b6;
|
||||
__m128i b7;
|
||||
|
||||
__m128i key = round_keys[0];
|
||||
|
||||
/* Get the last byte of the block. */
|
||||
static_assert(util::IsLittleEndian());
|
||||
const u8 counter_val = _mm_extract_epi16(counter, 7) >> BITSIZEOF(u8);
|
||||
|
||||
/* Do initial encryption of each block. */
|
||||
if (CounterThreshold <= counter_val) {
|
||||
/* We'll overwrap, so take slow path for counter. */
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(m_counter), counter);
|
||||
|
||||
b0 = _mm_xor_si128(_mm_loadu_si128(reinterpret_cast<const __m128i *>(m_counter)), key);
|
||||
this->IncrementCounter();
|
||||
b1 = _mm_xor_si128(_mm_loadu_si128(reinterpret_cast<const __m128i *>(m_counter)), key);
|
||||
this->IncrementCounter();
|
||||
b2 = _mm_xor_si128(_mm_loadu_si128(reinterpret_cast<const __m128i *>(m_counter)), key);
|
||||
this->IncrementCounter();
|
||||
b3 = _mm_xor_si128(_mm_loadu_si128(reinterpret_cast<const __m128i *>(m_counter)), key);
|
||||
this->IncrementCounter();
|
||||
b4 = _mm_xor_si128(_mm_loadu_si128(reinterpret_cast<const __m128i *>(m_counter)), key);
|
||||
this->IncrementCounter();
|
||||
b5 = _mm_xor_si128(_mm_loadu_si128(reinterpret_cast<const __m128i *>(m_counter)), key);
|
||||
this->IncrementCounter();
|
||||
b6 = _mm_xor_si128(_mm_loadu_si128(reinterpret_cast<const __m128i *>(m_counter)), key);
|
||||
this->IncrementCounter();
|
||||
b7 = _mm_xor_si128(_mm_loadu_si128(reinterpret_cast<const __m128i *>(m_counter)), key);
|
||||
this->IncrementCounter();
|
||||
|
||||
counter = _mm_loadu_si128(reinterpret_cast<const __m128i *>(m_counter));
|
||||
} else {
|
||||
/* We can take the fast path for the counter. */
|
||||
b0 = _mm_xor_si128(counter, key);
|
||||
counter = _mm_add_epi64(counter, One);
|
||||
b1 = _mm_xor_si128(counter, key);
|
||||
counter = _mm_add_epi64(counter, One);
|
||||
b2 = _mm_xor_si128(counter, key);
|
||||
counter = _mm_add_epi64(counter, One);
|
||||
b3 = _mm_xor_si128(counter, key);
|
||||
counter = _mm_add_epi64(counter, One);
|
||||
b4 = _mm_xor_si128(counter, key);
|
||||
counter = _mm_add_epi64(counter, One);
|
||||
b5 = _mm_xor_si128(counter, key);
|
||||
counter = _mm_add_epi64(counter, One);
|
||||
b6 = _mm_xor_si128(counter, key);
|
||||
counter = _mm_add_epi64(counter, One);
|
||||
b7 = _mm_xor_si128(counter, key);
|
||||
counter = _mm_add_epi64(counter, One);
|
||||
}
|
||||
|
||||
/* Do encryption for all rounds. */
|
||||
key = round_keys[1];
|
||||
b0 = _mm_aesenc_si128(b0, key);
|
||||
b1 = _mm_aesenc_si128(b1, key);
|
||||
b2 = _mm_aesenc_si128(b2, key);
|
||||
b3 = _mm_aesenc_si128(b3, key);
|
||||
b4 = _mm_aesenc_si128(b4, key);
|
||||
b5 = _mm_aesenc_si128(b5, key);
|
||||
b6 = _mm_aesenc_si128(b6, key);
|
||||
b7 = _mm_aesenc_si128(b7, key);
|
||||
|
||||
key = round_keys[2];
|
||||
b0 = _mm_aesenc_si128(b0, key);
|
||||
b1 = _mm_aesenc_si128(b1, key);
|
||||
b2 = _mm_aesenc_si128(b2, key);
|
||||
b3 = _mm_aesenc_si128(b3, key);
|
||||
b4 = _mm_aesenc_si128(b4, key);
|
||||
b5 = _mm_aesenc_si128(b5, key);
|
||||
b6 = _mm_aesenc_si128(b6, key);
|
||||
b7 = _mm_aesenc_si128(b7, key);
|
||||
|
||||
key = round_keys[3];
|
||||
b0 = _mm_aesenc_si128(b0, key);
|
||||
b1 = _mm_aesenc_si128(b1, key);
|
||||
b2 = _mm_aesenc_si128(b2, key);
|
||||
b3 = _mm_aesenc_si128(b3, key);
|
||||
b4 = _mm_aesenc_si128(b4, key);
|
||||
b5 = _mm_aesenc_si128(b5, key);
|
||||
b6 = _mm_aesenc_si128(b6, key);
|
||||
b7 = _mm_aesenc_si128(b7, key);
|
||||
|
||||
key = round_keys[4];
|
||||
b0 = _mm_aesenc_si128(b0, key);
|
||||
b1 = _mm_aesenc_si128(b1, key);
|
||||
b2 = _mm_aesenc_si128(b2, key);
|
||||
b3 = _mm_aesenc_si128(b3, key);
|
||||
b4 = _mm_aesenc_si128(b4, key);
|
||||
b5 = _mm_aesenc_si128(b5, key);
|
||||
b6 = _mm_aesenc_si128(b6, key);
|
||||
b7 = _mm_aesenc_si128(b7, key);
|
||||
|
||||
key = round_keys[5];
|
||||
b0 = _mm_aesenc_si128(b0, key);
|
||||
b1 = _mm_aesenc_si128(b1, key);
|
||||
b2 = _mm_aesenc_si128(b2, key);
|
||||
b3 = _mm_aesenc_si128(b3, key);
|
||||
b4 = _mm_aesenc_si128(b4, key);
|
||||
b5 = _mm_aesenc_si128(b5, key);
|
||||
b6 = _mm_aesenc_si128(b6, key);
|
||||
b7 = _mm_aesenc_si128(b7, key);
|
||||
|
||||
key = round_keys[6];
|
||||
b0 = _mm_aesenc_si128(b0, key);
|
||||
b1 = _mm_aesenc_si128(b1, key);
|
||||
b2 = _mm_aesenc_si128(b2, key);
|
||||
b3 = _mm_aesenc_si128(b3, key);
|
||||
b4 = _mm_aesenc_si128(b4, key);
|
||||
b5 = _mm_aesenc_si128(b5, key);
|
||||
b6 = _mm_aesenc_si128(b6, key);
|
||||
b7 = _mm_aesenc_si128(b7, key);
|
||||
|
||||
key = round_keys[7];
|
||||
b0 = _mm_aesenc_si128(b0, key);
|
||||
b1 = _mm_aesenc_si128(b1, key);
|
||||
b2 = _mm_aesenc_si128(b2, key);
|
||||
b3 = _mm_aesenc_si128(b3, key);
|
||||
b4 = _mm_aesenc_si128(b4, key);
|
||||
b5 = _mm_aesenc_si128(b5, key);
|
||||
b6 = _mm_aesenc_si128(b6, key);
|
||||
b7 = _mm_aesenc_si128(b7, key);
|
||||
|
||||
key = round_keys[8];
|
||||
b0 = _mm_aesenc_si128(b0, key);
|
||||
b1 = _mm_aesenc_si128(b1, key);
|
||||
b2 = _mm_aesenc_si128(b2, key);
|
||||
b3 = _mm_aesenc_si128(b3, key);
|
||||
b4 = _mm_aesenc_si128(b4, key);
|
||||
b5 = _mm_aesenc_si128(b5, key);
|
||||
b6 = _mm_aesenc_si128(b6, key);
|
||||
b7 = _mm_aesenc_si128(b7, key);
|
||||
|
||||
key = round_keys[9];
|
||||
b0 = _mm_aesenc_si128(b0, key);
|
||||
b1 = _mm_aesenc_si128(b1, key);
|
||||
b2 = _mm_aesenc_si128(b2, key);
|
||||
b3 = _mm_aesenc_si128(b3, key);
|
||||
b4 = _mm_aesenc_si128(b4, key);
|
||||
b5 = _mm_aesenc_si128(b5, key);
|
||||
b6 = _mm_aesenc_si128(b6, key);
|
||||
b7 = _mm_aesenc_si128(b7, key);
|
||||
|
||||
key = round_keys[10];
|
||||
b0 = _mm_aesenclast_si128(b0, key);
|
||||
b1 = _mm_aesenclast_si128(b1, key);
|
||||
b2 = _mm_aesenclast_si128(b2, key);
|
||||
b3 = _mm_aesenclast_si128(b3, key);
|
||||
b4 = _mm_aesenclast_si128(b4, key);
|
||||
b5 = _mm_aesenclast_si128(b5, key);
|
||||
b6 = _mm_aesenclast_si128(b6, key);
|
||||
b7 = _mm_aesenclast_si128(b7, key);
|
||||
|
||||
/* Write the blocks. */
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(dst + BlockSize * 0), _mm_xor_si128(_mm_loadu_si128(reinterpret_cast<const __m128i *>(src + BlockSize * 0)), b0));
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(dst + BlockSize * 1), _mm_xor_si128(_mm_loadu_si128(reinterpret_cast<const __m128i *>(src + BlockSize * 1)), b1));
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(dst + BlockSize * 2), _mm_xor_si128(_mm_loadu_si128(reinterpret_cast<const __m128i *>(src + BlockSize * 2)), b2));
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(dst + BlockSize * 3), _mm_xor_si128(_mm_loadu_si128(reinterpret_cast<const __m128i *>(src + BlockSize * 3)), b3));
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(dst + BlockSize * 4), _mm_xor_si128(_mm_loadu_si128(reinterpret_cast<const __m128i *>(src + BlockSize * 4)), b4));
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(dst + BlockSize * 5), _mm_xor_si128(_mm_loadu_si128(reinterpret_cast<const __m128i *>(src + BlockSize * 5)), b5));
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(dst + BlockSize * 6), _mm_xor_si128(_mm_loadu_si128(reinterpret_cast<const __m128i *>(src + BlockSize * 6)), b6));
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(dst + BlockSize * 7), _mm_xor_si128(_mm_loadu_si128(reinterpret_cast<const __m128i *>(src + BlockSize * 7)), b7));
|
||||
|
||||
src += BlockSize * UnrolledBlockCount;
|
||||
dst += BlockSize * UnrolledBlockCount;
|
||||
}
|
||||
|
||||
/* Store the updated counter. */
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(m_counter), counter);
|
||||
|
||||
/* Process blocks one at a time. */
|
||||
for (/* ... */; cur_blocks < num_blocks; ++cur_blocks) {
|
||||
/* Load current counter. */
|
||||
__m128i b = _mm_loadu_si128(reinterpret_cast<const __m128i *>(m_counter));
|
||||
|
||||
/* Do aes rounds. */
|
||||
b = _mm_xor_si128(b, round_keys[0]);
|
||||
b = _mm_aesenc_si128(b, round_keys[1]);
|
||||
b = _mm_aesenc_si128(b, round_keys[2]);
|
||||
b = _mm_aesenc_si128(b, round_keys[3]);
|
||||
b = _mm_aesenc_si128(b, round_keys[4]);
|
||||
b = _mm_aesenc_si128(b, round_keys[5]);
|
||||
b = _mm_aesenc_si128(b, round_keys[6]);
|
||||
b = _mm_aesenc_si128(b, round_keys[7]);
|
||||
b = _mm_aesenc_si128(b, round_keys[8]);
|
||||
b = _mm_aesenc_si128(b, round_keys[9]);
|
||||
b = _mm_aesenclast_si128(b, round_keys[10]);
|
||||
|
||||
/* Write the block. */
|
||||
_mm_storeu_si128(reinterpret_cast<__m128i *>(dst), _mm_xor_si128(_mm_loadu_si128(reinterpret_cast<const __m128i *>(src)), b));
|
||||
|
||||
/* Advance. */
|
||||
src += BlockSize;
|
||||
dst += BlockSize;
|
||||
this->IncrementCounter();
|
||||
}
|
||||
} else {
|
||||
/* Fall back to the default implementation. */
|
||||
while (num_blocks--) {
|
||||
this->ProcessBlock(dst, src, BlockSize);
|
||||
dst += BlockSize;
|
||||
src += BlockSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,306 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
|
||||
/* TODO: EL0 implementation. */
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* EL1+ implementation. */
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr u64 GetMultiplyFactor(u8 value) {
|
||||
constexpr size_t Shift = BITSIZEOF(u8) - 1;
|
||||
constexpr u8 Mask = (1u << Shift);
|
||||
return (value & Mask) >> Shift;
|
||||
}
|
||||
|
||||
/* TODO: Big endian support, eventually? */
|
||||
constexpr void GaloisShiftLeft(u64 *block) {
|
||||
/* Shift the block left by one. */
|
||||
block[1] <<= 1;
|
||||
block[1] |= (block[0] & (static_cast<u64>(1) << (BITSIZEOF(u64) - 1))) >> (BITSIZEOF(u64) - 1);
|
||||
block[0] <<= 1;
|
||||
}
|
||||
|
||||
constexpr u8 GaloisShiftRight(u64 *block) {
|
||||
/* Determine the mask to return. */
|
||||
constexpr u8 GaloisFieldMask = 0xE1;
|
||||
const u8 mask = (block[0] & 1) * GaloisFieldMask;
|
||||
|
||||
/* Shift the block right by one. */
|
||||
block[0] >>= 1;
|
||||
block[0] |= (block[1] & 1) << (BITSIZEOF(u64) - 1);
|
||||
block[1] >>= 1;
|
||||
|
||||
/* Return the mask. */
|
||||
return mask;
|
||||
}
|
||||
|
||||
/* Multiply two 128-bit numbers X, Y in the GF(128) Galois Field. */
|
||||
void GaloisFieldMult(void *dst, const void *x, const void *y) {
|
||||
/* Our block size is 16 bytes (for a 128-bit integer). */
|
||||
constexpr size_t BlockSize = 16;
|
||||
constexpr size_t FieldSize = 128;
|
||||
|
||||
/* Declare work blocks for us to store temporary values. */
|
||||
u8 x_block[BlockSize];
|
||||
u8 y_block[BlockSize];
|
||||
u8 out[BlockSize];
|
||||
|
||||
/* Declare 64-bit pointers for our convenience. */
|
||||
u64 *x_64 = static_cast<u64 *>(static_cast<void *>(x_block));
|
||||
u64 *y_64 = static_cast<u64 *>(static_cast<void *>(y_block));
|
||||
u64 *out_64 = static_cast<u64 *>(static_cast<void *>(out));
|
||||
|
||||
/* Initialize our work blocks. */
|
||||
for (size_t i = 0; i < BlockSize; ++i) {
|
||||
x_block[i] = static_cast<const u8 *>(x)[BlockSize - 1 - i];
|
||||
y_block[i] = static_cast<const u8 *>(y)[BlockSize - 1 - i];
|
||||
out[i] = 0;
|
||||
}
|
||||
|
||||
/* Perform multiplication on each bit in y. */
|
||||
for (size_t i = 0; i < FieldSize; ++i) {
|
||||
/* Get the multiply factor for this bit. */
|
||||
const auto y_mult = GetMultiplyFactor(y_block[BlockSize - 1]);
|
||||
|
||||
/* Multiply x by the factor. */
|
||||
out_64[0] ^= x_64[0] * y_mult;
|
||||
out_64[1] ^= x_64[1] * y_mult;
|
||||
|
||||
/* Shift left y by one. */
|
||||
GaloisShiftLeft(y_64);
|
||||
|
||||
/* Shift right x by one, and mask appropriately. */
|
||||
const u8 x_mask = GaloisShiftRight(x_64);
|
||||
x_block[BlockSize - 1] ^= x_mask;
|
||||
}
|
||||
|
||||
/* Copy out our result. */
|
||||
for (size_t i = 0; i < BlockSize; ++i) {
|
||||
static_cast<u8 *>(dst)[i] = out[BlockSize - 1 - i];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<class BlockCipher>
|
||||
void GcmModeImpl<BlockCipher>::Initialize(const BlockCipher *block_cipher) {
|
||||
/* Set member variables. */
|
||||
m_block_cipher = block_cipher;
|
||||
m_cipher_func = std::addressof(GcmModeImpl<BlockCipher>::ProcessBlock);
|
||||
|
||||
/* Pre-calculate values to speed up galois field multiplications later. */
|
||||
this->InitializeHashKey();
|
||||
|
||||
/* Note that we're initialized. */
|
||||
m_state = State_Initialized;
|
||||
}
|
||||
|
||||
template<class BlockCipher>
|
||||
void GcmModeImpl<BlockCipher>::Reset(const void *iv, size_t iv_size) {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(m_state >= State_Initialized);
|
||||
|
||||
/* Reset blocks. */
|
||||
m_block_x.block_128.Clear();
|
||||
m_block_tmp.block_128.Clear();
|
||||
|
||||
/* Clear sizes. */
|
||||
m_aad_size = 0;
|
||||
m_msg_size = 0;
|
||||
m_aad_remaining = 0;
|
||||
m_msg_remaining = 0;
|
||||
|
||||
/* Update our state. */
|
||||
m_state = State_ProcessingAad;
|
||||
|
||||
/* Set our iv. */
|
||||
if (iv_size == 12) {
|
||||
/* If our iv is the correct size, simply copy in the iv, and set the magic bit. */
|
||||
std::memcpy(std::addressof(m_block_ek0), iv, iv_size);
|
||||
util::StoreBigEndian(m_block_ek0.block_32 + 3, static_cast<u32>(1));
|
||||
} else {
|
||||
/* Clear our ek0 block. */
|
||||
m_block_ek0.block_128.Clear();
|
||||
|
||||
/* Update using the iv as aad. */
|
||||
this->UpdateAad(iv, iv_size);
|
||||
|
||||
/* Treat the iv as fake msg for the mac that will become our iv. */
|
||||
m_msg_size = m_aad_size;
|
||||
m_aad_size = 0;
|
||||
|
||||
/* Compute a non-final mac. */
|
||||
this->ComputeMac(false);
|
||||
|
||||
/* Set our ek0 block to our calculated mac block. */
|
||||
m_block_ek0 = m_block_x;
|
||||
|
||||
/* Clear our calculated mac block. */
|
||||
m_block_x.block_128.Clear();
|
||||
|
||||
/* Reset our state. */
|
||||
m_msg_size = 0;
|
||||
m_aad_size = 0;
|
||||
m_msg_remaining = 0;
|
||||
m_aad_remaining = 0;
|
||||
}
|
||||
|
||||
/* Set the working block to the iv. */
|
||||
m_block_ek = m_block_ek0;
|
||||
}
|
||||
|
||||
template<class BlockCipher>
|
||||
void GcmModeImpl<BlockCipher>::UpdateAad(const void *aad, size_t aad_size) {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(m_state == State_ProcessingAad);
|
||||
AMS_ASSERT(m_msg_size == 0);
|
||||
|
||||
/* Update our aad size. */
|
||||
m_aad_size += aad_size;
|
||||
|
||||
/* Define a working tracker variable. */
|
||||
const u8 *cur_aad = static_cast<const u8 *>(aad);
|
||||
|
||||
/* Process any leftover aad data from a previous invocation. */
|
||||
if (m_aad_remaining > 0) {
|
||||
while (aad_size > 0) {
|
||||
/* Copy in a byte of the aad to our partial block. */
|
||||
m_block_x.block_8[m_aad_remaining] ^= *(cur_aad++);
|
||||
|
||||
/* Note that we consumed a byte. */
|
||||
--aad_size;
|
||||
|
||||
/* Increment our partial block size. */
|
||||
m_aad_remaining = (m_aad_remaining + 1) % BlockSize;
|
||||
|
||||
/* If we have a complete block, process it and move onward. */
|
||||
GaloisFieldMult(std::addressof(m_block_x), std::addressof(m_block_x), std::addressof(m_h_mult_blocks[0]));
|
||||
}
|
||||
}
|
||||
|
||||
/* Process as many blocks as we can. */
|
||||
while (aad_size >= BlockSize) {
|
||||
/* Xor the current aad into our work block. */
|
||||
for (size_t i = 0; i < BlockSize; ++i) {
|
||||
m_block_x.block_8[i] ^= *(cur_aad++);
|
||||
}
|
||||
|
||||
/* Multiply the blocks in our galois field. */
|
||||
GaloisFieldMult(std::addressof(m_block_x), std::addressof(m_block_x), std::addressof(m_h_mult_blocks[0]));
|
||||
|
||||
/* Note that we've processed a block. */
|
||||
aad_size -= BlockSize;
|
||||
}
|
||||
|
||||
/* Update our state with whatever aad is left over. */
|
||||
if (aad_size > 0) {
|
||||
/* Note how much left over data we have. */
|
||||
m_aad_remaining = static_cast<u32>(aad_size);
|
||||
|
||||
/* Xor the data in. */
|
||||
for (size_t i = 0; i < aad_size; ++i) {
|
||||
m_block_x.block_8[i] ^= *(cur_aad++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: template<class BlockCipher> size_t GcmModeImpl<BlockCipher>::UpdateEncrypt(void *dst, size_t dst_size, const void *src, size_t src_size); */
|
||||
|
||||
/* TODO: template<class BlockCipher> size_t GcmModeImpl<BlockCipher>::UpdateDecrypt(void *dst, size_t dst_size, const void *src, size_t src_size); */
|
||||
|
||||
template<class BlockCipher>
|
||||
void GcmModeImpl<BlockCipher>::GetMac(void *dst, size_t dst_size) {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(State_ProcessingAad <= m_state && m_state <= State_Done);
|
||||
AMS_ASSERT(dst != nullptr);
|
||||
AMS_ASSERT(dst_size >= MacSize);
|
||||
AMS_ASSERT(m_aad_remaining == 0);
|
||||
AMS_ASSERT(m_msg_remaining == 0);
|
||||
AMS_UNUSED(dst_size);
|
||||
|
||||
/* If we haven't already done so, compute the final mac. */
|
||||
if (m_state != State_Done) {
|
||||
this->ComputeMac(true);
|
||||
m_state = State_Done;
|
||||
}
|
||||
|
||||
static_assert(sizeof(m_block_x) == MacSize);
|
||||
std::memcpy(dst, std::addressof(m_block_x), MacSize);
|
||||
}
|
||||
|
||||
template<class BlockCipher>
|
||||
void GcmModeImpl<BlockCipher>::InitializeHashKey() {
|
||||
/* We want to encrypt an empty block to use for intermediate calculations. */
|
||||
/* NOTE: Non-EL1 implementations will do multiple encryptions ahead of time, */
|
||||
/* to speed up galois field arithmetic. */
|
||||
constexpr const Block EmptyBlock = {};
|
||||
|
||||
this->ProcessBlock(std::addressof(m_h_mult_blocks[0]), std::addressof(EmptyBlock), m_block_cipher);
|
||||
}
|
||||
|
||||
template<class BlockCipher>
|
||||
void GcmModeImpl<BlockCipher>::ComputeMac(bool encrypt) {
|
||||
/* If we have leftover data, process it. */
|
||||
if (m_aad_remaining > 0 || m_msg_remaining > 0) {
|
||||
GaloisFieldMult(std::addressof(m_block_x), std::addressof(m_block_x), std::addressof(m_h_mult_blocks[0]));
|
||||
}
|
||||
|
||||
/* Setup the last block. */
|
||||
Block last_block = Block{ .block_128 = { m_msg_size, m_aad_size } };
|
||||
|
||||
/* Multiply the last block by 8 to account for bit vs byte sizes. */
|
||||
static_assert(AMS_OFFSETOF(Block128, hi) == 0);
|
||||
GaloisShiftLeft(std::addressof(last_block.block_128.hi));
|
||||
GaloisShiftLeft(std::addressof(last_block.block_128.hi));
|
||||
GaloisShiftLeft(std::addressof(last_block.block_128.hi));
|
||||
|
||||
/* Xor the data in. */
|
||||
for (size_t i = 0; i < BlockSize; ++i) {
|
||||
m_block_x.block_8[BlockSize - 1 - i] ^= last_block.block_8[i];
|
||||
}
|
||||
|
||||
/* Perform the final multiplication. */
|
||||
GaloisFieldMult(std::addressof(m_block_x), std::addressof(m_block_x), std::addressof(m_h_mult_blocks[0]));
|
||||
|
||||
/* If we need to do an encryption, do so. */
|
||||
if (encrypt) {
|
||||
/* Encrypt the iv. */
|
||||
u8 enc_result[BlockSize];
|
||||
this->ProcessBlock(enc_result, std::addressof(m_block_ek0), m_block_cipher);
|
||||
|
||||
/* Xor the iv in. */
|
||||
for (size_t i = 0; i < BlockSize; ++i) {
|
||||
m_block_x.block_8[i] ^= enc_result[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Explicitly instantiate the valid template classes. */
|
||||
template class GcmModeImpl<AesEncryptor128>;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,255 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
struct Md5Constants {
|
||||
static constexpr const u32 A = 0x67452301;
|
||||
static constexpr const u32 B = 0xEFCDAB89;
|
||||
static constexpr const u32 C = 0x98BADCFE;
|
||||
static constexpr const u32 D = 0x10325476;
|
||||
|
||||
static constexpr const u32 T[] = {
|
||||
0xD76AA478, 0xE8C7B756, 0x242070DB, 0xC1BDCEEE,
|
||||
0xF57C0FAF, 0x4787C62A, 0xA8304613, 0xFD469501,
|
||||
0x698098D8, 0x8B44F7AF, 0xFFFF5BB1, 0x895CD7BE,
|
||||
0x6B901122, 0xFD987193, 0xA679438E, 0x49B40821,
|
||||
0xF61E2562, 0xC040B340, 0x265E5A51, 0xE9B6C7AA,
|
||||
0xD62F105D, 0x02441453, 0xD8A1E681, 0xE7D3FBC8,
|
||||
0x21E1CDE6, 0xC33707D6, 0xF4D50D87, 0x455A14ED,
|
||||
0xA9E3E905, 0xFCEFA3F8, 0x676F02D9, 0x8D2A4C8A,
|
||||
0xFFFA3942, 0x8771F681, 0x6D9D6122, 0xFDE5380C,
|
||||
0xA4BEEA44, 0x4BDECFA9, 0xF6BB4B60, 0xBEBFBC70,
|
||||
0x289B7EC6, 0xEAA127FA, 0xD4EF3085, 0x04881D05,
|
||||
0xD9D4D039, 0xE6DB99E5, 0x1FA27CF8, 0xC4AC5665,
|
||||
0xF4292244, 0x432AFF97, 0xAB9423A7, 0xFC93A039,
|
||||
0x655B59C3, 0x8F0CCC92, 0xFFEFF47D, 0x85845DD1,
|
||||
0x6FA87E4F, 0xFE2CE6E0, 0xA3014314, 0x4E0811A1,
|
||||
0xF7537E82, 0xBD3AF235, 0x2AD7D2BB, 0xEB86D391,
|
||||
};
|
||||
|
||||
static constexpr u32 K[] = {
|
||||
0x1, 0x6, 0xB, 0x0,
|
||||
0x5, 0xA, 0xF, 0x4,
|
||||
0x9, 0xE, 0x3, 0x8,
|
||||
0xD, 0x2, 0x7, 0xC,
|
||||
0x5, 0x8, 0xB, 0xE,
|
||||
0x1, 0x4, 0x7, 0xA,
|
||||
0xD, 0x0, 0x3, 0x6,
|
||||
0x9, 0xC, 0xF, 0x2,
|
||||
0x0, 0x7, 0xE, 0x5,
|
||||
0xC, 0x3, 0xA, 0x1,
|
||||
0x8, 0xF, 0x6, 0xD,
|
||||
0x4, 0xB, 0x2, 0x9,
|
||||
};
|
||||
|
||||
static constexpr u8 Padding[] = {
|
||||
0x80
|
||||
};
|
||||
};
|
||||
|
||||
constexpr ALWAYS_INLINE u32 F(u32 x, u32 y, u32 z) { return (x & y) | ((~x) & z); }
|
||||
constexpr ALWAYS_INLINE u32 G(u32 x, u32 y, u32 z) { return (x & z) | (y & (~z)); }
|
||||
constexpr ALWAYS_INLINE u32 H(u32 x, u32 y, u32 z) { return x ^ y ^ z; }
|
||||
constexpr ALWAYS_INLINE u32 I(u32 x, u32 y, u32 z) { return y ^ (x | (~z)); }
|
||||
|
||||
constexpr ALWAYS_INLINE u32 CalculateRound1(u32 a, u32 b, u32 c, u32 d, u32 x, u32 s, u32 t) { return b + util::RotateLeft<u32>(a + F(b, c, d) + x + t, s); }
|
||||
constexpr ALWAYS_INLINE u32 CalculateRound2(u32 a, u32 b, u32 c, u32 d, u32 x, u32 s, u32 t) { return b + util::RotateLeft<u32>(a + G(b, c, d) + x + t, s); }
|
||||
constexpr ALWAYS_INLINE u32 CalculateRound3(u32 a, u32 b, u32 c, u32 d, u32 x, u32 s, u32 t) { return b + util::RotateLeft<u32>(a + H(b, c, d) + x + t, s); }
|
||||
constexpr ALWAYS_INLINE u32 CalculateRound4(u32 a, u32 b, u32 c, u32 d, u32 x, u32 s, u32 t) { return b + util::RotateLeft<u32>(a + I(b, c, d) + x + t, s); }
|
||||
|
||||
void Encode(u32 *dst, const u32 *src, size_t size) {
|
||||
if constexpr (util::IsBigEndian()) {
|
||||
for (size_t i = 0; i < size; i += sizeof(u32)) {
|
||||
util::StoreLittleEndian(dst + i, src[i]);
|
||||
}
|
||||
} else {
|
||||
std::memcpy(dst, src, size);
|
||||
}
|
||||
}
|
||||
|
||||
void Decode(u32 *dst, const u32 *src, size_t size) {
|
||||
if constexpr (util::IsBigEndian()) {
|
||||
for (size_t i = 0; i < size; i += sizeof(u32)) {
|
||||
dst[i] = util::LoadLittleEndian(src + i);
|
||||
}
|
||||
} else {
|
||||
std::memcpy(dst, src, size);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Md5Impl::Initialize() {
|
||||
/* Set constants. */
|
||||
m_x.p.a = Md5Constants::A;
|
||||
m_x.p.b = Md5Constants::B;
|
||||
m_x.p.c = Md5Constants::C;
|
||||
m_x.p.d = Md5Constants::D;
|
||||
|
||||
/* Set size. */
|
||||
m_size = 0;
|
||||
|
||||
/* Set initialized. */
|
||||
m_state = State_Initialized;
|
||||
}
|
||||
|
||||
void Md5Impl::Update(const void *data, size_t size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(m_state == State_Initialized);
|
||||
|
||||
/* Determine how much we can process. */
|
||||
const size_t work_idx = m_size % BlockSize;
|
||||
const size_t work_remaining = BlockSize - work_idx;
|
||||
|
||||
/* Increment our size. */
|
||||
m_size += size;
|
||||
|
||||
/* Copy in the data to our buffer, if we don't have a full block. */
|
||||
if (work_remaining > size) {
|
||||
if (size > 0) {
|
||||
std::memcpy(m_y + work_idx, data, size);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Copy what we can to complete our block. */
|
||||
std::memcpy(m_y + work_idx, data, work_remaining);
|
||||
|
||||
/* Process the block. */
|
||||
this->ProcessBlock();
|
||||
|
||||
/* Adjust size to account for what we've processed. */
|
||||
size -= work_remaining;
|
||||
|
||||
/* Process as many full blocks as we can. */
|
||||
const u8 *cur_block = static_cast<const u8 *>(data) + work_remaining;
|
||||
for (size_t i = 0; i < size / BlockSize; ++i) {
|
||||
std::memcpy(m_y, cur_block, BlockSize);
|
||||
cur_block += BlockSize;
|
||||
|
||||
this->ProcessBlock();
|
||||
}
|
||||
|
||||
/* Copy in any leftover data. */
|
||||
if (const auto left = size % BlockSize; left > 0) {
|
||||
std::memcpy(m_y, cur_block, left);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Md5Impl::GetHash(void *dst, size_t size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(m_state == State_Initialized || m_state == State_Done);
|
||||
AMS_ASSERT(size >= HashSize);
|
||||
AMS_UNUSED(size);
|
||||
|
||||
/* If we need to, finish processing. */
|
||||
if (m_state == State_Initialized) {
|
||||
this->ProcessLastBlock();
|
||||
m_state = State_Done;
|
||||
}
|
||||
|
||||
/* Encode the result. */
|
||||
Encode(static_cast<u32 *>(dst), m_x.state, HashSize);
|
||||
}
|
||||
|
||||
void Md5Impl::ProcessBlock() {
|
||||
/* Declare tracking pointers for rounds. */
|
||||
u32 x[BlockSize / sizeof(u32)];
|
||||
const u32 *p_t = Md5Constants::T;
|
||||
const u32 *p_k = Md5Constants::K;
|
||||
const u32 *p_x = x;
|
||||
|
||||
/* Extract current state. */
|
||||
u32 a = m_x.p.a;
|
||||
u32 b = m_x.p.b;
|
||||
u32 c = m_x.p.c;
|
||||
u32 d = m_x.p.d;
|
||||
|
||||
/* Decode the block into native endian. */
|
||||
Decode(x, reinterpret_cast<const u32 *>(m_y), BlockSize);
|
||||
|
||||
/* Perform round 1. */
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
a = CalculateRound1(a, b, c, d, *p_x++, 7, *p_t++);
|
||||
d = CalculateRound1(d, a, b, c, *p_x++, 12, *p_t++);
|
||||
c = CalculateRound1(c, d, a, b, *p_x++, 17, *p_t++);
|
||||
b = CalculateRound1(b, c, d, a, *p_x++, 22, *p_t++);
|
||||
}
|
||||
|
||||
/* Perform round 2. */
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
a = CalculateRound2(a, b, c, d, x[*p_k++], 5, *p_t++);
|
||||
d = CalculateRound2(d, a, b, c, x[*p_k++], 9, *p_t++);
|
||||
c = CalculateRound2(c, d, a, b, x[*p_k++], 14, *p_t++);
|
||||
b = CalculateRound2(b, c, d, a, x[*p_k++], 20, *p_t++);
|
||||
}
|
||||
|
||||
/* Perform round 3. */
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
a = CalculateRound3(a, b, c, d, x[*p_k++], 4, *p_t++);
|
||||
d = CalculateRound3(d, a, b, c, x[*p_k++], 11, *p_t++);
|
||||
c = CalculateRound3(c, d, a, b, x[*p_k++], 16, *p_t++);
|
||||
b = CalculateRound3(b, c, d, a, x[*p_k++], 23, *p_t++);
|
||||
}
|
||||
|
||||
/* Perform round 4. */
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
a = CalculateRound4(a, b, c, d, x[*p_k++], 6, *p_t++);
|
||||
d = CalculateRound4(d, a, b, c, x[*p_k++], 10, *p_t++);
|
||||
c = CalculateRound4(c, d, a, b, x[*p_k++], 15, *p_t++);
|
||||
b = CalculateRound4(b, c, d, a, x[*p_k++], 21, *p_t++);
|
||||
}
|
||||
|
||||
/* Mix the result back into our state. */
|
||||
m_x.p.a += a;
|
||||
m_x.p.b += b;
|
||||
m_x.p.c += c;
|
||||
m_x.p.d += d;
|
||||
}
|
||||
|
||||
void Md5Impl::ProcessLastBlock() {
|
||||
/* Get bit count. */
|
||||
const u64 bit_count = m_size * BITSIZEOF(u8);
|
||||
|
||||
/* Add padding byte unconditionally. */
|
||||
this->Update(Md5Constants::Padding, sizeof(Md5Constants::Padding));
|
||||
|
||||
/* Determine remaining. */
|
||||
size_t work_idx = m_size % BlockSize;
|
||||
size_t work_remaining = BlockSize - work_idx;
|
||||
|
||||
/* We want to process 8000.....{bit count}. */
|
||||
if (work_remaining < sizeof(u64)) {
|
||||
std::memset(m_y + work_idx, 0, work_remaining);
|
||||
this->ProcessBlock();
|
||||
work_idx = 0;
|
||||
work_remaining = BlockSize;
|
||||
}
|
||||
if (work_remaining > sizeof(u64)) {
|
||||
std::memset(m_y + work_idx, 0, work_remaining - sizeof(u64));
|
||||
}
|
||||
|
||||
util::StoreLittleEndian<u64>(reinterpret_cast<u64 *>(m_y + BlockSize - sizeof(u64)), bit_count);
|
||||
|
||||
this->ProcessBlock();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,247 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <arm_neon.h>
|
||||
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const u32 RoundConstants[4] = {
|
||||
0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6
|
||||
};
|
||||
|
||||
/* Define for loading work var from message. */
|
||||
#define SHA1_LOAD_W_FROM_MESSAGE(which) \
|
||||
w[which] = vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(data))); \
|
||||
data += 0x10
|
||||
|
||||
#define SHA1_CALCULATE_W_FROM_PREVIOUS(i) \
|
||||
w[i] = vsha1su1q_u32(vsha1su0q_u32(w[i-4], w[i-3], w[i-2]), w[i-1])
|
||||
|
||||
/* Define for doing four rounds of SHA1. */
|
||||
#define SHA1_DO_ROUND(r, insn, constant) \
|
||||
do { \
|
||||
const u32 a = vgetq_lane_u32(cur_abcd, 0); \
|
||||
cur_abcd = v##insn##q_u32(cur_abcd, cur_e, vaddq_u32(w[r], constant)); \
|
||||
cur_e = vsha1h_u32(a); \
|
||||
} while (0)
|
||||
|
||||
|
||||
}
|
||||
|
||||
void Sha1Impl::Initialize() {
|
||||
/* Reset buffered bytes/bits. */
|
||||
m_buffered_bytes = 0;
|
||||
m_bits_consumed = 0;
|
||||
|
||||
/* Set intermediate hash. */
|
||||
m_intermediate_hash[0] = 0x67452301;
|
||||
m_intermediate_hash[1] = 0xEFCDAB89;
|
||||
m_intermediate_hash[2] = 0x98BADCFE;
|
||||
m_intermediate_hash[3] = 0x10325476;
|
||||
m_intermediate_hash[4] = 0xC3D2E1F0;
|
||||
|
||||
/* Set state. */
|
||||
m_state = State_Initialized;
|
||||
}
|
||||
|
||||
void Sha1Impl::Update(const void *data, size_t size) {
|
||||
/* Verify we're in a state to update. */
|
||||
AMS_ASSERT(m_state == State_Initialized);
|
||||
|
||||
/* Advance our input bit count. */
|
||||
m_bits_consumed += BITSIZEOF(u8) * (((m_buffered_bytes + size) / BlockSize) * BlockSize);
|
||||
|
||||
/* Process anything we have buffered. */
|
||||
const u8 *data8 = static_cast<const u8 *>(data);
|
||||
size_t remaining = size;
|
||||
|
||||
if (m_buffered_bytes > 0) {
|
||||
const size_t copy_size = std::min(BlockSize - m_buffered_bytes, remaining);
|
||||
std::memcpy(m_buffer + m_buffered_bytes, data8, copy_size);
|
||||
|
||||
data8 += copy_size;
|
||||
remaining -= copy_size;
|
||||
m_buffered_bytes += copy_size;
|
||||
|
||||
/* Process a block, if we filled one. */
|
||||
if (m_buffered_bytes == BlockSize) {
|
||||
this->ProcessBlock(m_buffer);
|
||||
m_buffered_bytes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Process blocks, if we have any. */
|
||||
if (remaining >= BlockSize) {
|
||||
const size_t blocks = remaining / BlockSize;
|
||||
|
||||
this->ProcessBlocks(data8, blocks);
|
||||
data8 += BlockSize * blocks;
|
||||
remaining -= BlockSize * blocks;
|
||||
}
|
||||
|
||||
/* Copy any leftover data to our buffer. */
|
||||
if (remaining > 0) {
|
||||
m_buffered_bytes = remaining;
|
||||
std::memcpy(m_buffer, data8, remaining);
|
||||
}
|
||||
}
|
||||
|
||||
void Sha1Impl::GetHash(void *dst, size_t size) {
|
||||
/* Verify we're in a state to get hash. */
|
||||
AMS_ASSERT(m_state == State_Initialized || m_state == State_Done);
|
||||
AMS_ASSERT(size >= HashSize);
|
||||
AMS_UNUSED(size);
|
||||
|
||||
/* If we need to, process the last block. */
|
||||
if (m_state == State_Initialized) {
|
||||
this->ProcessLastBlock();
|
||||
m_state = State_Done;
|
||||
}
|
||||
|
||||
/* Copy the output hash. */
|
||||
if constexpr (util::IsLittleEndian()) {
|
||||
static_assert(HashSize % sizeof(u32) == 0);
|
||||
|
||||
u32 *dst_32 = static_cast<u32 *>(dst);
|
||||
for (size_t i = 0; i < HashSize / sizeof(u32); ++i) {
|
||||
dst_32[i] = util::LoadBigEndian<u32>(m_intermediate_hash + i);
|
||||
}
|
||||
} else {
|
||||
std::memcpy(dst, m_intermediate_hash, HashSize);
|
||||
}
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void Sha1Impl::ProcessBlock(const void *data) {
|
||||
return this->ProcessBlocks(static_cast<const u8 *>(data), 1);
|
||||
}
|
||||
|
||||
void Sha1Impl::ProcessBlocks(const u8 *data, size_t block_count) {
|
||||
/* Setup round constants. */
|
||||
const uint32x4_t k0 = vdupq_n_u32(RoundConstants[0]);
|
||||
const uint32x4_t k1 = vdupq_n_u32(RoundConstants[1]);
|
||||
const uint32x4_t k2 = vdupq_n_u32(RoundConstants[2]);
|
||||
const uint32x4_t k3 = vdupq_n_u32(RoundConstants[3]);
|
||||
|
||||
/* Load hash variables with intermediate state. */
|
||||
uint32x4_t cur_abcd = vld1q_u32(m_intermediate_hash + 0);
|
||||
u32 cur_e = m_intermediate_hash[4];
|
||||
|
||||
/* Actually do hash processing blocks. */
|
||||
do {
|
||||
/* Save current state. */
|
||||
const uint32x4_t prev_abcd = cur_abcd;
|
||||
const u32 prev_e = cur_e;
|
||||
|
||||
uint32x4_t w[20];
|
||||
|
||||
/* Setup w[0-3] with message. */
|
||||
SHA1_LOAD_W_FROM_MESSAGE(0);
|
||||
SHA1_LOAD_W_FROM_MESSAGE(1);
|
||||
SHA1_LOAD_W_FROM_MESSAGE(2);
|
||||
SHA1_LOAD_W_FROM_MESSAGE(3);
|
||||
|
||||
/* Calculate w[4-19], w[i] = sha1su1(sha1su0(w[i-4], w[i-3], w[i-2]), w[i-1]); */
|
||||
SHA1_CALCULATE_W_FROM_PREVIOUS(4);
|
||||
SHA1_CALCULATE_W_FROM_PREVIOUS(5);
|
||||
SHA1_CALCULATE_W_FROM_PREVIOUS(6);
|
||||
SHA1_CALCULATE_W_FROM_PREVIOUS(7);
|
||||
SHA1_CALCULATE_W_FROM_PREVIOUS(8);
|
||||
SHA1_CALCULATE_W_FROM_PREVIOUS(9);
|
||||
SHA1_CALCULATE_W_FROM_PREVIOUS(10);
|
||||
SHA1_CALCULATE_W_FROM_PREVIOUS(11);
|
||||
SHA1_CALCULATE_W_FROM_PREVIOUS(12);
|
||||
SHA1_CALCULATE_W_FROM_PREVIOUS(13);
|
||||
SHA1_CALCULATE_W_FROM_PREVIOUS(14);
|
||||
SHA1_CALCULATE_W_FROM_PREVIOUS(15);
|
||||
SHA1_CALCULATE_W_FROM_PREVIOUS(16);
|
||||
SHA1_CALCULATE_W_FROM_PREVIOUS(17);
|
||||
SHA1_CALCULATE_W_FROM_PREVIOUS(18);
|
||||
SHA1_CALCULATE_W_FROM_PREVIOUS(19);
|
||||
|
||||
/* Do round calculations 0-20. Uses sha1c, k0. */
|
||||
SHA1_DO_ROUND(0, sha1c, k0);
|
||||
SHA1_DO_ROUND(1, sha1c, k0);
|
||||
SHA1_DO_ROUND(2, sha1c, k0);
|
||||
SHA1_DO_ROUND(3, sha1c, k0);
|
||||
SHA1_DO_ROUND(4, sha1c, k0);
|
||||
|
||||
/* Do round calculations 20-40. Uses sha1p, k1. */
|
||||
SHA1_DO_ROUND(5, sha1p, k1);
|
||||
SHA1_DO_ROUND(6, sha1p, k1);
|
||||
SHA1_DO_ROUND(7, sha1p, k1);
|
||||
SHA1_DO_ROUND(8, sha1p, k1);
|
||||
SHA1_DO_ROUND(9, sha1p, k1);
|
||||
|
||||
/* Do round calculations 40-60. Uses sha1m, k2. */
|
||||
SHA1_DO_ROUND(10, sha1m, k2);
|
||||
SHA1_DO_ROUND(11, sha1m, k2);
|
||||
SHA1_DO_ROUND(12, sha1m, k2);
|
||||
SHA1_DO_ROUND(13, sha1m, k2);
|
||||
SHA1_DO_ROUND(14, sha1m, k2);
|
||||
|
||||
/* Do round calculations 60-80. Uses sha1p, k3. */
|
||||
SHA1_DO_ROUND(15, sha1p, k3);
|
||||
SHA1_DO_ROUND(16, sha1p, k3);
|
||||
SHA1_DO_ROUND(17, sha1p, k3);
|
||||
SHA1_DO_ROUND(18, sha1p, k3);
|
||||
SHA1_DO_ROUND(19, sha1p, k3);
|
||||
|
||||
/* Add to previous. */
|
||||
cur_abcd = vaddq_u32(cur_abcd, prev_abcd);
|
||||
cur_e = cur_e + prev_e;
|
||||
} while (--block_count != 0);
|
||||
|
||||
/* Save result to intermediate hash. */
|
||||
vst1q_u32(m_intermediate_hash, cur_abcd);
|
||||
m_intermediate_hash[4] = cur_e;
|
||||
}
|
||||
|
||||
void Sha1Impl::ProcessLastBlock() {
|
||||
/* Setup the final block. */
|
||||
constexpr const auto BlockSizeWithoutSizeField = BlockSize - sizeof(u64);
|
||||
|
||||
/* Increment our bits consumed. */
|
||||
m_bits_consumed += BITSIZEOF(u8) * m_buffered_bytes;
|
||||
|
||||
/* Add 0x80 terminator. */
|
||||
m_buffer[m_buffered_bytes++] = 0x80;
|
||||
|
||||
/* If we can process the size field directly, do so, otherwise set up to process it. */
|
||||
if (m_buffered_bytes <= BlockSizeWithoutSizeField) {
|
||||
/* Clear up to size field. */
|
||||
std::memset(m_buffer + m_buffered_bytes, 0, BlockSizeWithoutSizeField - m_buffered_bytes);
|
||||
} else {
|
||||
/* Consume full block */
|
||||
std::memset(m_buffer + m_buffered_bytes, 0, BlockSize - m_buffered_bytes);
|
||||
this->ProcessBlock(m_buffer);
|
||||
|
||||
/* Clear up to size field. */
|
||||
std::memset(m_buffer, 0, BlockSizeWithoutSizeField);
|
||||
}
|
||||
|
||||
/* Store the size field. */
|
||||
util::StoreBigEndian<u64>(reinterpret_cast<u64 *>(m_buffer + BlockSizeWithoutSizeField), m_bits_consumed);
|
||||
|
||||
/* Process the final block. */
|
||||
this->ProcessBlock(m_buffer);
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -1,225 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const u32 RoundConstants[4] = {
|
||||
0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6
|
||||
};
|
||||
|
||||
constexpr ALWAYS_INLINE u32 Choose(u32 x, u32 y, u32 z) {
|
||||
return (x & y) ^ ((~x) & z);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE u32 Majority(u32 x, u32 y, u32 z) {
|
||||
return (x & y) ^ (x & z) ^ (y & z);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE u32 Parity(u32 x, u32 y, u32 z) {
|
||||
return x ^ y ^ z;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Sha1Impl::Initialize() {
|
||||
/* Reset buffered bytes/bits. */
|
||||
m_buffered_bytes = 0;
|
||||
m_bits_consumed = 0;
|
||||
|
||||
/* Set intermediate hash. */
|
||||
m_intermediate_hash[0] = 0x67452301;
|
||||
m_intermediate_hash[1] = 0xEFCDAB89;
|
||||
m_intermediate_hash[2] = 0x98BADCFE;
|
||||
m_intermediate_hash[3] = 0x10325476;
|
||||
m_intermediate_hash[4] = 0xC3D2E1F0;
|
||||
|
||||
/* Set state. */
|
||||
m_state = State_Initialized;
|
||||
}
|
||||
|
||||
void Sha1Impl::Update(const void *data, size_t size) {
|
||||
/* Verify we're in a state to update. */
|
||||
AMS_ASSERT(m_state == State_Initialized);
|
||||
|
||||
/* Advance our input bit count. */
|
||||
m_bits_consumed += BITSIZEOF(u8) * (((m_buffered_bytes + size) / BlockSize) * BlockSize);
|
||||
|
||||
/* Process anything we have buffered. */
|
||||
const u8 *data8 = static_cast<const u8 *>(data);
|
||||
size_t remaining = size;
|
||||
|
||||
if (m_buffered_bytes > 0) {
|
||||
const size_t copy_size = std::min(BlockSize - m_buffered_bytes, remaining);
|
||||
std::memcpy(m_buffer + m_buffered_bytes, data8, copy_size);
|
||||
|
||||
data8 += copy_size;
|
||||
remaining -= copy_size;
|
||||
m_buffered_bytes += copy_size;
|
||||
|
||||
/* Process a block, if we filled one. */
|
||||
if (m_buffered_bytes == BlockSize) {
|
||||
this->ProcessBlock(m_buffer);
|
||||
m_buffered_bytes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Process blocks, while we have any. */
|
||||
while (remaining >= BlockSize) {
|
||||
this->ProcessBlock(data8);
|
||||
data8 += BlockSize;
|
||||
remaining -= BlockSize;
|
||||
}
|
||||
|
||||
/* Copy any leftover data to our buffer. */
|
||||
if (remaining > 0) {
|
||||
m_buffered_bytes = remaining;
|
||||
std::memcpy(m_buffer, data8, remaining);
|
||||
}
|
||||
}
|
||||
|
||||
void Sha1Impl::GetHash(void *dst, size_t size) {
|
||||
/* Verify we're in a state to get hash. */
|
||||
AMS_ASSERT(m_state == State_Initialized || m_state == State_Done);
|
||||
AMS_ASSERT(size >= HashSize);
|
||||
AMS_UNUSED(size);
|
||||
|
||||
/* If we need to, process the last block. */
|
||||
if (m_state == State_Initialized) {
|
||||
this->ProcessLastBlock();
|
||||
m_state = State_Done;
|
||||
}
|
||||
|
||||
/* Copy the output hash. */
|
||||
if constexpr (util::IsLittleEndian()) {
|
||||
static_assert(HashSize % sizeof(u32) == 0);
|
||||
|
||||
u32 *dst_32 = static_cast<u32 *>(dst);
|
||||
for (size_t i = 0; i < HashSize / sizeof(u32); ++i) {
|
||||
dst_32[i] = util::LoadBigEndian<u32>(m_intermediate_hash + i);
|
||||
}
|
||||
} else {
|
||||
std::memcpy(dst, m_intermediate_hash, HashSize);
|
||||
}
|
||||
}
|
||||
|
||||
void Sha1Impl::ProcessBlock(const void *data) {
|
||||
/* Load work variables. */
|
||||
u32 a = m_intermediate_hash[0];
|
||||
u32 b = m_intermediate_hash[1];
|
||||
u32 c = m_intermediate_hash[2];
|
||||
u32 d = m_intermediate_hash[3];
|
||||
u32 e = m_intermediate_hash[4];
|
||||
u32 tmp;
|
||||
size_t i;
|
||||
|
||||
/* Copy the input. */
|
||||
u32 w[80];
|
||||
if constexpr (util::IsLittleEndian()) {
|
||||
static_assert(BlockSize % sizeof(u32) == 0);
|
||||
|
||||
const u32 *src_32 = static_cast<const u32 *>(data);
|
||||
for (size_t i = 0; i < BlockSize / sizeof(u32); ++i) {
|
||||
w[i] = util::LoadBigEndian<u32>(src_32 + i);
|
||||
}
|
||||
} else {
|
||||
std::memcpy(w, data, BlockSize);
|
||||
}
|
||||
|
||||
/* Initialize the rest of w. */
|
||||
for (i = BlockSize / sizeof(u32); i < util::size(w); ++i) {
|
||||
const u32 *prev = w + (i - BlockSize / sizeof(u32));
|
||||
w[i] = util::RotateLeft<u32>(prev[0] ^ prev[2] ^ prev[8] ^ prev[13], 1);
|
||||
}
|
||||
|
||||
/* Perform rounds. */
|
||||
for (i = 0; i < 20; ++i) {
|
||||
tmp = util::RotateLeft<u32>(a, 5) + Choose(b, c, d) + e + w[i] + RoundConstants[0];
|
||||
e = d;
|
||||
d = c;
|
||||
c = util::RotateLeft<u32>(b, 30);
|
||||
b = a;
|
||||
a = tmp;
|
||||
}
|
||||
|
||||
for (/* ... */; i < 40; ++i) {
|
||||
tmp = util::RotateLeft<u32>(a, 5) + Parity(b, c, d) + e + w[i] + RoundConstants[1];
|
||||
e = d;
|
||||
d = c;
|
||||
c = util::RotateLeft<u32>(b, 30);
|
||||
b = a;
|
||||
a = tmp;
|
||||
}
|
||||
|
||||
for (/* ... */; i < 60; ++i) {
|
||||
tmp = util::RotateLeft<u32>(a, 5) + Majority(b, c, d) + e + w[i] + RoundConstants[2];
|
||||
e = d;
|
||||
d = c;
|
||||
c = util::RotateLeft<u32>(b, 30);
|
||||
b = a;
|
||||
a = tmp;
|
||||
}
|
||||
|
||||
for (/* ... */; i < 80; ++i) {
|
||||
tmp = util::RotateLeft<u32>(a, 5) + Parity(b, c, d) + e + w[i] + RoundConstants[3];
|
||||
e = d;
|
||||
d = c;
|
||||
c = util::RotateLeft<u32>(b, 30);
|
||||
b = a;
|
||||
a = tmp;
|
||||
}
|
||||
|
||||
/* Update intermediate hash. */
|
||||
m_intermediate_hash[0] += a;
|
||||
m_intermediate_hash[1] += b;
|
||||
m_intermediate_hash[2] += c;
|
||||
m_intermediate_hash[3] += d;
|
||||
m_intermediate_hash[4] += e;
|
||||
}
|
||||
|
||||
void Sha1Impl::ProcessLastBlock() {
|
||||
/* Setup the final block. */
|
||||
constexpr const auto BlockSizeWithoutSizeField = BlockSize - sizeof(u64);
|
||||
|
||||
/* Increment our bits consumed. */
|
||||
m_bits_consumed += BITSIZEOF(u8) * m_buffered_bytes;
|
||||
|
||||
/* Add 0x80 terminator. */
|
||||
m_buffer[m_buffered_bytes++] = 0x80;
|
||||
|
||||
/* If we can process the size field directly, do so, otherwise set up to process it. */
|
||||
if (m_buffered_bytes <= BlockSizeWithoutSizeField) {
|
||||
/* Clear up to size field. */
|
||||
std::memset(m_buffer + m_buffered_bytes, 0, BlockSizeWithoutSizeField - m_buffered_bytes);
|
||||
} else {
|
||||
/* Consume full block */
|
||||
std::memset(m_buffer + m_buffered_bytes, 0, BlockSize - m_buffered_bytes);
|
||||
this->ProcessBlock(m_buffer);
|
||||
|
||||
/* Clear up to size field. */
|
||||
std::memset(m_buffer, 0, BlockSizeWithoutSizeField);
|
||||
}
|
||||
|
||||
/* Store the size field. */
|
||||
util::StoreBigEndian<u64>(reinterpret_cast<u64 *>(m_buffer + BlockSizeWithoutSizeField), m_bits_consumed);
|
||||
|
||||
/* Process the final block. */
|
||||
this->ProcessBlock(m_buffer);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,327 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <arm_neon.h>
|
||||
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
alignas(Sha256Impl::BlockSize) constexpr const u32 RoundConstants[0x40] = {
|
||||
0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5,
|
||||
0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,
|
||||
0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,
|
||||
0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,
|
||||
0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC,
|
||||
0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
|
||||
0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7,
|
||||
0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,
|
||||
0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,
|
||||
0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,
|
||||
0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3,
|
||||
0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
|
||||
0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5,
|
||||
0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,
|
||||
0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,
|
||||
0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void Sha256Impl::Initialize() {
|
||||
/* Reset buffered bytes/bits. */
|
||||
m_buffered_bytes = 0;
|
||||
m_bits_consumed = 0;
|
||||
|
||||
/* Set intermediate hash. */
|
||||
m_intermediate_hash[0] = 0x6A09E667;
|
||||
m_intermediate_hash[1] = 0xBB67AE85;
|
||||
m_intermediate_hash[2] = 0x3C6EF372;
|
||||
m_intermediate_hash[3] = 0xA54FF53A;
|
||||
m_intermediate_hash[4] = 0x510E527F;
|
||||
m_intermediate_hash[5] = 0x9B05688C;
|
||||
m_intermediate_hash[6] = 0x1F83D9AB;
|
||||
m_intermediate_hash[7] = 0x5BE0CD19;
|
||||
|
||||
/* Set state. */
|
||||
m_state = State_Initialized;
|
||||
}
|
||||
|
||||
void Sha256Impl::Update(const void *data, size_t size) {
|
||||
/* Verify we're in a state to update. */
|
||||
AMS_ASSERT(m_state == State_Initialized);
|
||||
|
||||
/* Advance our input bit count. */
|
||||
m_bits_consumed += BITSIZEOF(u8) * (((m_buffered_bytes + size) / BlockSize) * BlockSize);
|
||||
|
||||
/* Process anything we have buffered. */
|
||||
const u8 *data8 = static_cast<const u8 *>(data);
|
||||
size_t remaining = size;
|
||||
|
||||
if (m_buffered_bytes > 0) {
|
||||
const size_t copy_size = std::min(BlockSize - m_buffered_bytes, remaining);
|
||||
std::memcpy(m_buffer + m_buffered_bytes, data8, copy_size);
|
||||
|
||||
data8 += copy_size;
|
||||
remaining -= copy_size;
|
||||
m_buffered_bytes += copy_size;
|
||||
|
||||
/* Process a block, if we filled one. */
|
||||
if (m_buffered_bytes == BlockSize) {
|
||||
this->ProcessBlock(m_buffer);
|
||||
m_buffered_bytes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Process blocks, if we have any. */
|
||||
if (remaining >= BlockSize) {
|
||||
const size_t blocks = remaining / BlockSize;
|
||||
|
||||
this->ProcessBlocks(data8, blocks);
|
||||
data8 += BlockSize * blocks;
|
||||
remaining -= BlockSize * blocks;
|
||||
}
|
||||
|
||||
/* Copy any leftover data to our buffer. */
|
||||
if (remaining > 0) {
|
||||
m_buffered_bytes = remaining;
|
||||
std::memcpy(m_buffer, data8, remaining);
|
||||
}
|
||||
}
|
||||
|
||||
void Sha256Impl::GetHash(void *dst, size_t size) {
|
||||
/* Verify we're in a state to get hash. */
|
||||
AMS_ASSERT(m_state == State_Initialized || m_state == State_Done);
|
||||
AMS_ASSERT(size >= HashSize);
|
||||
AMS_UNUSED(size);
|
||||
|
||||
/* If we need to, process the last block. */
|
||||
if (m_state == State_Initialized) {
|
||||
this->ProcessLastBlock();
|
||||
m_state = State_Done;
|
||||
}
|
||||
|
||||
/* Copy the output hash. */
|
||||
if constexpr (util::IsLittleEndian()) {
|
||||
static_assert(HashSize % sizeof(u32) == 0);
|
||||
|
||||
u32 *dst_32 = static_cast<u32 *>(dst);
|
||||
for (size_t i = 0; i < HashSize / sizeof(u32); ++i) {
|
||||
dst_32[i] = util::LoadBigEndian<u32>(m_intermediate_hash + i);
|
||||
}
|
||||
} else {
|
||||
std::memcpy(dst, m_intermediate_hash, HashSize);
|
||||
}
|
||||
}
|
||||
|
||||
void Sha256Impl::InitializeWithContext(const Sha256Context *context) {
|
||||
/* Copy state in from the context. */
|
||||
std::memcpy(m_intermediate_hash, context->intermediate_hash, sizeof(m_intermediate_hash));
|
||||
m_bits_consumed = context->bits_consumed;
|
||||
|
||||
/* Reset other fields. */
|
||||
m_buffered_bytes = 0;
|
||||
m_state = State_Initialized;
|
||||
}
|
||||
|
||||
size_t Sha256Impl::GetContext(Sha256Context *context) const {
|
||||
/* Check our state. */
|
||||
AMS_ASSERT(m_state == State_Initialized);
|
||||
|
||||
/* Copy out the context. */
|
||||
std::memcpy(context->intermediate_hash, m_intermediate_hash, sizeof(context->intermediate_hash));
|
||||
context->bits_consumed = m_bits_consumed;
|
||||
|
||||
return m_buffered_bytes;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void Sha256Impl::ProcessBlock(const void *data) {
|
||||
return this->ProcessBlocks(static_cast<const u8 *>(data), 1);
|
||||
}
|
||||
|
||||
void Sha256Impl::ProcessBlocks(const u8 *data, size_t block_count) {
|
||||
/* Load previous hash with intermediate state, current hash with zeroes. */
|
||||
uint32x4_t prev_hash0 = vld1q_u32(m_intermediate_hash + 0);
|
||||
uint32x4_t prev_hash1 = vld1q_u32(m_intermediate_hash + 4);
|
||||
uint32x4_t cur_hash0 = vdupq_n_u32(0);
|
||||
uint32x4_t cur_hash1 = vdupq_n_u32(0);
|
||||
|
||||
/* Process blocks. */
|
||||
do {
|
||||
uint32x4_t round_constant0, round_constant1;
|
||||
uint32x4_t data0, data1, data2, data3;
|
||||
uint32x4_t tmp0, tmp1, tmp2, tmp3;
|
||||
uint32x4_t tmp_hash;
|
||||
|
||||
/* Use optimized ASM implementation to process the block. */
|
||||
__asm__ __volatile__ (
|
||||
"ldp %q[data0], %q[data1], [%[data]], #0x20\n"
|
||||
"ldp %q[data2], %q[data3], [%[data]], #0x20\n"
|
||||
"add %[cur_hash0].4s, %[cur_hash0].4s, %[prev_hash0].4s\n"
|
||||
"ldp %q[round_constant0], %q[round_constant1], [%[round_constants], 0x00]\n"
|
||||
"add %[cur_hash1].4s, %[cur_hash1].4s, %[prev_hash1].4s\n"
|
||||
"rev32 %[data0].16b, %[data0].16b\n"
|
||||
"rev32 %[data1].16b, %[data1].16b\n"
|
||||
"rev32 %[data2].16b, %[data2].16b\n"
|
||||
"rev32 %[data3].16b, %[data3].16b\n"
|
||||
"add %[tmp0].4s, %[data0].4s, %[round_constant0].4s\n"
|
||||
"add %[tmp1].4s, %[data1].4s, %[round_constant1].4s\n"
|
||||
"ldp %q[round_constant0], %q[round_constant1], [%[round_constants], 0x20]\n"
|
||||
"sha256su0 %[data0].4s, %[data1].4s\n"
|
||||
"mov %[prev_hash0].16b, %[cur_hash0].16b\n"
|
||||
"sha256h %q[cur_hash0], %q[cur_hash1], %[tmp0].4s\n"
|
||||
"mov %[prev_hash1].16b, %[cur_hash1].16b\n"
|
||||
"sha256h2 %q[cur_hash1], %q[prev_hash0], %[tmp0].4s\n"
|
||||
"sha256su0 %[data1].4s, %[data2].4s\n"
|
||||
"sha256su1 %[data0].4s, %[data2].4s, %[data3].4s\n"
|
||||
"add %[tmp2].4s, %[data2].4s, %[round_constant0].4s\n"
|
||||
"mov %[tmp_hash].16b, %[cur_hash0].16b\n"
|
||||
"sha256h %q[cur_hash0], %q[cur_hash1], %[tmp1].4s\n"
|
||||
"sha256h2 %q[cur_hash1], %q[tmp_hash], %[tmp1].4s\n"
|
||||
"sha256su0 %[data2].4s, %[data3].4s\n"
|
||||
"sha256su1 %[data1].4s, %[data3].4s, %[data0].4s\n"
|
||||
"add %[tmp3].4s, %[data3].4s, %[round_constant1].4s\n"
|
||||
"mov %[tmp_hash].16b, %[cur_hash0].16b\n"
|
||||
"ldp %q[round_constant0], %q[round_constant1], [%[round_constants], 0x40]\n"
|
||||
"sha256h %q[cur_hash0], %q[cur_hash1], %[tmp2].4s\n"
|
||||
"sha256h2 %q[cur_hash1], %q[tmp_hash], %[tmp2].4s\n"
|
||||
"sha256su0 %[data3].4s, %[data0].4s\n"
|
||||
"sha256su1 %[data2].4s, %[data0].4s, %[data1].4s\n"
|
||||
"add %[tmp0].4s, %[data0].4s, %[round_constant0].4s\n"
|
||||
"mov %[tmp_hash].16b, %[cur_hash0].16b\n"
|
||||
"sha256h %q[cur_hash0], %q[cur_hash1], %[tmp3].4s\n"
|
||||
"sha256h2 %q[cur_hash1], %q[tmp_hash], %[tmp3].4s\n"
|
||||
"sha256su0 %[data0].4s, %[data1].4s\n"
|
||||
"sha256su1 %[data3].4s, %[data1].4s, %[data2].4s\n"
|
||||
"add %[tmp1].4s, %[data1].4s, %[round_constant1].4s\n"
|
||||
"mov %[tmp_hash].16b, %[cur_hash0].16b\n"
|
||||
"ldp %q[round_constant0], %q[round_constant1], [%[round_constants], 0x60]\n"
|
||||
"sha256h %q[cur_hash0], %q[cur_hash1], %[tmp0].4s\n"
|
||||
"sha256h2 %q[cur_hash1], %q[tmp_hash], %[tmp0].4s\n"
|
||||
"sha256su0 %[data1].4s, %[data2].4s\n"
|
||||
"sha256su1 %[data0].4s, %[data2].4s, %[data3].4s\n"
|
||||
"add %[tmp2].4s, %[data2].4s, %[round_constant0].4s\n"
|
||||
"mov %[tmp_hash].16b, %[cur_hash0].16b\n"
|
||||
"sha256h %q[cur_hash0], %q[cur_hash1], %[tmp1].4s\n"
|
||||
"sha256h2 %q[cur_hash1], %q[tmp_hash], %[tmp1].4s\n"
|
||||
"sha256su0 %[data2].4s, %[data3].4s\n"
|
||||
"sha256su1 %[data1].4s, %[data3].4s, %[data0].4s\n"
|
||||
"add %[tmp3].4s, %[data3].4s, %[round_constant1].4s\n"
|
||||
"mov %[tmp_hash].16b, %[cur_hash0].16b\n"
|
||||
"ldp %q[round_constant0], %q[round_constant1], [%[round_constants], 0x80]\n"
|
||||
"sha256h %q[cur_hash0], %q[cur_hash1], %[tmp2].4s\n"
|
||||
"sha256h2 %q[cur_hash1], %q[tmp_hash], %[tmp2].4s\n"
|
||||
"sha256su0 %[data3].4s, %[data0].4s\n"
|
||||
"sha256su1 %[data2].4s, %[data0].4s, %[data1].4s\n"
|
||||
"add %[tmp0].4s, %[data0].4s, %[round_constant0].4s\n"
|
||||
"mov %[tmp_hash].16b, %[cur_hash0].16b\n"
|
||||
"sha256h %q[cur_hash0], %q[cur_hash1], %[tmp3].4s\n"
|
||||
"sha256h2 %q[cur_hash1], %q[tmp_hash], %[tmp3].4s\n"
|
||||
"sha256su0 %[data0].4s, %[data1].4s\n"
|
||||
"sha256su1 %[data3].4s, %[data1].4s, %[data2].4s\n"
|
||||
"add %[tmp1].4s, %[data1].4s, %[round_constant1].4s\n"
|
||||
"mov %[tmp_hash].16b, %[cur_hash0].16b\n"
|
||||
"ldp %q[round_constant0], %q[round_constant1], [%[round_constants], 0xA0]\n"
|
||||
"sha256h %q[cur_hash0], %q[cur_hash1], %[tmp0].4s\n"
|
||||
"sha256h2 %q[cur_hash1], %q[tmp_hash], %[tmp0].4s\n"
|
||||
"sha256su0 %[data1].4s, %[data2].4s\n"
|
||||
"sha256su1 %[data0].4s, %[data2].4s, %[data3].4s\n"
|
||||
"add %[tmp2].4s, %[data2].4s, %[round_constant0].4s\n"
|
||||
"mov %[tmp_hash].16b, %[cur_hash0].16b\n"
|
||||
"sha256h %q[cur_hash0], %q[cur_hash1], %[tmp1].4s\n"
|
||||
"sha256h2 %q[cur_hash1], %q[tmp_hash], %[tmp1].4s\n"
|
||||
"sha256su0 %[data2].4s, %[data3].4s\n"
|
||||
"sha256su1 %[data1].4s, %[data3].4s, %[data0].4s\n"
|
||||
"add %[tmp3].4s, %[data3].4s, %[round_constant1].4s\n"
|
||||
"mov %[tmp_hash].16b, %[cur_hash0].16b\n"
|
||||
"ldp %q[round_constant0], %q[round_constant1], [%[round_constants], 0xC0]\n"
|
||||
"sha256h %q[cur_hash0], %q[cur_hash1], %[tmp2].4s\n"
|
||||
"sha256h2 %q[cur_hash1], %q[tmp_hash], %[tmp2].4s\n"
|
||||
"sha256su0 %[data3].4s, %[data0].4s\n"
|
||||
"sha256su1 %[data2].4s, %[data0].4s, %[data1].4s\n"
|
||||
"add %[tmp0].4s, %[data0].4s, %[round_constant0].4s\n"
|
||||
"mov %[tmp_hash].16b, %[cur_hash0].16b\n"
|
||||
"sha256h %q[cur_hash0], %q[cur_hash1], %[tmp3].4s\n"
|
||||
"sha256h2 %q[cur_hash1], %q[tmp_hash], %[tmp3].4s\n"
|
||||
"sha256su1 %[data3].4s, %[data1].4s, %[data2].4s\n"
|
||||
"add %[tmp1].4s, %[data1].4s, %[round_constant1].4s\n"
|
||||
"mov %[tmp_hash].16b, %[cur_hash0].16b\n"
|
||||
"ldp %q[round_constant0], %q[round_constant1], [%[round_constants], 0xE0]\n"
|
||||
"sha256h %q[cur_hash0], %q[cur_hash1], %[tmp0].4s\n"
|
||||
"sha256h2 %q[cur_hash1], %q[tmp_hash], %[tmp0].4s\n"
|
||||
"add %[tmp2].4s, %[data2].4s, %[round_constant0].4s\n"
|
||||
"mov %[tmp_hash].16b, %[cur_hash0].16b\n"
|
||||
"sha256h %q[cur_hash0], %q[cur_hash1], %[tmp1].4s\n"
|
||||
"sha256h2 %q[cur_hash1], %q[tmp_hash], %[tmp1].4s\n"
|
||||
"add %[tmp3].4s, %[data3].4s, %[round_constant1].4s\n"
|
||||
"mov %[tmp_hash].16b, %[cur_hash0].16b\n"
|
||||
"sha256h %q[cur_hash0], %q[cur_hash1], %[tmp2].4s\n"
|
||||
"sha256h2 %q[cur_hash1], %q[tmp_hash], %[tmp2].4s\n"
|
||||
"mov %[tmp_hash].16b, %[cur_hash0].16b\n"
|
||||
"sha256h %q[cur_hash0], %q[cur_hash1], %[tmp3].4s\n"
|
||||
"sha256h2 %q[cur_hash1], %q[tmp_hash], %[tmp3].4s\n"
|
||||
: [data0]"=w"(data0), [data1]"=w"(data1), [data2]"=w"(data2), [data3]"=w"(data3),
|
||||
[tmp0]"=w"(tmp0), [tmp1]"=w"(tmp1), [tmp2]"=w"(tmp2), [tmp3]"=w"(tmp3),
|
||||
[round_constant0]"=w"(round_constant0), [round_constant1]"=w"(round_constant1),
|
||||
[cur_hash0]"+w"(cur_hash0), [cur_hash1]"+w"(cur_hash1),
|
||||
[prev_hash0]"+w"(prev_hash0), [prev_hash1]"+w"(prev_hash1),
|
||||
[tmp_hash]"=w"(tmp_hash), [data]"+r"(data)
|
||||
: "m"(*(const u8 (*)[block_count*BlockSize])data), [round_constants]"r"(RoundConstants)
|
||||
:
|
||||
);
|
||||
} while (--block_count != 0);
|
||||
|
||||
/* Add hashes together, and store. */
|
||||
cur_hash0 = vaddq_u32(prev_hash0, cur_hash0);
|
||||
cur_hash1 = vaddq_u32(prev_hash1, cur_hash1);
|
||||
vst1q_u32(m_intermediate_hash + 0, cur_hash0);
|
||||
vst1q_u32(m_intermediate_hash + 4, cur_hash1);
|
||||
}
|
||||
|
||||
void Sha256Impl::ProcessLastBlock() {
|
||||
/* Setup the final block. */
|
||||
constexpr const auto BlockSizeWithoutSizeField = BlockSize - sizeof(u64);
|
||||
|
||||
/* Increment our bits consumed. */
|
||||
m_bits_consumed += BITSIZEOF(u8) * m_buffered_bytes;
|
||||
|
||||
/* Add 0x80 terminator. */
|
||||
m_buffer[m_buffered_bytes++] = 0x80;
|
||||
|
||||
/* If we can process the size field directly, do so, otherwise set up to process it. */
|
||||
if (m_buffered_bytes <= BlockSizeWithoutSizeField) {
|
||||
/* Clear up to size field. */
|
||||
std::memset(m_buffer + m_buffered_bytes, 0, BlockSizeWithoutSizeField - m_buffered_bytes);
|
||||
} else {
|
||||
/* Consume full block */
|
||||
std::memset(m_buffer + m_buffered_bytes, 0, BlockSize - m_buffered_bytes);
|
||||
this->ProcessBlock(m_buffer);
|
||||
|
||||
/* Clear up to size field. */
|
||||
std::memset(m_buffer, 0, BlockSizeWithoutSizeField);
|
||||
}
|
||||
|
||||
/* Store the size field. */
|
||||
util::StoreBigEndian<u64>(reinterpret_cast<u64 *>(m_buffer + BlockSizeWithoutSizeField), m_bits_consumed);
|
||||
|
||||
/* Process the final block. */
|
||||
this->ProcessBlock(m_buffer);
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -1,260 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
alignas(Sha256Impl::BlockSize) constexpr const u32 RoundConstants[0x40] = {
|
||||
0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5,
|
||||
0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,
|
||||
0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,
|
||||
0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,
|
||||
0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC,
|
||||
0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
|
||||
0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7,
|
||||
0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,
|
||||
0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,
|
||||
0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,
|
||||
0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3,
|
||||
0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
|
||||
0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5,
|
||||
0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,
|
||||
0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,
|
||||
0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2,
|
||||
};
|
||||
|
||||
constexpr ALWAYS_INLINE u32 Choose(u32 x, u32 y, u32 z) {
|
||||
return (x & y) ^ ((~x) & z);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE u32 Majority(u32 x, u32 y, u32 z) {
|
||||
return (x & y) ^ (x & z) ^ (y & z);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE u32 LargeSigma0(u32 x) {
|
||||
return util::RotateRight<u32>(x, 2) ^ util::RotateRight<u32>(x, 13) ^ util::RotateRight<u32>(x, 22);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE u32 LargeSigma1(u32 x) {
|
||||
return util::RotateRight<u32>(x, 6) ^ util::RotateRight<u32>(x, 11) ^ util::RotateRight<u32>(x, 25);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE u32 SmallSigma0(u32 x) {
|
||||
return util::RotateRight<u32>(x, 7) ^ util::RotateRight<u32>(x, 18) ^ (x >> 3);
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE u32 SmallSigma1(u32 x) {
|
||||
return util::RotateRight<u32>(x, 17) ^ util::RotateRight<u32>(x, 19) ^ (x >> 10);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Sha256Impl::Initialize() {
|
||||
/* Reset buffered bytes/bits. */
|
||||
m_buffered_bytes = 0;
|
||||
m_bits_consumed = 0;
|
||||
|
||||
/* Set intermediate hash. */
|
||||
m_intermediate_hash[0] = 0x6A09E667;
|
||||
m_intermediate_hash[1] = 0xBB67AE85;
|
||||
m_intermediate_hash[2] = 0x3C6EF372;
|
||||
m_intermediate_hash[3] = 0xA54FF53A;
|
||||
m_intermediate_hash[4] = 0x510E527F;
|
||||
m_intermediate_hash[5] = 0x9B05688C;
|
||||
m_intermediate_hash[6] = 0x1F83D9AB;
|
||||
m_intermediate_hash[7] = 0x5BE0CD19;
|
||||
|
||||
/* Set state. */
|
||||
m_state = State_Initialized;
|
||||
}
|
||||
|
||||
void Sha256Impl::Update(const void *data, size_t size) {
|
||||
/* Verify we're in a state to update. */
|
||||
AMS_ASSERT(m_state == State_Initialized);
|
||||
|
||||
/* Advance our input bit count. */
|
||||
m_bits_consumed += BITSIZEOF(u8) * (((m_buffered_bytes + size) / BlockSize) * BlockSize);
|
||||
|
||||
/* Process anything we have buffered. */
|
||||
const u8 *data8 = static_cast<const u8 *>(data);
|
||||
size_t remaining = size;
|
||||
|
||||
if (m_buffered_bytes > 0) {
|
||||
const size_t copy_size = std::min(BlockSize - m_buffered_bytes, remaining);
|
||||
std::memcpy(m_buffer + m_buffered_bytes, data8, copy_size);
|
||||
|
||||
data8 += copy_size;
|
||||
remaining -= copy_size;
|
||||
m_buffered_bytes += copy_size;
|
||||
|
||||
/* Process a block, if we filled one. */
|
||||
if (m_buffered_bytes == BlockSize) {
|
||||
this->ProcessBlock(m_buffer);
|
||||
m_buffered_bytes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Process blocks, if we have any. */
|
||||
while (remaining >= BlockSize) {
|
||||
this->ProcessBlock(data8);
|
||||
data8 += BlockSize;
|
||||
remaining -= BlockSize;
|
||||
}
|
||||
|
||||
/* Copy any leftover data to our buffer. */
|
||||
if (remaining > 0) {
|
||||
m_buffered_bytes = remaining;
|
||||
std::memcpy(m_buffer, data8, remaining);
|
||||
}
|
||||
}
|
||||
|
||||
void Sha256Impl::GetHash(void *dst, size_t size) {
|
||||
/* Verify we're in a state to get hash. */
|
||||
AMS_ASSERT(m_state == State_Initialized || m_state == State_Done);
|
||||
AMS_ASSERT(size >= HashSize);
|
||||
AMS_UNUSED(size);
|
||||
|
||||
/* If we need to, process the last block. */
|
||||
if (m_state == State_Initialized) {
|
||||
this->ProcessLastBlock();
|
||||
m_state = State_Done;
|
||||
}
|
||||
|
||||
/* Copy the output hash. */
|
||||
if constexpr (util::IsLittleEndian()) {
|
||||
static_assert(HashSize % sizeof(u32) == 0);
|
||||
|
||||
u32 *dst_32 = static_cast<u32 *>(dst);
|
||||
for (size_t i = 0; i < HashSize / sizeof(u32); ++i) {
|
||||
dst_32[i] = util::LoadBigEndian<u32>(m_intermediate_hash + i);
|
||||
}
|
||||
} else {
|
||||
std::memcpy(dst, m_intermediate_hash, HashSize);
|
||||
}
|
||||
}
|
||||
|
||||
void Sha256Impl::InitializeWithContext(const Sha256Context *context) {
|
||||
/* Copy state in from the context. */
|
||||
std::memcpy(m_intermediate_hash, context->intermediate_hash, sizeof(m_intermediate_hash));
|
||||
m_bits_consumed = context->bits_consumed;
|
||||
|
||||
/* Reset other fields. */
|
||||
m_buffered_bytes = 0;
|
||||
m_state = State_Initialized;
|
||||
}
|
||||
|
||||
size_t Sha256Impl::GetContext(Sha256Context *context) const {
|
||||
/* Check our state. */
|
||||
AMS_ASSERT(m_state == State_Initialized);
|
||||
|
||||
/* Copy out the context. */
|
||||
std::memcpy(context->intermediate_hash, m_intermediate_hash, sizeof(context->intermediate_hash));
|
||||
context->bits_consumed = m_bits_consumed;
|
||||
|
||||
return m_buffered_bytes;
|
||||
}
|
||||
|
||||
void Sha256Impl::ProcessBlock(const void *data) {
|
||||
/* Load work variables. */
|
||||
u32 a = m_intermediate_hash[0];
|
||||
u32 b = m_intermediate_hash[1];
|
||||
u32 c = m_intermediate_hash[2];
|
||||
u32 d = m_intermediate_hash[3];
|
||||
u32 e = m_intermediate_hash[4];
|
||||
u32 f = m_intermediate_hash[5];
|
||||
u32 g = m_intermediate_hash[6];
|
||||
u32 h = m_intermediate_hash[7];
|
||||
u32 tmp[2];
|
||||
size_t i;
|
||||
|
||||
/* Copy the input. */
|
||||
u32 w[64];
|
||||
if constexpr (util::IsLittleEndian()) {
|
||||
static_assert(BlockSize % sizeof(u32) == 0);
|
||||
|
||||
const u32 *src_32 = static_cast<const u32 *>(data);
|
||||
for (size_t i = 0; i < BlockSize / sizeof(u32); ++i) {
|
||||
w[i] = util::LoadBigEndian<u32>(src_32 + i);
|
||||
}
|
||||
} else {
|
||||
std::memcpy(w, data, BlockSize);
|
||||
}
|
||||
|
||||
/* Initialize the rest of w. */
|
||||
for (i = BlockSize / sizeof(u32); i < util::size(w); ++i) {
|
||||
const u32 *prev = w + (i - BlockSize / sizeof(u32));
|
||||
w[i] = prev[0] + SmallSigma0(prev[1]) + prev[9] + SmallSigma1(prev[14]);
|
||||
}
|
||||
|
||||
/* Perform rounds. */
|
||||
for (i = 0; i < 64; ++i) {
|
||||
tmp[0] = h + LargeSigma1(e) + Choose(e, f, g) + RoundConstants[i] + w[i];
|
||||
tmp[1] = LargeSigma0(a) + Majority(a, b, c);
|
||||
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = d + tmp[0];
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = tmp[0] + tmp[1];
|
||||
}
|
||||
|
||||
/* Update intermediate hash. */
|
||||
m_intermediate_hash[0] += a;
|
||||
m_intermediate_hash[1] += b;
|
||||
m_intermediate_hash[2] += c;
|
||||
m_intermediate_hash[3] += d;
|
||||
m_intermediate_hash[4] += e;
|
||||
m_intermediate_hash[5] += f;
|
||||
m_intermediate_hash[6] += g;
|
||||
m_intermediate_hash[7] += h;
|
||||
}
|
||||
|
||||
void Sha256Impl::ProcessLastBlock() {
|
||||
/* Setup the final block. */
|
||||
constexpr const auto BlockSizeWithoutSizeField = BlockSize - sizeof(u64);
|
||||
|
||||
/* Increment our bits consumed. */
|
||||
m_bits_consumed += BITSIZEOF(u8) * m_buffered_bytes;
|
||||
|
||||
/* Add 0x80 terminator. */
|
||||
m_buffer[m_buffered_bytes++] = 0x80;
|
||||
|
||||
/* If we can process the size field directly, do so, otherwise set up to process it. */
|
||||
if (m_buffered_bytes <= BlockSizeWithoutSizeField) {
|
||||
/* Clear up to size field. */
|
||||
std::memset(m_buffer + m_buffered_bytes, 0, BlockSizeWithoutSizeField - m_buffered_bytes);
|
||||
} else {
|
||||
/* Consume full block */
|
||||
std::memset(m_buffer + m_buffered_bytes, 0, BlockSize - m_buffered_bytes);
|
||||
this->ProcessBlock(m_buffer);
|
||||
|
||||
/* Clear up to size field. */
|
||||
std::memset(m_buffer, 0, BlockSizeWithoutSizeField);
|
||||
}
|
||||
|
||||
/* Store the size field. */
|
||||
util::StoreBigEndian<u64>(reinterpret_cast<u64 *>(m_buffer + BlockSizeWithoutSizeField), m_bits_consumed);
|
||||
|
||||
/* Process the final block. */
|
||||
this->ProcessBlock(m_buffer);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,240 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto NumRounds = 24;
|
||||
|
||||
constexpr const u64 IotaRoundConstant[NumRounds] = {
|
||||
UINT64_C(0x0000000000000001), UINT64_C(0x0000000000008082),
|
||||
UINT64_C(0x800000000000808A), UINT64_C(0x8000000080008000),
|
||||
UINT64_C(0x000000000000808B), UINT64_C(0x0000000080000001),
|
||||
UINT64_C(0x8000000080008081), UINT64_C(0x8000000000008009),
|
||||
UINT64_C(0x000000000000008A), UINT64_C(0x0000000000000088),
|
||||
UINT64_C(0x0000000080008009), UINT64_C(0x000000008000000A),
|
||||
UINT64_C(0x000000008000808B), UINT64_C(0x800000000000008B),
|
||||
UINT64_C(0x8000000000008089), UINT64_C(0x8000000000008003),
|
||||
UINT64_C(0x8000000000008002), UINT64_C(0x8000000000000080),
|
||||
UINT64_C(0x000000000000800A), UINT64_C(0x800000008000000A),
|
||||
UINT64_C(0x8000000080008081), UINT64_C(0x8000000000008080),
|
||||
UINT64_C(0x0000000080000001), UINT64_C(0x8000000080008008)
|
||||
};
|
||||
|
||||
constexpr const int RhoShiftBit[NumRounds] = {
|
||||
1, 3, 6, 10, 15, 21, 28, 36,
|
||||
45, 55, 2, 14, 27, 41, 56, 8,
|
||||
25, 43, 62, 18, 39, 61, 20, 44
|
||||
};
|
||||
|
||||
constexpr const int RhoNextIndex[NumRounds] = {
|
||||
10, 7, 11, 17, 18, 3, 5, 16,
|
||||
8, 21, 24, 4, 15, 23, 19, 13,
|
||||
12, 2, 20, 14, 22, 9, 6, 1
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<size_t HashSize>
|
||||
void Sha3Impl<HashSize>::Initialize() {
|
||||
/* Clear internal state. */
|
||||
std::memset(m_internal_state, 0, sizeof(m_internal_state));
|
||||
|
||||
/* Reset buffered bytes. */
|
||||
m_buffered_bytes = 0;
|
||||
|
||||
/* Set state. */
|
||||
m_state = State_Initialized;
|
||||
}
|
||||
|
||||
template<size_t HashSize>
|
||||
void Sha3Impl<HashSize>::Update(const void *data, size_t size) {
|
||||
/* Verify we're in a state to update. */
|
||||
AMS_ASSERT(m_state == State_Initialized);
|
||||
|
||||
/* Process we have anything buffered. */
|
||||
const u8 *data8 = static_cast<const u8 *>(data);
|
||||
size_t remaining = size;
|
||||
if (m_buffered_bytes > 0) {
|
||||
/* Determine how much we can copy. */
|
||||
const size_t copy_size = std::min(BlockSize - m_buffered_bytes, remaining);
|
||||
|
||||
/* Mix the bytes into our state. */
|
||||
u8 *dst8 = reinterpret_cast<u8 *>(m_internal_state) + m_buffered_bytes;
|
||||
for (size_t i = 0; i < copy_size; ++i) {
|
||||
dst8[i] ^= data8[i];
|
||||
}
|
||||
|
||||
/* Advance. */
|
||||
data8 += copy_size;
|
||||
remaining -= copy_size;
|
||||
m_buffered_bytes += copy_size;
|
||||
|
||||
/* Process a block, if we filled one. */
|
||||
if (m_buffered_bytes == BlockSize) {
|
||||
this->ProcessBlock();
|
||||
m_buffered_bytes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Process blocks, if we have any. */
|
||||
while (remaining >= BlockSize) {
|
||||
/* Mix the bytes into our state. */
|
||||
u8 *dst8 = reinterpret_cast<u8 *>(m_internal_state);
|
||||
for (size_t i = 0; i < BlockSize; ++i) {
|
||||
dst8[i] ^= data8[i];
|
||||
}
|
||||
|
||||
this->ProcessBlock();
|
||||
|
||||
data8 += BlockSize;
|
||||
remaining -= BlockSize;
|
||||
}
|
||||
|
||||
/* Copy any leftover data to our buffer. */
|
||||
if (remaining > 0) {
|
||||
u8 *dst8 = reinterpret_cast<u8 *>(m_internal_state);
|
||||
for (size_t i = 0; i < remaining; ++i) {
|
||||
dst8[i] ^= data8[i];
|
||||
}
|
||||
|
||||
m_buffered_bytes = remaining;
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t HashSize>
|
||||
void Sha3Impl<HashSize>::GetHash(void *dst, size_t size) {
|
||||
/* Verify we're in a state to get hash. */
|
||||
AMS_ASSERT(m_state == State_Initialized || m_state == State_Done);
|
||||
AMS_ASSERT(size >= HashSize);
|
||||
AMS_UNUSED(size);
|
||||
|
||||
/* If we need to, process the last block. */
|
||||
if (m_state == State_Initialized) {
|
||||
this->ProcessLastBlock();
|
||||
m_state = State_Done;
|
||||
}
|
||||
|
||||
/* Copy the output hash. */
|
||||
std::memcpy(dst, m_internal_state, HashSize);
|
||||
}
|
||||
|
||||
template<size_t HashSize>
|
||||
void Sha3Impl<HashSize>::InitializeWithContext(const Sha3Context *context) {
|
||||
/* Check the context is for the right hash size. */
|
||||
AMS_ASSERT(context->hash_size == HashSize);
|
||||
|
||||
/* Set buffered bytes. */
|
||||
m_buffered_bytes = context->buffered_bytes;
|
||||
|
||||
/* Copy state in from the context. */
|
||||
std::memcpy(m_internal_state, context->internal_state, sizeof(m_internal_state));
|
||||
|
||||
/* Reset other fields. */
|
||||
m_state = State_Initialized;
|
||||
}
|
||||
|
||||
template<size_t HashSize>
|
||||
void Sha3Impl<HashSize>::GetContext(Sha3Context *context) const {
|
||||
/* Check our state. */
|
||||
AMS_ASSERT(m_state == State_Initialized);
|
||||
|
||||
/* Set the output hash size. */
|
||||
context->hash_size = HashSize;
|
||||
|
||||
/* Set buffered bytes. */
|
||||
context->buffered_bytes = m_buffered_bytes;
|
||||
|
||||
/* Copy out the context. */
|
||||
std::memcpy(context->internal_state, m_internal_state, sizeof(context->internal_state));
|
||||
}
|
||||
|
||||
template<size_t HashSize>
|
||||
void Sha3Impl<HashSize>::ProcessBlock() {
|
||||
/* Ensure correct endianness. */
|
||||
if constexpr (util::IsBigEndian()) {
|
||||
for (size_t i = 0; i < util::size(m_internal_state); ++i) {
|
||||
m_internal_state[i] = util::LoadLittleEndian<u64>(m_internal_state + i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform all rounds. */
|
||||
uint64_t tmp, C[5];
|
||||
for (auto round = 0; round < NumRounds; ++round) {
|
||||
/* Handle theta. */
|
||||
for (size_t i = 0; i < 5; ++i) {
|
||||
C[i] = m_internal_state[i] ^ m_internal_state[i + 5] ^ m_internal_state[i + 10] ^ m_internal_state[i + 15] ^ m_internal_state[i + 20];
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 5; ++i) {
|
||||
tmp = C[(i + 4) % 5] ^ util::RotateLeft<u64>(C[(i + 1) % 5], 1);
|
||||
for (size_t j = 0; j < 5; ++j) {
|
||||
m_internal_state[5 * j + i] ^= tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle rho/pi. */
|
||||
tmp = m_internal_state[1];
|
||||
for (size_t i = 0; i < NumRounds; ++i) {
|
||||
const auto rho_next_idx = RhoNextIndex[i];
|
||||
C[0] = m_internal_state[rho_next_idx];
|
||||
m_internal_state[rho_next_idx] = util::RotateLeft<u64>(tmp, RhoShiftBit[i]);
|
||||
tmp = C[0];
|
||||
}
|
||||
|
||||
/* Handle chi. */
|
||||
for (size_t i = 0; i < 5; ++i) {
|
||||
for (size_t j = 0; j < 5; ++j) {
|
||||
C[j] = m_internal_state[5 * i + j];
|
||||
}
|
||||
for (size_t j = 0; j < 5; ++j) {
|
||||
m_internal_state[5 * i + j] ^= (~C[(j + 1) % 5]) & C[(j + 2) % 5];
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle iota. */
|
||||
m_internal_state[0] ^= IotaRoundConstant[round];
|
||||
}
|
||||
/* Ensure correct endianness. */
|
||||
if constexpr (util::IsBigEndian()) {
|
||||
for (size_t i = 0; i < util::size(m_internal_state); ++i) {
|
||||
util::StoreLittleEndian<u64>(m_internal_state + i, m_internal_state[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t HashSize>
|
||||
void Sha3Impl<HashSize>::ProcessLastBlock() {
|
||||
/* Mix final bits (011) into our state. */
|
||||
reinterpret_cast<u8 *>(m_internal_state)[m_buffered_bytes] ^= 0b110;
|
||||
|
||||
/* Mix in the high bit of the last word in our block. */
|
||||
constexpr u64 FinalMask = UINT64_C(0x8000000000000000);
|
||||
m_internal_state[(BlockSize / sizeof(u64)) - 1] ^= FinalMask;
|
||||
|
||||
/* Process the last block. */
|
||||
this->ProcessBlock();
|
||||
}
|
||||
|
||||
/* Explicitly instantiate the supported hash sizes. */
|
||||
template class Sha3Impl<224 / BITSIZEOF(u8)>;
|
||||
template class Sha3Impl<256 / BITSIZEOF(u8)>;
|
||||
template class Sha3Impl<384 / BITSIZEOF(u8)>;
|
||||
template class Sha3Impl<512 / BITSIZEOF(u8)>;
|
||||
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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.hpp>
|
||||
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
template<typename Cipher, typename Self>
|
||||
void UpdateImpl(Self *self, const void *src, size_t src_size) {
|
||||
const size_t BlockSize = self->GetBlockSize();
|
||||
|
||||
const u8 *src_u8 = static_cast<const u8 *>(src);
|
||||
size_t remaining = src_size;
|
||||
|
||||
if (const size_t buffered = self->GetBufferedDataSize(); buffered > 0) {
|
||||
const size_t partial = std::min(BlockSize - buffered, remaining);
|
||||
|
||||
self->ProcessPartialData(src_u8, partial);
|
||||
src_u8 += partial;
|
||||
remaining -= partial;
|
||||
}
|
||||
|
||||
if (remaining >= BlockSize) {
|
||||
const size_t num_blocks = remaining / BlockSize;
|
||||
|
||||
self->template ProcessBlocks<Cipher>(src_u8, num_blocks);
|
||||
|
||||
const size_t processed = num_blocks * BlockSize;
|
||||
src_u8 += processed;
|
||||
remaining -= processed;
|
||||
}
|
||||
|
||||
if (remaining > 0) {
|
||||
self->ProcessRemainingData(src_u8, remaining);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Cipher, typename Self>
|
||||
size_t UpdateImpl(Self *self, void *dst, size_t dst_size, const void *src, size_t src_size) {
|
||||
AMS_UNUSED(dst_size);
|
||||
|
||||
const size_t BlockSize = self->GetBlockSize();
|
||||
|
||||
const u8 *src_u8 = static_cast<const u8 *>(src);
|
||||
u8 *dst_u8 = static_cast<u8 *>(dst);
|
||||
size_t remaining = src_size;
|
||||
size_t total_processed = 0;
|
||||
|
||||
if (const size_t buffered = self->GetBufferedDataSize(); buffered > 0) {
|
||||
const size_t partial = std::min(BlockSize - buffered, remaining);
|
||||
|
||||
const size_t processed = self->ProcessPartialData(dst_u8, src_u8, partial);
|
||||
|
||||
dst_u8 += processed;
|
||||
total_processed += processed;
|
||||
|
||||
src_u8 += partial;
|
||||
remaining -= partial;
|
||||
}
|
||||
|
||||
if (remaining >= BlockSize) {
|
||||
const size_t num_blocks = remaining / BlockSize;
|
||||
const size_t input_size = num_blocks * BlockSize;
|
||||
|
||||
const size_t processed = self->template ProcessBlocks<Cipher>(dst_u8, src_u8, num_blocks);
|
||||
|
||||
dst_u8 += processed;
|
||||
total_processed += processed;
|
||||
|
||||
src_u8 += input_size;
|
||||
remaining -= input_size;
|
||||
}
|
||||
|
||||
if (remaining > 0) {
|
||||
const size_t processed = self->ProcessRemainingData(dst_u8, src_u8, remaining);
|
||||
total_processed += processed;
|
||||
}
|
||||
|
||||
return total_processed;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
#include "crypto_update_impl.hpp"
|
||||
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
size_t XtsModeImpl::UpdateGeneric(void *dst, size_t dst_size, const void *src, size_t src_size) {
|
||||
AMS_ASSERT(m_state == State_Initialized || m_state == State_Processing);
|
||||
|
||||
return UpdateImpl<void>(this, dst, dst_size, src, src_size);
|
||||
}
|
||||
|
||||
size_t XtsModeImpl::ProcessBlocksGeneric(u8 *dst, const u8 *src, size_t num_blocks) {
|
||||
size_t processed = BlockSize * (num_blocks - 1);
|
||||
|
||||
if (m_state == State_Processing) {
|
||||
this->ProcessBlock(dst, m_last_block);
|
||||
dst += BlockSize;
|
||||
processed += BlockSize;
|
||||
}
|
||||
|
||||
while ((--num_blocks) > 0) {
|
||||
this->ProcessBlock(dst, src);
|
||||
dst += BlockSize;
|
||||
src += BlockSize;
|
||||
}
|
||||
|
||||
std::memcpy(m_last_block, src, BlockSize);
|
||||
|
||||
m_state = State_Processing;
|
||||
|
||||
return processed;
|
||||
}
|
||||
|
||||
template<> size_t XtsModeImpl::Update<AesEncryptor128>(void *dst, size_t dst_size, const void *src, size_t src_size) { return this->UpdateGeneric(dst, dst_size, src, src_size); }
|
||||
template<> size_t XtsModeImpl::Update<AesEncryptor192>(void *dst, size_t dst_size, const void *src, size_t src_size) { return this->UpdateGeneric(dst, dst_size, src, src_size); }
|
||||
template<> size_t XtsModeImpl::Update<AesEncryptor256>(void *dst, size_t dst_size, const void *src, size_t src_size) { return this->UpdateGeneric(dst, dst_size, src, src_size); }
|
||||
|
||||
template<> size_t XtsModeImpl::Update<AesDecryptor128>(void *dst, size_t dst_size, const void *src, size_t src_size) { return this->UpdateGeneric(dst, dst_size, src, src_size); }
|
||||
template<> size_t XtsModeImpl::Update<AesDecryptor192>(void *dst, size_t dst_size, const void *src, size_t src_size) { return this->UpdateGeneric(dst, dst_size, src, src_size); }
|
||||
template<> size_t XtsModeImpl::Update<AesDecryptor256>(void *dst, size_t dst_size, const void *src, size_t src_size) { return this->UpdateGeneric(dst, dst_size, src, src_size); }
|
||||
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::crypto::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
/* TODO: Support non-Nintendo Endianness */
|
||||
|
||||
void MultiplyTweakGeneric(u64 *tweak) {
|
||||
const u64 carry = tweak[1] & (static_cast<u64>(1) << (BITSIZEOF(u64) - 1));
|
||||
|
||||
tweak[1] = ((tweak[1] << 1) | (tweak[0] >> (BITSIZEOF(u64) - 1)));
|
||||
tweak[0] = (tweak[0] << 1);
|
||||
|
||||
if (carry) {
|
||||
tweak[0] ^= static_cast<u64>(0x87);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void XtsModeImpl::ProcessBlock(u8 *dst, const u8 *src) {
|
||||
u8 tmp[BlockSize];
|
||||
|
||||
/* Xor. */
|
||||
for (size_t i = 0; i < BlockSize; i++) {
|
||||
tmp[i] = m_tweak[i] ^ src[i];
|
||||
}
|
||||
|
||||
/* Crypt */
|
||||
m_cipher_func(tmp, tmp, m_cipher_ctx);
|
||||
|
||||
/* Xor. */
|
||||
for (size_t i = 0; i < BlockSize; i++) {
|
||||
dst[i] = m_tweak[i] ^ tmp[i];
|
||||
}
|
||||
|
||||
MultiplyTweakGeneric(reinterpret_cast<u64 *>(m_tweak));
|
||||
}
|
||||
|
||||
size_t XtsModeImpl::FinalizeEncryption(void *dst, size_t dst_size) {
|
||||
AMS_ASSERT(m_state == State_Processing);
|
||||
AMS_UNUSED(dst_size);
|
||||
|
||||
u8 *dst_u8 = static_cast<u8 *>(dst);
|
||||
size_t processed = 0;
|
||||
|
||||
if (m_num_buffered == 0) {
|
||||
this->ProcessBlock(dst_u8, m_last_block);
|
||||
processed = BlockSize;
|
||||
} else {
|
||||
this->ProcessBlock(m_last_block, m_last_block);
|
||||
|
||||
std::memcpy(m_buffer + m_num_buffered, m_last_block + m_num_buffered, BlockSize - m_num_buffered);
|
||||
|
||||
this->ProcessBlock(dst_u8, m_buffer);
|
||||
|
||||
std::memcpy(dst_u8 + BlockSize, m_last_block, m_num_buffered);
|
||||
|
||||
processed = BlockSize + m_num_buffered;
|
||||
}
|
||||
|
||||
m_state = State_Done;
|
||||
return processed;
|
||||
}
|
||||
|
||||
size_t XtsModeImpl::FinalizeDecryption(void *dst, size_t dst_size) {
|
||||
AMS_ASSERT(m_state == State_Processing);
|
||||
AMS_UNUSED(dst_size);
|
||||
|
||||
u8 *dst_u8 = static_cast<u8 *>(dst);
|
||||
size_t processed = 0;
|
||||
|
||||
if (m_num_buffered == 0) {
|
||||
this->ProcessBlock(dst_u8, m_last_block);
|
||||
processed = BlockSize;
|
||||
} else {
|
||||
u8 tmp_tweak[BlockSize];
|
||||
std::memcpy(tmp_tweak, m_tweak, BlockSize);
|
||||
MultiplyTweakGeneric(reinterpret_cast<u64 *>(m_tweak));
|
||||
|
||||
this->ProcessBlock(m_last_block, m_last_block);
|
||||
|
||||
std::memcpy(m_buffer + m_num_buffered, m_last_block + m_num_buffered, BlockSize - m_num_buffered);
|
||||
|
||||
std::memcpy(m_tweak, tmp_tweak, BlockSize);
|
||||
|
||||
this->ProcessBlock(dst_u8, m_buffer);
|
||||
|
||||
std::memcpy(dst_u8 + BlockSize, m_last_block, m_num_buffered);
|
||||
|
||||
processed = BlockSize + m_num_buffered;
|
||||
}
|
||||
|
||||
m_state = State_Done;
|
||||
return processed;
|
||||
}
|
||||
|
||||
size_t XtsModeImpl::ProcessPartialData(u8 *dst, const u8 *src, size_t size) {
|
||||
size_t processed = 0;
|
||||
|
||||
std::memcpy(m_buffer + m_num_buffered, src, size);
|
||||
m_num_buffered += size;
|
||||
|
||||
if (m_num_buffered == BlockSize) {
|
||||
if (m_state == State_Processing) {
|
||||
this->ProcessBlock(dst, m_last_block);
|
||||
processed += BlockSize;
|
||||
}
|
||||
|
||||
std::memcpy(m_last_block, m_buffer, BlockSize);
|
||||
m_num_buffered = 0;
|
||||
|
||||
m_state = State_Processing;
|
||||
}
|
||||
|
||||
return processed;
|
||||
}
|
||||
|
||||
size_t XtsModeImpl::ProcessRemainingData(u8 *dst, const u8 *src, size_t size) {
|
||||
AMS_UNUSED(dst);
|
||||
|
||||
std::memcpy(m_buffer, src, size);
|
||||
m_num_buffered = size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
#include "impl/dd_select_cache_impl.hpp"
|
||||
|
||||
namespace ams::dd {
|
||||
|
||||
void InvalidateDataCache(void *addr, size_t size) {
|
||||
return impl::InvalidateDataCacheImpl(addr, size);
|
||||
}
|
||||
|
||||
void StoreDataCache(void *addr, size_t size) {
|
||||
return impl::StoreDataCacheImpl(addr, size);
|
||||
}
|
||||
|
||||
void FlushDataCache(void *addr, size_t size) {
|
||||
return impl::FlushDataCacheImpl(addr, size);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
|
||||
namespace ams::dd {
|
||||
|
||||
uintptr_t QueryIoMapping(dd::PhysicalAddress phys_addr, size_t size) {
|
||||
#if defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
/* TODO: Do this in a less shitty way. */
|
||||
if (secmon::MemoryRegionPhysicalDeviceClkRst.Contains(phys_addr, size)) {
|
||||
return secmon::MemoryRegionVirtualDeviceClkRst.GetAddress() + phys_addr - secmon::MemoryRegionPhysicalDeviceClkRst.GetAddress();
|
||||
} else if (secmon::MemoryRegionPhysicalDeviceGpio.Contains(phys_addr, size)) {
|
||||
return secmon::MemoryRegionVirtualDeviceGpio.GetAddress() + phys_addr - secmon::MemoryRegionPhysicalDeviceGpio.GetAddress();
|
||||
} else if (secmon::MemoryRegionPhysicalDeviceApbMisc.Contains(phys_addr, size)) {
|
||||
return secmon::MemoryRegionVirtualDeviceApbMisc.GetAddress() + phys_addr - secmon::MemoryRegionPhysicalDeviceApbMisc.GetAddress();
|
||||
} else if (secmon::MemoryRegionPhysicalDeviceSdmmc.Contains(phys_addr, size)) {
|
||||
return secmon::MemoryRegionVirtualDeviceSdmmc.GetAddress() + phys_addr - secmon::MemoryRegionPhysicalDeviceSdmmc.GetAddress();
|
||||
} else if (secmon::MemoryRegionPhysicalDevicePmc.Contains(phys_addr, size)) {
|
||||
return secmon::MemoryRegionVirtualDevicePmc.GetAddress() + phys_addr - secmon::MemoryRegionPhysicalDevicePmc.GetAddress();
|
||||
} else if (secmon::MemoryRegionPhysicalDeviceI2c5.Contains(phys_addr, size)) {
|
||||
return secmon::MemoryRegionVirtualDeviceI2c5.GetAddress() + phys_addr - secmon::MemoryRegionPhysicalDeviceI2c5.GetAddress();
|
||||
} else if (secmon::MemoryRegionPhysicalDeviceI2c1.Contains(phys_addr, size)) {
|
||||
return secmon::MemoryRegionVirtualDeviceI2c1.GetAddress() + phys_addr - secmon::MemoryRegionPhysicalDeviceI2c1.GetAddress();
|
||||
} else {
|
||||
AMS_UNUSED(size);
|
||||
return static_cast<uintptr_t>(phys_addr);
|
||||
}
|
||||
|
||||
#elif defined(ATMOSPHERE_ARCH_ARM)
|
||||
/* TODO: BPMP translation? */
|
||||
AMS_UNUSED(size);
|
||||
return static_cast<uintptr_t>(phys_addr);
|
||||
#else
|
||||
#error "Unknown architecture for ams::dd::QueryIoMapping (EXOSPHERE)!"
|
||||
#endif
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
/* TODO: Kernel address translation? */
|
||||
AMS_UNUSED(size);
|
||||
return static_cast<uintptr_t>(phys_addr);
|
||||
#elif defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
|
||||
svc::Address virt_addr = 0;
|
||||
const dd::PhysicalAddress aligned_addr = util::AlignDown(phys_addr, os::MemoryPageSize);
|
||||
const size_t offset = phys_addr - aligned_addr;
|
||||
const size_t aligned_size = size + offset;
|
||||
|
||||
if (hos::GetVersion() >= hos::Version_10_0_0) {
|
||||
svc::Size region_size = 0;
|
||||
R_TRY_CATCH(svc::QueryMemoryMapping(std::addressof(virt_addr), std::addressof(region_size), aligned_addr, aligned_size)) {
|
||||
/* Official software handles this by returning 0. */
|
||||
R_CATCH(svc::ResultNotFound) { return 0; }
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
AMS_ASSERT(region_size >= aligned_size);
|
||||
} else {
|
||||
R_TRY_CATCH(svc::LegacyQueryIoMapping(std::addressof(virt_addr), aligned_addr, aligned_size)) {
|
||||
/* Official software handles this by returning 0. */
|
||||
R_CATCH(svc::ResultNotFound) { return 0; }
|
||||
} R_END_TRY_CATCH_WITH_ABORT_UNLESS;
|
||||
}
|
||||
|
||||
return static_cast<uintptr_t>(virt_addr) + offset;
|
||||
|
||||
#else
|
||||
#error "Unknown execution context for ams::dd::QueryIoMapping!"
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
|
||||
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
||||
|
||||
constexpr dd::PhysicalAddress PmcPhysStart = 0x7000E400;
|
||||
constexpr dd::PhysicalAddress PmcPhysLast = 0x7000EFFF;
|
||||
|
||||
constexpr bool IsValidPmcPhysicalAddress(dd::PhysicalAddress phys_addr) {
|
||||
return util::IsAligned(phys_addr, alignof(u32)) && PmcPhysStart <= phys_addr && phys_addr <= PmcPhysLast;
|
||||
}
|
||||
|
||||
u32 ReadWritePmcRegisterImpl(dd::PhysicalAddress phys_addr, u32 value, u32 mask) {
|
||||
u32 out_value;
|
||||
R_ABORT_UNLESS(spl::smc::ConvertResult(spl::smc::AtmosphereReadWriteRegister(phys_addr, mask, value, std::addressof(out_value))));
|
||||
return out_value;
|
||||
}
|
||||
|
||||
bool TryReadModifyWritePmcRegister(u32 *out, dd::PhysicalAddress phys_addr, u32 value, u32 mask) {
|
||||
if (IsValidPmcPhysicalAddress(phys_addr)) {
|
||||
*out = ReadWritePmcRegisterImpl(phys_addr, value, mask);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
bool TryReadModifyWritePmcRegister(u32 *out, dd::PhysicalAddress phys_addr, u32 value, u32 mask) {
|
||||
AMS_UNUSED(out, phys_addr, value, mask);
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
u32 ReadIoRegister(dd::PhysicalAddress phys_addr) {
|
||||
#if defined(ATMOSPHERE_IS_EXOSPHERE) || defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
return reg::Read(dd::QueryIoMapping(phys_addr, sizeof(u32)));
|
||||
#elif defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
|
||||
u32 val;
|
||||
if (!TryReadModifyWritePmcRegister(std::addressof(val), phys_addr, 0, 0)) {
|
||||
R_ABORT_UNLESS(svc::ReadWriteRegister(std::addressof(val), phys_addr, 0, 0));
|
||||
}
|
||||
return val;
|
||||
|
||||
#else
|
||||
#error "Unknown execution context for ams::dd::ReadIoRegister!"
|
||||
#endif
|
||||
}
|
||||
|
||||
void WriteIoRegister(dd::PhysicalAddress phys_addr, u32 value) {
|
||||
#if defined(ATMOSPHERE_IS_EXOSPHERE) || defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
reg::Write(dd::QueryIoMapping(phys_addr, sizeof(u32)), value);
|
||||
#elif defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
|
||||
u32 out_val;
|
||||
if (!TryReadModifyWritePmcRegister(std::addressof(out_val), phys_addr, value, 0xFFFFFFFF)) {
|
||||
R_ABORT_UNLESS(svc::ReadWriteRegister(std::addressof(out_val), phys_addr, 0xFFFFFFFF, value));
|
||||
}
|
||||
AMS_UNUSED(out_val);
|
||||
|
||||
#else
|
||||
#error "Unknown execution context for ams::dd::WriteIoRegister!"
|
||||
#endif
|
||||
}
|
||||
|
||||
u32 ReadModifyWriteIoRegister(PhysicalAddress phys_addr, u32 value, u32 mask) {
|
||||
#if defined(ATMOSPHERE_IS_EXOSPHERE) || defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
AMS_UNUSED(phys_addr, value, mask);
|
||||
AMS_ABORT("ReadModifyWriteIoRegister TODO under non-stratosphere");
|
||||
#elif defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
|
||||
u32 out_val;
|
||||
if (!TryReadModifyWritePmcRegister(std::addressof(out_val), phys_addr, value, mask)) {
|
||||
R_ABORT_UNLESS(svc::ReadWriteRegister(std::addressof(out_val), phys_addr, mask, value));
|
||||
}
|
||||
return out_val;
|
||||
|
||||
#else
|
||||
#error "Unknown execution context for ams::dd::ReadModifyWriteIoRegister!"
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
|
||||
namespace ams::dd::impl {
|
||||
|
||||
void StoreDataCacheImpl(void *addr, size_t size) {
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
/* On aarch64, we can use cache maintenance instructions. */
|
||||
|
||||
/* Get cache line size. */
|
||||
uintptr_t ctr_el0 = 0;
|
||||
__asm__ __volatile__("mrs %[ctr_el0], ctr_el0" : [ctr_el0]"=r"(ctr_el0));
|
||||
const uintptr_t cache_line_size = 4 << ((ctr_el0 >> 16) & 0xF);
|
||||
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
/* Get the thread local region. */
|
||||
auto * const tlr = svc::GetThreadLocalRegion();
|
||||
|
||||
/* Note to the kernel that we're performing cache maintenance, in case we get interrupted while touching cache lines. */
|
||||
tlr->cache_maintenance_flag = 1;
|
||||
ON_SCOPE_EXIT { tlr->cache_maintenance_flag = 0; };
|
||||
#endif
|
||||
|
||||
/* Invalidate the cache. */
|
||||
const uintptr_t start_addr = reinterpret_cast<uintptr_t>(addr) & ~(cache_line_size - 1);
|
||||
const uintptr_t end_addr = reinterpret_cast<uintptr_t>(addr) + size;
|
||||
for (uintptr_t cur = start_addr; cur < end_addr; cur += cache_line_size) {
|
||||
__asm__ __volatile__("dc cvac, %[cur]" : : [cur]"r"(cur));
|
||||
}
|
||||
|
||||
/* Add a memory barrier. */
|
||||
__asm__ __volatile__("dsb sy" ::: "memory");
|
||||
#else
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
/* Invoke the relevant svc. */
|
||||
const auto result = svc::StoreProcessDataCache(svc::PseudoHandle::CurrentProcess, reinterpret_cast<uintptr_t>(addr), size);
|
||||
R_ASSERT(result);
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE) && defined(__BPMP__)
|
||||
return hw::StoreDataCache(addr, size);
|
||||
#else
|
||||
#error "Unknown execution context for ams::dd::impl::StoreDataCacheImpl"
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void FlushDataCacheImpl(void *addr, size_t size) {
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
/* On aarch64, we can use cache maintenance instructions. */
|
||||
|
||||
/* Get cache line size. */
|
||||
uintptr_t ctr_el0 = 0;
|
||||
__asm__ __volatile__("mrs %[ctr_el0], ctr_el0" : [ctr_el0]"=r"(ctr_el0));
|
||||
const uintptr_t cache_line_size = 4 << ((ctr_el0 >> 16) & 0xF);
|
||||
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
/* Get the thread local region. */
|
||||
auto * const tlr = svc::GetThreadLocalRegion();
|
||||
|
||||
/* Note to the kernel that we're performing cache maintenance, in case we get interrupted while touching cache lines. */
|
||||
tlr->cache_maintenance_flag = 1;
|
||||
ON_SCOPE_EXIT { tlr->cache_maintenance_flag = 0; };
|
||||
#endif
|
||||
|
||||
/* Invalidate the cache. */
|
||||
const uintptr_t start_addr = reinterpret_cast<uintptr_t>(addr) & ~(cache_line_size - 1);
|
||||
const uintptr_t end_addr = reinterpret_cast<uintptr_t>(addr) + size;
|
||||
for (uintptr_t cur = start_addr; cur < end_addr; cur += cache_line_size) {
|
||||
__asm__ __volatile__("dc civac, %[cur]" : : [cur]"r"(cur));
|
||||
}
|
||||
|
||||
/* Add a memory barrier. */
|
||||
__asm__ __volatile__("dsb sy" ::: "memory");
|
||||
#else
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
/* Invoke the relevant svc. */
|
||||
const auto result = svc::FlushProcessDataCache(svc::PseudoHandle::CurrentProcess, reinterpret_cast<uintptr_t>(addr), size);
|
||||
R_ASSERT(result);
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE) && defined(__BPMP__)
|
||||
return hw::FlushDataCache(addr, size);
|
||||
#else
|
||||
#error "Unknown execution context for ams::dd::impl::FlushDataCacheImpl"
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void InvalidateDataCacheImpl(void *addr, size_t size) {
|
||||
#if defined(ATMOSPHERE_IS_EXOSPHERE) && defined(__BPMP__)
|
||||
return hw::InvalidateDataCache(addr, size);
|
||||
#else
|
||||
/* Just perform a flush, which is clean + invalidate. */
|
||||
return FlushDataCacheImpl(addr, size);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
|
||||
namespace ams::dd::impl {
|
||||
|
||||
void StoreDataCacheImpl(void *addr, size_t size) {
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
/* On aarch64, we can use cache maintenance instructions. */
|
||||
|
||||
/* Get cache line size. */
|
||||
uintptr_t ctr_el0 = 0;
|
||||
__asm__ __volatile__("mrs %[ctr_el0], ctr_el0" : [ctr_el0]"=r"(ctr_el0));
|
||||
const uintptr_t cache_line_size = 4 << ((ctr_el0 >> 16) & 0xF);
|
||||
|
||||
/* Invalidate the cache. */
|
||||
const uintptr_t start_addr = reinterpret_cast<uintptr_t>(addr) & ~(cache_line_size - 1);
|
||||
const uintptr_t end_addr = reinterpret_cast<uintptr_t>(addr) + size;
|
||||
for (uintptr_t cur = start_addr; cur < end_addr; cur += cache_line_size) {
|
||||
__asm__ __volatile__("dc cvac, %[cur]" : : [cur]"r"(cur));
|
||||
}
|
||||
|
||||
/* Add a memory barrier. */
|
||||
__asm__ __volatile__("dsb sy" ::: "memory");
|
||||
#elif defined(ATMOSPHERE_ARCH_X64) || defined(ATMOSPHERE_ARCH_X86)
|
||||
/* Don't do anything, cache maintenance isn't available/relevant to userland. */
|
||||
AMS_UNUSED(addr, size);
|
||||
#else
|
||||
#error "Unknown architecture for linux dd::StoreDataCacheImpl"
|
||||
#endif
|
||||
}
|
||||
|
||||
void FlushDataCacheImpl(void *addr, size_t size) {
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
/* On aarch64, we can use cache maintenance instructions. */
|
||||
|
||||
/* Get cache line size. */
|
||||
uintptr_t ctr_el0 = 0;
|
||||
__asm__ __volatile__("mrs %[ctr_el0], ctr_el0" : [ctr_el0]"=r"(ctr_el0));
|
||||
const uintptr_t cache_line_size = 4 << ((ctr_el0 >> 16) & 0xF);
|
||||
|
||||
/* Invalidate the cache. */
|
||||
const uintptr_t start_addr = reinterpret_cast<uintptr_t>(addr) & ~(cache_line_size - 1);
|
||||
const uintptr_t end_addr = reinterpret_cast<uintptr_t>(addr) + size;
|
||||
for (uintptr_t cur = start_addr; cur < end_addr; cur += cache_line_size) {
|
||||
__asm__ __volatile__("dc civac, %[cur]" : : [cur]"r"(cur));
|
||||
}
|
||||
|
||||
/* Add a memory barrier. */
|
||||
__asm__ __volatile__("dsb sy" ::: "memory");
|
||||
#elif defined(ATMOSPHERE_ARCH_X64) || defined(ATMOSPHERE_ARCH_X86)
|
||||
/* Don't do anything, cache maintenance isn't available/relevant to userland. */
|
||||
AMS_UNUSED(addr, size);
|
||||
#else
|
||||
#error "Unknown architecture for linux dd::FlushDataCacheImpl"
|
||||
#endif
|
||||
}
|
||||
|
||||
void InvalidateDataCacheImpl(void *addr, size_t size) {
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
/* Just perform a flush, which is clean + invalidate. */
|
||||
return FlushDataCacheImpl(addr, size);
|
||||
#elif defined(ATMOSPHERE_ARCH_X64) || defined(ATMOSPHERE_ARCH_X86)
|
||||
/* Don't do anything, cache maintenance isn't available/relevant to userland. */
|
||||
AMS_UNUSED(addr, size);
|
||||
#else
|
||||
#error "Unknown architecture for linux dd::InvalidateDataCacheImpl"
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
|
||||
namespace ams::dd::impl {
|
||||
|
||||
void StoreDataCacheImpl(void *addr, size_t size) {
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
/* On aarch64, we can use cache maintenance instructions. */
|
||||
|
||||
/* Get cache line size. */
|
||||
uintptr_t ctr_el0 = 0;
|
||||
__asm__ __volatile__("mrs %[ctr_el0], ctr_el0" : [ctr_el0]"=r"(ctr_el0));
|
||||
const uintptr_t cache_line_size = 4 << ((ctr_el0 >> 16) & 0xF);
|
||||
|
||||
/* Invalidate the cache. */
|
||||
const uintptr_t start_addr = reinterpret_cast<uintptr_t>(addr) & ~(cache_line_size - 1);
|
||||
const uintptr_t end_addr = reinterpret_cast<uintptr_t>(addr) + size;
|
||||
for (uintptr_t cur = start_addr; cur < end_addr; cur += cache_line_size) {
|
||||
__asm__ __volatile__("dc cvac, %[cur]" : : [cur]"r"(cur));
|
||||
}
|
||||
|
||||
/* Add a memory barrier. */
|
||||
__asm__ __volatile__("dsb sy" ::: "memory");
|
||||
#elif defined(ATMOSPHERE_ARCH_X64) || defined(ATMOSPHERE_ARCH_X86)
|
||||
/* Don't do anything, cache maintenance isn't available/relevant to userland. */
|
||||
AMS_UNUSED(addr, size);
|
||||
#else
|
||||
#error "Unknown architecture for macOS dd::StoreDataCacheImpl"
|
||||
#endif
|
||||
}
|
||||
|
||||
void FlushDataCacheImpl(void *addr, size_t size) {
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
/* On aarch64, we can use cache maintenance instructions. */
|
||||
|
||||
/* Get cache line size. */
|
||||
uintptr_t ctr_el0 = 0;
|
||||
__asm__ __volatile__("mrs %[ctr_el0], ctr_el0" : [ctr_el0]"=r"(ctr_el0));
|
||||
const uintptr_t cache_line_size = 4 << ((ctr_el0 >> 16) & 0xF);
|
||||
|
||||
/* Invalidate the cache. */
|
||||
const uintptr_t start_addr = reinterpret_cast<uintptr_t>(addr) & ~(cache_line_size - 1);
|
||||
const uintptr_t end_addr = reinterpret_cast<uintptr_t>(addr) + size;
|
||||
for (uintptr_t cur = start_addr; cur < end_addr; cur += cache_line_size) {
|
||||
__asm__ __volatile__("dc civac, %[cur]" : : [cur]"r"(cur));
|
||||
}
|
||||
|
||||
/* Add a memory barrier. */
|
||||
__asm__ __volatile__("dsb sy" ::: "memory");
|
||||
#elif defined(ATMOSPHERE_ARCH_X64) || defined(ATMOSPHERE_ARCH_X86)
|
||||
/* Don't do anything, cache maintenance isn't available/relevant to userland. */
|
||||
AMS_UNUSED(addr, size);
|
||||
#else
|
||||
#error "Unknown architecture for macOS dd::FlushDataCacheImpl"
|
||||
#endif
|
||||
}
|
||||
|
||||
void InvalidateDataCacheImpl(void *addr, size_t size) {
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
/* Just perform a flush, which is clean + invalidate. */
|
||||
return FlushDataCacheImpl(addr, size);
|
||||
#elif defined(ATMOSPHERE_ARCH_X64) || defined(ATMOSPHERE_ARCH_X86)
|
||||
/* Don't do anything, cache maintenance isn't available/relevant to userland. */
|
||||
AMS_UNUSED(addr, size);
|
||||
#else
|
||||
#error "Unknown architecture for macOS dd::InvalidateDataCacheImpl"
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
|
||||
namespace ams::dd::impl {
|
||||
|
||||
void StoreDataCacheImpl(void *addr, size_t size) {
|
||||
#if defined(ATMOSPHERE_ARCH_X64) || defined(ATMOSPHERE_ARCH_X86)
|
||||
/* Don't do anything, cache maintenance isn't available/relevant to userland. */
|
||||
AMS_UNUSED(addr, size);
|
||||
#else
|
||||
#error "Unknown architecture for windows dd::StoreDataCacheImpl"
|
||||
#endif
|
||||
}
|
||||
|
||||
void FlushDataCacheImpl(void *addr, size_t size) {
|
||||
#if defined(ATMOSPHERE_ARCH_X64) || defined(ATMOSPHERE_ARCH_X86)
|
||||
/* Don't do anything, cache maintenance isn't available/relevant to userland. */
|
||||
AMS_UNUSED(addr, size);
|
||||
#else
|
||||
#error "Unknown architecture for windows dd::FlushDataCacheImpl"
|
||||
#endif
|
||||
}
|
||||
|
||||
void InvalidateDataCacheImpl(void *addr, size_t size) {
|
||||
#if defined(ATMOSPHERE_ARCH_X64) || defined(ATMOSPHERE_ARCH_X86)
|
||||
/* Don't do anything, cache maintenance isn't available/relevant to userland. */
|
||||
AMS_UNUSED(addr, size);
|
||||
#else
|
||||
#error "Unknown architecture for windows dd::InvalidateDataCacheImpl"
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
|
||||
#if defined(ATMOSPHERE_OS_HORIZON)
|
||||
#include "dd_cache_impl.os.horizon.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_WINDOWS)
|
||||
#include "dd_cache_impl.os.windows.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_LINUX)
|
||||
#include "dd_cache_impl.os.linux.hpp"
|
||||
#elif defined(ATMOSPHERE_OS_MACOS)
|
||||
#include "dd_cache_impl.os.macos.hpp"
|
||||
#else
|
||||
#error "Unknown OS for ams::dd::CacheImpl"
|
||||
#endif
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#if !defined(ATMOSPHERE_OS_HORIZON) && (defined(AMS_BUILD_FOR_DEBUGGING) || defined(AMS_BUILD_FOR_AUDITING))
|
||||
#define AMS_AUTO_GENERATE_RESULT_NAMES
|
||||
#endif
|
||||
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams {
|
||||
|
||||
#if defined(AMS_AUTO_GENERATE_RESULT_NAMES)
|
||||
|
||||
#define AMS_INVOKE_MACRO_01(EXPR, n) EXPR(n); EXPR(n + (1 << 0));
|
||||
|
||||
#define AMS_INVOKE_MACRO_02(EXPR, n) AMS_INVOKE_MACRO_01(EXPR, n); AMS_INVOKE_MACRO_01(EXPR, n + (1 << 1));
|
||||
#define AMS_INVOKE_MACRO_03(EXPR, n) AMS_INVOKE_MACRO_02(EXPR, n); AMS_INVOKE_MACRO_02(EXPR, n + (1 << 2));
|
||||
#define AMS_INVOKE_MACRO_04(EXPR, n) AMS_INVOKE_MACRO_03(EXPR, n); AMS_INVOKE_MACRO_03(EXPR, n + (1 << 3));
|
||||
#define AMS_INVOKE_MACRO_05(EXPR, n) AMS_INVOKE_MACRO_04(EXPR, n); AMS_INVOKE_MACRO_04(EXPR, n + (1 << 4));
|
||||
#define AMS_INVOKE_MACRO_06(EXPR, n) AMS_INVOKE_MACRO_05(EXPR, n); AMS_INVOKE_MACRO_05(EXPR, n + (1 << 5));
|
||||
#define AMS_INVOKE_MACRO_07(EXPR, n) AMS_INVOKE_MACRO_06(EXPR, n); AMS_INVOKE_MACRO_06(EXPR, n + (1 << 6));
|
||||
#define AMS_INVOKE_MACRO_08(EXPR, n) AMS_INVOKE_MACRO_07(EXPR, n); AMS_INVOKE_MACRO_07(EXPR, n + (1 << 7));
|
||||
#define AMS_INVOKE_MACRO_09(EXPR, n) AMS_INVOKE_MACRO_08(EXPR, n); AMS_INVOKE_MACRO_08(EXPR, n + (1 << 8));
|
||||
#define AMS_INVOKE_MACRO_10(EXPR, n) AMS_INVOKE_MACRO_09(EXPR, n); AMS_INVOKE_MACRO_09(EXPR, n + (1 << 9));
|
||||
#define AMS_INVOKE_MACRO_11(EXPR, n) AMS_INVOKE_MACRO_10(EXPR, n); AMS_INVOKE_MACRO_10(EXPR, n + (1 << 10));
|
||||
#define AMS_INVOKE_MACRO_12(EXPR, n) AMS_INVOKE_MACRO_11(EXPR, n); AMS_INVOKE_MACRO_11(EXPR, n + (1 << 11));
|
||||
#define AMS_INVOKE_MACRO_13(EXPR, n) AMS_INVOKE_MACRO_12(EXPR, n); AMS_INVOKE_MACRO_12(EXPR, n + (1 << 12));
|
||||
|
||||
namespace {
|
||||
|
||||
template<int Module, int Description>
|
||||
constexpr const char *GetResultNameByModuleAndDescription() {
|
||||
return ::ams::result::impl::ResultNameSpaceExistsImpl<Module>::template NameHolder<Description>::Name;
|
||||
}
|
||||
|
||||
template<int Module>
|
||||
constexpr const char *GetResultNameByModule(int description) {
|
||||
#define AMS_TEST_RESULT_DESCRIPTION_DEFINED(n) if constexpr (::ams::result::impl::ResultNameSpaceExistsImpl<Module>::template NameHolder<n>::Exists) { if (description == n) { return GetResultNameByModuleAndDescription<Module, n>(); } }
|
||||
|
||||
AMS_INVOKE_MACRO_13(AMS_TEST_RESULT_DESCRIPTION_DEFINED, 0)
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const char *GetResultName(int module, int description) {
|
||||
#define AMS_TEST_RESULT_MODULE_DEFINED(n) if constexpr (::ams::result::impl::ResultNameSpaceExistsImpl<n>::Exists) { if (module == n) { return GetResultNameByModule<n>(description); } }
|
||||
|
||||
AMS_INVOKE_MACRO_08(AMS_TEST_RESULT_MODULE_DEFINED, 0)
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
#else
|
||||
const char *GetResultName(int, int) {
|
||||
return "Unknown";
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -1,581 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
#include "sdmmc_base_device_accessor.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
#if defined(AMS_SDMMC_THREAD_SAFE)
|
||||
|
||||
#define AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX() std::scoped_lock lk(m_base_device->m_device_mutex)
|
||||
|
||||
#else
|
||||
|
||||
#define AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX()
|
||||
|
||||
#endif
|
||||
|
||||
void BaseDevice::GetLegacyCapacityParameters(u8 *out_c_size_mult, u8 *out_read_bl_len) const {
|
||||
AMS_ABORT_UNLESS(out_c_size_mult != nullptr);
|
||||
AMS_ABORT_UNLESS(out_read_bl_len != nullptr);
|
||||
|
||||
/* Extract C_SIZE_MULT and READ_BL_LEN from the CSD. */
|
||||
*out_c_size_mult = static_cast<u8>((m_csd[2] >> 7) & 0x7);
|
||||
*out_read_bl_len = static_cast<u8>((m_csd[4] >> 8) & 0xF);
|
||||
}
|
||||
|
||||
Result BaseDevice::SetLegacyMemoryCapacity() {
|
||||
/* Get csize from the csd. */
|
||||
const u32 c_size = ((m_csd[3] >> 6) & 0x3FF) | ((m_csd[4] & 0x3) << 10);
|
||||
|
||||
/* Get c_size_mult and read_bl_len. */
|
||||
u8 c_size_mult, read_bl_len;
|
||||
this->GetLegacyCapacityParameters(std::addressof(c_size_mult), std::addressof(read_bl_len));
|
||||
|
||||
/* Validate the parameters. */
|
||||
R_UNLESS((read_bl_len + c_size_mult + 2) >= 9, sdmmc::ResultUnexpectedDeviceCsdValue());
|
||||
|
||||
/* Set memory capacity. */
|
||||
m_memory_capacity = (c_size + 1) << ((read_bl_len + c_size_mult + 2) - 9);
|
||||
m_is_valid_memory_capacity = true;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result BaseDevice::CheckDeviceStatus(u32 r1_resp) const {
|
||||
/* Check if there are any errors at all. */
|
||||
R_SUCCEED_IF((r1_resp & DeviceStatus_ErrorMask) == 0);
|
||||
|
||||
/* Check errors individually. */
|
||||
#define AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(__ERROR__) R_UNLESS((r1_resp & DeviceStatus_##__ERROR__) == 0, sdmmc::ResultDeviceStatus##__ERROR__())
|
||||
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(ComCrcError);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(DeviceEccFailed);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(CcError);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(Error);
|
||||
|
||||
if (this->GetDeviceType() == DeviceType_Mmc) {
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(SwitchError);
|
||||
}
|
||||
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(AddressMisaligned);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(BlockLenError);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(EraseSeqError);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(EraseParam);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(WpViolation);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(LockUnlockFailed);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(CidCsdOverwrite);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(WpEraseSkip);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(WpEraseSkip);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(EraseReset);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(IllegalCommand);
|
||||
AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR(AddressOutOfRange);
|
||||
|
||||
#undef AMS_SDMMC_CHECK_DEVICE_STATUS_ERROR
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
DeviceState BaseDevice::GetDeviceState(u32 r1_resp) const {
|
||||
return static_cast<DeviceState>((r1_resp & DeviceStatus_CurrentStateMask) >> DeviceStatus_CurrentStateShift);
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::IssueCommandAndCheckR1(u32 *out_response, u32 command_index, u32 command_arg, bool is_busy, DeviceState expected_state, u32 status_ignore_mask) const {
|
||||
/* Issue the command. */
|
||||
constexpr ResponseType CommandResponseType = ResponseType_R1;
|
||||
Command command(command_index, command_arg, CommandResponseType, is_busy);
|
||||
R_TRY(m_host_controller->IssueCommand(std::addressof(command)));
|
||||
|
||||
/* Get the response. */
|
||||
AMS_ABORT_UNLESS(out_response != nullptr);
|
||||
m_host_controller->GetLastResponse(out_response, sizeof(u32), CommandResponseType);
|
||||
|
||||
/* Mask out the ignored status bits. */
|
||||
if (status_ignore_mask != 0) {
|
||||
*out_response &= ~status_ignore_mask;
|
||||
}
|
||||
|
||||
/* Check the r1 response for errors. */
|
||||
AMS_ABORT_UNLESS(m_base_device != nullptr);
|
||||
R_TRY(m_base_device->CheckDeviceStatus(*out_response));
|
||||
|
||||
/* Check the device state. */
|
||||
if (expected_state != DeviceState_Unknown) {
|
||||
R_UNLESS(m_base_device->GetDeviceState(*out_response) == expected_state, sdmmc::ResultUnexpectedDeviceState());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::IssueCommandGoIdleState() const {
|
||||
/* Issue the command. */
|
||||
Command command(CommandIndex_GoIdleState, 0, ResponseType_R0, false);
|
||||
R_RETURN(m_host_controller->IssueCommand(std::addressof(command)));
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::IssueCommandAllSendCid(void *dst, size_t dst_size) const {
|
||||
/* Issue the command. */
|
||||
constexpr ResponseType CommandResponseType = ResponseType_R2;
|
||||
Command command(CommandIndex_AllSendCid, 0, CommandResponseType, false);
|
||||
R_TRY(m_host_controller->IssueCommand(std::addressof(command)));
|
||||
|
||||
/* Copy the data out. */
|
||||
AMS_ABORT_UNLESS(dst != nullptr);
|
||||
AMS_ABORT_UNLESS(util::IsAligned(reinterpret_cast<uintptr_t>(dst), alignof(u32)));
|
||||
AMS_ABORT_UNLESS(dst_size >= DeviceCidSize);
|
||||
m_host_controller->GetLastResponse(static_cast<u32 *>(dst), DeviceCidSize, CommandResponseType);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::IssueCommandSelectCard() const {
|
||||
/* Get the command argument. */
|
||||
AMS_ABORT_UNLESS(m_base_device != nullptr);
|
||||
const u32 arg = static_cast<u32>(m_base_device->GetRca()) << 16;
|
||||
|
||||
/* Issue the command. */
|
||||
R_RETURN(this->IssueCommandAndCheckR1(CommandIndex_SelectCard, arg, true, DeviceState_Unknown));
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::IssueCommandSendCsd(void *dst, size_t dst_size) const {
|
||||
/* Get the command argument. */
|
||||
AMS_ABORT_UNLESS(m_base_device != nullptr);
|
||||
const u32 arg = static_cast<u32>(m_base_device->GetRca()) << 16;
|
||||
|
||||
/* Issue the command. */
|
||||
constexpr ResponseType CommandResponseType = ResponseType_R2;
|
||||
Command command(CommandIndex_SendCsd, arg, CommandResponseType, false);
|
||||
R_TRY(m_host_controller->IssueCommand(std::addressof(command)));
|
||||
|
||||
/* Copy the data out. */
|
||||
AMS_ABORT_UNLESS(dst != nullptr);
|
||||
AMS_ABORT_UNLESS(util::IsAligned(reinterpret_cast<uintptr_t>(dst), alignof(u32)));
|
||||
AMS_ABORT_UNLESS(dst_size >= DeviceCsdSize);
|
||||
m_host_controller->GetLastResponse(static_cast<u32 *>(dst), DeviceCsdSize, CommandResponseType);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::IssueCommandSendStatus(u32 *out_device_status, u32 status_ignore_mask) const {
|
||||
/* Get the command argument. */
|
||||
AMS_ABORT_UNLESS(m_base_device != nullptr);
|
||||
const u32 arg = static_cast<u32>(m_base_device->GetRca()) << 16;
|
||||
|
||||
/* Issue the command. */
|
||||
R_RETURN(this->IssueCommandAndCheckR1(out_device_status, CommandIndex_SendStatus, arg, false, DeviceState_Tran, status_ignore_mask));
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::IssueCommandSetBlockLenToSectorSize() const {
|
||||
/* Issue the command. */
|
||||
R_RETURN(this->IssueCommandAndCheckR1(CommandIndex_SetBlockLen, SectorSize, false, DeviceState_Tran));
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::IssueCommandMultipleBlock(u32 *out_num_transferred_blocks, u32 sector_index, u32 num_sectors, void *buf, bool is_read) const {
|
||||
/* Get the argument. */
|
||||
AMS_ABORT_UNLESS(m_base_device != nullptr);
|
||||
const u32 arg = m_base_device->IsHighCapacity() ? sector_index : sector_index * SectorSize;
|
||||
|
||||
/* Get the command index and transfer direction. */
|
||||
const u32 command_index = is_read ? CommandIndex_ReadMultipleBlock : CommandIndex_WriteMultipleBlock;
|
||||
const auto xfer_direction = is_read ? TransferDirection_ReadFromDevice : TransferDirection_WriteToDevice;
|
||||
|
||||
/* Issue the command. */
|
||||
constexpr ResponseType CommandResponseType = ResponseType_R1;
|
||||
Command command(command_index, arg, CommandResponseType, false);
|
||||
TransferData xfer_data(buf, SectorSize, num_sectors, xfer_direction, true, true);
|
||||
Result result = m_host_controller->IssueCommand(std::addressof(command), std::addressof(xfer_data), out_num_transferred_blocks);
|
||||
|
||||
/* Handle the failure case. */
|
||||
if (R_FAILED(result)) {
|
||||
/* Check if we were removed. */
|
||||
R_TRY(this->CheckRemoved());
|
||||
|
||||
/* By default, we'll want to return the result we just got. */
|
||||
Result result_to_return = result;
|
||||
|
||||
/* Stop transmission. */
|
||||
u32 resp = 0;
|
||||
result = m_host_controller->IssueStopTransmissionCommand(std::addressof(resp));
|
||||
if (R_SUCCEEDED(result)) {
|
||||
result = m_base_device->CheckDeviceStatus(resp & (~DeviceStatus_IllegalCommand));
|
||||
if (R_FAILED(result)) {
|
||||
result_to_return = result;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if we were removed. */
|
||||
R_TRY(this->CheckRemoved());
|
||||
|
||||
/* Get the device status. */
|
||||
u32 device_status = 0;
|
||||
result = this->IssueCommandSendStatus(std::addressof(device_status), DeviceStatus_IllegalCommand);
|
||||
|
||||
/* If there's a device status error we don't already have, we prefer to return it. */
|
||||
if (!sdmmc::ResultDeviceStatusHasError::Includes(result_to_return) && sdmmc::ResultDeviceStatusHasError::Includes(result)) {
|
||||
result_to_return = result;
|
||||
}
|
||||
|
||||
/* Return the result we chose. */
|
||||
R_RETURN(result_to_return);
|
||||
}
|
||||
|
||||
/* Get the responses. */
|
||||
u32 resp, st_resp;
|
||||
m_host_controller->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType);
|
||||
m_host_controller->GetLastStopTransmissionResponse(std::addressof(st_resp), sizeof(st_resp));
|
||||
|
||||
/* Check the device status. */
|
||||
R_TRY(m_base_device->CheckDeviceStatus(resp));
|
||||
|
||||
/* Decide on what errors to ignore. */
|
||||
u32 status_ignore_mask = 0;
|
||||
if (is_read) {
|
||||
AMS_ABORT_UNLESS(out_num_transferred_blocks != nullptr);
|
||||
if ((*out_num_transferred_blocks + sector_index) == m_base_device->GetMemoryCapacity()) {
|
||||
status_ignore_mask = DeviceStatus_AddressOutOfRange;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check the device status. */
|
||||
R_TRY(m_base_device->CheckDeviceStatus(st_resp & ~status_ignore_mask));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::ReadWriteSingle(u32 *out_num_transferred_blocks, u32 sector_index, u32 num_sectors, void *buf, bool is_read) const {
|
||||
/* Issue the read/write command. */
|
||||
AMS_ABORT_UNLESS(out_num_transferred_blocks != nullptr);
|
||||
R_TRY(this->IssueCommandMultipleBlock(out_num_transferred_blocks, sector_index, num_sectors, buf, is_read));
|
||||
|
||||
/* Decide on what errors to ignore. */
|
||||
u32 status_ignore_mask = 0;
|
||||
if (is_read) {
|
||||
AMS_ABORT_UNLESS(m_base_device != nullptr);
|
||||
if ((*out_num_transferred_blocks + sector_index) == m_base_device->GetMemoryCapacity()) {
|
||||
status_ignore_mask = DeviceStatus_AddressOutOfRange;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get and check the status. */
|
||||
u32 device_status;
|
||||
R_TRY(this->IssueCommandSendStatus(std::addressof(device_status), status_ignore_mask));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::ReadWriteMultiple(u32 sector_index, u32 num_sectors, u32 sector_index_alignment, void *buf, size_t buf_size, bool is_read) {
|
||||
/* Verify that we can send the command. */
|
||||
AMS_ABORT_UNLESS(m_base_device != nullptr);
|
||||
|
||||
/* If we want to read zero sectors, there's no work for us to do. */
|
||||
R_SUCCEED_IF(num_sectors == 0);
|
||||
|
||||
/* Check that the buffer is big enough for the sectors we're reading. */
|
||||
AMS_ABORT_UNLESS((buf_size / SectorSize) >= num_sectors);
|
||||
|
||||
/* Read sectors repeatedly until we've read all the ones we want. */
|
||||
u32 cur_sector_index = sector_index;
|
||||
u32 remaining_sectors = num_sectors;
|
||||
u8 *cur_buf = static_cast<u8 *>(buf);
|
||||
while (remaining_sectors > 0) {
|
||||
/* Determine how many sectors we can read in this iteration. */
|
||||
u32 cur_sectors = remaining_sectors;
|
||||
if (sector_index_alignment > 0) {
|
||||
AMS_ABORT_UNLESS((cur_sector_index % sector_index_alignment) == 0);
|
||||
|
||||
const u32 max_sectors = m_host_controller->GetMaxTransferNumBlocks();
|
||||
if (remaining_sectors > max_sectors) {
|
||||
cur_sectors = max_sectors - (max_sectors % sector_index_alignment);
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to perform the read/write. */
|
||||
u32 num_transferred_blocks = 0;
|
||||
Result result = this->ReadWriteSingle(std::addressof(num_transferred_blocks), cur_sector_index, cur_sectors, cur_buf, is_read);
|
||||
if (R_FAILED(result)) {
|
||||
/* Check if we were removed. */
|
||||
R_TRY(this->CheckRemoved());
|
||||
|
||||
/* Log that we failed to read/write. */
|
||||
this->PushErrorLog(false, "%s %X %X:%X", is_read ? "R" : "W", cur_sector_index, cur_sectors, result.GetValue());
|
||||
|
||||
/* Retry the read/write. */
|
||||
num_transferred_blocks = 0;
|
||||
result = this->ReadWriteSingle(std::addressof(num_transferred_blocks), cur_sector_index, cur_sectors, cur_buf, is_read);
|
||||
if (R_FAILED(result)) {
|
||||
/* Check if we were removed. */
|
||||
R_TRY(this->CheckRemoved());
|
||||
|
||||
/* Log that we failed to read/write. */
|
||||
this->PushErrorLog(false, "%s %X %X:%X", is_read ? "R" : "W", cur_sector_index, cur_sectors, result.GetValue());
|
||||
|
||||
/* Re-startup the connection, to see if that helps. */
|
||||
R_TRY(this->ReStartup());
|
||||
|
||||
/* Retry the read/write a third time. */
|
||||
num_transferred_blocks = 0;
|
||||
result = this->ReadWriteSingle(std::addressof(num_transferred_blocks), cur_sector_index, cur_sectors, cur_buf, is_read);
|
||||
if (R_FAILED(result)) {
|
||||
/* Log that we failed after a re-startup. */
|
||||
this->PushErrorLog(true, "%s %X %X:%X", is_read ? "R" : "W", cur_sector_index, cur_sectors, result.GetValue());
|
||||
R_RETURN(result);
|
||||
}
|
||||
|
||||
/* Log that we succeeded after a retry. */
|
||||
this->PushErrorLog(true, "%s %X %X:0", is_read ? "R" : "W", cur_sector_index, cur_sectors);
|
||||
|
||||
/* Increment the number of error corrections we've done. */
|
||||
++m_num_read_write_error_corrections;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update our tracking variables. */
|
||||
AMS_ABORT_UNLESS(remaining_sectors >= num_transferred_blocks);
|
||||
remaining_sectors -= num_transferred_blocks;
|
||||
cur_sector_index += num_transferred_blocks;
|
||||
cur_buf += num_transferred_blocks * SectorSize;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
|
||||
void BaseDeviceAccessor::RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(m_base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Register the address. */
|
||||
return m_host_controller->RegisterDeviceVirtualAddress(buffer, buffer_size, buffer_device_virtual_address);
|
||||
}
|
||||
|
||||
void BaseDeviceAccessor::UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(m_base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Register the address. */
|
||||
return m_host_controller->UnregisterDeviceVirtualAddress(buffer, buffer_size, buffer_device_virtual_address);
|
||||
}
|
||||
#endif
|
||||
|
||||
Result BaseDeviceAccessor::Activate() {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(m_base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is awake. */
|
||||
R_UNLESS(m_base_device->IsAwake(), sdmmc::ResultNotAwakened());
|
||||
|
||||
/* If the device is already active, we don't need to do anything. */
|
||||
R_SUCCEED_IF(m_base_device->IsActive());
|
||||
|
||||
/* Activate the base device. */
|
||||
auto activate_guard = SCOPE_GUARD { ++m_num_activation_failures; };
|
||||
R_TRY(this->OnActivate());
|
||||
|
||||
/* We successfully activated the device. */
|
||||
activate_guard.Cancel();
|
||||
m_base_device->SetActive();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void BaseDeviceAccessor::Deactivate() {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(m_base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Deactivate the base device. */
|
||||
if (m_base_device->IsActive()) {
|
||||
m_host_controller->Shutdown();
|
||||
m_base_device->Deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::ReadWrite(u32 sector_index, u32 num_sectors, void *buffer, size_t buffer_size, bool is_read) {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(m_base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is accessible. */
|
||||
R_TRY(m_base_device->CheckAccessible());
|
||||
|
||||
/* Perform the read/write. */
|
||||
auto rw_guard = SCOPE_GUARD { ++m_num_read_write_failures; };
|
||||
R_TRY(this->OnReadWrite(sector_index, num_sectors, buffer, buffer_size, is_read));
|
||||
|
||||
/* We successfully performed the read/write. */
|
||||
rw_guard.Cancel();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::CheckConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width) {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(m_base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is accessible. */
|
||||
R_TRY(m_base_device->CheckAccessible());
|
||||
|
||||
/* Get the current speed mode/bus width. */
|
||||
*out_speed_mode = m_host_controller->GetSpeedMode();
|
||||
*out_bus_width = m_host_controller->GetBusWidth();
|
||||
|
||||
/* Verify that we can get the status. */
|
||||
R_TRY(m_host_controller->GetInternalStatus());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::GetMemoryCapacity(u32 *out_sectors) const {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(m_base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is accessible. */
|
||||
R_TRY(m_base_device->CheckAccessible());
|
||||
|
||||
/* Get the capacity. */
|
||||
AMS_ABORT_UNLESS(out_sectors != nullptr);
|
||||
*out_sectors = m_base_device->GetMemoryCapacity();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::GetDeviceStatus(u32 *out) const {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(m_base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is accessible. */
|
||||
R_TRY(m_base_device->CheckAccessible());
|
||||
|
||||
/* Get the status. */
|
||||
R_TRY(this->IssueCommandSendStatus(out, 0));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::GetOcr(u32 *out) const {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(m_base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is accessible. */
|
||||
R_TRY(m_base_device->CheckAccessible());
|
||||
|
||||
/* Get the ocr. */
|
||||
AMS_ABORT_UNLESS(out != nullptr);
|
||||
*out = m_base_device->GetOcr();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::GetRca(u16 *out) const {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(m_base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is accessible. */
|
||||
R_TRY(m_base_device->CheckAccessible());
|
||||
|
||||
/* Get the rca. */
|
||||
AMS_ABORT_UNLESS(out != nullptr);
|
||||
*out = m_base_device->GetRca();
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::GetCid(void *out, size_t size) const {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(m_base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is accessible. */
|
||||
R_TRY(m_base_device->CheckAccessible());
|
||||
|
||||
/* Get the cid. */
|
||||
m_base_device->GetCid(out, size);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result BaseDeviceAccessor::GetCsd(void *out, size_t size) const {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(m_base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Check that the device is accessible. */
|
||||
R_TRY(m_base_device->CheckAccessible());
|
||||
|
||||
/* Get the csd. */
|
||||
m_base_device->GetCsd(out, size);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void BaseDeviceAccessor::GetAndClearErrorInfo(ErrorInfo *out_error_info, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size) {
|
||||
/* Lock exclusive access of the base device. */
|
||||
AMS_ABORT_UNLESS(m_base_device != nullptr);
|
||||
AMS_SDMMC_LOCK_BASE_DEVICE_MUTEX();
|
||||
|
||||
/* Set the output error info. */
|
||||
AMS_ABORT_UNLESS(out_error_info != nullptr);
|
||||
out_error_info->num_activation_failures = m_num_activation_failures;
|
||||
out_error_info->num_activation_error_corrections = m_num_activation_error_corrections;
|
||||
out_error_info->num_read_write_failures = m_num_read_write_failures;
|
||||
out_error_info->num_read_write_error_corrections = m_num_read_write_error_corrections;
|
||||
this->ClearErrorInfo();
|
||||
|
||||
/* Check if we should write logs. */
|
||||
if (out_log_size == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if we can write logs. */
|
||||
if (out_log_buffer == nullptr || log_buffer_size == 0) {
|
||||
*out_log_size = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get and clear our logs. */
|
||||
#if defined(AMS_SDMMC_USE_LOGGER)
|
||||
{
|
||||
if (m_error_logger.HasLog()) {
|
||||
this->PushErrorTimeStamp();
|
||||
|
||||
*out_log_size = m_error_logger.GetAndClearLogs(out_log_buffer, log_buffer_size);
|
||||
} else {
|
||||
*out_log_size = 0;
|
||||
}
|
||||
}
|
||||
#else
|
||||
{
|
||||
*out_log_size = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,512 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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.hpp>
|
||||
#include "sdmmc_i_host_controller.hpp"
|
||||
#include "sdmmc_i_device_accessor.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
#if defined(AMS_SDMMC_USE_LOGGER)
|
||||
class Logger {
|
||||
private:
|
||||
static constexpr size_t LogLengthMax = 0x20;
|
||||
static constexpr size_t LogCountMax = 0x10;
|
||||
private:
|
||||
char m_logs[LogCountMax][LogLengthMax];
|
||||
int m_log_index;
|
||||
private:
|
||||
void Clear() {
|
||||
for (size_t i = 0; i < LogCountMax; ++i) {
|
||||
m_logs[i][0] = '\0';
|
||||
}
|
||||
m_log_index = 0;
|
||||
}
|
||||
|
||||
size_t Pop(char *dst, size_t dst_size) {
|
||||
/* Decrease log index. */
|
||||
if ((--m_log_index) < 0) {
|
||||
m_log_index = LogCountMax - 1;
|
||||
}
|
||||
|
||||
/* Check if we have a log. */
|
||||
if (m_logs[m_log_index][0] == '\0') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Copy log to output. */
|
||||
const int len = ::ams::util::Strlcpy(dst, m_logs[m_log_index], dst_size);
|
||||
|
||||
/* Clear the log we copied. */
|
||||
m_logs[m_log_index][0] = '\0';
|
||||
|
||||
return static_cast<size_t>(len);
|
||||
}
|
||||
|
||||
public:
|
||||
Logger() {
|
||||
this->Clear();
|
||||
}
|
||||
|
||||
void Push(const char *fmt, std::va_list vl) {
|
||||
/* Format the log into the current buffer. */
|
||||
::ams::util::TVSNPrintf(m_logs[m_log_index], LogLengthMax, fmt, vl);
|
||||
|
||||
/* Update our log index. */
|
||||
if ((++m_log_index) >= static_cast<int>(LogCountMax)) {
|
||||
m_log_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Push(const char *fmt, ...) {
|
||||
std::va_list vl;
|
||||
va_start(vl, fmt);
|
||||
this->Push(fmt, vl);
|
||||
va_end(vl);
|
||||
}
|
||||
|
||||
bool HasLog() const {
|
||||
const int index = m_log_index > 0 ? m_log_index - 1 : static_cast<int>(LogCountMax - 1);
|
||||
return m_logs[index][0] != '\0';
|
||||
}
|
||||
|
||||
size_t GetAndClearLogs(char *dst, size_t dst_size) {
|
||||
AMS_ABORT_UNLESS(dst != nullptr);
|
||||
AMS_ABORT_UNLESS(dst_size > 0);
|
||||
|
||||
/* Pop logs until we run out of them. */
|
||||
size_t total_len = 0;
|
||||
while (true) {
|
||||
/* Pop the current log. */
|
||||
const size_t cur_len = this->Pop(dst + total_len, dst_size - total_len);
|
||||
if (cur_len == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check if the log exceeded the buffer size. */
|
||||
if (total_len + cur_len + 1 >= dst_size) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Advance the total length. */
|
||||
total_len += cur_len;
|
||||
|
||||
/* Check if there's space for our separator. */
|
||||
if (total_len + 3 >= dst_size) {
|
||||
break;
|
||||
}
|
||||
|
||||
dst[total_len + 0] = ',';
|
||||
dst[total_len + 1] = ' ';
|
||||
total_len += 2;
|
||||
}
|
||||
|
||||
/* Ensure that the complete log fits in the buffer. */
|
||||
if (total_len >= dst_size) {
|
||||
total_len = dst_size - 1;
|
||||
}
|
||||
|
||||
/* Ensure null termination. */
|
||||
dst[total_len] = '\0';
|
||||
|
||||
/* Clear any remaining logs. */
|
||||
this->Clear();
|
||||
|
||||
/* Return the length of the logs, including null terminator. */
|
||||
return total_len + 1;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
enum DeviceType {
|
||||
DeviceType_Mmc = 0,
|
||||
DeviceType_SdCard = 1,
|
||||
DeviceType_GcAsic = 2,
|
||||
};
|
||||
|
||||
enum DeviceState {
|
||||
DeviceState_Idle = 0,
|
||||
DeviceState_Ready = 1,
|
||||
DeviceState_Ident = 2,
|
||||
DeviceState_Stby = 3,
|
||||
DeviceState_Tran = 4,
|
||||
DeviceState_Data = 5,
|
||||
DeviceState_Rcv = 6,
|
||||
DeviceState_Prg = 7,
|
||||
DeviceState_Dis = 8,
|
||||
DeviceState_Rsvd0 = 9,
|
||||
DeviceState_Rsvd1 = 10,
|
||||
DeviceState_Rsvd2 = 11,
|
||||
DeviceState_Rsvd3 = 12,
|
||||
DeviceState_Rsvd4 = 13,
|
||||
DeviceState_Rsvd5 = 14,
|
||||
DeviceState_RsvdIoMode = 15,
|
||||
DeviceState_Unknown = 16,
|
||||
};
|
||||
|
||||
enum DeviceStatus : u32 {
|
||||
DeviceStatus_AkeSeqError = (1u << 3),
|
||||
DeviceStatus_AppCmd = (1u << 5),
|
||||
DeviceStatus_SwitchError = (1u << 7),
|
||||
DeviceStatus_EraseReset = (1u << 13),
|
||||
DeviceStatus_WpEraseSkip = (1u << 15),
|
||||
DeviceStatus_CidCsdOverwrite = (1u << 16),
|
||||
DeviceStatus_Error = (1u << 19),
|
||||
DeviceStatus_CcError = (1u << 20),
|
||||
DeviceStatus_DeviceEccFailed = (1u << 21),
|
||||
DeviceStatus_IllegalCommand = (1u << 22),
|
||||
DeviceStatus_ComCrcError = (1u << 23),
|
||||
DeviceStatus_LockUnlockFailed = (1u << 24),
|
||||
DeviceStatus_WpViolation = (1u << 26),
|
||||
DeviceStatus_EraseParam = (1u << 27),
|
||||
DeviceStatus_EraseSeqError = (1u << 28),
|
||||
DeviceStatus_BlockLenError = (1u << 29),
|
||||
DeviceStatus_AddressMisaligned = (1u << 30),
|
||||
DeviceStatus_AddressOutOfRange = (1u << 31),
|
||||
|
||||
DeviceStatus_CurrentStateShift = 9,
|
||||
DeviceStatus_CurrentStateMask = (0b1111u << DeviceStatus_CurrentStateShift),
|
||||
|
||||
DeviceStatus_ErrorMask = (DeviceStatus_SwitchError |
|
||||
DeviceStatus_EraseReset |
|
||||
DeviceStatus_WpEraseSkip |
|
||||
DeviceStatus_CidCsdOverwrite |
|
||||
DeviceStatus_Error |
|
||||
DeviceStatus_CcError |
|
||||
DeviceStatus_DeviceEccFailed |
|
||||
DeviceStatus_IllegalCommand |
|
||||
DeviceStatus_ComCrcError |
|
||||
DeviceStatus_LockUnlockFailed |
|
||||
DeviceStatus_WpViolation |
|
||||
DeviceStatus_EraseParam |
|
||||
DeviceStatus_EraseSeqError |
|
||||
DeviceStatus_BlockLenError |
|
||||
DeviceStatus_AddressMisaligned |
|
||||
DeviceStatus_AddressOutOfRange),
|
||||
};
|
||||
|
||||
class BaseDevice {
|
||||
private:
|
||||
u32 m_ocr;
|
||||
u8 m_cid[DeviceCidSize];
|
||||
u16 m_csd[DeviceCsdSize / sizeof(u16)];
|
||||
u32 m_memory_capacity;
|
||||
bool m_is_high_capacity;
|
||||
bool m_is_valid_ocr;
|
||||
bool m_is_valid_cid;
|
||||
bool m_is_valid_csd;
|
||||
bool m_is_valid_high_capacity;
|
||||
bool m_is_valid_memory_capacity;
|
||||
bool m_is_active;
|
||||
bool m_is_awake;
|
||||
public:
|
||||
#if defined(AMS_SDMMC_THREAD_SAFE)
|
||||
mutable os::SdkRecursiveMutex m_device_mutex;
|
||||
public:
|
||||
BaseDevice() : m_device_mutex()
|
||||
#else
|
||||
BaseDevice()
|
||||
#endif
|
||||
{
|
||||
m_is_awake = true;
|
||||
m_ocr = 0;
|
||||
m_memory_capacity = 0;
|
||||
m_is_high_capacity = false;
|
||||
this->OnDeactivate();
|
||||
}
|
||||
|
||||
void OnDeactivate() {
|
||||
m_is_active = false;
|
||||
m_is_valid_ocr = false;
|
||||
m_is_valid_cid = false;
|
||||
m_is_valid_csd = false;
|
||||
m_is_valid_high_capacity = false;
|
||||
m_is_valid_memory_capacity = false;
|
||||
}
|
||||
|
||||
bool IsAwake() const {
|
||||
return m_is_awake;
|
||||
}
|
||||
|
||||
void Awaken() {
|
||||
m_is_awake = true;
|
||||
}
|
||||
|
||||
void PutToSleep() {
|
||||
m_is_awake = false;
|
||||
}
|
||||
|
||||
bool IsActive() const {
|
||||
return m_is_active;
|
||||
}
|
||||
|
||||
void SetActive() {
|
||||
m_is_active = true;
|
||||
}
|
||||
|
||||
virtual void Deactivate() {
|
||||
this->OnDeactivate();
|
||||
}
|
||||
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
virtual os::EventType *GetRemovedEvent() const = 0;
|
||||
#endif
|
||||
|
||||
virtual DeviceType GetDeviceType() const = 0;
|
||||
virtual u16 GetRca() const = 0;
|
||||
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
void InitializeRemovedEvent() {
|
||||
if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) {
|
||||
os::InitializeEvent(removed_event, false, os::EventClearMode_ManualClear);
|
||||
}
|
||||
}
|
||||
|
||||
void FinalizeRemovedEvent() {
|
||||
if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) {
|
||||
os::FinalizeEvent(removed_event);
|
||||
}
|
||||
}
|
||||
|
||||
void SignalRemovedEvent() {
|
||||
if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) {
|
||||
os::SignalEvent(removed_event);
|
||||
}
|
||||
}
|
||||
|
||||
void ClearRemovedEvent() {
|
||||
if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) {
|
||||
os::ClearEvent(removed_event);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsRemoved() const {
|
||||
if (os::EventType *removed_event = this->GetRemovedEvent(); removed_event != nullptr) {
|
||||
return os::TryWaitEvent(removed_event);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
Result CheckRemoved() const {
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
R_UNLESS(!this->IsRemoved(), sdmmc::ResultDeviceRemoved());
|
||||
#endif
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheckAccessible() const {
|
||||
/* Check awake. */
|
||||
R_UNLESS(this->IsAwake(), sdmmc::ResultNotAwakened());
|
||||
|
||||
/* Check active. */
|
||||
R_UNLESS(this->IsActive(), sdmmc::ResultNotActivated());
|
||||
|
||||
/* Check removed. */
|
||||
R_TRY(this->CheckRemoved());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void SetHighCapacity(bool en) {
|
||||
m_is_high_capacity = en;
|
||||
m_is_valid_high_capacity = true;
|
||||
}
|
||||
|
||||
bool IsHighCapacity() const {
|
||||
AMS_ABORT_UNLESS(m_is_valid_high_capacity);
|
||||
return m_is_high_capacity;
|
||||
}
|
||||
|
||||
void SetOcr(u32 o) {
|
||||
m_ocr = o;
|
||||
m_is_valid_ocr = true;
|
||||
}
|
||||
|
||||
u32 GetOcr() const {
|
||||
AMS_ABORT_UNLESS(m_is_valid_ocr);
|
||||
return m_ocr;
|
||||
}
|
||||
|
||||
void SetCid(const void *src, size_t src_size) {
|
||||
AMS_ABORT_UNLESS(src != nullptr);
|
||||
AMS_ABORT_UNLESS(src_size >= DeviceCidSize);
|
||||
std::memcpy(m_cid, src, DeviceCidSize);
|
||||
m_is_valid_cid = true;
|
||||
}
|
||||
|
||||
void GetCid(void *dst, size_t dst_size) const {
|
||||
AMS_ABORT_UNLESS(m_is_valid_cid);
|
||||
AMS_ABORT_UNLESS(dst != nullptr);
|
||||
AMS_ABORT_UNLESS(dst_size >= DeviceCidSize);
|
||||
std::memcpy(dst, m_cid, DeviceCidSize);
|
||||
}
|
||||
|
||||
void SetCsd(const void *src, size_t src_size) {
|
||||
AMS_ABORT_UNLESS(src != nullptr);
|
||||
AMS_ABORT_UNLESS(src_size >= DeviceCsdSize);
|
||||
std::memcpy(m_csd, src, DeviceCsdSize);
|
||||
m_is_valid_csd = true;
|
||||
}
|
||||
|
||||
void GetCsd(void *dst, size_t dst_size) const {
|
||||
AMS_ABORT_UNLESS(m_is_valid_csd);
|
||||
AMS_ABORT_UNLESS(dst != nullptr);
|
||||
AMS_ABORT_UNLESS(dst_size >= DeviceCsdSize);
|
||||
std::memcpy(dst, m_csd, DeviceCsdSize);
|
||||
}
|
||||
|
||||
void SetMemoryCapacity(u32 num_sectors) {
|
||||
m_memory_capacity = num_sectors;
|
||||
m_is_valid_memory_capacity = true;
|
||||
}
|
||||
|
||||
u32 GetMemoryCapacity() const {
|
||||
AMS_ABORT_UNLESS(m_is_valid_memory_capacity);
|
||||
return m_memory_capacity;
|
||||
}
|
||||
|
||||
void GetLegacyCapacityParameters(u8 *out_c_size_mult, u8 *out_read_bl_len) const;
|
||||
Result SetLegacyMemoryCapacity();
|
||||
|
||||
Result CheckDeviceStatus(u32 r1_resp) const;
|
||||
DeviceState GetDeviceState(u32 r1_resp) const;
|
||||
};
|
||||
|
||||
class BaseDeviceAccessor : public IDeviceAccessor {
|
||||
private:
|
||||
IHostController *m_host_controller;
|
||||
BaseDevice *m_base_device;
|
||||
u32 m_num_activation_failures;
|
||||
u32 m_num_activation_error_corrections;
|
||||
u32 m_num_read_write_failures;
|
||||
u32 m_num_read_write_error_corrections;
|
||||
#if defined(AMS_SDMMC_USE_LOGGER)
|
||||
Logger m_error_logger;
|
||||
#endif
|
||||
private:
|
||||
void ClearErrorInfo() {
|
||||
m_num_activation_failures = 0;
|
||||
m_num_activation_error_corrections = 0;
|
||||
m_num_read_write_failures = 0;
|
||||
m_num_read_write_error_corrections = 0;
|
||||
}
|
||||
protected:
|
||||
explicit BaseDeviceAccessor(IHostController *hc) : m_host_controller(hc), m_base_device(nullptr) {
|
||||
this->ClearErrorInfo();
|
||||
}
|
||||
|
||||
IHostController *GetHostController() const {
|
||||
return m_host_controller;
|
||||
}
|
||||
|
||||
void SetDevice(BaseDevice *bd) {
|
||||
m_base_device = bd;
|
||||
}
|
||||
|
||||
Result CheckRemoved() const {
|
||||
R_RETURN(m_base_device->CheckRemoved());
|
||||
}
|
||||
|
||||
Result IssueCommandAndCheckR1(u32 *out_response, u32 command_index, u32 command_arg, bool is_busy, DeviceState expected_state, u32 status_ignore_mask) const;
|
||||
|
||||
Result IssueCommandAndCheckR1(u32 command_index, u32 command_arg, bool is_busy, DeviceState expected_state, u32 status_ignore_mask) const {
|
||||
u32 dummy;
|
||||
R_RETURN(this->IssueCommandAndCheckR1(std::addressof(dummy), command_index, command_arg, is_busy, expected_state, status_ignore_mask));
|
||||
}
|
||||
|
||||
Result IssueCommandAndCheckR1(u32 command_index, u32 command_arg, bool is_busy, DeviceState expected_state) const {
|
||||
R_RETURN(this->IssueCommandAndCheckR1(command_index, command_arg, is_busy, expected_state, 0));
|
||||
}
|
||||
|
||||
Result IssueCommandGoIdleState() const;
|
||||
Result IssueCommandAllSendCid(void *dst, size_t dst_size) const;
|
||||
Result IssueCommandSelectCard() const;
|
||||
Result IssueCommandSendCsd(void *dst, size_t dst_size) const;
|
||||
Result IssueCommandSendStatus(u32 *out_device_status, u32 status_ignore_mask) const;
|
||||
|
||||
Result IssueCommandSendStatus(u32 status_ignore_mask) const {
|
||||
u32 dummy;
|
||||
R_RETURN(this->IssueCommandSendStatus(std::addressof(dummy), status_ignore_mask));
|
||||
}
|
||||
|
||||
Result IssueCommandSendStatus() const {
|
||||
R_RETURN(this->IssueCommandSendStatus(0));
|
||||
}
|
||||
|
||||
Result IssueCommandSetBlockLenToSectorSize() const;
|
||||
Result IssueCommandMultipleBlock(u32 *out_num_transferred_blocks, u32 sector_index, u32 num_sectors, void *buf, bool is_read) const;
|
||||
Result ReadWriteSingle(u32 *out_num_transferred_blocks, u32 sector_index, u32 num_sectors, void *buf, bool is_read) const;
|
||||
Result ReadWriteMultiple(u32 sector_index, u32 num_sectors, u32 sector_index_alignment, void *buf, size_t buf_size, bool is_read);
|
||||
|
||||
void IncrementNumActivationErrorCorrections() {
|
||||
++m_num_activation_error_corrections;
|
||||
}
|
||||
|
||||
void PushErrorTimeStamp() {
|
||||
#if defined(AMS_SDMMC_USE_LOGGER)
|
||||
{
|
||||
m_error_logger.Push("%u", static_cast<u32>(os::ConvertToTimeSpan(os::GetSystemTick()).GetSeconds()));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void PushErrorLog(bool with_timestamp, const char *fmt, ...) {
|
||||
#if defined(AMS_SDMMC_USE_LOGGER)
|
||||
{
|
||||
std::va_list vl;
|
||||
va_start(vl, fmt);
|
||||
m_error_logger.Push(fmt, vl);
|
||||
va_end(vl);
|
||||
|
||||
if (with_timestamp) {
|
||||
this->PushErrorTimeStamp();
|
||||
}
|
||||
}
|
||||
#else
|
||||
{
|
||||
AMS_UNUSED(with_timestamp, fmt);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual Result OnActivate() = 0;
|
||||
virtual Result OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) = 0;
|
||||
virtual Result ReStartup() = 0;
|
||||
public:
|
||||
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
|
||||
virtual void RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) override;
|
||||
virtual void UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) override;
|
||||
#endif
|
||||
|
||||
virtual Result Activate() override;
|
||||
virtual void Deactivate() override;
|
||||
|
||||
virtual Result ReadWrite(u32 sector_index, u32 num_sectors, void *buffer, size_t buffer_size, bool is_read) override;
|
||||
virtual Result CheckConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width) override;
|
||||
|
||||
virtual Result GetMemoryCapacity(u32 *out_sectors) const override;
|
||||
virtual Result GetDeviceStatus(u32 *out) const override;
|
||||
virtual Result GetOcr(u32 *out) const override;
|
||||
virtual Result GetRca(u16 *out) const override;
|
||||
virtual Result GetCid(void *out, size_t size) const override;
|
||||
virtual Result GetCsd(void *out, size_t size) const override;
|
||||
|
||||
virtual void GetAndClearErrorInfo(ErrorInfo *out_error_info, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size) override;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
#include "sdmmc_clock_reset_controller.hpp"
|
||||
#include "sdmmc_clock_reset_controller.reg.board.nintendo_nx.hpp"
|
||||
#include "sdmmc_clock_reset_controller.pcv.board.nintendo_nx.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl::ClockResetController {
|
||||
|
||||
namespace {
|
||||
|
||||
constinit bool g_is_module_initialized[Module_Count] = {};
|
||||
|
||||
#if defined(AMS_SDMMC_THREAD_SAFE)
|
||||
|
||||
constinit os::SdkMutex g_module_mutex;
|
||||
|
||||
#define AMS_SDMMC_LOCK_MODULE_MUTEX() std::scoped_lock lk(g_module_mutex)
|
||||
|
||||
#else
|
||||
|
||||
#define AMS_SDMMC_LOCK_MODULE_MUTEX()
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
|
||||
constinit bool g_is_pcv_control = false;
|
||||
|
||||
#define AMS_SDMMC_IF_IS_PCV_CONTROL() if (g_is_pcv_control)
|
||||
|
||||
#else
|
||||
|
||||
#define AMS_SDMMC_IF_IS_PCV_CONTROL() if constexpr (false)
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void Initialize(Module module) {
|
||||
/* Acquire exclusive access to module state. */
|
||||
AMS_SDMMC_LOCK_MODULE_MUTEX();
|
||||
|
||||
/* Mark the module as initialized. */
|
||||
g_is_module_initialized[module] = true;
|
||||
|
||||
/* Initialize the module. */
|
||||
AMS_SDMMC_IF_IS_PCV_CONTROL() {
|
||||
ClockResetController::pcv::Initialize(module);
|
||||
} else {
|
||||
ClockResetController::reg::Initialize(module);
|
||||
}
|
||||
}
|
||||
|
||||
void Finalize(Module module) {
|
||||
/* Acquire exclusive access to module state. */
|
||||
AMS_SDMMC_LOCK_MODULE_MUTEX();
|
||||
|
||||
/* Finalize the module. */
|
||||
AMS_SDMMC_IF_IS_PCV_CONTROL() {
|
||||
ClockResetController::pcv::Finalize(module);
|
||||
} else {
|
||||
ClockResetController::reg::Finalize(module);
|
||||
}
|
||||
|
||||
/* Mark the module as finalized. */
|
||||
g_is_module_initialized[module] = false;
|
||||
}
|
||||
|
||||
bool IsAvailable(Module module) {
|
||||
AMS_SDMMC_IF_IS_PCV_CONTROL() {
|
||||
return ClockResetController::pcv::IsAvailable(module);
|
||||
} else {
|
||||
return ClockResetController::reg::IsAvailable(module);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
|
||||
void SwitchToPcvControl() {
|
||||
/* Acquire exclusive access to module state. */
|
||||
AMS_SDMMC_LOCK_MODULE_MUTEX();
|
||||
|
||||
/* If we're already using pcv control, we don't need to do anything. */
|
||||
AMS_SDMMC_IF_IS_PCV_CONTROL() {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Finalize all modules. */
|
||||
for (int i = 0; i < Module_Count; ++i) {
|
||||
if (g_is_module_initialized[i]) {
|
||||
ClockResetController::reg::Finalize(static_cast<Module>(i));
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark that we've switched to pcv control. */
|
||||
|
||||
/* Initialize modules using pcv control. */
|
||||
for (int i = 0; i < Module_Count; ++i) {
|
||||
if (g_is_module_initialized[i]) {
|
||||
ClockResetController::pcv::Initialize(static_cast<Module>(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency) {
|
||||
AMS_SDMMC_IF_IS_PCV_CONTROL() {
|
||||
return ClockResetController::pcv::SetClockFrequencyKHz(out_actual_frequency, module, target_frequency);
|
||||
} else {
|
||||
return ClockResetController::reg::SetClockFrequencyKHz(out_actual_frequency, module, target_frequency);
|
||||
}
|
||||
}
|
||||
|
||||
void AssertReset(Module module) {
|
||||
AMS_SDMMC_IF_IS_PCV_CONTROL() {
|
||||
return ClockResetController::pcv::AssertReset(module);
|
||||
} else {
|
||||
return ClockResetController::reg::AssertReset(module);
|
||||
}
|
||||
}
|
||||
|
||||
void ReleaseReset(Module module, u32 target_frequency_khz) {
|
||||
AMS_SDMMC_IF_IS_PCV_CONTROL() {
|
||||
return ClockResetController::pcv::ReleaseReset(module, target_frequency_khz);
|
||||
} else {
|
||||
return ClockResetController::reg::ReleaseReset(module, target_frequency_khz);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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.hpp>
|
||||
|
||||
namespace ams::sdmmc::impl::ClockResetController {
|
||||
|
||||
enum Module {
|
||||
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
||||
Module_Sdmmc1 = 0,
|
||||
Module_Sdmmc2 = 1,
|
||||
Module_Sdmmc3 = 2,
|
||||
Module_Sdmmc4 = 3,
|
||||
#endif
|
||||
|
||||
Module_Count,
|
||||
};
|
||||
|
||||
void Initialize(Module module);
|
||||
void Finalize(Module module);
|
||||
bool IsAvailable(Module module);
|
||||
|
||||
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
|
||||
void SwitchToPcvControl();
|
||||
#endif
|
||||
|
||||
void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency);
|
||||
void AssertReset(Module module);
|
||||
void ReleaseReset(Module module, u32 target_frequency_khz);
|
||||
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
#include "sdmmc_clock_reset_controller.pcv.board.nintendo_nx.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl::ClockResetController::pcv {
|
||||
|
||||
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
|
||||
|
||||
void Initialize(Module module) {
|
||||
AMS_UNUSED(module);
|
||||
AMS_ABORT("PCV Control not implemented");
|
||||
}
|
||||
|
||||
void Finalize(Module module) {
|
||||
AMS_UNUSED(module);
|
||||
AMS_ABORT("PCV Control not implemented");
|
||||
}
|
||||
|
||||
bool IsAvailable(Module module) {
|
||||
AMS_UNUSED(module);
|
||||
AMS_ABORT("PCV Control not implemented");
|
||||
}
|
||||
|
||||
void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency) {
|
||||
AMS_UNUSED(out_actual_frequency, module, target_frequency);
|
||||
AMS_ABORT("PCV Control not implemented");
|
||||
}
|
||||
|
||||
void AssertReset(Module module) {
|
||||
AMS_UNUSED(module);
|
||||
AMS_ABORT("PCV Control not implemented");
|
||||
}
|
||||
|
||||
void ReleaseReset(Module module, u32 target_frequency_khz) {
|
||||
AMS_UNUSED(module, target_frequency_khz);
|
||||
AMS_ABORT("PCV Control not implemented");
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void Initialize(Module module) {
|
||||
AMS_UNUSED(module);
|
||||
AMS_ABORT("PCV Control not supported");
|
||||
}
|
||||
|
||||
void Finalize(Module module) {
|
||||
AMS_UNUSED(module);
|
||||
AMS_ABORT("PCV Control not supported");
|
||||
}
|
||||
|
||||
bool IsAvailable(Module module) {
|
||||
AMS_UNUSED(module);
|
||||
AMS_ABORT("PCV Control not supported");
|
||||
}
|
||||
|
||||
void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency) {
|
||||
AMS_UNUSED(out_actual_frequency, module, target_frequency);
|
||||
AMS_ABORT("PCV Control not supported");
|
||||
}
|
||||
|
||||
void AssertReset(Module module) {
|
||||
AMS_UNUSED(module);
|
||||
AMS_ABORT("PCV Control not supported");
|
||||
}
|
||||
|
||||
void ReleaseReset(Module module, u32 target_frequency_khz) {
|
||||
AMS_UNUSED(module, target_frequency_khz);
|
||||
AMS_ABORT("PCV Control not supported");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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.hpp>
|
||||
#include "sdmmc_clock_reset_controller.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl::ClockResetController::pcv {
|
||||
|
||||
void Initialize(Module module);
|
||||
void Finalize(Module module);
|
||||
bool IsAvailable(Module module);
|
||||
|
||||
void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency);
|
||||
void AssertReset(Module module);
|
||||
void ReleaseReset(Module module, u32 target_frequency_khz);
|
||||
|
||||
}
|
||||
@@ -1,391 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
#include "sdmmc_clock_reset_controller.reg.board.nintendo_nx.hpp"
|
||||
#include "sdmmc_timer.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl::ClockResetController::reg {
|
||||
|
||||
namespace {
|
||||
|
||||
#if defined(AMS_SDMMC_THREAD_SAFE)
|
||||
|
||||
constinit os::SdkMutex g_init_mutex;
|
||||
|
||||
#define AMS_SDMMC_LOCK_INIT_MUTEX() std::scoped_lock lk(g_init_mutex)
|
||||
|
||||
#else
|
||||
|
||||
#define AMS_SDMMC_LOCK_INIT_MUTEX()
|
||||
|
||||
#endif
|
||||
|
||||
constinit bool g_is_initialized = false;
|
||||
|
||||
struct ModuleInfo {
|
||||
u32 target_frequency_khz;
|
||||
u32 actual_frequency_khz;
|
||||
};
|
||||
|
||||
constinit ModuleInfo g_module_infos[Module_Count] = {};
|
||||
|
||||
constexpr inline dd::PhysicalAddress ClockResetControllerRegistersPhysicalAddress = UINT64_C(0x60006000);
|
||||
constexpr inline size_t ClockResetControllerRegistersSize = 4_KB;
|
||||
|
||||
constinit uintptr_t g_clkrst_registers_address = 0;
|
||||
|
||||
[[maybe_unused]] void InitializePllc4() {
|
||||
/* Check that we have registers we can write to. */
|
||||
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||
|
||||
/* Check if PLLC4_BASE has the expected value; if it does, we have nothing to do. */
|
||||
constexpr u32 ExpectedPllc4Base = 0x58006804;
|
||||
if (ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE) == ExpectedPllc4Base) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Disable PLLC4_ENABLE, if it's currently set. */
|
||||
if (ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_ENABLE, ENABLE))) {
|
||||
ams::reg::ReadWrite(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_ENABLE, DISABLE));
|
||||
}
|
||||
|
||||
/* Operate on the register with PLLC4_ENABLE cleared. */
|
||||
{
|
||||
/* Clear IDDQ, read to be sure it takes, wait 5 us. */
|
||||
ams::reg::ReadWrite(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_IDDQ, OFF));
|
||||
ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE);
|
||||
WaitMicroSeconds(5);
|
||||
|
||||
/* Write the expected value sans IDDQ/PLLC4_ENABLE. */
|
||||
constexpr u32 ExpectedPllc4BaseMask = ~ams::reg::EncodeMask(CLK_RST_REG_BITS_MASK(PLLC4_BASE_PLLC4_ENABLE),
|
||||
CLK_RST_REG_BITS_MASK(PLLC4_BASE_PLLC4_IDDQ));
|
||||
ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, ExpectedPllc4Base & ExpectedPllc4BaseMask);
|
||||
}
|
||||
|
||||
/* Write PLLC4_ENABLE, and read to be sure our configuration takes. */
|
||||
ams::reg::ReadWrite(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_ENABLE, ENABLE));
|
||||
ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE);
|
||||
|
||||
/* Wait up to 1s for changes to take. */
|
||||
{
|
||||
ManualTimer timer(1000);
|
||||
while (true) {
|
||||
/* Check if we're done. */
|
||||
if (!ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_PLLC4_BASE, CLK_RST_REG_BITS_ENUM(PLLC4_BASE_PLLC4_LOCK, NOT_LOCK))) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check that we haven't timed out. */
|
||||
AMS_ABORT_UNLESS(timer.Update());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InitializeLegacyTmClk() {
|
||||
/* Check that we have registers we can write to. */
|
||||
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||
|
||||
/* Configure the legacy tm clock as 12MHz. */
|
||||
ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC_LEGACY_TM, CLK_RST_REG_BITS_ENUM (CLK_SOURCE_LEGACY_TM_CLK_SRC, PLLP_OUT0),
|
||||
CLK_RST_REG_BITS_VALUE(CLK_SOURCE_LEGACY_TM_CLK_DIVISOR, 66));
|
||||
|
||||
/* Enable clock to the legacy tm. */
|
||||
ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_Y_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_Y_CLK_ENB_LEGACY_TM, ENABLE));
|
||||
}
|
||||
|
||||
bool IsResetReleased(Module module) {
|
||||
/* Check that we have registers we can write to. */
|
||||
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||
|
||||
/* Get the reset bit from RST_DEVICES_* */
|
||||
switch (module) {
|
||||
case Module_Sdmmc1: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_REG_BITS_ENUM(RST_DEVICES_L_SWR_SDMMC1_RST, DISABLE));
|
||||
case Module_Sdmmc2: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_REG_BITS_ENUM(RST_DEVICES_L_SWR_SDMMC2_RST, DISABLE));
|
||||
case Module_Sdmmc3: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_U, CLK_RST_REG_BITS_ENUM(RST_DEVICES_U_SWR_SDMMC3_RST, DISABLE));
|
||||
case Module_Sdmmc4: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L, CLK_RST_REG_BITS_ENUM(RST_DEVICES_L_SWR_SDMMC4_RST, DISABLE));
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
void SetReset(Module module) {
|
||||
/* Check that we have registers we can write to. */
|
||||
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||
|
||||
/* Set reset in RST_DEV_*_SET */
|
||||
switch (module) {
|
||||
case Module_Sdmmc1: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC1_RST, ENABLE));
|
||||
case Module_Sdmmc2: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC2_RST, ENABLE));
|
||||
case Module_Sdmmc3: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_U_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_U_SDMMC3_RST, ENABLE));
|
||||
case Module_Sdmmc4: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC4_RST, ENABLE));
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
void ClearReset(Module module) {
|
||||
/* Check that we have registers we can write to. */
|
||||
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||
|
||||
/* Set reset in RST_DEV_*_CLR */
|
||||
switch (module) {
|
||||
case Module_Sdmmc1: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC1_RST, ENABLE));
|
||||
case Module_Sdmmc2: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC2_RST, ENABLE));
|
||||
case Module_Sdmmc3: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_U_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_U_SDMMC3_RST, ENABLE));
|
||||
case Module_Sdmmc4: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEV_L_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SDMMC4_RST, ENABLE));
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
bool IsClockEnabled(Module module) {
|
||||
/* Check that we have registers we can write to. */
|
||||
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||
|
||||
/* Get the enable bit from CLK_OUT_ENB_* */
|
||||
switch (module) {
|
||||
case Module_Sdmmc1: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_L_CLK_ENB_SDMMC1, ENABLE));
|
||||
case Module_Sdmmc2: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_L_CLK_ENB_SDMMC2, ENABLE));
|
||||
case Module_Sdmmc3: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_OUT_ENB_U, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_U_CLK_ENB_SDMMC3, ENABLE));
|
||||
case Module_Sdmmc4: return ams::reg::HasValue(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_OUT_ENB_L, CLK_RST_REG_BITS_ENUM(CLK_OUT_ENB_L_CLK_ENB_SDMMC4, ENABLE));
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
void SetClockEnable(Module module) {
|
||||
/* Check that we have registers we can write to. */
|
||||
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||
|
||||
/* Set clock enable bit in CLK_ENB_*_SET */
|
||||
switch (module) {
|
||||
case Module_Sdmmc1: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC1, ENABLE));
|
||||
case Module_Sdmmc2: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC2, ENABLE));
|
||||
case Module_Sdmmc3: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_U_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_U_CLK_ENB_SDMMC3, ENABLE));
|
||||
case Module_Sdmmc4: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC4, ENABLE));
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
void ClearClockEnable(Module module) {
|
||||
/* Check that we have registers we can write to. */
|
||||
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||
|
||||
/* Set clock enable bit in CLK_ENB_*_CLR */
|
||||
switch (module) {
|
||||
case Module_Sdmmc1: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC1, ENABLE));
|
||||
case Module_Sdmmc2: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC2, ENABLE));
|
||||
case Module_Sdmmc3: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_U_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_U_CLK_ENB_SDMMC3, ENABLE));
|
||||
case Module_Sdmmc4: return ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_ENB_L_CLR, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_CLK_ENB_SDMMC4, ENABLE));
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
void SetClockSourceSdmmc(u32 *out_actual_frequency_khz, Module module, u32 target_frequency_khz) {
|
||||
/* Check that we can write to output. */
|
||||
AMS_ABORT_UNLESS(out_actual_frequency_khz != nullptr);
|
||||
|
||||
/* Determine frequency/divisor. */
|
||||
u32 clk_m = ams::reg::Encode(CLK_RST_REG_BITS_ENUM(CLK_SOURCE_SDMMCX_SDMMCX_CLK_SRC, PLLP_OUT0));
|
||||
u8 n;
|
||||
switch (target_frequency_khz) {
|
||||
case 25'000:
|
||||
*out_actual_frequency_khz = 24'728;
|
||||
n = 31;
|
||||
break;
|
||||
case 26'000:
|
||||
*out_actual_frequency_khz = 25'500;
|
||||
n = 30;
|
||||
break;
|
||||
case 40'800:
|
||||
*out_actual_frequency_khz = 40'800;
|
||||
n = 18;
|
||||
break;
|
||||
case 50'000:
|
||||
*out_actual_frequency_khz = 48'000;
|
||||
n = 15;
|
||||
break;
|
||||
case 52'000:
|
||||
*out_actual_frequency_khz = 51'000;
|
||||
n = 14;
|
||||
break;
|
||||
case 100'000:
|
||||
#if defined(AMS_SDMMC_SET_PLLC4_BASE)
|
||||
*out_actual_frequency_khz = 99'840;
|
||||
n = 2;
|
||||
clk_m = ams::reg::Encode(CLK_RST_REG_BITS_ENUM(CLK_SOURCE_SDMMCX_SDMMCX_CLK_SRC, PLLC4_OUT2));
|
||||
#else
|
||||
*out_actual_frequency_khz = 90'667;
|
||||
n = 7;
|
||||
#endif
|
||||
break;
|
||||
case 200'000:
|
||||
#if defined(AMS_SDMMC_SET_PLLC4_BASE)
|
||||
*out_actual_frequency_khz = 199'680;
|
||||
n = 0;
|
||||
if (module == Module_Sdmmc2 || module == Module_Sdmmc4) {
|
||||
clk_m = ams::reg::Encode(CLK_RST_REG_BITS_ENUM(CLK_SOURCE_SDMMC24_SDMMC24_CLK_SRC, PLLC4_OUT2_LJ));
|
||||
} else {
|
||||
clk_m = ams::reg::Encode(CLK_RST_REG_BITS_ENUM(CLK_SOURCE_SDMMCX_SDMMCX_CLK_SRC, PLLC4_OUT2));
|
||||
}
|
||||
#else
|
||||
*out_actual_frequency_khz = 163'200;
|
||||
n = 3;
|
||||
#endif
|
||||
break;
|
||||
case 208'000:
|
||||
*out_actual_frequency_khz = 204'000;
|
||||
n = 2;
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
/* Set frequencies in module info. */
|
||||
g_module_infos[module].target_frequency_khz = target_frequency_khz;
|
||||
g_module_infos[module].actual_frequency_khz = *out_actual_frequency_khz;
|
||||
|
||||
/* Check that we have registers we can write to. */
|
||||
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||
|
||||
/* Update the clock source. */
|
||||
switch (module) {
|
||||
case Module_Sdmmc1: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC1, clk_m | static_cast<u32>(n)); break;
|
||||
case Module_Sdmmc2: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC2, clk_m | static_cast<u32>(n)); break;
|
||||
case Module_Sdmmc3: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC3, clk_m | static_cast<u32>(n)); break;
|
||||
case Module_Sdmmc4: ams::reg::Write(g_clkrst_registers_address + CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC4, clk_m | static_cast<u32>(n)); break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
void EnsureControl(Module module) {
|
||||
/* Read from RST_DEVICES_* to be sure previous configuration takes. */
|
||||
switch (module) {
|
||||
case Module_Sdmmc1: ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L); break;
|
||||
case Module_Sdmmc2: ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L); break;
|
||||
case Module_Sdmmc3: ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_U); break;
|
||||
case Module_Sdmmc4: ams::reg::Read(g_clkrst_registers_address + CLK_RST_CONTROLLER_RST_DEVICES_L); break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Initialize(Module module) {
|
||||
/* Initialization isn't module specific. */
|
||||
AMS_UNUSED(module);
|
||||
|
||||
/* Acquire exclusive access to the initialization lock. */
|
||||
AMS_SDMMC_LOCK_INIT_MUTEX();
|
||||
|
||||
/* If we've already initialized, we don't need to do anything. */
|
||||
if (g_is_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Clear module infos. */
|
||||
std::memset(g_module_infos, 0, sizeof(g_module_infos));
|
||||
|
||||
/* Get the registers address. */
|
||||
g_clkrst_registers_address = dd::QueryIoMapping(ClockResetControllerRegistersPhysicalAddress, ClockResetControllerRegistersSize);
|
||||
AMS_ABORT_UNLESS(g_clkrst_registers_address != 0);
|
||||
|
||||
/* Perform register initialization. */
|
||||
#if defined(AMS_SDMMC_SET_PLLC4_BASE)
|
||||
InitializePllc4();
|
||||
#endif
|
||||
InitializeLegacyTmClk();
|
||||
|
||||
/* Mark that we've initialized. */
|
||||
g_is_initialized = true;
|
||||
}
|
||||
|
||||
void Finalize(Module module) {
|
||||
/* Nothing is needed for finalization. */
|
||||
AMS_UNUSED(module);
|
||||
}
|
||||
|
||||
bool IsAvailable(Module module) {
|
||||
return IsResetReleased(module) && IsClockEnabled(module);
|
||||
}
|
||||
|
||||
void SetClockFrequencyKHz(u32 *out_actual_frequency_khz, Module module, u32 target_frequency_khz) {
|
||||
/* If we're not changing the clock frequency, we don't need to do anything. */
|
||||
if (target_frequency_khz == g_module_infos[module].target_frequency_khz) {
|
||||
*out_actual_frequency_khz = g_module_infos[module].actual_frequency_khz;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Temporarily disable clock. */
|
||||
const bool clock_enabled = IsClockEnabled(module);
|
||||
if (clock_enabled) {
|
||||
ClearClockEnable(module);
|
||||
}
|
||||
|
||||
/* Set the clock source. */
|
||||
SetClockSourceSdmmc(out_actual_frequency_khz, module, target_frequency_khz);
|
||||
|
||||
/* Re-enable clock, if we should. */
|
||||
if (clock_enabled) {
|
||||
SetClockEnable(module);
|
||||
}
|
||||
|
||||
/* Ensure that our configuration takes. */
|
||||
EnsureControl(module);
|
||||
}
|
||||
|
||||
void AssertReset(Module module) {
|
||||
/* Set reset and disable clock. */
|
||||
SetReset(module);
|
||||
ClearClockEnable(module);
|
||||
|
||||
/* Ensure that our configuration takes. */
|
||||
EnsureControl(module);
|
||||
}
|
||||
|
||||
void ReleaseReset(Module module, u32 target_frequency_khz) {
|
||||
/* Disable clock if it's enabled. */
|
||||
if (IsClockEnabled(module)) {
|
||||
ClearClockEnable(module);
|
||||
}
|
||||
|
||||
/* Set reset. */
|
||||
SetReset(module);
|
||||
|
||||
/* Set the clock source. */
|
||||
u32 actual_source_frequency_khz;
|
||||
SetClockSourceSdmmc(std::addressof(actual_source_frequency_khz), module, target_frequency_khz);
|
||||
|
||||
/* Enable clock. */
|
||||
SetClockEnable(module);
|
||||
|
||||
/* Ensure that our configuration takes. */
|
||||
EnsureControl(module);
|
||||
|
||||
/* Wait 100 clocks. */
|
||||
WaitClocks(100, actual_source_frequency_khz);
|
||||
|
||||
/* Clear reset. */
|
||||
ClearReset(module);
|
||||
|
||||
/* Ensure that our configuration takes. */
|
||||
EnsureControl(module);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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.hpp>
|
||||
#include "sdmmc_clock_reset_controller.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl::ClockResetController::reg {
|
||||
|
||||
void Initialize(Module module);
|
||||
void Finalize(Module module);
|
||||
bool IsAvailable(Module module);
|
||||
|
||||
void SetClockFrequencyKHz(u32 *out_actual_frequency, Module module, u32 target_frequency);
|
||||
void AssertReset(Module module);
|
||||
void ReleaseReset(Module module, u32 target_frequency_khz);
|
||||
|
||||
}
|
||||
@@ -1,272 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
|
||||
#include "sdmmc_device_detector.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
bool DeviceDetector::IsCurrentInserted() {
|
||||
return gpio::GetValue(std::addressof(m_gpio_pad_session)) == m_inserted_gpio_value;
|
||||
}
|
||||
|
||||
void DeviceDetector::HandleDeviceStatus(bool prev_inserted, bool cur_inserted) {
|
||||
if (!prev_inserted && !cur_inserted) {
|
||||
/* Not inserted -> Not inserted, nothing to do. */
|
||||
} else if (!prev_inserted && cur_inserted) {
|
||||
/* Card was inserted. */
|
||||
if (m_callback_info.inserted_callback != nullptr) {
|
||||
m_callback_info.inserted_callback(m_callback_info.inserted_callback_arg);
|
||||
}
|
||||
} else if (prev_inserted && !cur_inserted) {
|
||||
/* Card was removed. */
|
||||
if (m_callback_info.removed_callback != nullptr) {
|
||||
m_callback_info.removed_callback(m_callback_info.removed_callback_arg);
|
||||
}
|
||||
} else /* if (prev_inserted && cur_inserted) */ {
|
||||
/* Card was removed, and then inserted. */
|
||||
if (m_callback_info.removed_callback != nullptr) {
|
||||
m_callback_info.removed_callback(m_callback_info.removed_callback_arg);
|
||||
}
|
||||
|
||||
if (m_callback_info.inserted_callback != nullptr) {
|
||||
m_callback_info.inserted_callback(m_callback_info.inserted_callback_arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceDetector::DetectorThread() {
|
||||
/* Initialize the gpio session. */
|
||||
gpio::Initialize();
|
||||
|
||||
/* Open and configure the pad session. */
|
||||
gpio::OpenSession(std::addressof(m_gpio_pad_session), m_gpio_device_code);
|
||||
gpio::SetDirection(std::addressof(m_gpio_pad_session), gpio::Direction_Input);
|
||||
gpio::SetDebounceTime(std::addressof(m_gpio_pad_session), m_gpio_debounce_ms);
|
||||
gpio::SetDebounceEnabled(std::addressof(m_gpio_pad_session), true);
|
||||
gpio::SetInterruptMode(std::addressof(m_gpio_pad_session), gpio::InterruptMode_AnyEdge);
|
||||
|
||||
/* Get the gpio session's interrupt event. */
|
||||
os::SystemEventType gpio_event;
|
||||
R_ABORT_UNLESS(gpio::BindInterrupt(std::addressof(gpio_event), std::addressof(m_gpio_pad_session)));
|
||||
|
||||
/* Initialize and link multi wait/holders. */
|
||||
os::MultiWaitType multi_wait;
|
||||
os::MultiWaitHolderType detector_thread_end_holder;
|
||||
os::MultiWaitHolderType request_sleep_wake_event_holder;
|
||||
os::MultiWaitHolderType gpio_event_holder;
|
||||
os::InitializeMultiWait(std::addressof(multi_wait));
|
||||
os::InitializeMultiWaitHolder(std::addressof(detector_thread_end_holder), std::addressof(m_detector_thread_end_event));
|
||||
os::LinkMultiWaitHolder(std::addressof(multi_wait), std::addressof(detector_thread_end_holder));
|
||||
os::InitializeMultiWaitHolder(std::addressof(request_sleep_wake_event_holder), std::addressof(m_request_sleep_wake_event));
|
||||
os::LinkMultiWaitHolder(std::addressof(multi_wait), std::addressof(request_sleep_wake_event_holder));
|
||||
os::InitializeMultiWaitHolder(std::addressof(gpio_event_holder), std::addressof(gpio_event));
|
||||
os::LinkMultiWaitHolder(std::addressof(multi_wait), std::addressof(gpio_event_holder));
|
||||
|
||||
/* Wait before detecting the initial state of the card. */
|
||||
os::SleepThread(TimeSpan::FromMilliSeconds(m_gpio_debounce_ms));
|
||||
bool cur_inserted = this->IsCurrentInserted();
|
||||
m_is_prev_inserted = cur_inserted;
|
||||
|
||||
/* Set state as awake. */
|
||||
m_state = State_Awake;
|
||||
os::SignalEvent(std::addressof(m_ready_device_status_event));
|
||||
|
||||
/* Enable interrupts to be informed of device status. */
|
||||
gpio::SetInterruptEnable(std::addressof(m_gpio_pad_session), true);
|
||||
|
||||
/* Wait, servicing our events. */
|
||||
while (true) {
|
||||
/* Get the signaled holder. */
|
||||
os::MultiWaitHolderType *signaled_holder = os::WaitAny(std::addressof(multi_wait));
|
||||
|
||||
/* Process the holder. */
|
||||
bool insert_change = false;
|
||||
if (signaled_holder == std::addressof(detector_thread_end_holder)) {
|
||||
/* We should kill ourselves. */
|
||||
os::ClearEvent(std::addressof(m_detector_thread_end_event));
|
||||
m_state = State_Finalized;
|
||||
break;
|
||||
} else if (signaled_holder == std::addressof(request_sleep_wake_event_holder)) {
|
||||
/* A request for us to sleep/wake has come in, so we'll acknowledge it. */
|
||||
os::ClearEvent(std::addressof(m_request_sleep_wake_event));
|
||||
m_state = State_Sleep;
|
||||
os::SignalEvent(std::addressof(m_acknowledge_sleep_awake_event));
|
||||
|
||||
/* Temporarily unlink our interrupt event. */
|
||||
os::UnlinkMultiWaitHolder(std::addressof(gpio_event_holder));
|
||||
|
||||
/* Wait to be signaled. */
|
||||
signaled_holder = os::WaitAny(std::addressof(multi_wait));
|
||||
|
||||
/* Link our interrupt event back in. */
|
||||
os::LinkMultiWaitHolder(std::addressof(multi_wait), std::addressof(gpio_event_holder));
|
||||
|
||||
/* We're awake again. Either because we should exit, or because we were asked to wake up. */
|
||||
os::ClearEvent(std::addressof(m_request_sleep_wake_event));
|
||||
m_state = State_Awake;
|
||||
os::SignalEvent(std::addressof(m_acknowledge_sleep_awake_event));
|
||||
|
||||
/* If we were asked to exit, do so. */
|
||||
if (signaled_holder == std::addressof(detector_thread_end_holder)) {
|
||||
/* We should kill ourselves. */
|
||||
os::ClearEvent(std::addressof(m_detector_thread_end_event));
|
||||
m_state = State_Finalized;
|
||||
break;
|
||||
} else /* if (signaled_holder == std::addressof(request_sleep_wake_event_holder)) */ {
|
||||
if ((m_force_detection) ||
|
||||
(({ bool active; R_SUCCEEDED(gpio::IsWakeEventActive(std::addressof(active), m_gpio_device_code)) && active; })) ||
|
||||
(os::TryWaitSystemEvent(std::addressof(gpio_event))) ||
|
||||
(m_is_prev_inserted != this->IsCurrentInserted()))
|
||||
{
|
||||
insert_change = true;
|
||||
}
|
||||
}
|
||||
} else /* if (signaled_holder == std::addressof(gpio_event_holder)) */ {
|
||||
/* An event was detected. */
|
||||
insert_change = true;
|
||||
}
|
||||
|
||||
/* Handle an insert change, if one occurred. */
|
||||
if (insert_change) {
|
||||
/* Call the relevant callback, if we have one. */
|
||||
if (m_device_detection_event_callback != nullptr) {
|
||||
m_device_detection_event_callback(m_device_detection_event_callback_arg);
|
||||
}
|
||||
|
||||
/* Clear the interrupt event. */
|
||||
os::ClearSystemEvent(std::addressof(gpio_event));
|
||||
gpio::ClearInterruptStatus(std::addressof(m_gpio_pad_session));
|
||||
gpio::SetInterruptEnable(std::addressof(m_gpio_pad_session), true);
|
||||
|
||||
/* Update insertion status. */
|
||||
cur_inserted = this->IsCurrentInserted();
|
||||
this->HandleDeviceStatus(m_is_prev_inserted, cur_inserted);
|
||||
m_is_prev_inserted = cur_inserted;
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable interrupts to our gpio event. */
|
||||
gpio::SetInterruptEnable(std::addressof(m_gpio_pad_session), false);
|
||||
|
||||
/* Finalize and unlink multi wait/holders. */
|
||||
os::UnlinkMultiWaitHolder(std::addressof(gpio_event_holder));
|
||||
os::FinalizeMultiWaitHolder(std::addressof(gpio_event_holder));
|
||||
os::UnlinkMultiWaitHolder(std::addressof(request_sleep_wake_event_holder));
|
||||
os::FinalizeMultiWaitHolder(std::addressof(request_sleep_wake_event_holder));
|
||||
os::UnlinkMultiWaitHolder(std::addressof(detector_thread_end_holder));
|
||||
os::FinalizeMultiWaitHolder(std::addressof(detector_thread_end_holder));
|
||||
os::FinalizeMultiWait(std::addressof(multi_wait));
|
||||
|
||||
/* Finalize the gpio session. */
|
||||
gpio::UnbindInterrupt(std::addressof(m_gpio_pad_session));
|
||||
gpio::CloseSession(std::addressof(m_gpio_pad_session));
|
||||
gpio::Finalize();
|
||||
}
|
||||
|
||||
void DeviceDetector::Initialize(CallbackInfo *ci) {
|
||||
/* Transition our state from finalized to initializing. */
|
||||
AMS_ABORT_UNLESS(m_state == State_Finalized);
|
||||
m_state = State_Initializing;
|
||||
|
||||
/* Set our callback infos. */
|
||||
m_callback_info = *ci;
|
||||
|
||||
/* Initialize our events. */
|
||||
os::InitializeEvent(std::addressof(m_ready_device_status_event), false, os::EventClearMode_ManualClear);
|
||||
os::InitializeEvent(std::addressof(m_request_sleep_wake_event), false, os::EventClearMode_ManualClear);
|
||||
os::InitializeEvent(std::addressof(m_acknowledge_sleep_awake_event), false, os::EventClearMode_ManualClear);
|
||||
os::InitializeEvent(std::addressof(m_detector_thread_end_event), false, os::EventClearMode_ManualClear);
|
||||
|
||||
/* Create and start the detector thread. */
|
||||
os::CreateThread(std::addressof(m_detector_thread), DetectorThreadEntry, this, m_detector_thread_stack, sizeof(m_detector_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(sdmmc, DeviceDetector));
|
||||
os::SetThreadNamePointer(std::addressof(m_detector_thread), AMS_GET_SYSTEM_THREAD_NAME(sdmmc, DeviceDetector));
|
||||
os::StartThread(std::addressof(m_detector_thread));
|
||||
}
|
||||
|
||||
void DeviceDetector::Finalize() {
|
||||
/* Ensure we're not already finalized. */
|
||||
AMS_ABORT_UNLESS(m_state != State_Finalized);
|
||||
|
||||
/* Signal event to end the detector thread. */
|
||||
os::SignalEvent(std::addressof(m_detector_thread_end_event));
|
||||
os::WaitThread(std::addressof(m_detector_thread));
|
||||
|
||||
/* Finalize thread and events. */
|
||||
os::DestroyThread(std::addressof(m_detector_thread));
|
||||
os::FinalizeEvent(std::addressof(m_ready_device_status_event));
|
||||
os::FinalizeEvent(std::addressof(m_request_sleep_wake_event));
|
||||
os::FinalizeEvent(std::addressof(m_acknowledge_sleep_awake_event));
|
||||
os::FinalizeEvent(std::addressof(m_detector_thread_end_event));
|
||||
}
|
||||
|
||||
void DeviceDetector::PutToSleep() {
|
||||
/* Signal request, wait for acknowledgement. */
|
||||
os::SignalEvent(std::addressof(m_request_sleep_wake_event));
|
||||
os::WaitEvent(std::addressof(m_acknowledge_sleep_awake_event));
|
||||
os::ClearEvent(std::addressof(m_acknowledge_sleep_awake_event));
|
||||
}
|
||||
|
||||
void DeviceDetector::Awaken(bool force_det) {
|
||||
/* Signal request, wait for acknowledgement. */
|
||||
m_force_detection = force_det;
|
||||
os::SignalEvent(std::addressof(m_request_sleep_wake_event));
|
||||
os::WaitEvent(std::addressof(m_acknowledge_sleep_awake_event));
|
||||
os::ClearEvent(std::addressof(m_acknowledge_sleep_awake_event));
|
||||
}
|
||||
|
||||
bool DeviceDetector::IsInserted() {
|
||||
bool inserted = false;
|
||||
|
||||
switch (m_state) {
|
||||
case State_Initializing:
|
||||
/* Wait for us to know whether the device is inserted. */
|
||||
os::WaitEvent(std::addressof(m_ready_device_status_event));
|
||||
[[fallthrough]];
|
||||
case State_Awake:
|
||||
/* Get whether the device is currently inserted. */
|
||||
inserted = this->IsCurrentInserted();
|
||||
break;
|
||||
case State_Sleep:
|
||||
case State_Finalized:
|
||||
/* Get whether the device was inserted when we last knew. */
|
||||
inserted = m_is_prev_inserted;
|
||||
break;
|
||||
}
|
||||
|
||||
return inserted;
|
||||
}
|
||||
|
||||
void DeviceDetector::RegisterDetectionEventCallback(DeviceDetectionEventCallback cb, void *arg) {
|
||||
m_device_detection_event_callback_arg = arg;
|
||||
m_device_detection_event_callback = cb;
|
||||
}
|
||||
|
||||
void DeviceDetector::UnregisterDetectionEventCallback() {
|
||||
m_device_detection_event_callback = nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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.hpp>
|
||||
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
using InsertedCallback = void (*)(void *);
|
||||
using RemovedCallback = void (*)(void *);
|
||||
|
||||
struct CallbackInfo {
|
||||
InsertedCallback inserted_callback;
|
||||
void *inserted_callback_arg;
|
||||
RemovedCallback removed_callback;
|
||||
void *removed_callback_arg;
|
||||
};
|
||||
|
||||
class DeviceDetector {
|
||||
private:
|
||||
enum State {
|
||||
State_Initializing = 0,
|
||||
State_Awake = 1,
|
||||
State_Sleep = 2,
|
||||
State_Finalized = 3,
|
||||
};
|
||||
private:
|
||||
alignas(os::ThreadStackAlignment) u8 m_detector_thread_stack[8_KB];
|
||||
State m_state;
|
||||
bool m_is_prev_inserted;
|
||||
bool m_force_detection;
|
||||
os::ThreadType m_detector_thread;
|
||||
os::EventType m_detector_thread_end_event;
|
||||
os::EventType m_request_sleep_wake_event;
|
||||
os::EventType m_acknowledge_sleep_awake_event;
|
||||
os::EventType m_ready_device_status_event;
|
||||
|
||||
DeviceCode m_gpio_device_code;
|
||||
gpio::GpioValue m_inserted_gpio_value;
|
||||
u32 m_gpio_debounce_ms;
|
||||
gpio::GpioPadSession m_gpio_pad_session;
|
||||
|
||||
CallbackInfo m_callback_info;
|
||||
|
||||
DeviceDetectionEventCallback m_device_detection_event_callback;
|
||||
void *m_device_detection_event_callback_arg;
|
||||
private:
|
||||
static void DetectorThreadEntry(void *arg) {
|
||||
reinterpret_cast<DeviceDetector *>(arg)->DetectorThread();
|
||||
}
|
||||
|
||||
void DetectorThread();
|
||||
bool IsCurrentInserted();
|
||||
void HandleDeviceStatus(bool prev_inserted, bool cur_inserted);
|
||||
public:
|
||||
explicit DeviceDetector(DeviceCode dc, gpio::GpioValue igv, u32 gd)
|
||||
: m_gpio_device_code(dc), m_inserted_gpio_value(igv), m_gpio_debounce_ms(gd)
|
||||
{
|
||||
m_state = State_Finalized;
|
||||
m_is_prev_inserted = false;
|
||||
m_force_detection = false;
|
||||
m_callback_info = {};
|
||||
m_device_detection_event_callback = nullptr;
|
||||
m_device_detection_event_callback_arg = nullptr;
|
||||
}
|
||||
|
||||
void Initialize(CallbackInfo *ci);
|
||||
void Finalize();
|
||||
|
||||
void PutToSleep();
|
||||
void Awaken(bool force_det);
|
||||
|
||||
u32 GetDebounceMilliSeconds() const {
|
||||
return m_gpio_debounce_ms;
|
||||
}
|
||||
|
||||
bool IsInserted();
|
||||
|
||||
void RegisterDetectionEventCallback(DeviceDetectionEventCallback cb, void *arg);
|
||||
void UnregisterDetectionEventCallback();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,352 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
#include "sdmmc_gc_asic_device_accessor.hpp"
|
||||
#include "sdmmc_timer.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
#if defined(AMS_SDMMC_THREAD_SAFE)
|
||||
|
||||
#define AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX() std::scoped_lock lk(m_gc_asic_device.m_device_mutex)
|
||||
|
||||
#else
|
||||
|
||||
#define AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX()
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
|
||||
#define AMS_SDMMC_CHECK_GC_ASIC_REMOVED() R_UNLESS(!m_gc_asic_device.IsRemoved(), sdmmc::ResultDeviceRemoved())
|
||||
|
||||
#else
|
||||
|
||||
#define AMS_SDMMC_CHECK_GC_ASIC_REMOVED()
|
||||
|
||||
#endif
|
||||
|
||||
Result GcAsicDeviceAccessor::IssueCommandWriteOperation(const void *op_buf, size_t op_buf_size) const {
|
||||
/* Validate the operation buffer. */
|
||||
AMS_ABORT_UNLESS(op_buf != nullptr);
|
||||
AMS_ABORT_UNLESS(op_buf_size >= GcAsicOperationSize);
|
||||
|
||||
/* Issue the command. */
|
||||
constexpr ResponseType CommandResponseType = ResponseType_R1;
|
||||
Command command(CommandIndex_GcAsicWriteOperation, 0, CommandResponseType, false);
|
||||
TransferData xfer_data(const_cast<void *>(op_buf), GcAsicOperationSize, 1, TransferDirection_WriteToDevice);
|
||||
IHostController *hc = BaseDeviceAccessor::GetHostController();
|
||||
Result result = hc->IssueCommand(std::addressof(command), std::addressof(xfer_data));
|
||||
if (R_FAILED(result)) {
|
||||
/* We failed to write operation. Check if we were removed. */
|
||||
AMS_SDMMC_CHECK_GC_ASIC_REMOVED();
|
||||
|
||||
/* Determine what result we should return. */
|
||||
Result return_result = result;
|
||||
{
|
||||
/* Issue a stop transmission command. */
|
||||
u32 resp = 0;
|
||||
result = hc->IssueStopTransmissionCommand(std::addressof(resp));
|
||||
if (R_SUCCEEDED(result)) {
|
||||
/* If we successfully stopped transmission but have an error status, we prefer to return that. */
|
||||
result = m_gc_asic_device.CheckDeviceStatus(resp);
|
||||
if (R_FAILED(result)) {
|
||||
return_result = result;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check again if we were removed. */
|
||||
AMS_SDMMC_CHECK_GC_ASIC_REMOVED();
|
||||
|
||||
/* Request device status. */
|
||||
u32 device_status;
|
||||
result = BaseDeviceAccessor::IssueCommandSendStatus(std::addressof(device_status), 0);
|
||||
|
||||
/* If we got a device status error here and we didn't previously, we prefer to return that. */
|
||||
if (!sdmmc::ResultDeviceStatusHasError::Includes(return_result) && sdmmc::ResultDeviceStatusHasError::Includes(result)) {
|
||||
return_result = result;
|
||||
}
|
||||
}
|
||||
R_RETURN(return_result);
|
||||
}
|
||||
|
||||
/* Get the response. */
|
||||
u32 resp;
|
||||
hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType);
|
||||
R_TRY(m_gc_asic_device.CheckDeviceStatus(resp));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GcAsicDeviceAccessor::IssueCommandFinishOperation() const {
|
||||
/* Issue the command. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_GcAsicFinishOperation, 0, true, DeviceState_Tran));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GcAsicDeviceAccessor::IssueCommandSleep() {
|
||||
/* Issue the command. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_GcAsicSleep, 0, true, DeviceState_Tran));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GcAsicDeviceAccessor::IssueCommandUpdateKey() const {
|
||||
/* Issue the command. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_GcAsicUpdateKey, 0, true, DeviceState_Tran));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GcAsicDeviceAccessor::StartupGcAsicDevice() {
|
||||
/* Start up the host controller. */
|
||||
IHostController *hc = BaseDeviceAccessor::GetHostController();
|
||||
R_TRY(hc->Startup(BusPower_1_8V, BusWidth_8Bit, SpeedMode_GcAsicSpeed, false));
|
||||
|
||||
/* Wait 10 clocks for configuration to take. */
|
||||
WaitClocks(10, hc->GetDeviceClockFrequencyKHz());
|
||||
|
||||
/* Perform tuning with command index 21. */
|
||||
AMS_ABORT_UNLESS(hc->IsSupportedTuning());
|
||||
R_TRY(hc->Tuning(SpeedMode_GcAsicSpeed, 21));
|
||||
|
||||
/* Set the device as low capacity/no memory. */
|
||||
m_gc_asic_device.SetHighCapacity(false);
|
||||
m_gc_asic_device.SetMemoryCapacity(0);
|
||||
|
||||
/* Enable power saving. */
|
||||
hc->SetPowerSaving(true);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GcAsicDeviceAccessor::OnActivate() {
|
||||
/* If we fail to start up the device, ensure the host controller is shut down. */
|
||||
auto power_guard = SCOPE_GUARD { BaseDeviceAccessor::GetHostController()->Shutdown(); };
|
||||
|
||||
/* Try to start up the device. */
|
||||
R_TRY(this->StartupGcAsicDevice());
|
||||
|
||||
/* We started up, so we don't need to power down. */
|
||||
power_guard.Cancel();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GcAsicDeviceAccessor::OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) {
|
||||
/* Check that we're not performing zero-byte rw. */
|
||||
AMS_ABORT_UNLESS(num_sectors > 0);
|
||||
|
||||
/* Check that the buffer is big enough for the rw. */
|
||||
AMS_ABORT_UNLESS((buf_size / SectorSize) >= num_sectors);
|
||||
|
||||
/* Perform the read/write. */
|
||||
u32 num_transferred_blocks;
|
||||
R_TRY(BaseDeviceAccessor::ReadWriteSingle(std::addressof(num_transferred_blocks), sector_index, num_sectors, buf, is_read));
|
||||
|
||||
/* Require that we read/wrote as many sectors as we expected. */
|
||||
AMS_ABORT_UNLESS(num_transferred_blocks == num_sectors);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void GcAsicDeviceAccessor::Initialize() {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
|
||||
|
||||
/* If we've already initialized, we don't need to do anything. */
|
||||
if (m_is_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set the base device to our gc asic device. */
|
||||
BaseDeviceAccessor::SetDevice(std::addressof(m_gc_asic_device));
|
||||
|
||||
/* Initialize. */
|
||||
IHostController *hc = BaseDeviceAccessor::GetHostController();
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
{
|
||||
m_gc_asic_device.InitializeRemovedEvent();
|
||||
hc->PreSetRemovedEvent(m_gc_asic_device.GetRemovedEvent());
|
||||
}
|
||||
#endif
|
||||
hc->Initialize();
|
||||
|
||||
/* Mark ourselves as initialized. */
|
||||
m_is_initialized = true;
|
||||
}
|
||||
|
||||
void GcAsicDeviceAccessor::Finalize() {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
|
||||
|
||||
/* If we've already finalized, we don't need to do anything. */
|
||||
if (!m_is_initialized) {
|
||||
return;
|
||||
}
|
||||
m_is_initialized = false;
|
||||
|
||||
/* Deactivate the device. */
|
||||
BaseDeviceAccessor::Deactivate();
|
||||
|
||||
/* Finalize the host controller. */
|
||||
BaseDeviceAccessor::GetHostController()->Finalize();
|
||||
|
||||
/* Finalize the removed event. */
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
{
|
||||
m_gc_asic_device.FinalizeRemovedEvent();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Result GcAsicDeviceAccessor::GetSpeedMode(SpeedMode *out_speed_mode) const {
|
||||
/* Check that we can write to output. */
|
||||
AMS_ABORT_UNLESS(out_speed_mode != nullptr);
|
||||
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
|
||||
|
||||
/* Check that we're accessible. */
|
||||
R_TRY(m_gc_asic_device.CheckAccessible());
|
||||
|
||||
*out_speed_mode = SpeedMode_GcAsicSpeed;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void GcAsicDeviceAccessor::PutGcAsicToSleep() {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
|
||||
|
||||
/* If the device isn't awake, we don't need to do anything. */
|
||||
if (!m_gc_asic_device.IsAwake()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* If necessary, put the host controller to sleep. */
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
if (m_gc_asic_device.IsActive() && !m_gc_asic_device.IsRemoved())
|
||||
#else
|
||||
if (m_gc_asic_device.IsActive())
|
||||
#endif
|
||||
{
|
||||
BaseDeviceAccessor::GetHostController()->PutToSleep();
|
||||
}
|
||||
|
||||
/* Put the gc asic device to sleep. */
|
||||
m_gc_asic_device.PutToSleep();
|
||||
}
|
||||
|
||||
Result GcAsicDeviceAccessor::AwakenGcAsic() {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
|
||||
|
||||
/* If the device is awake, we don't need to do anything. */
|
||||
R_SUCCEED_IF(m_gc_asic_device.IsAwake());
|
||||
|
||||
/* Wake the device. */
|
||||
m_gc_asic_device.Awaken();
|
||||
|
||||
/* Wake the host controller, if we need to.*/
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
if (m_gc_asic_device.IsActive() && !m_gc_asic_device.IsRemoved())
|
||||
#else
|
||||
if (m_gc_asic_device.IsActive())
|
||||
#endif
|
||||
{
|
||||
R_TRY(BaseDeviceAccessor::GetHostController()->Awaken());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GcAsicDeviceAccessor::WriteGcAsicOperation(const void *op_buf, size_t op_buf_size) {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
|
||||
|
||||
/* Check that we're accessible. */
|
||||
R_TRY(m_gc_asic_device.CheckAccessible());
|
||||
|
||||
/* Issue the command. */
|
||||
R_TRY(this->IssueCommandWriteOperation(op_buf, op_buf_size));
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GcAsicDeviceAccessor::FinishGcAsicOperation() {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
|
||||
|
||||
/* Check that we're accessible. */
|
||||
R_TRY(m_gc_asic_device.CheckAccessible());
|
||||
|
||||
/* Issue the command. */
|
||||
R_TRY(this->IssueCommandFinishOperation());
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GcAsicDeviceAccessor::AbortGcAsicOperation() {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
|
||||
|
||||
/* Check that we're accessible. */
|
||||
R_TRY(m_gc_asic_device.CheckAccessible());
|
||||
|
||||
/* Issue stop transmission command. */
|
||||
u32 resp = 0;
|
||||
R_TRY(BaseDeviceAccessor::GetHostController()->IssueStopTransmissionCommand(std::addressof(resp)));
|
||||
R_TRY(m_gc_asic_device.CheckDeviceStatus(resp));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GcAsicDeviceAccessor::SleepGcAsic() {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
|
||||
|
||||
/* Check that we're accessible. */
|
||||
R_TRY(m_gc_asic_device.CheckAccessible());
|
||||
|
||||
/* Issue the command. */
|
||||
R_TRY(this->IssueCommandSleep());
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result GcAsicDeviceAccessor::UpdateGcAsicKey() {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_GC_ASIC_DEVICE_MUTEX();
|
||||
|
||||
/* Check that we're accessible. */
|
||||
R_TRY(m_gc_asic_device.CheckAccessible());
|
||||
|
||||
/* Issue the command. */
|
||||
R_TRY(this->IssueCommandUpdateKey());
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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.hpp>
|
||||
#include "sdmmc_base_device_accessor.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
class GcAsicDevice : public BaseDevice {
|
||||
private:
|
||||
static constexpr u16 Rca = 0;
|
||||
private:
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
mutable os::EventType m_removed_event;
|
||||
#endif
|
||||
public:
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
virtual os::EventType *GetRemovedEvent() const override {
|
||||
return std::addressof(m_removed_event);
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual DeviceType GetDeviceType() const override {
|
||||
return DeviceType_GcAsic;
|
||||
}
|
||||
|
||||
virtual u16 GetRca() const override {
|
||||
return Rca;
|
||||
}
|
||||
};
|
||||
|
||||
class GcAsicDeviceAccessor : public BaseDeviceAccessor {
|
||||
private:
|
||||
GcAsicDevice m_gc_asic_device;
|
||||
bool m_is_initialized;
|
||||
private:
|
||||
Result IssueCommandWriteOperation(const void *op_buf, size_t op_buf_size) const;
|
||||
Result IssueCommandFinishOperation() const;
|
||||
Result IssueCommandSleep();
|
||||
Result IssueCommandUpdateKey() const;
|
||||
Result StartupGcAsicDevice();
|
||||
protected:
|
||||
virtual Result OnActivate() override;
|
||||
virtual Result OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) override;
|
||||
|
||||
virtual Result ReStartup() override {
|
||||
AMS_ABORT("Can't ReStartup GcAsic\n");
|
||||
}
|
||||
public:
|
||||
virtual void Initialize() override;
|
||||
virtual void Finalize() override;
|
||||
virtual Result GetSpeedMode(SpeedMode *out_speed_mode) const override;
|
||||
public:
|
||||
explicit GcAsicDeviceAccessor(IHostController *hc) : BaseDeviceAccessor(hc), m_is_initialized(false) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
void PutGcAsicToSleep();
|
||||
Result AwakenGcAsic();
|
||||
Result WriteGcAsicOperation(const void *op_buf, size_t op_buf_size);
|
||||
Result FinishGcAsicOperation();
|
||||
Result AbortGcAsicOperation();
|
||||
Result SleepGcAsic();
|
||||
Result UpdateGcAsicKey();
|
||||
|
||||
void SignalGcRemovedEvent() {
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
m_gc_asic_device.SignalRemovedEvent();
|
||||
#else
|
||||
AMS_ABORT("SignalGcRemovedEvent called without event support\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void ClearGcRemovedEvent() {
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
m_gc_asic_device.ClearRemovedEvent();
|
||||
#else
|
||||
AMS_ABORT("ClearGcRemovedEvent called without event support\n");
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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.hpp>
|
||||
#include "sdmmc_i_host_controller.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
class IDeviceAccessor {
|
||||
public:
|
||||
virtual void Initialize() = 0;
|
||||
virtual void Finalize() = 0;
|
||||
|
||||
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
|
||||
virtual void RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) = 0;
|
||||
virtual void UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) = 0;
|
||||
#endif
|
||||
|
||||
virtual Result Activate() = 0;
|
||||
virtual void Deactivate() = 0;
|
||||
|
||||
virtual Result ReadWrite(u32 sector_index, u32 num_sectors, void *buffer, size_t buffer_size, bool is_read) = 0;
|
||||
virtual Result CheckConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width) = 0;
|
||||
|
||||
virtual Result GetSpeedMode(SpeedMode *out) const = 0;
|
||||
virtual Result GetMemoryCapacity(u32 *out_sectors) const = 0;
|
||||
virtual Result GetDeviceStatus(u32 *out) const = 0;
|
||||
virtual Result GetOcr(u32 *out) const = 0;
|
||||
virtual Result GetRca(u16 *out) const = 0;
|
||||
virtual Result GetCid(void *out, size_t size) const = 0;
|
||||
virtual Result GetCsd(void *out, size_t size) const = 0;
|
||||
|
||||
virtual void GetAndClearErrorInfo(ErrorInfo *out_error_info, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,196 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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.hpp>
|
||||
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
#include <stratosphere/os.hpp>
|
||||
#endif
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
enum ResponseType {
|
||||
ResponseType_R0 = 0,
|
||||
ResponseType_R1 = 1,
|
||||
ResponseType_R2 = 2,
|
||||
ResponseType_R3 = 3,
|
||||
ResponseType_R6 = 4,
|
||||
ResponseType_R7 = 5,
|
||||
};
|
||||
|
||||
enum TransferDirection {
|
||||
TransferDirection_ReadFromDevice = 0,
|
||||
TransferDirection_WriteToDevice = 1,
|
||||
};
|
||||
|
||||
enum CommandIndex {
|
||||
/* Generic commands. */
|
||||
CommandIndex_GoIdleState = 0,
|
||||
CommandIndex_SendOpCond = 1,
|
||||
CommandIndex_AllSendCid = 2,
|
||||
CommandIndex_SendRelativeAddr = 3,
|
||||
CommandIndex_SetRelativeAddr = 3,
|
||||
CommandIndex_SetDsr = 4,
|
||||
|
||||
CommandIndex_Switch = 6,
|
||||
CommandIndex_SelectCard = 7,
|
||||
CommandIndex_DeselectCard = 7,
|
||||
CommandIndex_SendIfCond = 8,
|
||||
CommandIndex_SendExtCsd = 8,
|
||||
CommandIndex_SendCsd = 9,
|
||||
CommandIndex_SendCid = 10,
|
||||
CommandIndex_VoltageSwitch = 11,
|
||||
CommandIndex_StopTransmission = 12,
|
||||
CommandIndex_SendStatus = 13,
|
||||
CommandIndex_SendTaskStatus = 13,
|
||||
|
||||
CommandIndex_GoInactiveState = 15,
|
||||
CommandIndex_SetBlockLen = 16,
|
||||
CommandIndex_ReadSingleBlock = 17,
|
||||
CommandIndex_ReadMultipleBlock = 18,
|
||||
CommandIndex_SendTuningBlock = 19,
|
||||
CommandIndex_SpeedClassControl = 20,
|
||||
|
||||
CommandIndex_AddressExtension = 22,
|
||||
CommandIndex_SetBlockCount = 23,
|
||||
CommandIndex_WriteBlock = 24,
|
||||
CommandIndex_WriteMultipleBlock = 25,
|
||||
|
||||
CommandIndex_ProgramCsd = 27,
|
||||
CommandIndex_SetWriteProt = 28,
|
||||
CommandIndex_ClearWriteProt = 29,
|
||||
CommandIndex_SendWriteProt = 30,
|
||||
|
||||
CommandIndex_EraseWriteBlockStart = 32,
|
||||
CommandIndex_EraseWriteBlockEnd = 33,
|
||||
|
||||
CommandIndex_EraseGroupStart = 35,
|
||||
CommandIndex_EraseGroupEnd = 36,
|
||||
|
||||
CommandIndex_Erase = 38,
|
||||
|
||||
CommandIndex_LockUnlock = 42,
|
||||
|
||||
CommandIndex_AppCmd = 55,
|
||||
CommandIndex_GenCmd = 56,
|
||||
|
||||
/* Nintendo specific vendor commands for lotus3. */
|
||||
CommandIndex_GcAsicWriteOperation = 60,
|
||||
CommandIndex_GcAsicFinishOperation = 61,
|
||||
CommandIndex_GcAsicSleep = 62,
|
||||
CommandIndex_GcAsicUpdateKey = 63,
|
||||
};
|
||||
|
||||
struct Command {
|
||||
u32 command_index;
|
||||
u32 command_argument;
|
||||
ResponseType response_type;
|
||||
bool is_busy;
|
||||
|
||||
constexpr Command(u32 ci, u32 ca, ResponseType r, bool b) : command_index(ci), command_argument(ca), response_type(r), is_busy(b) { /* ... */ }
|
||||
};
|
||||
|
||||
struct TransferData {
|
||||
void *buffer;
|
||||
size_t block_size;
|
||||
u32 num_blocks;
|
||||
TransferDirection transfer_direction;
|
||||
bool is_multi_block_transfer;
|
||||
bool is_stop_transmission_command_enabled;
|
||||
|
||||
constexpr TransferData(void *b, size_t bs, u32 nb, TransferDirection xd, bool mb, bool st)
|
||||
: buffer(b), block_size(bs), num_blocks(nb), transfer_direction(xd), is_multi_block_transfer(mb), is_stop_transmission_command_enabled(st)
|
||||
{
|
||||
if (this->num_blocks > 1) {
|
||||
AMS_ABORT_UNLESS(this->is_multi_block_transfer);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr TransferData(void *b, size_t bs, u32 nb, TransferDirection xd)
|
||||
: buffer(b), block_size(bs), num_blocks(nb), transfer_direction(xd), is_multi_block_transfer(false), is_stop_transmission_command_enabled(false)
|
||||
{
|
||||
AMS_ABORT_UNLESS(this->num_blocks == 1);
|
||||
}
|
||||
};
|
||||
|
||||
class IHostController {
|
||||
public:
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
virtual void PreSetRemovedEvent(ams::os::EventType *event) = 0;
|
||||
#endif
|
||||
|
||||
virtual void Initialize() = 0;
|
||||
virtual void Finalize() = 0;
|
||||
|
||||
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
|
||||
virtual void RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) = 0;
|
||||
virtual void UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) = 0;
|
||||
#endif
|
||||
|
||||
virtual void SetWorkBuffer(void *wb, size_t wb_size) = 0;
|
||||
|
||||
virtual Result Startup(BusPower bus_power, BusWidth bus_width, SpeedMode speed_mode, bool power_saving_enable) = 0;
|
||||
virtual void Shutdown() = 0;
|
||||
virtual void PutToSleep() = 0;
|
||||
virtual Result Awaken() = 0;
|
||||
|
||||
virtual Result SwitchToSdr12();
|
||||
|
||||
virtual bool IsSupportedBusPower(BusPower bus_power) const = 0;
|
||||
virtual BusPower GetBusPower() const = 0;
|
||||
|
||||
virtual bool IsSupportedBusWidth(BusWidth bus_width) const = 0;
|
||||
virtual void SetBusWidth(BusWidth bus_width) = 0;
|
||||
virtual BusWidth GetBusWidth() const = 0;
|
||||
|
||||
virtual Result SetSpeedMode(SpeedMode speed_mode) = 0;
|
||||
virtual SpeedMode GetSpeedMode() const = 0;
|
||||
|
||||
virtual u32 GetDeviceClockFrequencyKHz() const = 0;
|
||||
|
||||
virtual void SetPowerSaving(bool en) = 0;
|
||||
virtual bool IsPowerSavingEnable() const = 0;
|
||||
|
||||
virtual void EnableDeviceClock() = 0;
|
||||
virtual void DisableDeviceClock() = 0;
|
||||
virtual bool IsDeviceClockEnable() const = 0;
|
||||
|
||||
virtual u32 GetMaxTransferNumBlocks() const = 0;
|
||||
|
||||
virtual void ChangeCheckTransferInterval(u32 ms) = 0;
|
||||
virtual void SetDefaultCheckTransferInterval() = 0;
|
||||
|
||||
virtual Result IssueCommand(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks) = 0;
|
||||
virtual Result IssueStopTransmissionCommand(u32 *out_response) = 0;
|
||||
|
||||
ALWAYS_INLINE Result IssueCommand(const Command *command, TransferData *xfer_data) {
|
||||
R_RETURN(this->IssueCommand(command, xfer_data, nullptr));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE Result IssueCommand(const Command *command) {
|
||||
R_RETURN(this->IssueCommand(command, nullptr, nullptr));
|
||||
}
|
||||
|
||||
virtual void GetLastResponse(u32 *out_response, size_t response_size, ResponseType response_type) const = 0;
|
||||
virtual void GetLastStopTransmissionResponse(u32 *out_response, size_t response_size) const = 0;
|
||||
|
||||
virtual bool IsSupportedTuning() const = 0;
|
||||
virtual Result Tuning(SpeedMode speed_mode, u32 command_index) = 0;
|
||||
virtual void SaveTuningStatusForHs400() = 0;
|
||||
virtual Result GetInternalStatus() const = 0;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,998 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
#include "sdmmc_timer.hpp"
|
||||
#include "sdmmc_sdmmc_controller.board.nintendo_nx.hpp"
|
||||
#include "sdmmc_io_impl.board.nintendo_nx.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
/* Lovingly taken from libexosphere. */
|
||||
namespace i2c_impl {
|
||||
|
||||
enum Port {
|
||||
Port_1 = 0,
|
||||
/* TODO: Support other ports? */
|
||||
Port_5 = 4,
|
||||
|
||||
Port_Count = 5,
|
||||
};
|
||||
|
||||
constexpr inline const dd::PhysicalAddress I2c5RegistersAddress = UINT64_C(0x7000D000);
|
||||
constexpr inline const size_t I2c5RegistersSize = 4_KB;
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline size_t MaxTransferSize = sizeof(u32);
|
||||
|
||||
constinit std::array<uintptr_t, Port_Count> g_register_addresses = [] {
|
||||
std::array<uintptr_t, Port_Count> arr = {};
|
||||
return arr;
|
||||
}();
|
||||
|
||||
void LoadConfig(uintptr_t address) {
|
||||
/* Configure for TIMEOUT and MSTR config load. */
|
||||
/* NOTE: Nintendo writes value 1 to reserved bit 5 here. This bit is documented as having no meaning. */
|
||||
/* We will reproduce the write just in case it is undocumented. */
|
||||
reg::Write(address + I2C_CONFIG_LOAD, I2C_REG_BITS_VALUE(CONFIG_LOAD_RESERVED_BIT_5, 1),
|
||||
I2C_REG_BITS_ENUM (CONFIG_LOAD_TIMEOUT_CONFIG_LOAD, ENABLE),
|
||||
I2C_REG_BITS_ENUM (CONFIG_LOAD_SLV_CONFIG_LOAD, DISABLE),
|
||||
I2C_REG_BITS_ENUM (CONFIG_LOAD_MSTR_CONFIG_LOAD, ENABLE));
|
||||
|
||||
/* Wait up to 20 microseconds for the master config to be loaded. */
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
if (reg::HasValue(address + I2C_CONFIG_LOAD, I2C_REG_BITS_ENUM(CONFIG_LOAD_MSTR_CONFIG_LOAD, DISABLE))) {
|
||||
return;
|
||||
}
|
||||
util::WaitMicroSeconds(1);
|
||||
}
|
||||
}
|
||||
|
||||
void ClearBus(uintptr_t address) {
|
||||
/* Configure the bus clear register. */
|
||||
reg::Write(address + I2C_BUS_CLEAR_CONFIG, I2C_REG_BITS_VALUE(BUS_CLEAR_CONFIG_BC_SCLK_THRESHOLD, 9),
|
||||
I2C_REG_BITS_ENUM (BUS_CLEAR_CONFIG_BC_STOP_COND, NO_STOP),
|
||||
I2C_REG_BITS_ENUM (BUS_CLEAR_CONFIG_BC_TERMINATE, IMMEDIATE),
|
||||
I2C_REG_BITS_ENUM (BUS_CLEAR_CONFIG_BC_ENABLE, ENABLE));
|
||||
|
||||
/* Load the config. */
|
||||
LoadConfig(address);
|
||||
|
||||
/* Wait up to 250us (in 25 us increments) until the bus clear is done. */
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
if (reg::HasValue(address + I2C_INTERRUPT_STATUS_REGISTER, I2C_REG_BITS_ENUM(INTERRUPT_STATUS_REGISTER_BUS_CLEAR_DONE, SET))) {
|
||||
break;
|
||||
}
|
||||
|
||||
util::WaitMicroSeconds(25);
|
||||
}
|
||||
|
||||
/* Read the bus clear status. */
|
||||
reg::Read(address + I2C_BUS_CLEAR_STATUS);
|
||||
}
|
||||
|
||||
void InitializePort(uintptr_t address) {
|
||||
/* Calculate the divisor. */
|
||||
constexpr int Divisor = util::DivideUp(19200, 8 * 400);
|
||||
|
||||
/* Set the divisor. */
|
||||
reg::Write(address + I2C_CLK_DIVISOR_REGISTER, I2C_REG_BITS_VALUE(CLK_DIVISOR_REGISTER_STD_FAST_MODE, Divisor - 1),
|
||||
I2C_REG_BITS_VALUE(CLK_DIVISOR_REGISTER_HSMODE, 1));
|
||||
|
||||
/* Clear the bus. */
|
||||
ClearBus(address);
|
||||
|
||||
/* Clear the status. */
|
||||
reg::Write(address + I2C_INTERRUPT_STATUS_REGISTER, reg::Read(address + I2C_INTERRUPT_STATUS_REGISTER));
|
||||
}
|
||||
|
||||
bool Write(uintptr_t base_address, Port port, int address, const void *src, size_t src_size, bool unused) {
|
||||
AMS_UNUSED(port, unused);
|
||||
|
||||
/* Ensure we don't write too much. */
|
||||
u32 data = 0;
|
||||
if (src_size > MaxTransferSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Copy the data to a transfer word. */
|
||||
std::memcpy(std::addressof(data), src, src_size);
|
||||
|
||||
|
||||
/* Configure the to write the 7-bit address. */
|
||||
reg::Write(base_address + I2C_I2C_CMD_ADDR0, I2C_REG_BITS_VALUE(I2C_CMD_ADDR0_7BIT_ADDR, address),
|
||||
I2C_REG_BITS_ENUM (I2C_CMD_ADDR0_7BIT_RW, WRITE));
|
||||
|
||||
/* Configure to write the data. */
|
||||
reg::Write(base_address + I2C_I2C_CMD_DATA1, data);
|
||||
|
||||
/* Configure to write the correct amount of data. */
|
||||
reg::Write(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM (I2C_CNFG_DEBOUNCE_CNT, DEBOUNCE_4T),
|
||||
I2C_REG_BITS_ENUM (I2C_CNFG_NEW_MASTER_FSM, ENABLE),
|
||||
I2C_REG_BITS_ENUM (I2C_CNFG_CMD1, WRITE),
|
||||
I2C_REG_BITS_VALUE(I2C_CNFG_LENGTH, src_size - 1));
|
||||
|
||||
/* Load the configuration. */
|
||||
LoadConfig(base_address);
|
||||
|
||||
/* Start the command. */
|
||||
reg::ReadWrite(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM(I2C_CNFG_SEND, GO));
|
||||
|
||||
/* Wait for the command to be done. */
|
||||
while (!reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_BUSY, NOT_BUSY))) { /* ... */ }
|
||||
|
||||
/* Check if the transfer was successful. */
|
||||
return reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_CMD1_STAT, SL1_XFER_SUCCESSFUL));
|
||||
}
|
||||
|
||||
bool Read(uintptr_t base_address, Port port, void *dst, size_t dst_size, int address, bool unused) {
|
||||
AMS_UNUSED(port, unused);
|
||||
|
||||
/* Ensure we don't read too much. */
|
||||
if (dst_size > MaxTransferSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Configure the to read the 7-bit address. */
|
||||
reg::Write(base_address + I2C_I2C_CMD_ADDR0, I2C_REG_BITS_VALUE(I2C_CMD_ADDR0_7BIT_ADDR, address),
|
||||
I2C_REG_BITS_ENUM (I2C_CMD_ADDR0_7BIT_RW, READ));
|
||||
|
||||
/* Configure to read the correct amount of data. */
|
||||
reg::Write(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM (I2C_CNFG_DEBOUNCE_CNT, DEBOUNCE_4T),
|
||||
I2C_REG_BITS_ENUM (I2C_CNFG_NEW_MASTER_FSM, ENABLE),
|
||||
I2C_REG_BITS_ENUM (I2C_CNFG_CMD1, READ),
|
||||
I2C_REG_BITS_VALUE(I2C_CNFG_LENGTH, dst_size - 1));
|
||||
|
||||
/* Load the configuration. */
|
||||
LoadConfig(base_address);
|
||||
|
||||
/* Start the command. */
|
||||
reg::ReadWrite(base_address + I2C_I2C_CNFG, I2C_REG_BITS_ENUM(I2C_CNFG_SEND, GO));
|
||||
|
||||
/* Wait for the command to be done. */
|
||||
while (!reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_BUSY, NOT_BUSY))) { /* ... */ }
|
||||
|
||||
/* Check that the transfer was successful. */
|
||||
if (!reg::HasValue(base_address + I2C_I2C_STATUS, I2C_REG_BITS_ENUM(I2C_STATUS_CMD1_STAT, SL1_XFER_SUCCESSFUL))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Read and copy out the data. */
|
||||
u32 data = reg::Read(base_address + I2C_I2C_CMD_DATA1);
|
||||
std::memcpy(dst, std::addressof(data), dst_size);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SetRegisterAddress(Port port, uintptr_t address) {
|
||||
g_register_addresses[port] = address;
|
||||
}
|
||||
|
||||
void Initialize(Port port) {
|
||||
InitializePort(g_register_addresses[port]);
|
||||
}
|
||||
|
||||
bool Query(void *dst, size_t dst_size, Port port, int address, int r) {
|
||||
const uintptr_t base_address = g_register_addresses[port];
|
||||
|
||||
/* Select the register we want to read. */
|
||||
bool success = Write(base_address, port, address, std::addressof(r), 1, false);
|
||||
if (success) {
|
||||
/* If we successfully selected, read data from the register. */
|
||||
success = Read(base_address, port, dst, dst_size, address, true);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool Send(Port port, int address, int r, const void *src, size_t src_size) {
|
||||
const uintptr_t base_address = g_register_addresses[port];
|
||||
|
||||
/* Create a transfer buffer, make sure we can use it. */
|
||||
u8 buffer[MaxTransferSize];
|
||||
if (src_size > sizeof(buffer) - 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Copy data into the buffer. */
|
||||
buffer[0] = static_cast<u8>(r);
|
||||
std::memcpy(buffer + 1, src, src_size);
|
||||
|
||||
return Write(base_address, port, address, buffer, src_size + 1, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace max7762x {
|
||||
|
||||
/* The only regulator we care about for SD card power is ldo2. */
|
||||
constexpr inline const u8 Max77620PwrI2cAddr = 0x3C;
|
||||
|
||||
constexpr inline const u32 Max77620Ldo2MvStep = 50'000; /* 50 mV (50K uV) steps. */
|
||||
constexpr inline const u32 Max77620Ldo2MvMin = 800'000; /* 0.8V min voltage. */
|
||||
constexpr inline const u32 Max77620Ldo2MvDefault = 1'800'000; /* 1.8V default voltage. */
|
||||
constexpr inline const u32 Max77620Ldo2MvMax = 3'300'000; /* 3.3V max voltage. */
|
||||
constexpr inline const u8 Max77620Ldo2VoltAddr = 0x27;
|
||||
constexpr inline const u8 Max77620Ldo2CfgAddr = 0x28;
|
||||
constexpr inline const u8 Max77620Ldo2VoltMask = 0x3F;
|
||||
constexpr inline const u8 Max77620Ldo2EnableMask = 0xC0;
|
||||
constexpr inline const u8 Max77620Ldo2EnableShift = 0x6;
|
||||
constexpr inline const u8 Max77620Ldo2StatusMask = 0x00;
|
||||
|
||||
namespace {
|
||||
|
||||
#if defined(AMS_SDMMC_THREAD_SAFE)
|
||||
constinit os::SdkMutex g_i2c_init_mutex;
|
||||
|
||||
#define AMS_SDMMC_LOCK_I2C_INIT_MUTEX() std::scoped_lock lk(g_i2c_init_mutex)
|
||||
|
||||
#else
|
||||
|
||||
#define AMS_SDMMC_LOCK_I2C_INIT_MUTEX()
|
||||
|
||||
#endif
|
||||
|
||||
constinit bool g_initialized_i2c = false;
|
||||
|
||||
void EnsureI2cInitialized() {
|
||||
if (AMS_UNLIKELY(!g_initialized_i2c)) {
|
||||
/* Ensure we have exclusive access to the i2c init status. */
|
||||
AMS_SDMMC_LOCK_I2C_INIT_MUTEX();
|
||||
|
||||
if (AMS_LIKELY(!g_initialized_i2c)) {
|
||||
i2c_impl::SetRegisterAddress(i2c_impl::Port_5, dd::QueryIoMapping(i2c_impl::I2c5RegistersAddress, i2c_impl::I2c5RegistersSize));
|
||||
i2c_impl::Initialize(i2c_impl::Port_5);
|
||||
g_initialized_i2c = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool SetVoltageEnabled(bool en) {
|
||||
/* Ensure that we can use i2c to communicate with the max7762x regulator. */
|
||||
EnsureI2cInitialized();
|
||||
|
||||
/* Read the current value. */
|
||||
u8 val;
|
||||
if (!i2c_impl::Query(std::addressof(val), sizeof(val), i2c_impl::Port_5, Max77620PwrI2cAddr, Max77620Ldo2VoltAddr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Set or clear the enable mask. */
|
||||
val &= ~Max77620Ldo2EnableMask;
|
||||
if (en) {
|
||||
val |= ((3 << Max77620Ldo2EnableShift) & Max77620Ldo2EnableMask);
|
||||
}
|
||||
|
||||
/* Write the updated value. */
|
||||
if (!i2c_impl::Send(i2c_impl::Port_5, Max77620PwrI2cAddr, Max77620Ldo2VoltAddr, std::addressof(val), sizeof(val))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Wait 1ms for change to take. */
|
||||
WaitMicroSeconds(1);
|
||||
|
||||
/* Voltage is now enabled/disabled. */
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetVoltageValue(u32 micro_volts) {
|
||||
/* Ensure that we can use i2c to communicate with the max7762x regulator. */
|
||||
EnsureI2cInitialized();
|
||||
|
||||
/* Check that the value is within range. */
|
||||
if (micro_volts < Max77620Ldo2MvMin || Max77620Ldo2MvMax < micro_volts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Determine the mult. */
|
||||
const u32 mult = util::DivideUp(micro_volts - Max77620Ldo2MvMin, Max77620Ldo2MvStep);
|
||||
|
||||
/* Read the current value. */
|
||||
u8 val;
|
||||
if (!i2c_impl::Query(std::addressof(val), sizeof(val), i2c_impl::Port_5, Max77620PwrI2cAddr, Max77620Ldo2VoltAddr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Set the new voltage. */
|
||||
val &= ~Max77620Ldo2VoltMask;
|
||||
val |= (mult & Max77620Ldo2VoltMask);
|
||||
|
||||
/* Write the updated value. */
|
||||
if (!i2c_impl::Send(i2c_impl::Port_5, Max77620PwrI2cAddr, Max77620Ldo2VoltAddr, std::addressof(val), sizeof(val))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Wait 1ms for change to take. */
|
||||
WaitMicroSeconds(1);
|
||||
|
||||
/* Voltage is now set. */
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Result SetSdCardVoltageEnabled(bool en) {
|
||||
/* TODO: A way for this to be non-fatal? */
|
||||
AMS_ABORT_UNLESS(max7762x::SetVoltageEnabled(en));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SetSdCardVoltageValue(u32 micro_volts) {
|
||||
/* TODO: A way for this to be non-fatal? */
|
||||
AMS_ABORT_UNLESS(max7762x::SetVoltageValue(micro_volts));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
namespace gpio_impl {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline dd::PhysicalAddress GpioRegistersPhysicalAddress = 0x6000d000;
|
||||
constexpr inline size_t GpioRegistersSize = 4_KB;
|
||||
|
||||
enum GpioPadPort {
|
||||
GpioPadPort_A = 0,
|
||||
GpioPadPort_B = 1,
|
||||
GpioPadPort_C = 2,
|
||||
GpioPadPort_D = 3,
|
||||
GpioPadPort_E = 4,
|
||||
GpioPadPort_F = 5,
|
||||
GpioPadPort_G = 6,
|
||||
GpioPadPort_H = 7,
|
||||
GpioPadPort_I = 8,
|
||||
GpioPadPort_J = 9,
|
||||
GpioPadPort_K = 10,
|
||||
GpioPadPort_L = 11,
|
||||
GpioPadPort_M = 12,
|
||||
GpioPadPort_N = 13,
|
||||
GpioPadPort_O = 14,
|
||||
GpioPadPort_P = 15,
|
||||
GpioPadPort_Q = 16,
|
||||
GpioPadPort_R = 17,
|
||||
GpioPadPort_S = 18,
|
||||
GpioPadPort_T = 19,
|
||||
GpioPadPort_U = 20,
|
||||
GpioPadPort_V = 21,
|
||||
GpioPadPort_W = 22,
|
||||
GpioPadPort_X = 23,
|
||||
GpioPadPort_Y = 24,
|
||||
GpioPadPort_Z = 25,
|
||||
GpioPadPort_AA = 26,
|
||||
GpioPadPort_BB = 27,
|
||||
GpioPadPort_CC = 28,
|
||||
GpioPadPort_DD = 29,
|
||||
GpioPadPort_EE = 30,
|
||||
GpioPadPort_FF = 31,
|
||||
};
|
||||
|
||||
consteval unsigned int GetInternalGpioPadNumber(GpioPadPort port, unsigned int which) {
|
||||
AMS_ASSUME(which < 8);
|
||||
|
||||
return (static_cast<unsigned int>(port) * 8) + which;
|
||||
}
|
||||
|
||||
enum InternalGpioPadNumber {
|
||||
InternalGpioPadNumber_E4 = GetInternalGpioPadNumber(GpioPadPort_E, 4),
|
||||
InternalGpioPadNumber_M0 = GetInternalGpioPadNumber(GpioPadPort_M, 0),
|
||||
};
|
||||
|
||||
constexpr int ConvertInternalGpioPadNumberToController(InternalGpioPadNumber number) {
|
||||
return (number >> 5);
|
||||
}
|
||||
|
||||
constexpr int ConvertInternalGpioPadNumberToPort(InternalGpioPadNumber number) {
|
||||
return (number >> 3);
|
||||
}
|
||||
|
||||
constexpr int ConvertInternalGpioPadNumberToBitIndex(InternalGpioPadNumber number) {
|
||||
return (number & 7);
|
||||
}
|
||||
|
||||
constexpr int ConvertPortNumberToOffset(int port_number) {
|
||||
return (port_number & 3);
|
||||
}
|
||||
|
||||
struct PadNameToInternalPadNumberEntry {
|
||||
GpioPadName pad_name;
|
||||
InternalGpioPadNumber internal_number;
|
||||
};
|
||||
|
||||
constexpr inline const PadNameToInternalPadNumberEntry PadNameToInternalPadNumberTable[] = {
|
||||
{ GpioPadName_PowSdEn, InternalGpioPadNumber_E4 },
|
||||
};
|
||||
|
||||
constexpr InternalGpioPadNumber ConvertPadNameToInternalPadNumber(GpioPadName pad) {
|
||||
const PadNameToInternalPadNumberEntry *target = nullptr;
|
||||
for (const auto &entry : PadNameToInternalPadNumberTable) {
|
||||
if (entry.pad_name == pad) {
|
||||
target = std::addressof(entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
AMS_ABORT_UNLESS(target != nullptr);
|
||||
return target->internal_number;
|
||||
}
|
||||
|
||||
enum GpioRegisterType {
|
||||
GpioRegisterType_GPIO_CNF = 0,
|
||||
GpioRegisterType_GPIO_OE = 1,
|
||||
GpioRegisterType_GPIO_OUT = 2,
|
||||
GpioRegisterType_GPIO_IN = 3,
|
||||
GpioRegisterType_GPIO_INT_STA = 4,
|
||||
GpioRegisterType_GPIO_INT_ENB = 5,
|
||||
GpioRegisterType_GPIO_INT_LVL = 6,
|
||||
GpioRegisterType_GPIO_INT_CLR = 7,
|
||||
GpioRegisterType_GPIO_DB_CTRL = 8,
|
||||
GpioRegisterType_GPIO_DB_CNT = 9,
|
||||
};
|
||||
|
||||
constexpr inline uintptr_t MaskedWriteAddressOffset = 0x80;
|
||||
constexpr inline int MaskedWriteBitOffset = 8;
|
||||
|
||||
constexpr uintptr_t GetGpioRegisterAddress(uintptr_t gpio_address, GpioRegisterType reg_type, InternalGpioPadNumber pad_number) {
|
||||
const auto controller = ConvertInternalGpioPadNumberToController(pad_number);
|
||||
const auto port = ConvertInternalGpioPadNumberToPort(pad_number);
|
||||
const auto offset = ConvertPortNumberToOffset(port);
|
||||
|
||||
switch (reg_type) {
|
||||
default:
|
||||
return gpio_address + (0x100 * controller) + (0x10 * reg_type) + (0x4 * offset);
|
||||
case GpioRegisterType_GPIO_DB_CTRL:
|
||||
return gpio_address + (0x100 * controller) + (0x10 * GpioRegisterType_GPIO_IN) + (0x4 * offset);
|
||||
case GpioRegisterType_GPIO_DB_CNT:
|
||||
return gpio_address + (0x100 * controller) + MaskedWriteAddressOffset + (0x10 * GpioRegisterType_GPIO_INT_CLR) + (0x4 * offset);
|
||||
}
|
||||
}
|
||||
|
||||
void SetMaskedBit(uintptr_t pad_address, int index, int value) {
|
||||
const uintptr_t mask_address = pad_address + MaskedWriteAddressOffset;
|
||||
|
||||
reg::Write(mask_address, (1u << (MaskedWriteBitOffset + index)) | (static_cast<unsigned int>(value) << index));
|
||||
}
|
||||
|
||||
void SetMaskedBits(uintptr_t pad_address, unsigned int mask, unsigned int value) {
|
||||
const uintptr_t mask_address = pad_address + MaskedWriteAddressOffset;
|
||||
|
||||
reg::Write(mask_address, (mask << MaskedWriteBitOffset) | (value));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void OpenSession(GpioPadName pad) {
|
||||
/* Convert the pad to an internal number. */
|
||||
const auto pad_number = ConvertPadNameToInternalPadNumber(pad);
|
||||
|
||||
/* Get the gpio registers address. */
|
||||
const uintptr_t gpio_address = dd::QueryIoMapping(GpioRegistersPhysicalAddress, GpioRegistersSize);
|
||||
|
||||
/* Configure the pad as GPIO by setting the appropriate bit in CNF. */
|
||||
const uintptr_t pad_address = GetGpioRegisterAddress(gpio_address, GpioRegisterType_GPIO_CNF, pad_number);
|
||||
const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number);
|
||||
SetMaskedBit(pad_address, pad_index, 1);
|
||||
|
||||
/* Read the pad address to make sure our configuration takes. */
|
||||
reg::Read(pad_address);
|
||||
}
|
||||
|
||||
void CloseSession(GpioPadName pad) {
|
||||
/* Nothing needs to be done here, as the only thing official code does is unbind the interrupt event. */
|
||||
AMS_UNUSED(pad);
|
||||
}
|
||||
|
||||
void SetDirection(GpioPadName pad, Direction direction) {
|
||||
/* Convert the pad to an internal number. */
|
||||
const auto pad_number = ConvertPadNameToInternalPadNumber(pad);
|
||||
|
||||
/* Get the gpio registers address. */
|
||||
const uintptr_t gpio_address = dd::QueryIoMapping(GpioRegistersPhysicalAddress, GpioRegistersSize);
|
||||
|
||||
/* Configure the pad direction modifying the appropriate bit in OE. */
|
||||
const uintptr_t pad_address = GetGpioRegisterAddress(gpio_address, GpioRegisterType_GPIO_OE, pad_number);
|
||||
const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number);
|
||||
SetMaskedBit(pad_address, pad_index, direction);
|
||||
|
||||
/* Read the pad address to make sure our configuration takes. */
|
||||
reg::Read(pad_address);
|
||||
}
|
||||
|
||||
void SetValue(GpioPadName pad, GpioValue value) {
|
||||
/* Convert the pad to an internal number. */
|
||||
const auto pad_number = ConvertPadNameToInternalPadNumber(pad);
|
||||
|
||||
/* Get the gpio registers address. */
|
||||
const uintptr_t gpio_address = dd::QueryIoMapping(GpioRegistersPhysicalAddress, GpioRegistersSize);
|
||||
|
||||
/* Configure the pad value modifying the appropriate bit in OUT. */
|
||||
const uintptr_t pad_address = GetGpioRegisterAddress(gpio_address, GpioRegisterType_GPIO_OUT, pad_number);
|
||||
const uintptr_t pad_index = ConvertInternalGpioPadNumberToBitIndex(pad_number);
|
||||
SetMaskedBit(pad_address, pad_index, value);
|
||||
|
||||
/* Read the pad address to make sure our configuration takes. */
|
||||
reg::Read(pad_address);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace pinmux_impl {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto Sdmmc1ClkCmdDat03PadNumber = gpio_impl::InternalGpioPadNumber_M0;
|
||||
|
||||
constexpr unsigned int Sdmmc1ClkCmdDat03PadMask = 0x3F;
|
||||
|
||||
constexpr unsigned int Sdmmc1ClkCmdDat03PadCnfGpio = 0x3F;
|
||||
constexpr unsigned int Sdmmc1ClkCmdDat03PadCnfSfio = 0x00;
|
||||
|
||||
constexpr unsigned int Sdmmc1ClkCmdDat03PadOutHigh = 0x3F;
|
||||
constexpr unsigned int Sdmmc1ClkCmdDat03PadOutLow = 0x00;
|
||||
|
||||
constexpr unsigned int Sdmmc1ClkCmdDat03PadOeOutput = 0x3F;
|
||||
constexpr unsigned int Sdmmc1ClkCmdDat03PadOeInput = 0x00;
|
||||
|
||||
struct PinmuxDefinition {
|
||||
u32 reg_offset;
|
||||
u32 mask_val;
|
||||
u32 pm_val;
|
||||
};
|
||||
|
||||
/* NOTE: We only use the SDMMC1 pins, which are conveniently the first few... */
|
||||
constexpr const PinmuxDefinition PinmuxDefinitionMap[] = {
|
||||
{0x00003000, 0x72FF, 0x01}, /* Sdmmc1Clk */
|
||||
{0x00003004, 0x72FF, 0x02}, /* Sdmmc1Cmd */
|
||||
{0x00003008, 0x72FF, 0x02}, /* Sdmmc1Dat3 */
|
||||
{0x0000300C, 0x72FF, 0x02}, /* Sdmmc1Dat2 */
|
||||
{0x00003010, 0x72FF, 0x02}, /* Sdmmc1Dat1 */
|
||||
{0x00003014, 0x72FF, 0x01}, /* Sdmmc1Dat0 */
|
||||
};
|
||||
|
||||
enum PinmuxPadIndex {
|
||||
PinmuxPadIndex_Sdmmc1Clk = 0,
|
||||
PinmuxPadIndex_Sdmmc1Cmd = 1,
|
||||
PinmuxPadIndex_Sdmmc1Dat3 = 2,
|
||||
PinmuxPadIndex_Sdmmc1Dat2 = 3,
|
||||
PinmuxPadIndex_Sdmmc1Dat1 = 4,
|
||||
PinmuxPadIndex_Sdmmc1Dat0 = 5,
|
||||
|
||||
PinmuxPadIndex_Count,
|
||||
};
|
||||
|
||||
static_assert(util::size(PinmuxDefinitionMap) == PinmuxPadIndex_Count);
|
||||
|
||||
consteval const PinmuxDefinition GetDefinition(PinmuxPadIndex pad_index) {
|
||||
AMS_ABORT_UNLESS(pad_index < PinmuxPadIndex_Count);
|
||||
|
||||
return PinmuxDefinitionMap[pad_index];
|
||||
}
|
||||
|
||||
template<PinmuxPadIndex PadIndex, u32 PinmuxConfigVal, u32 PinmuxConfigMaskVal>
|
||||
ALWAYS_INLINE u32 UpdatePinmuxPad(uintptr_t pinmux_base_vaddr) {
|
||||
constexpr const PinmuxDefinition Definition = GetDefinition(PadIndex);
|
||||
|
||||
/* Fetch this PINMUX's register offset */
|
||||
constexpr u32 PinmuxRegOffset = Definition.reg_offset;
|
||||
|
||||
/* Fetch this PINMUX's mask value */
|
||||
constexpr u32 PinmuxMaskVal = Definition.mask_val;
|
||||
|
||||
/* Get current register ptr. */
|
||||
const uintptr_t pinmux_reg = pinmux_base_vaddr + PinmuxRegOffset;
|
||||
|
||||
/* Read from the PINMUX register */
|
||||
u32 pinmux_val = reg::Read(pinmux_reg);
|
||||
|
||||
/* This PINMUX register is locked */
|
||||
AMS_ABORT_UNLESS((pinmux_val & 0x80) == 0);
|
||||
|
||||
constexpr u32 PmVal = (PinmuxConfigVal & 0x07);
|
||||
|
||||
/* Adjust PM */
|
||||
if constexpr (PinmuxConfigMaskVal & 0x07) {
|
||||
/* Apply additional changes first */
|
||||
if constexpr (PmVal == 0x05) {
|
||||
/* This pin supports PUPD change */
|
||||
if constexpr (PinmuxMaskVal & 0x0C) {
|
||||
/* Change PUPD */
|
||||
if ((pinmux_val & 0x0C) != 0x04) {
|
||||
pinmux_val &= 0xFFFFFFF3;
|
||||
pinmux_val |= 0x04;
|
||||
}
|
||||
}
|
||||
|
||||
/* This pin supports Tristate change */
|
||||
if constexpr (PinmuxMaskVal & 0x10) {
|
||||
/* Change Tristate */
|
||||
if (!(pinmux_val & 0x10)) {
|
||||
pinmux_val |= 0x10;
|
||||
}
|
||||
}
|
||||
|
||||
/* This pin supports EInput change */
|
||||
if constexpr (PinmuxMaskVal & 0x40) {
|
||||
/* Change EInput */
|
||||
if (pinmux_val & 0x40) {
|
||||
pinmux_val &= 0xFFFFFFBF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Translate PM value if necessary */
|
||||
constexpr u32 TranslatedPmVal = (PmVal == 0x04 || PmVal == 0x05 || PmVal >= 0x06) ? Definition.pm_val : PmVal;
|
||||
|
||||
/* This pin supports PM change */
|
||||
if constexpr (PinmuxMaskVal & 0x03) {
|
||||
/* Change PM */
|
||||
if ((pinmux_val & 0x03) != (TranslatedPmVal & 0x03)) {
|
||||
pinmux_val &= 0xFFFFFFFC;
|
||||
pinmux_val |= (TranslatedPmVal & 0x03);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr u32 PupdConfigVal = (PinmuxConfigVal & 0x18);
|
||||
|
||||
/* Adjust PUPD */
|
||||
if constexpr (PinmuxConfigMaskVal & 0x18) {
|
||||
if constexpr (PupdConfigVal < 0x11) {
|
||||
/* This pin supports PUPD change */
|
||||
if constexpr (PinmuxMaskVal & 0x0C) {
|
||||
/* Change PUPD */
|
||||
if (((pinmux_val >> 0x02) & 0x03) != (PupdConfigVal >> 0x03)) {
|
||||
pinmux_val &= 0xFFFFFFF3;
|
||||
pinmux_val |= (PupdConfigVal >> 0x01);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr u32 EodConfigVal = (PinmuxConfigVal & 0x60);
|
||||
|
||||
/* Adjust EOd field */
|
||||
if constexpr (PinmuxConfigMaskVal & 0x60) {
|
||||
if constexpr (EodConfigVal == 0x20) {
|
||||
/* This pin supports Tristate change */
|
||||
if constexpr (PinmuxMaskVal & 0x10) {
|
||||
/* Change Tristate */
|
||||
if (!(pinmux_val & 0x10)) {
|
||||
pinmux_val |= 0x10;
|
||||
}
|
||||
}
|
||||
|
||||
/* This pin supports EInput change */
|
||||
if constexpr (PinmuxMaskVal & 0x40) {
|
||||
/* Change EInput */
|
||||
if (!(pinmux_val & 0x40)) {
|
||||
pinmux_val |= 0x40;
|
||||
}
|
||||
}
|
||||
|
||||
/* This pin supports EOd change */
|
||||
if constexpr (PinmuxMaskVal & 0x800) {
|
||||
/* Change EOd */
|
||||
if (pinmux_val & 0x800) {
|
||||
pinmux_val &= 0xFFFFF7FF;
|
||||
}
|
||||
}
|
||||
} else if constexpr (EodConfigVal == 0x40) {
|
||||
/* This pin supports Tristate change */
|
||||
if constexpr (PinmuxMaskVal & 0x10) {
|
||||
/* Change Tristate */
|
||||
if (pinmux_val & 0x10) {
|
||||
pinmux_val &= 0xFFFFFFEF;
|
||||
}
|
||||
}
|
||||
|
||||
/* This pin supports EInput change */
|
||||
if constexpr (PinmuxMaskVal & 0x40) {
|
||||
/* Change EInput */
|
||||
if (!(pinmux_val & 0x40)) {
|
||||
pinmux_val |= 0x40;
|
||||
}
|
||||
}
|
||||
|
||||
/* This pin supports EOd change */
|
||||
if constexpr (PinmuxMaskVal & 0x800) {
|
||||
/* Change EOd */
|
||||
if (pinmux_val & 0x800) {
|
||||
pinmux_val &= 0xFFFFF7FF;
|
||||
}
|
||||
}
|
||||
} else if constexpr (EodConfigVal == 0x60) {
|
||||
/* This pin supports Tristate change */
|
||||
if constexpr (PinmuxMaskVal & 0x10) {
|
||||
/* Change Tristate */
|
||||
if (pinmux_val & 0x10) {
|
||||
pinmux_val &= 0xFFFFFFEF;
|
||||
}
|
||||
}
|
||||
|
||||
/* This pin supports EInput change */
|
||||
if constexpr (PinmuxMaskVal & 0x40) {
|
||||
/* Change EInput */
|
||||
if (!(pinmux_val & 0x40)) {
|
||||
pinmux_val |= 0x40;
|
||||
}
|
||||
}
|
||||
|
||||
/* This pin supports EOd change */
|
||||
if constexpr (PinmuxMaskVal & 0x800) {
|
||||
/* Change EOd */
|
||||
if (!(pinmux_val & 0x800)) {
|
||||
pinmux_val |= 0x800;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* This pin supports Tristate change */
|
||||
if constexpr (PinmuxMaskVal & 0x10) {
|
||||
/* Change Tristate */
|
||||
if (pinmux_val & 0x10) {
|
||||
pinmux_val &= 0xFFFFFFEF;
|
||||
}
|
||||
}
|
||||
|
||||
/* This pin supports EInput change */
|
||||
if constexpr (PinmuxMaskVal & 0x40) {
|
||||
/* Change EInput */
|
||||
if (pinmux_val & 0x40) {
|
||||
pinmux_val &= 0xFFFFFFBF;
|
||||
}
|
||||
}
|
||||
|
||||
/* This pin supports EOd change */
|
||||
if constexpr (PinmuxMaskVal & 0x800) {
|
||||
/* Change EOd */
|
||||
if (pinmux_val & 0x800) {
|
||||
pinmux_val &= 0xFFFFF7FF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr u32 LockConfigVal = (PinmuxConfigVal & 0x80);
|
||||
|
||||
/* Adjust Lock */
|
||||
if constexpr (PinmuxConfigMaskVal & 0x80) {
|
||||
/* This pin supports Lock change */
|
||||
if constexpr (PinmuxMaskVal & 0x80) {
|
||||
/* Change Lock */
|
||||
if ((pinmux_val ^ PinmuxConfigVal) & 0x80) {
|
||||
pinmux_val &= 0xFFFFFF7F;
|
||||
pinmux_val |= LockConfigVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr u32 IoResetConfigVal = (((PinmuxConfigVal >> 0x08) & 0x1) << 0x10);
|
||||
|
||||
/* Adjust IoReset */
|
||||
if constexpr (PinmuxConfigMaskVal & 0x100) {
|
||||
/* This pin supports IoReset change */
|
||||
if constexpr (PinmuxMaskVal & 0x10000) {
|
||||
/* Change IoReset */
|
||||
if (((pinmux_val >> 0x10) ^ (PinmuxConfigVal >> 0x08)) & 0x01) {
|
||||
pinmux_val &= 0xFFFEFFFF;
|
||||
pinmux_val |= IoResetConfigVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr u32 ParkConfigVal = (((PinmuxConfigVal >> 0x0A) & 0x1) << 0x5);
|
||||
|
||||
/* Adjust Park */
|
||||
if constexpr (PinmuxConfigMaskVal & 0x400) {
|
||||
/* This pin supports Park change */
|
||||
if constexpr (PinmuxMaskVal & 0x20) {
|
||||
/* Change Park */
|
||||
if (((pinmux_val >> 0x05) ^ (PinmuxConfigVal >> 0x0A)) & 0x01) {
|
||||
pinmux_val &= 0xFFFFFFDF;
|
||||
pinmux_val |= ParkConfigVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr u32 ElpdrConfigVal = (((PinmuxConfigVal >> 0x0B) & 0x1) << 0x08);
|
||||
|
||||
/* Adjust ELpdr */
|
||||
if constexpr (PinmuxConfigMaskVal & 0x800) {
|
||||
/* This pin supports ELpdr change */
|
||||
if constexpr (PinmuxMaskVal & 0x100) {
|
||||
/* Change ELpdr */
|
||||
if (((pinmux_val >> 0x08) ^ (PinmuxConfigVal >> 0x0B)) & 0x01) {
|
||||
pinmux_val &= 0xFFFFFEFF;
|
||||
pinmux_val |= ElpdrConfigVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr u32 EhsmConfigVal = (((PinmuxConfigVal >> 0x0C) & 0x1) << 0x09);
|
||||
|
||||
/* Adjust EHsm */
|
||||
if constexpr (PinmuxConfigMaskVal & 0x1000) {
|
||||
/* This pin supports EHsm change */
|
||||
if constexpr (PinmuxMaskVal & 0x200) {
|
||||
/* Change EHsm */
|
||||
if (((pinmux_val >> 0x09) ^ (PinmuxConfigVal >> 0x0C)) & 0x01) {
|
||||
pinmux_val &= 0xFFFFFDFF;
|
||||
pinmux_val |= EhsmConfigVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr u32 EIoHvConfigVal = (((PinmuxConfigVal >> 0x09) & 0x1) << 0x0A);
|
||||
|
||||
/* Adjust EIoHv */
|
||||
if constexpr (PinmuxConfigMaskVal & 0x200) {
|
||||
/* This pin supports EIoHv change */
|
||||
if constexpr (PinmuxMaskVal & 0x400) {
|
||||
/* Change EIoHv */
|
||||
if (((pinmux_val >> 0x0A) ^ (PinmuxConfigVal >> 0x09)) & 0x01) {
|
||||
pinmux_val &= 0xFFFFFBFF;
|
||||
pinmux_val |= EIoHvConfigVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr u32 EschmtConfigVal = (((PinmuxConfigVal >> 0x0D) & 0x1) << 0x0C);
|
||||
|
||||
/* Adjust ESchmt */
|
||||
if constexpr (PinmuxConfigMaskVal & 0x2000) {
|
||||
/* This pin supports ESchmt change */
|
||||
if constexpr (PinmuxMaskVal & 0x1000) {
|
||||
/* Change ESchmt */
|
||||
if (((pinmux_val >> 0x0C) ^ (PinmuxConfigVal >> 0x0D)) & 0x01) {
|
||||
pinmux_val &= 0xFFFFEFFF;
|
||||
pinmux_val |= EschmtConfigVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr u32 PreempConfigVal = (((PinmuxConfigVal >> 0x10) & 0x1) << 0xF);
|
||||
|
||||
/* Adjust Preemp */
|
||||
if constexpr (PinmuxConfigMaskVal & 0x10000) {
|
||||
/* This pin supports Preemp change */
|
||||
if constexpr (PinmuxMaskVal & 0x8000) {
|
||||
/* Change Preemp */
|
||||
if (((pinmux_val >> 0x0F) ^ (PinmuxConfigVal >> 0x10)) & 0x01) {
|
||||
pinmux_val &= 0xFFFF7FFF;
|
||||
pinmux_val |= PreempConfigVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr u32 DrvTypeConfigVal = (((PinmuxConfigVal >> 0x0E) & 0x3) << 0xD);
|
||||
|
||||
/* Adjust DrvType */
|
||||
if constexpr (PinmuxConfigMaskVal & 0xC000) {
|
||||
/* This pin supports DrvType change */
|
||||
if constexpr (PinmuxMaskVal & 0x6000) {
|
||||
/* Change DrvType */
|
||||
if (((pinmux_val >> 0x0D) ^ (PinmuxConfigVal >> 0x0E)) & 0x03) {
|
||||
pinmux_val &= 0xFFFF9FFF;
|
||||
pinmux_val |= DrvTypeConfigVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Write to the appropriate PINMUX register */
|
||||
reg::Write(pinmux_reg, pinmux_val);
|
||||
|
||||
/* Do a dummy read from the PINMUX register */
|
||||
pinmux_val = reg::Read(pinmux_reg);
|
||||
|
||||
return pinmux_val;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SetPinAssignment(PinAssignment assignment) {
|
||||
/* Get the apb registers address. */
|
||||
const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize);
|
||||
AMS_UNUSED(apb_address);
|
||||
|
||||
/* Set the pin assignment. */
|
||||
switch (assignment) {
|
||||
case PinAssignment_Sdmmc1OutputHigh:
|
||||
{
|
||||
/* Clear Sdmmc1Clk pulldown. */
|
||||
UpdatePinmuxPad<PinmuxPadIndex_Sdmmc1Clk, 0, 0x18>(apb_address);
|
||||
|
||||
/* Get the gpio registers address. */
|
||||
const uintptr_t gpio_address = dd::QueryIoMapping(gpio_impl::GpioRegistersPhysicalAddress, gpio_impl::GpioRegistersSize);
|
||||
|
||||
/* Configure GPIO M0-5 (SDMMC1 CLK + CMD + DAT0/1/2/3) as gpio. */
|
||||
const uintptr_t cnf_address = gpio_impl::GetGpioRegisterAddress(gpio_address, gpio_impl::GpioRegisterType_GPIO_CNF, Sdmmc1ClkCmdDat03PadNumber);
|
||||
gpio_impl::SetMaskedBits(cnf_address, Sdmmc1ClkCmdDat03PadMask, Sdmmc1ClkCmdDat03PadCnfGpio);
|
||||
|
||||
/* Configure GPIO M0-5 (SDMMC1 CLK + CMD + DAT0/1/2/3) as high. */
|
||||
const uintptr_t out_address = gpio_impl::GetGpioRegisterAddress(gpio_address, gpio_impl::GpioRegisterType_GPIO_OUT, Sdmmc1ClkCmdDat03PadNumber);
|
||||
gpio_impl::SetMaskedBits(out_address, Sdmmc1ClkCmdDat03PadMask, Sdmmc1ClkCmdDat03PadOutHigh);
|
||||
|
||||
/* Configure GPIO M0-5 (SDMMC1 CLK + CMD + DAT0/1/2/3) as output. */
|
||||
const uintptr_t oe_address = gpio_impl::GetGpioRegisterAddress(gpio_address, gpio_impl::GpioRegisterType_GPIO_OE, Sdmmc1ClkCmdDat03PadNumber);
|
||||
gpio_impl::SetMaskedBits(oe_address, Sdmmc1ClkCmdDat03PadMask, Sdmmc1ClkCmdDat03PadOeOutput);
|
||||
|
||||
/* Read to be sure that our configuration takes. */
|
||||
reg::Read(oe_address);
|
||||
}
|
||||
break;
|
||||
case PinAssignment_Sdmmc1ResetState:
|
||||
{
|
||||
/* Get the gpio registers address. */
|
||||
const uintptr_t gpio_address = dd::QueryIoMapping(gpio_impl::GpioRegistersPhysicalAddress, gpio_impl::GpioRegistersSize);
|
||||
|
||||
/* Configure GPIO M0-5 (SDMMC1 CLK + CMD + DAT0/1/2/3) as sfio. */
|
||||
const uintptr_t cnf_address = gpio_impl::GetGpioRegisterAddress(gpio_address, gpio_impl::GpioRegisterType_GPIO_CNF, Sdmmc1ClkCmdDat03PadNumber);
|
||||
gpio_impl::SetMaskedBits(cnf_address, Sdmmc1ClkCmdDat03PadMask, Sdmmc1ClkCmdDat03PadCnfSfio);
|
||||
|
||||
/* Configure GPIO M0-5 (SDMMC1 CLK + CMD + DAT0/1/2/3) as low. */
|
||||
const uintptr_t out_address = gpio_impl::GetGpioRegisterAddress(gpio_address, gpio_impl::GpioRegisterType_GPIO_OUT, Sdmmc1ClkCmdDat03PadNumber);
|
||||
gpio_impl::SetMaskedBits(out_address, Sdmmc1ClkCmdDat03PadMask, Sdmmc1ClkCmdDat03PadOutLow);
|
||||
|
||||
/* Configure GPIO M0-5 (SDMMC1 CLK + CMD + DAT0/1/2/3) as input. */
|
||||
const uintptr_t oe_address = gpio_impl::GetGpioRegisterAddress(gpio_address, gpio_impl::GpioRegisterType_GPIO_OE, Sdmmc1ClkCmdDat03PadNumber);
|
||||
gpio_impl::SetMaskedBits(oe_address, Sdmmc1ClkCmdDat03PadMask, Sdmmc1ClkCmdDat03PadOeInput);
|
||||
|
||||
/* Read to be sure that our configuration takes. */
|
||||
reg::Read(oe_address);
|
||||
|
||||
/* Set Sdmmc1Clk pulldown. */
|
||||
UpdatePinmuxPad<PinmuxPadIndex_Sdmmc1Clk, 0x8, 0x18>(apb_address);
|
||||
}
|
||||
break;
|
||||
case PinAssignment_Sdmmc1SchmtEnable:
|
||||
{
|
||||
/* Set Schmitt enable for all pins in the group. */
|
||||
UpdatePinmuxPad<PinmuxPadIndex_Sdmmc1Clk, 0x2000, 0x2000>(apb_address);
|
||||
UpdatePinmuxPad<PinmuxPadIndex_Sdmmc1Cmd, 0x2000, 0x2000>(apb_address);
|
||||
UpdatePinmuxPad<PinmuxPadIndex_Sdmmc1Dat3, 0x2000, 0x2000>(apb_address);
|
||||
UpdatePinmuxPad<PinmuxPadIndex_Sdmmc1Dat2, 0x2000, 0x2000>(apb_address);
|
||||
UpdatePinmuxPad<PinmuxPadIndex_Sdmmc1Dat1, 0x2000, 0x2000>(apb_address);
|
||||
UpdatePinmuxPad<PinmuxPadIndex_Sdmmc1Dat0, 0x2000, 0x2000>(apb_address);
|
||||
}
|
||||
break;
|
||||
case PinAssignment_Sdmmc1SchmtDisable:
|
||||
{
|
||||
/* Set Schmitt disable for all pins in the group. */
|
||||
UpdatePinmuxPad<PinmuxPadIndex_Sdmmc1Clk, 0x0000, 0x2000>(apb_address);
|
||||
UpdatePinmuxPad<PinmuxPadIndex_Sdmmc1Cmd, 0x0000, 0x2000>(apb_address);
|
||||
UpdatePinmuxPad<PinmuxPadIndex_Sdmmc1Dat3, 0x0000, 0x2000>(apb_address);
|
||||
UpdatePinmuxPad<PinmuxPadIndex_Sdmmc1Dat2, 0x0000, 0x2000>(apb_address);
|
||||
UpdatePinmuxPad<PinmuxPadIndex_Sdmmc1Dat1, 0x0000, 0x2000>(apb_address);
|
||||
UpdatePinmuxPad<PinmuxPadIndex_Sdmmc1Dat0, 0x0000, 0x2000>(apb_address);
|
||||
}
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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.hpp>
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
Result SetSdCardVoltageEnabled(bool en);
|
||||
Result SetSdCardVoltageValue(u32 micro_volts);
|
||||
|
||||
namespace pinmux_impl {
|
||||
|
||||
enum PinAssignment {
|
||||
PinAssignment_Sdmmc1OutputHigh = 2,
|
||||
PinAssignment_Sdmmc1ResetState = 3,
|
||||
PinAssignment_Sdmmc1SchmtEnable = 4,
|
||||
PinAssignment_Sdmmc1SchmtDisable = 5,
|
||||
};
|
||||
|
||||
void SetPinAssignment(PinAssignment assignment);
|
||||
|
||||
}
|
||||
|
||||
namespace gpio_impl {
|
||||
|
||||
enum GpioValue {
|
||||
GpioValue_Low = 0,
|
||||
GpioValue_High = 1
|
||||
};
|
||||
|
||||
enum Direction {
|
||||
Direction_Input = 0,
|
||||
Direction_Output = 1,
|
||||
};
|
||||
|
||||
enum GpioPadName {
|
||||
GpioPadName_PowSdEn = 2,
|
||||
};
|
||||
|
||||
void OpenSession(GpioPadName pad);
|
||||
void CloseSession(GpioPadName pad);
|
||||
|
||||
void SetDirection(GpioPadName pad, Direction direction);
|
||||
void SetValue(GpioPadName pad, GpioValue value);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,728 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
#include "sdmmc_mmc_device_accessor.hpp"
|
||||
#include "sdmmc_timer.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
#if defined(AMS_SDMMC_THREAD_SAFE)
|
||||
|
||||
#define AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX() std::scoped_lock lk(m_mmc_device.m_device_mutex)
|
||||
|
||||
#else
|
||||
|
||||
#define AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX()
|
||||
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline u32 OcrCardPowerUpStatus = (1 << 31);
|
||||
|
||||
constexpr inline u32 OcrAccessMode_Mask = (3 << 29);
|
||||
constexpr inline u32 OcrAccessMode_SectorMode = (2 << 29);
|
||||
|
||||
constexpr inline u8 ManufacturerId_Toshiba = 0x11;
|
||||
|
||||
enum DeviceType : u8 {
|
||||
DeviceType_HighSpeed26MHz = (1u << 0),
|
||||
DeviceType_HighSpeed52MHz = (1u << 1),
|
||||
DeviceType_HighSpeedDdr52MHz1_8VOr3_0V = (1u << 2),
|
||||
DeviceType_HighSpeedDdr52MHz1_2V = (1u << 3),
|
||||
DeviceType_Hs200Sdr200MHz1_8V = (1u << 4),
|
||||
DeviceType_Hs200Sdr200MHz1_2V = (1u << 5),
|
||||
DeviceType_Hs400Sdr200MHz1_8V = (1u << 6),
|
||||
DeviceType_Hs400Sdr200MHz1_2V = (1u << 7),
|
||||
};
|
||||
|
||||
constexpr bool IsToshibaMmc(const u8 *cid) {
|
||||
/* Check whether the CID's manufacturer id field is Toshiba. */
|
||||
AMS_ABORT_UNLESS(cid != nullptr);
|
||||
return cid[14] == ManufacturerId_Toshiba;
|
||||
}
|
||||
|
||||
constexpr bool IsLessThanSpecification4(const u8 *csd) {
|
||||
const u8 spec_vers = ((csd[14] >> 2) & 0xF);
|
||||
return spec_vers < 4;
|
||||
}
|
||||
|
||||
constexpr bool IsBkopAutoEnable(const u8 *ext_csd) {
|
||||
/* Check the AUTO_EN bit of BKOPS_EN. */
|
||||
return (ext_csd[163] & (1u << 1)) != 0;
|
||||
}
|
||||
|
||||
constexpr u8 GetDeviceType(const u8 *ext_csd) {
|
||||
/* Get the DEVICE_TYPE register. */
|
||||
AMS_ABORT_UNLESS(ext_csd != nullptr);
|
||||
return ext_csd[196];
|
||||
}
|
||||
|
||||
constexpr bool IsSupportedHs400(u8 device_type) {
|
||||
return (device_type & DeviceType_Hs400Sdr200MHz1_8V) != 0;
|
||||
}
|
||||
|
||||
constexpr bool IsSupportedHs200(u8 device_type) {
|
||||
return (device_type & DeviceType_Hs200Sdr200MHz1_8V) != 0;
|
||||
}
|
||||
|
||||
constexpr bool IsSupportedHighSpeed(u8 device_type) {
|
||||
return (device_type & DeviceType_HighSpeed52MHz) != 0;
|
||||
}
|
||||
|
||||
constexpr u32 GetMemoryCapacityFromExtCsd(const u32 *ext_csd) {
|
||||
/* Get the SEC_COUNT register. */
|
||||
AMS_ABORT_UNLESS(ext_csd != nullptr);
|
||||
return ext_csd[212 / sizeof(u32)];
|
||||
}
|
||||
|
||||
constexpr u32 GetBootPartitionMemoryCapacityFromExtCsd(const u8 *ext_csd) {
|
||||
/* Get the BOOT_SIZE_MULT register. */
|
||||
AMS_ABORT_UNLESS(ext_csd != nullptr);
|
||||
return ext_csd[226] * (128_KB / SectorSize);
|
||||
}
|
||||
|
||||
constexpr Result GetCurrentSpeedModeFromExtCsd(SpeedMode *out, const u8 *ext_csd) {
|
||||
/* Get the HS_TIMING register. */
|
||||
AMS_ABORT_UNLESS(out != nullptr);
|
||||
AMS_ABORT_UNLESS(ext_csd != nullptr);
|
||||
|
||||
switch (ext_csd[185] & 0xF) {
|
||||
case 0: *out = SpeedMode_MmcLegacySpeed; break;
|
||||
case 1: *out = SpeedMode_MmcHighSpeed; break;
|
||||
case 2: *out = SpeedMode_MmcHs200; break;
|
||||
case 3: *out = SpeedMode_MmcHs400; break;
|
||||
default: R_THROW(sdmmc::ResultUnexpectedMmcExtendedCsdValue());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MmcDevice::SetOcrAndHighCapacity(u32 ocr) {
|
||||
/* Set ocr. */
|
||||
BaseDevice::SetOcr(ocr);
|
||||
|
||||
/* Set high capacity. */
|
||||
BaseDevice::SetHighCapacity((ocr & OcrAccessMode_Mask) == OcrAccessMode_SectorMode);
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::IssueCommandSendOpCond(u32 *out_ocr, BusPower bus_power) const {
|
||||
/* Get the command argument. */
|
||||
u32 arg = OcrAccessMode_SectorMode;
|
||||
switch (bus_power) {
|
||||
case BusPower_1_8V: arg |= 0x000080; break;
|
||||
case BusPower_3_3V: arg |= 0x03F800; break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
/* Issue the command. */
|
||||
constexpr ResponseType CommandResponseType = ResponseType_R3;
|
||||
Command command(CommandIndex_SendOpCond, arg, CommandResponseType, false);
|
||||
IHostController *hc = BaseDeviceAccessor::GetHostController();
|
||||
R_TRY(hc->IssueCommand(std::addressof(command)));
|
||||
|
||||
/* Get the response. */
|
||||
hc->GetLastResponse(out_ocr, sizeof(*out_ocr), CommandResponseType);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::IssueCommandSetRelativeAddr() const {
|
||||
/* Get rca. */
|
||||
const u32 rca = m_mmc_device.GetRca();
|
||||
AMS_ABORT_UNLESS(rca > 0);
|
||||
|
||||
/* Issue comamnd. */
|
||||
const u32 arg = rca << 16;
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_SetRelativeAddr, arg, false, DeviceState_Unknown));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::IssueCommandSwitch(CommandSwitch cs) const {
|
||||
/* Get the command argument. */
|
||||
const u32 arg = GetCommandSwitchArgument(cs);
|
||||
|
||||
/* Issue the command. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_Switch, arg, true, DeviceState_Unknown));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::IssueCommandSendExtCsd(void *dst, size_t dst_size) const {
|
||||
/* Validate the output buffer. */
|
||||
AMS_ABORT_UNLESS(dst != nullptr);
|
||||
AMS_ABORT_UNLESS(dst_size >= MmcExtendedCsdSize);
|
||||
|
||||
/* Issue the command. */
|
||||
constexpr ResponseType CommandResponseType = ResponseType_R1;
|
||||
Command command(CommandIndex_SendExtCsd, 0, CommandResponseType, false);
|
||||
TransferData xfer_data(dst, MmcExtendedCsdSize, 1, TransferDirection_ReadFromDevice);
|
||||
IHostController *hc = BaseDeviceAccessor::GetHostController();
|
||||
R_TRY(hc->IssueCommand(std::addressof(command), std::addressof(xfer_data)));
|
||||
|
||||
/* Get the response. */
|
||||
u32 resp;
|
||||
hc->GetLastResponse(std::addressof(resp), sizeof(resp), CommandResponseType);
|
||||
R_TRY(m_mmc_device.CheckDeviceStatus(resp));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::IssueCommandEraseGroupStart(u32 sector_index) const {
|
||||
/* Get the command argument. */
|
||||
const u32 arg = m_mmc_device.IsHighCapacity() ? sector_index : sector_index * SectorSize;
|
||||
|
||||
/* Issue the command. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_EraseGroupStart, arg, false, DeviceState_Unknown));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::IssueCommandEraseGroupEnd(u32 sector_index) const {
|
||||
/* Get the command argument. */
|
||||
const u32 arg = m_mmc_device.IsHighCapacity() ? sector_index : sector_index * SectorSize;
|
||||
|
||||
/* Issue the command. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_EraseGroupEnd, arg, false, DeviceState_Tran));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::IssueCommandErase() const {
|
||||
/* Issue the command. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandAndCheckR1(CommandIndex_Erase, 0, false, DeviceState_Tran));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::CancelToshibaMmcModel() {
|
||||
/* Special erase sequence done by Nintendo on Toshiba MMCs. */
|
||||
R_TRY(this->IssueCommandSwitch(CommandSwitch_SetBitsProductionStateAwarenessEnable));
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
R_TRY(this->IssueCommandSwitch(CommandSwitch_ClearBitsAutoModeEnable));
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteProductionStateAwarenessPreSolderingWrites));
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteProductionStateAwarenessPreSolderingPostWrites));
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteProductionStateAwarenessNormal));
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::ChangeToReadyState(BusPower bus_power) {
|
||||
/* Be prepared to wait up to 1.5 seconds to change state. */
|
||||
ManualTimer timer(1500);
|
||||
while (true) {
|
||||
/* Get the ocr, and check if we're done. */
|
||||
u32 ocr;
|
||||
R_TRY(this->IssueCommandSendOpCond(std::addressof(ocr), bus_power));
|
||||
if ((ocr & OcrCardPowerUpStatus) != 0) {
|
||||
m_mmc_device.SetOcrAndHighCapacity(ocr);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* Check if we've timed out. */
|
||||
R_UNLESS(timer.Update(), sdmmc::ResultMmcInitializationSoftwareTimeout());
|
||||
|
||||
/* Try again in 1ms. */
|
||||
WaitMicroSeconds(1000);
|
||||
}
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::ExtendBusWidth(BusWidth max_bw) {
|
||||
/* If the maximum bus width is 1bit, we can't extend. */
|
||||
R_SUCCEED_IF(max_bw == BusWidth_1Bit);
|
||||
|
||||
/* Determine what bus width to switch to. */
|
||||
IHostController *hc = BaseDeviceAccessor::GetHostController();
|
||||
BusWidth target_bw = BusWidth_1Bit;
|
||||
CommandSwitch cs;
|
||||
if (max_bw == BusWidth_8Bit && hc->IsSupportedBusWidth(BusWidth_8Bit)) {
|
||||
target_bw = BusWidth_8Bit;
|
||||
cs = CommandSwitch_WriteBusWidth8Bit;
|
||||
} else if ((max_bw == BusWidth_8Bit || max_bw == BusWidth_4Bit) && hc->IsSupportedBusWidth(BusWidth_4Bit)) {
|
||||
target_bw = BusWidth_4Bit;
|
||||
cs = CommandSwitch_WriteBusWidth4Bit;
|
||||
} else {
|
||||
/* Target bus width is 1bit. */
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* Set the bus width. */
|
||||
R_TRY(this->IssueCommandSwitch(cs));
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
hc->SetBusWidth(target_bw);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::EnableBkopsAuto() {
|
||||
/* Issue the command. */
|
||||
R_TRY(this->IssueCommandSwitch(CommandSwitch_SetBitsBkopsEnAutoEn));
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::ChangeToHighSpeed(bool check_before) {
|
||||
/* Issue high speed command. */
|
||||
R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteHsTimingHighSpeed));
|
||||
|
||||
/* If we should check status before setting mode, do so. */
|
||||
if (check_before) {
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
}
|
||||
|
||||
/* Set the host controller to high speed. */
|
||||
R_TRY(BaseDeviceAccessor::GetHostController()->SetSpeedMode(SpeedMode_MmcHighSpeed));
|
||||
|
||||
/* If we should check status after setting mode, do so. */
|
||||
if (!check_before) {
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::ChangeToHs200() {
|
||||
/* Issue Hs200 command. */
|
||||
R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteHsTimingHs200));
|
||||
|
||||
/* Set the host controller to Hs200. */
|
||||
IHostController *hc = BaseDeviceAccessor::GetHostController();
|
||||
R_TRY(hc->SetSpeedMode(SpeedMode_MmcHs200));
|
||||
|
||||
/* Perform tuning using command index 21. */
|
||||
R_TRY(hc->Tuning(SpeedMode_MmcHs200, 21));
|
||||
|
||||
/* Check status. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::ChangeToHs400() {
|
||||
/* Change first to Hs200. */
|
||||
R_TRY(this->ChangeToHs200());
|
||||
|
||||
/* Save tuning status. */
|
||||
IHostController *hc = BaseDeviceAccessor::GetHostController();
|
||||
hc->SaveTuningStatusForHs400();
|
||||
|
||||
/* Change to high speed. */
|
||||
R_TRY(this->ChangeToHighSpeed(false));
|
||||
|
||||
/* Issue Hs400 command. */
|
||||
R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteBusWidth8BitDdr));
|
||||
R_TRY(this->IssueCommandSwitch(CommandSwitch_WriteHsTimingHs400));
|
||||
|
||||
/* Set the host controller to Hs400. */
|
||||
R_TRY(hc->SetSpeedMode(SpeedMode_MmcHs400));
|
||||
|
||||
/* Check status. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::ExtendBusSpeed(u8 device_type, SpeedMode max_sm) {
|
||||
/* We want to switch to the highest speed we can. */
|
||||
/* Check Hs400/Hs200 first. */
|
||||
IHostController *hc = BaseDeviceAccessor::GetHostController();
|
||||
if (hc->IsSupportedTuning() && hc->GetBusPower() == BusPower_1_8V) {
|
||||
if (hc->GetBusWidth() == BusWidth_8Bit && IsSupportedHs400(device_type) && max_sm == SpeedMode_MmcHs400) {
|
||||
R_RETURN(this->ChangeToHs400());
|
||||
} else if ((hc->GetBusWidth() == BusWidth_8Bit || hc->GetBusWidth() == BusWidth_4Bit) && IsSupportedHs200(device_type) && (max_sm == SpeedMode_MmcHs400 || max_sm == SpeedMode_MmcHs200)) {
|
||||
R_RETURN(this->ChangeToHs200());
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if we can switch to high speed. */
|
||||
if (IsSupportedHighSpeed(device_type)) {
|
||||
R_RETURN(this->ChangeToHighSpeed(true));
|
||||
}
|
||||
|
||||
/* We can't, so stay at normal speeds. */
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::StartupMmcDevice(BusWidth max_bw, SpeedMode max_sm, void *wb, size_t wb_size) {
|
||||
/* Start up at an appropriate bus power. */
|
||||
IHostController *hc = BaseDeviceAccessor::GetHostController();
|
||||
const BusPower bp = hc->IsSupportedBusPower(BusPower_1_8V) ? BusPower_1_8V : BusPower_3_3V;
|
||||
R_TRY(hc->Startup(bp, BusWidth_1Bit, SpeedMode_MmcIdentification, false));
|
||||
|
||||
/* Wait 1ms for configuration to take. */
|
||||
WaitMicroSeconds(1000);
|
||||
|
||||
/* Wait an additional 74 clocks for configuration to take. */
|
||||
WaitClocks(74, hc->GetDeviceClockFrequencyKHz());
|
||||
|
||||
/* Go to idle state. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandGoIdleState());
|
||||
m_current_partition = MmcPartition_UserData;
|
||||
|
||||
/* Go to ready state. */
|
||||
R_TRY(this->ChangeToReadyState(bp));
|
||||
|
||||
/* Get the CID. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandAllSendCid(wb, wb_size));
|
||||
m_mmc_device.SetCid(wb, wb_size);
|
||||
const bool is_toshiba = IsToshibaMmc(static_cast<const u8 *>(wb));
|
||||
|
||||
/* Issue set relative addr. */
|
||||
R_TRY(this->IssueCommandSetRelativeAddr());
|
||||
|
||||
/* Get the CSD. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendCsd(wb, wb_size));
|
||||
m_mmc_device.SetCsd(wb, wb_size);
|
||||
const bool spec_under_4 = IsLessThanSpecification4(static_cast<const u8 *>(wb));
|
||||
|
||||
/* Set the speed mode to legacy. */
|
||||
R_TRY(hc->SetSpeedMode(SpeedMode_MmcLegacySpeed));
|
||||
|
||||
/* Issue select card command. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSelectCard());
|
||||
|
||||
/* Set block length to sector size. */
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSetBlockLenToSectorSize());
|
||||
|
||||
/* If the device SPEC_VERS is less than 4, extended csd/switch aren't supported. */
|
||||
if (spec_under_4) {
|
||||
R_TRY(m_mmc_device.SetLegacyMemoryCapacity());
|
||||
|
||||
m_mmc_device.SetActive();
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* Extend the bus width to the largest that we can. */
|
||||
R_TRY(this->ExtendBusWidth(max_bw));
|
||||
|
||||
/* Get the extended csd. */
|
||||
R_TRY(this->IssueCommandSendExtCsd(wb, wb_size));
|
||||
AMS_ABORT_UNLESS(util::IsAligned(reinterpret_cast<uintptr_t>(wb), alignof(u32)));
|
||||
m_mmc_device.SetMemoryCapacity(GetMemoryCapacityFromExtCsd(static_cast<const u32 *>(wb)));
|
||||
|
||||
/* If the mmc is manufactured by toshiba, try to enable bkops auto. */
|
||||
if (is_toshiba && !IsBkopAutoEnable(static_cast<const u8 *>(wb))) {
|
||||
/* NOTE: Nintendo does not check the result of this. */
|
||||
this->EnableBkopsAuto();
|
||||
}
|
||||
|
||||
/* Extend the bus speed to as fast as we can. */
|
||||
const u8 device_type = GetDeviceType(static_cast<const u8 *>(wb));
|
||||
R_TRY(this->ExtendBusSpeed(device_type, max_sm));
|
||||
|
||||
/* Enable power saving. */
|
||||
hc->SetPowerSaving(true);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::OnActivate() {
|
||||
/* Define the possible startup parameters. */
|
||||
constexpr const struct {
|
||||
BusWidth bus_width;
|
||||
SpeedMode speed_mode;
|
||||
} StartupParameters[] = {
|
||||
#if defined(AMS_SDMMC_ENABLE_MMC_HS400)
|
||||
{ BusWidth_8Bit, SpeedMode_MmcHs400 },
|
||||
#else
|
||||
{ BusWidth_8Bit, SpeedMode_MmcHighSpeed },
|
||||
#endif
|
||||
{ BusWidth_8Bit, SpeedMode_MmcHighSpeed },
|
||||
{ BusWidth_1Bit, SpeedMode_MmcHighSpeed },
|
||||
};
|
||||
|
||||
/* Try to start up with each set of parameters. */
|
||||
Result result;
|
||||
for (int i = 0; i < static_cast<int>(util::size(StartupParameters)); ++i) {
|
||||
/* Alias the parameters. */
|
||||
const auto ¶ms = StartupParameters[i];
|
||||
|
||||
/* Set our max bus width/speed mode. */
|
||||
m_max_bus_width = params.bus_width;
|
||||
m_max_speed_mode = params.speed_mode;
|
||||
|
||||
/* Try to start up the device. */
|
||||
result = this->StartupMmcDevice(m_max_bus_width, m_max_speed_mode, m_work_buffer, m_work_buffer_size);
|
||||
if (R_SUCCEEDED(result)) {
|
||||
/* If we previously failed to start up the device, log the error correction. */
|
||||
if (i != 0) {
|
||||
BaseDeviceAccessor::PushErrorLog(true, "S %d %d:0", m_max_bus_width, m_max_speed_mode);
|
||||
BaseDeviceAccessor::IncrementNumActivationErrorCorrections();
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* Log that our startup failed. */
|
||||
BaseDeviceAccessor::PushErrorLog(false, "S %d %d:%X", m_max_bus_width, m_max_speed_mode, result.GetValue());
|
||||
|
||||
/* Shut down the host controller before we try to start up again. */
|
||||
BaseDeviceAccessor::GetHostController()->Shutdown();
|
||||
}
|
||||
|
||||
/* We failed to start up with all sets of parameters. */
|
||||
BaseDeviceAccessor::PushErrorTimeStamp();
|
||||
|
||||
R_RETURN(result);
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) {
|
||||
/* Get the sector index alignment. */
|
||||
u32 sector_index_alignment = 0;
|
||||
if (!is_read) {
|
||||
constexpr u32 MmcWriteSectorAlignment = 16_KB / SectorSize;
|
||||
sector_index_alignment = MmcWriteSectorAlignment;
|
||||
AMS_ABORT_UNLESS(util::IsAligned(sector_index, MmcWriteSectorAlignment));
|
||||
}
|
||||
|
||||
/* Do the read/write. */
|
||||
R_RETURN(BaseDeviceAccessor::ReadWriteMultiple(sector_index, num_sectors, sector_index_alignment, buf, buf_size, is_read));
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::ReStartup() {
|
||||
/* Shut down the host controller. */
|
||||
BaseDeviceAccessor::GetHostController()->Shutdown();
|
||||
|
||||
/* Perform start up. */
|
||||
Result result = this->StartupMmcDevice(m_max_bus_width, m_max_speed_mode, m_work_buffer, m_work_buffer_size);
|
||||
if (R_FAILED(result)) {
|
||||
BaseDeviceAccessor::PushErrorLog(false, "S %d %d:%X", m_max_bus_width, m_max_speed_mode, result.GetValue());
|
||||
R_RETURN(result);
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void MmcDeviceAccessor::Initialize() {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX();
|
||||
|
||||
/* If we've already initialized, we don't need to do anything. */
|
||||
if (m_is_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set the base device to our mmc device. */
|
||||
BaseDeviceAccessor::SetDevice(std::addressof(m_mmc_device));
|
||||
|
||||
/* Initialize. */
|
||||
BaseDeviceAccessor::GetHostController()->Initialize();
|
||||
m_is_initialized = true;
|
||||
}
|
||||
|
||||
void MmcDeviceAccessor::Finalize() {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX();
|
||||
|
||||
/* If we've already finalized, we don't need to do anything. */
|
||||
if (!m_is_initialized) {
|
||||
return;
|
||||
}
|
||||
m_is_initialized = false;
|
||||
|
||||
/* Deactivate the device. */
|
||||
BaseDeviceAccessor::Deactivate();
|
||||
|
||||
/* Finalize the host controller. */
|
||||
BaseDeviceAccessor::GetHostController()->Finalize();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::GetSpeedMode(SpeedMode *out_speed_mode) const {
|
||||
/* Check that we can write to output. */
|
||||
AMS_ABORT_UNLESS(out_speed_mode != nullptr);
|
||||
|
||||
/* Get the current speed mode from the ext csd. */
|
||||
R_TRY(GetMmcExtendedCsd(m_work_buffer, m_work_buffer_size));
|
||||
R_TRY(GetCurrentSpeedModeFromExtCsd(out_speed_mode, static_cast<const u8 *>(m_work_buffer)));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void MmcDeviceAccessor::PutMmcToSleep() {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX();
|
||||
|
||||
/* If the device isn't awake, we don't need to do anything. */
|
||||
if (!m_mmc_device.IsAwake()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Put the device to sleep. */
|
||||
m_mmc_device.PutToSleep();
|
||||
|
||||
/* If necessary, put the host controller to sleep. */
|
||||
if (m_mmc_device.IsActive()) {
|
||||
BaseDeviceAccessor::GetHostController()->PutToSleep();
|
||||
}
|
||||
}
|
||||
|
||||
void MmcDeviceAccessor::AwakenMmc() {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX();
|
||||
|
||||
/* If the device is awake, we don't need to do anything. */
|
||||
if (m_mmc_device.IsAwake()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Wake the host controller, if we need to.*/
|
||||
if (m_mmc_device.IsActive()) {
|
||||
const Result result = BaseDeviceAccessor::GetHostController()->Awaken();
|
||||
if (R_FAILED(result)) {
|
||||
BaseDeviceAccessor::PushErrorLog(true, "A:%X", result.GetValue());
|
||||
}
|
||||
}
|
||||
|
||||
/* Wake the device. */
|
||||
m_mmc_device.Awaken();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::SelectMmcPartition(MmcPartition part) {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX();
|
||||
|
||||
/* Check that we can access the device. */
|
||||
R_TRY(m_mmc_device.CheckAccessible());
|
||||
|
||||
/* Determine the appropriate SWITCH subcommand. */
|
||||
CommandSwitch cs;
|
||||
switch (part) {
|
||||
case MmcPartition_UserData: cs = CommandSwitch_WritePartitionAccessDefault; break;
|
||||
case MmcPartition_BootPartition1: cs = CommandSwitch_WritePartitionAccessRwBootPartition1; break;
|
||||
case MmcPartition_BootPartition2: cs = CommandSwitch_WritePartitionAccessRwBootPartition2; break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
/* Change partition. */
|
||||
m_current_partition = MmcPartition_Unknown;
|
||||
{
|
||||
R_TRY(this->IssueCommandSwitch(cs));
|
||||
R_TRY(BaseDeviceAccessor::IssueCommandSendStatus());
|
||||
}
|
||||
m_current_partition = part;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::EraseMmc() {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX();
|
||||
|
||||
/* Check that we can access the device. */
|
||||
R_TRY(m_mmc_device.CheckAccessible());
|
||||
|
||||
/* Get the partition capacity. */
|
||||
u32 part_capacity;
|
||||
switch (m_current_partition) {
|
||||
case MmcPartition_UserData:
|
||||
part_capacity = m_mmc_device.GetMemoryCapacity();
|
||||
break;
|
||||
case MmcPartition_BootPartition1:
|
||||
case MmcPartition_BootPartition2:
|
||||
R_TRY(this->GetMmcBootPartitionCapacity(std::addressof(part_capacity)));
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
/* Begin the erase. */
|
||||
R_TRY(this->IssueCommandEraseGroupStart(0));
|
||||
R_TRY(this->IssueCommandEraseGroupEnd(part_capacity - 1));
|
||||
|
||||
/* Issue the erase command, allowing 30 seconds for it to complete. */
|
||||
ManualTimer timer(30000);
|
||||
Result result = this->IssueCommandErase();
|
||||
R_TRY_CATCH(result) {
|
||||
R_CATCH(sdmmc::ResultDataTimeoutError) { /* Data timeout error is acceptable. */ }
|
||||
R_CATCH(sdmmc::ResultCommandCompleteSoftwareTimeout) { /* Command complete software timeout error is acceptable. */ }
|
||||
R_CATCH(sdmmc::ResultBusySoftwareTimeout) { /* Busy software timeout error is acceptable. */ }
|
||||
} R_END_TRY_CATCH;
|
||||
|
||||
/* Wait for the erase to finish. */
|
||||
while (true) {
|
||||
/* Check if we're done. */
|
||||
result = BaseDeviceAccessor::IssueCommandSendStatus();
|
||||
if (R_SUCCEEDED(result)) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Otherwise, check if we should reject the error. */
|
||||
if (!sdmmc::ResultUnexpectedDeviceState::Includes(result)) {
|
||||
R_RETURN(result);
|
||||
}
|
||||
|
||||
/* Check if timeout has been exceeded. */
|
||||
R_UNLESS(timer.Update(), sdmmc::ResultMmcEraseSoftwareTimeout());
|
||||
}
|
||||
|
||||
/* If the partition is user data, check if we need to perform toshiba-specific erase. */
|
||||
if (m_current_partition == MmcPartition_UserData) {
|
||||
u8 cid[DeviceCidSize];
|
||||
m_mmc_device.GetCid(cid, sizeof(cid));
|
||||
if (IsToshibaMmc(cid)) {
|
||||
/* NOTE: Nintendo does not check the result of this operation. */
|
||||
this->CancelToshibaMmcModel();
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::GetMmcBootPartitionCapacity(u32 *out_num_sectors) const {
|
||||
/* Get the capacity from the extended csd. */
|
||||
AMS_ABORT_UNLESS(out_num_sectors != nullptr);
|
||||
R_TRY(this->GetMmcExtendedCsd(m_work_buffer, m_work_buffer_size));
|
||||
|
||||
*out_num_sectors = GetBootPartitionMemoryCapacityFromExtCsd(static_cast<const u8 *>(m_work_buffer));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result MmcDeviceAccessor::GetMmcExtendedCsd(void *dst, size_t dst_size) const {
|
||||
/* Acquire exclusive access to the device. */
|
||||
AMS_SDMMC_LOCK_MMC_DEVICE_MUTEX();
|
||||
|
||||
/* Check that we can access the device. */
|
||||
R_TRY(m_mmc_device.CheckAccessible());
|
||||
|
||||
/* Get the csd. */
|
||||
u8 csd[DeviceCsdSize];
|
||||
m_mmc_device.GetCsd(csd, sizeof(csd));
|
||||
|
||||
/* Check that the card supports ext csd. */
|
||||
R_UNLESS(!IsLessThanSpecification4(csd), sdmmc::ResultMmcNotSupportExtendedCsd());
|
||||
|
||||
/* Get the ext csd. */
|
||||
R_TRY(this->IssueCommandSendExtCsd(dst, dst_size));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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.hpp>
|
||||
#include "sdmmc_base_device_accessor.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
class MmcDevice : public BaseDevice {
|
||||
private:
|
||||
static constexpr u16 Rca = 2;
|
||||
public:
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
virtual os::EventType *GetRemovedEvent() const override {
|
||||
/* Mmc can't be removed. */
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual DeviceType GetDeviceType() const override {
|
||||
return DeviceType_Mmc;
|
||||
}
|
||||
|
||||
virtual u16 GetRca() const override {
|
||||
return Rca;
|
||||
}
|
||||
|
||||
void SetOcrAndHighCapacity(u32 ocr);
|
||||
};
|
||||
|
||||
class MmcDeviceAccessor : public BaseDeviceAccessor {
|
||||
private:
|
||||
MmcDevice m_mmc_device;
|
||||
void *m_work_buffer;
|
||||
size_t m_work_buffer_size;
|
||||
BusWidth m_max_bus_width;
|
||||
SpeedMode m_max_speed_mode;
|
||||
MmcPartition m_current_partition;
|
||||
bool m_is_initialized;
|
||||
private:
|
||||
enum CommandSwitch {
|
||||
CommandSwitch_SetBitsProductionStateAwarenessEnable = 0,
|
||||
CommandSwitch_ClearBitsAutoModeEnable = 1,
|
||||
CommandSwitch_WriteProductionStateAwarenessNormal = 2,
|
||||
CommandSwitch_WriteProductionStateAwarenessPreSolderingWrites = 3,
|
||||
CommandSwitch_WriteProductionStateAwarenessPreSolderingPostWrites = 4,
|
||||
CommandSwitch_SetBitsBkopsEnAutoEn = 5,
|
||||
CommandSwitch_WriteBusWidth1Bit = 6,
|
||||
CommandSwitch_WriteBusWidth4Bit = 7,
|
||||
CommandSwitch_WriteBusWidth8Bit = 8,
|
||||
CommandSwitch_WriteBusWidth8BitDdr = 9,
|
||||
CommandSwitch_WriteHsTimingLegacySpeed = 10,
|
||||
CommandSwitch_WriteHsTimingHighSpeed = 11,
|
||||
CommandSwitch_WriteHsTimingHs200 = 12,
|
||||
CommandSwitch_WriteHsTimingHs400 = 13,
|
||||
CommandSwitch_WritePartitionAccessDefault = 14,
|
||||
CommandSwitch_WritePartitionAccessRwBootPartition1 = 15,
|
||||
CommandSwitch_WritePartitionAccessRwBootPartition2 = 16,
|
||||
};
|
||||
|
||||
static constexpr ALWAYS_INLINE u32 GetCommandSwitchArgument(CommandSwitch cs) {
|
||||
switch (cs) {
|
||||
case CommandSwitch_SetBitsProductionStateAwarenessEnable: return 0x01111000;
|
||||
case CommandSwitch_ClearBitsAutoModeEnable: return 0x02112000;
|
||||
case CommandSwitch_WriteProductionStateAwarenessNormal: return 0x03850000;
|
||||
case CommandSwitch_WriteProductionStateAwarenessPreSolderingWrites: return 0x03850100;
|
||||
case CommandSwitch_WriteProductionStateAwarenessPreSolderingPostWrites: return 0x03850200;
|
||||
case CommandSwitch_SetBitsBkopsEnAutoEn: return 0x01A30200;
|
||||
case CommandSwitch_WriteBusWidth1Bit: return 0x03B70000;
|
||||
case CommandSwitch_WriteBusWidth4Bit: return 0x03B70100;
|
||||
case CommandSwitch_WriteBusWidth8Bit: return 0x03B70200;
|
||||
case CommandSwitch_WriteBusWidth8BitDdr: return 0x03B70600;
|
||||
case CommandSwitch_WriteHsTimingLegacySpeed: return 0x03B90000;
|
||||
case CommandSwitch_WriteHsTimingHighSpeed: return 0x03B90100;
|
||||
case CommandSwitch_WriteHsTimingHs200: return 0x03B90200;
|
||||
case CommandSwitch_WriteHsTimingHs400: return 0x03B90300;
|
||||
case CommandSwitch_WritePartitionAccessDefault: return 0x03B30000;
|
||||
case CommandSwitch_WritePartitionAccessRwBootPartition1: return 0x03B30100;
|
||||
case CommandSwitch_WritePartitionAccessRwBootPartition2: return 0x03B30200;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
private:
|
||||
Result IssueCommandSendOpCond(u32 *out_ocr, BusPower bus_power) const;
|
||||
Result IssueCommandSetRelativeAddr() const;
|
||||
Result IssueCommandSwitch(CommandSwitch cs) const;
|
||||
Result IssueCommandSendExtCsd(void *dst, size_t dst_size) const;
|
||||
Result IssueCommandEraseGroupStart(u32 sector_index) const;
|
||||
Result IssueCommandEraseGroupEnd(u32 sector_index) const;
|
||||
Result IssueCommandErase() const;
|
||||
Result CancelToshibaMmcModel();
|
||||
Result ChangeToReadyState(BusPower bus_power);
|
||||
Result ExtendBusWidth(BusWidth max_bus_width);
|
||||
Result EnableBkopsAuto();
|
||||
Result ChangeToHighSpeed(bool check_before);
|
||||
Result ChangeToHs200();
|
||||
Result ChangeToHs400();
|
||||
Result ExtendBusSpeed(u8 device_type, SpeedMode max_sm);
|
||||
Result StartupMmcDevice(BusWidth max_bw, SpeedMode max_sm, void *wb, size_t wb_size);
|
||||
protected:
|
||||
virtual Result OnActivate() override;
|
||||
virtual Result OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) override;
|
||||
virtual Result ReStartup() override;
|
||||
public:
|
||||
virtual void Initialize() override;
|
||||
virtual void Finalize() override;
|
||||
virtual Result GetSpeedMode(SpeedMode *out_speed_mode) const override;
|
||||
public:
|
||||
explicit MmcDeviceAccessor(IHostController *hc)
|
||||
: BaseDeviceAccessor(hc), m_work_buffer(nullptr), m_work_buffer_size(0),
|
||||
m_max_bus_width(BusWidth_8Bit), m_max_speed_mode(SpeedMode_MmcHs400), m_current_partition(MmcPartition_Unknown),
|
||||
m_is_initialized(false)
|
||||
{
|
||||
/* ... */
|
||||
}
|
||||
|
||||
void SetMmcWorkBuffer(void *wb, size_t wb_size) {
|
||||
m_work_buffer = wb;
|
||||
m_work_buffer_size = wb_size;
|
||||
}
|
||||
|
||||
void PutMmcToSleep();
|
||||
void AwakenMmc();
|
||||
Result SelectMmcPartition(MmcPartition part);
|
||||
Result EraseMmc();
|
||||
Result GetMmcBootPartitionCapacity(u32 *out_num_sectors) const;
|
||||
Result GetMmcExtendedCsd(void *dst, size_t dst_size) const;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
#include "sdmmc_port_gc_asic0.hpp"
|
||||
#include "sdmmc_select_sdmmc_controller.hpp"
|
||||
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
SdmmcControllerForPortGcAsic0 g_gc_asic0_host_controller;
|
||||
GcAsicDeviceAccessor g_gc_asic0_device_accessor(std::addressof(g_gc_asic0_host_controller));
|
||||
|
||||
}
|
||||
|
||||
IHostController *GetHostControllerOfPortGcAsic0() {
|
||||
return std::addressof(g_gc_asic0_host_controller);
|
||||
}
|
||||
|
||||
IDeviceAccessor *GetDeviceAccessorOfPortGcAsic0() {
|
||||
return std::addressof(g_gc_asic0_device_accessor);
|
||||
}
|
||||
|
||||
GcAsicDeviceAccessor *GetGcAsicDeviceAccessorOfPortGcAsic0() {
|
||||
return std::addressof(g_gc_asic0_device_accessor);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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.hpp>
|
||||
#include "sdmmc_i_host_controller.hpp"
|
||||
#include "sdmmc_i_device_accessor.hpp"
|
||||
#include "sdmmc_gc_asic_device_accessor.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
IHostController *GetHostControllerOfPortGcAsic0();
|
||||
IDeviceAccessor *GetDeviceAccessorOfPortGcAsic0();
|
||||
GcAsicDeviceAccessor *GetGcAsicDeviceAccessorOfPortGcAsic0();
|
||||
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
#include "sdmmc_port_mmc0.hpp"
|
||||
#include "sdmmc_select_sdmmc_controller.hpp"
|
||||
#include "sdmmc_base_device_accessor.hpp"
|
||||
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
SdmmcControllerForPortMmc0 g_mmc0_host_controller;
|
||||
MmcDeviceAccessor g_mmc0_device_accessor(std::addressof(g_mmc0_host_controller));
|
||||
|
||||
}
|
||||
|
||||
IHostController *GetHostControllerOfPortMmc0() {
|
||||
return std::addressof(g_mmc0_host_controller);
|
||||
}
|
||||
|
||||
IDeviceAccessor *GetDeviceAccessorOfPortMmc0() {
|
||||
return std::addressof(g_mmc0_device_accessor);
|
||||
}
|
||||
|
||||
MmcDeviceAccessor *GetMmcDeviceAccessorOfPortMmc0() {
|
||||
return std::addressof(g_mmc0_device_accessor);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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.hpp>
|
||||
#include "sdmmc_i_host_controller.hpp"
|
||||
#include "sdmmc_i_device_accessor.hpp"
|
||||
#include "sdmmc_mmc_device_accessor.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
IHostController *GetHostControllerOfPortMmc0();
|
||||
IDeviceAccessor *GetDeviceAccessorOfPortMmc0();
|
||||
MmcDeviceAccessor *GetMmcDeviceAccessorOfPortMmc0();
|
||||
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
#include "sdmmc_port_sd_card0.hpp"
|
||||
#include "sdmmc_select_sdmmc_controller.hpp"
|
||||
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
SdmmcControllerForPortSdCard0 g_sd_card0_host_controller;
|
||||
|
||||
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
|
||||
|
||||
constexpr inline u32 SdCard0DebounceMilliSeconds = 128;
|
||||
DeviceDetector g_sd_card0_detector(gpio::DeviceCode_SdCd, gpio::GpioValue_Low, SdCard0DebounceMilliSeconds);
|
||||
|
||||
SdCardDeviceAccessor g_sd_card0_device_accessor(std::addressof(g_sd_card0_host_controller), std::addressof(g_sd_card0_detector));
|
||||
|
||||
#else
|
||||
|
||||
SdCardDeviceAccessor g_sd_card0_device_accessor(std::addressof(g_sd_card0_host_controller));
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
IHostController *GetHostControllerOfPortSdCard0() {
|
||||
return std::addressof(g_sd_card0_host_controller);
|
||||
}
|
||||
|
||||
IDeviceAccessor *GetDeviceAccessorOfPortSdCard0() {
|
||||
return std::addressof(g_sd_card0_device_accessor);
|
||||
}
|
||||
|
||||
SdCardDeviceAccessor *GetSdCardDeviceAccessorOfPortSdCard0() {
|
||||
return std::addressof(g_sd_card0_device_accessor);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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.hpp>
|
||||
#include "sdmmc_i_host_controller.hpp"
|
||||
#include "sdmmc_i_device_accessor.hpp"
|
||||
#include "sdmmc_sd_card_device_accessor.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
IHostController *GetHostControllerOfPortSdCard0();
|
||||
IDeviceAccessor *GetDeviceAccessorOfPortSdCard0();
|
||||
SdCardDeviceAccessor *GetSdCardDeviceAccessorOfPortSdCard0();
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,222 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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.hpp>
|
||||
#include "sdmmc_base_device_accessor.hpp"
|
||||
|
||||
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
|
||||
#include "sdmmc_device_detector.hpp"
|
||||
#endif
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
class SdCardDevice : public BaseDevice {
|
||||
private:
|
||||
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
|
||||
mutable os::EventType m_removed_event;
|
||||
#endif
|
||||
u16 m_rca;
|
||||
bool m_is_valid_rca;
|
||||
bool m_is_uhs_i_mode;
|
||||
public:
|
||||
SdCardDevice() : m_rca(0) {
|
||||
this->OnDeactivate();
|
||||
}
|
||||
|
||||
virtual void Deactivate() override {
|
||||
this->OnDeactivate();
|
||||
BaseDevice::Deactivate();
|
||||
}
|
||||
|
||||
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
|
||||
virtual os::EventType *GetRemovedEvent() const override {
|
||||
return std::addressof(m_removed_event);
|
||||
}
|
||||
#elif defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
virtual os::EventType *GetRemovedEvent() const override {
|
||||
/* Mmc can't be removed. */
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual DeviceType GetDeviceType() const override {
|
||||
return DeviceType_SdCard;
|
||||
}
|
||||
|
||||
virtual u16 GetRca() const override {
|
||||
AMS_ABORT_UNLESS(m_is_valid_rca);
|
||||
return m_rca;
|
||||
}
|
||||
|
||||
void OnDeactivate() {
|
||||
m_is_valid_rca = false;
|
||||
m_is_uhs_i_mode = false;
|
||||
}
|
||||
|
||||
void SetRca(u16 v) {
|
||||
m_rca = v;
|
||||
m_is_valid_rca = true;
|
||||
}
|
||||
|
||||
void SetOcrAndHighCapacity(u32 ocr);
|
||||
|
||||
void SetUhsIMode(bool en) {
|
||||
m_is_uhs_i_mode = en;
|
||||
}
|
||||
|
||||
bool IsUhsIMode() const {
|
||||
return m_is_uhs_i_mode;
|
||||
}
|
||||
};
|
||||
|
||||
enum SdCardApplicationCommandIndex : std::underlying_type<CommandIndex>::type {
|
||||
SdApplicationCommandIndex_SetBusWidth = 6,
|
||||
|
||||
SdApplicationCommandIndex_SdStatus = 13,
|
||||
|
||||
SdApplicationCommandIndex_SendNumWriteBlocks = 22,
|
||||
SdApplicationCommandIndex_SetWriteBlockEraseCount = 23,
|
||||
|
||||
SdApplicationCommandIndex_SdSendOpCond = 41,
|
||||
SdApplicationCommandIndex_SetClearCardDetect = 42,
|
||||
|
||||
SdApplicationCommandIndex_SendScr = 51,
|
||||
};
|
||||
|
||||
enum SwitchFunctionAccessMode {
|
||||
SwitchFunctionAccessMode_Default = 0,
|
||||
SwitchFunctionAccessMode_HighSpeed = 1,
|
||||
SwitchFunctionAccessMode_Sdr50 = 2,
|
||||
SwitchFunctionAccessMode_Sdr104 = 3,
|
||||
SwitchFunctionAccessMode_Ddr50 = 4,
|
||||
};
|
||||
|
||||
class SdCardDeviceAccessor : public BaseDeviceAccessor {
|
||||
private:
|
||||
SdCardDevice m_sd_card_device;
|
||||
void *m_work_buffer;
|
||||
size_t m_work_buffer_size;
|
||||
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
|
||||
DeviceDetector *m_sd_card_detector;
|
||||
#endif
|
||||
BusWidth m_max_bus_width;
|
||||
SpeedMode m_max_speed_mode;
|
||||
bool m_is_initialized;
|
||||
private:
|
||||
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
|
||||
void RemovedCallback();
|
||||
|
||||
static void RemovedCallbackEntry(void *arg) {
|
||||
static_cast<SdCardDeviceAccessor *>(arg)->RemovedCallback();
|
||||
}
|
||||
#endif
|
||||
|
||||
Result IssueCommandSendRelativeAddr(u16 *out_rca) const;
|
||||
Result IssueCommandSendIfCond() const;
|
||||
Result IssueCommandCheckSupportedFunction(void *dst, size_t dst_size) const;
|
||||
Result IssueCommandSwitchAccessMode(void *dst, size_t dst_size, bool set_function, SwitchFunctionAccessMode access_mode) const;
|
||||
Result IssueCommandVoltageSwitch() const;
|
||||
Result IssueCommandAppCmd(DeviceState expected_state, u32 ignore_mask = 0) const;
|
||||
Result IssueCommandSetBusWidth4Bit() const;
|
||||
Result IssueCommandSdStatus(void *dst, size_t dst_size) const;
|
||||
Result IssueCommandSendOpCond(u32 *out_ocr, bool spec_under_2, bool uhs_i_supported) const;
|
||||
Result IssueCommandClearCardDetect() const;
|
||||
Result IssueCommandSendScr(void *dst, size_t dst_size) const;
|
||||
|
||||
Result EnterUhsIMode();
|
||||
Result ChangeToReadyState(bool spec_under_2, bool uhs_i_supported);
|
||||
Result ChangeToStbyStateAndGetRca();
|
||||
Result SetMemoryCapacity(const void *csd);
|
||||
Result GetScr(void *dst, size_t dst_size) const;
|
||||
Result ExtendBusWidth(BusWidth max_bw, u8 sd_bw);
|
||||
Result SwitchAccessMode(SwitchFunctionAccessMode access_mode, void *wb, size_t wb_size);
|
||||
Result ExtendBusSpeedAtUhsIMode(SpeedMode max_sm, void *wb, size_t wb_size);
|
||||
Result ExtendBusSpeedAtNonUhsIMode(SpeedMode max_sm, bool spec_under_1_1, void *wb, size_t wb_size);
|
||||
Result GetSdStatus(void *dst, size_t dst_size) const;
|
||||
Result StartupSdCardDevice(BusWidth max_bw, SpeedMode max_sm, void *wb, size_t wb_size);
|
||||
|
||||
void TryDisconnectDat3PullUpResistor() const;
|
||||
protected:
|
||||
virtual Result OnActivate() override;
|
||||
virtual Result OnReadWrite(u32 sector_index, u32 num_sectors, void *buf, size_t buf_size, bool is_read) override;
|
||||
virtual Result ReStartup() override;
|
||||
public:
|
||||
virtual void Initialize() override;
|
||||
virtual void Finalize() override;
|
||||
virtual Result Activate() override;
|
||||
virtual Result GetSpeedMode(SpeedMode *out_speed_mode) const override;
|
||||
public:
|
||||
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
|
||||
explicit SdCardDeviceAccessor(IHostController *hc, DeviceDetector *dd) : BaseDeviceAccessor(hc), m_sd_card_detector(dd)
|
||||
#else
|
||||
explicit SdCardDeviceAccessor(IHostController *hc) : BaseDeviceAccessor(hc)
|
||||
#endif
|
||||
{
|
||||
m_work_buffer = nullptr;
|
||||
m_work_buffer_size = 0;
|
||||
m_max_bus_width = BusWidth_4Bit;
|
||||
m_max_speed_mode = SpeedMode_SdCardSdr104;
|
||||
m_is_initialized = false;
|
||||
}
|
||||
|
||||
void SetSdCardWorkBuffer(void *wb, size_t wb_size) {
|
||||
m_work_buffer = wb;
|
||||
m_work_buffer_size = wb_size;
|
||||
}
|
||||
|
||||
void PutSdCardToSleep();
|
||||
void AwakenSdCard();
|
||||
Result GetSdCardProtectedAreaCapacity(u32 *out_num_sectors) const;
|
||||
Result GetSdCardScr(void *dst, size_t dst_size) const;
|
||||
Result GetSdCardSwitchFunctionStatus(void *dst, size_t dst_size, SdCardSwitchFunction switch_function) const;
|
||||
Result GetSdCardCurrentConsumption(u16 *out_current_consumption, SpeedMode speed_mode) const;
|
||||
Result GetSdCardSdStatus(void *dst, size_t dst_size) const;
|
||||
|
||||
bool IsSdCardInserted() {
|
||||
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
|
||||
return m_sd_card_detector->IsInserted();
|
||||
#else
|
||||
AMS_ABORT("IsSdCardInserted without SdCardDetector");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IsSdCardRemoved() {
|
||||
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
|
||||
return m_sd_card_device.IsRemoved();
|
||||
#else
|
||||
AMS_ABORT("IsSdCardRemoved without SdCardDetector");
|
||||
#endif
|
||||
}
|
||||
|
||||
void RegisterSdCardDetectionEventCallback(DeviceDetectionEventCallback cb, void *arg) {
|
||||
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
|
||||
return m_sd_card_detector->RegisterDetectionEventCallback(cb, arg);
|
||||
#else
|
||||
AMS_UNUSED(cb, arg);
|
||||
AMS_ABORT("RegisterSdCardDetectionEventCallback without SdCardDetector");
|
||||
#endif
|
||||
}
|
||||
|
||||
void UnregisterSdCardDetectionEventCallback() {
|
||||
#if defined(AMS_SDMMC_USE_SD_CARD_DETECTOR)
|
||||
return m_sd_card_detector->UnregisterDetectionEventCallback();
|
||||
#else
|
||||
AMS_ABORT("UnregisterSdCardDetectionEventCallback without SdCardDetector");
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,188 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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.hpp>
|
||||
#include "sdmmc_i_host_controller.hpp"
|
||||
#include "sdmmc_sd_host_standard_registers.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
class SdHostStandardController : public IHostController {
|
||||
protected:
|
||||
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
|
||||
struct BufferInfo {
|
||||
uintptr_t buffer_address;
|
||||
size_t buffer_size;
|
||||
dd::DeviceVirtualAddress buffer_device_virtual_address;
|
||||
};
|
||||
|
||||
static constexpr inline auto NumBufferInfos = 3;
|
||||
#endif
|
||||
protected:
|
||||
SdHostStandardRegisters *m_registers;
|
||||
|
||||
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
|
||||
BufferInfo m_buffer_infos[NumBufferInfos];
|
||||
#endif
|
||||
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
os::MultiWaitType m_multi_wait;
|
||||
os::InterruptEventType *m_interrupt_event;
|
||||
os::MultiWaitHolderType m_interrupt_event_holder;
|
||||
os::EventType *m_removed_event;
|
||||
os::MultiWaitHolderType m_removed_event_holder;
|
||||
#endif
|
||||
|
||||
u64 m_next_sdma_address;
|
||||
u32 m_check_transfer_interval_ms;
|
||||
|
||||
u32 m_device_clock_frequency_khz;
|
||||
bool m_is_power_saving_enable;
|
||||
bool m_is_device_clock_enable;
|
||||
|
||||
ResponseType m_last_response_type;
|
||||
u32 m_last_response[4];
|
||||
u32 m_last_stop_transmission_response;
|
||||
protected:
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
void PreSetInterruptEvent(os::InterruptEventType *ie) {
|
||||
m_interrupt_event = ie;
|
||||
}
|
||||
|
||||
bool IsRemoved() const {
|
||||
return m_removed_event != nullptr && os::TryWaitEvent(m_removed_event);
|
||||
}
|
||||
#endif
|
||||
|
||||
void SetDeviceClockFrequencyKHz(u32 khz) {
|
||||
m_device_clock_frequency_khz = khz;
|
||||
}
|
||||
|
||||
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
|
||||
void ResetBufferInfos();
|
||||
dd::DeviceVirtualAddress GetDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size);
|
||||
#endif
|
||||
|
||||
void EnsureControl();
|
||||
Result EnableInternalClock();
|
||||
void SetBusPower(BusPower bus_power);
|
||||
|
||||
void EnableInterruptStatus();
|
||||
void DisableInterruptStatus();
|
||||
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
Result WaitInterrupt(u32 timeout_ms);
|
||||
void ClearInterrupt();
|
||||
#endif
|
||||
|
||||
void SetTransfer(u32 *out_num_transferred_blocks, const TransferData *xfer_data);
|
||||
void SetTransferForTuning();
|
||||
|
||||
void SetCommand(const Command *command, bool has_xfer_data);
|
||||
void SetCommandForTuning(u32 command_index);
|
||||
|
||||
Result ResetCmdDatLine();
|
||||
Result AbortTransaction();
|
||||
Result WaitWhileCommandInhibit(bool has_dat);
|
||||
Result CheckAndClearInterruptStatus(volatile u16 *out_normal_int_status, u16 wait_mask);
|
||||
Result WaitCommandComplete();
|
||||
Result WaitTransferComplete();
|
||||
Result WaitWhileBusy();
|
||||
|
||||
void GetResponse(u32 *out_response, size_t response_size, ResponseType response_type) const;
|
||||
|
||||
Result IssueCommandWithDeviceClock(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks);
|
||||
Result IssueStopTransmissionCommandWithDeviceClock(u32 *out_response);
|
||||
|
||||
ALWAYS_INLINE Result CheckRemoved() {
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
R_UNLESS(!this->IsRemoved(), sdmmc::ResultDeviceRemoved());
|
||||
#endif
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
public:
|
||||
SdHostStandardController(dd::PhysicalAddress registers_phys_addr, size_t registers_size);
|
||||
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
virtual void PreSetRemovedEvent(os::EventType *e) override {
|
||||
m_removed_event = e;
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual void Initialize() override;
|
||||
virtual void Finalize() override;
|
||||
|
||||
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
|
||||
virtual void RegisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) override;
|
||||
virtual void UnregisterDeviceVirtualAddress(uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) override;
|
||||
#endif
|
||||
|
||||
virtual void SetWorkBuffer(void *wb, size_t wb_size) override;
|
||||
|
||||
virtual Result SwitchToSdr12() override {
|
||||
AMS_ABORT("SwitchToSdr12 not supported\n");
|
||||
}
|
||||
|
||||
virtual BusPower GetBusPower() const override;
|
||||
|
||||
virtual void SetBusWidth(BusWidth bus_width) override;
|
||||
virtual BusWidth GetBusWidth() const override;
|
||||
|
||||
virtual u32 GetDeviceClockFrequencyKHz() const override {
|
||||
return m_device_clock_frequency_khz;
|
||||
}
|
||||
|
||||
virtual void SetPowerSaving(bool en) override;
|
||||
virtual bool IsPowerSavingEnable() const override {
|
||||
return m_is_power_saving_enable;
|
||||
}
|
||||
|
||||
virtual void EnableDeviceClock() override;
|
||||
virtual void DisableDeviceClock() override;
|
||||
virtual bool IsDeviceClockEnable() const override {
|
||||
return m_is_device_clock_enable;
|
||||
}
|
||||
|
||||
virtual u32 GetMaxTransferNumBlocks() const override {
|
||||
return SdHostStandardRegisters::BlockCountMax;
|
||||
}
|
||||
|
||||
virtual void ChangeCheckTransferInterval(u32 ms) override;
|
||||
virtual void SetDefaultCheckTransferInterval() override;
|
||||
|
||||
virtual Result IssueCommand(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks) override;
|
||||
virtual Result IssueStopTransmissionCommand(u32 *out_response) override;
|
||||
|
||||
virtual void GetLastResponse(u32 *out_response, size_t response_size, ResponseType response_type) const override;
|
||||
virtual void GetLastStopTransmissionResponse(u32 *out_response, size_t response_size) const override;
|
||||
|
||||
virtual bool IsSupportedTuning() const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual Result Tuning(SpeedMode speed_mode, u32 command_index) override {
|
||||
AMS_UNUSED(speed_mode, command_index);
|
||||
AMS_ABORT("Tuning not supported\n");
|
||||
}
|
||||
|
||||
virtual void SaveTuningStatusForHs400() override {
|
||||
AMS_ABORT("SaveTuningStatusForHs400 not supported\n");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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.hpp>
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
struct SdHostStandardRegisters {
|
||||
volatile uint32_t dma_address;
|
||||
volatile uint16_t block_size;
|
||||
volatile uint16_t block_count;
|
||||
volatile uint32_t argument;
|
||||
volatile uint16_t transfer_mode;
|
||||
volatile uint16_t command;
|
||||
volatile uint32_t response[0x4];
|
||||
volatile uint32_t buffer;
|
||||
volatile uint32_t present_state;
|
||||
volatile uint8_t host_control;
|
||||
volatile uint8_t power_control;
|
||||
volatile uint8_t block_gap_control;
|
||||
volatile uint8_t wake_up_control;
|
||||
volatile uint16_t clock_control;
|
||||
volatile uint8_t timeout_control;
|
||||
volatile uint8_t software_reset;
|
||||
volatile uint16_t normal_int_status;
|
||||
volatile uint16_t error_int_status;
|
||||
volatile uint16_t normal_int_enable;
|
||||
volatile uint16_t error_int_enable;
|
||||
volatile uint16_t normal_signal_enable;
|
||||
volatile uint16_t error_signal_enable;
|
||||
volatile uint16_t acmd12_err;
|
||||
volatile uint16_t host_control2;
|
||||
volatile uint32_t capabilities;
|
||||
volatile uint32_t capabilities_1;
|
||||
volatile uint32_t max_current;
|
||||
volatile uint32_t _0x4c;
|
||||
volatile uint16_t set_acmd12_error;
|
||||
volatile uint16_t set_int_error;
|
||||
volatile uint8_t adma_error;
|
||||
volatile uint8_t _0x56[0x3];
|
||||
volatile uint32_t adma_address;
|
||||
volatile uint32_t upper_adma_address;
|
||||
volatile uint16_t preset_for_init;
|
||||
volatile uint16_t preset_for_default;
|
||||
volatile uint16_t preset_for_high;
|
||||
volatile uint16_t preset_for_sdr12;
|
||||
volatile uint16_t preset_for_sdr25;
|
||||
volatile uint16_t preset_for_sdr50;
|
||||
volatile uint16_t preset_for_sdr104;
|
||||
volatile uint16_t preset_for_ddr50;
|
||||
volatile uint32_t _0x70[0x23];
|
||||
volatile uint16_t slot_int_status;
|
||||
volatile uint16_t host_version;
|
||||
|
||||
static constexpr inline u16 BlockCountMax = 0xFFFF;
|
||||
};
|
||||
static_assert(sizeof(SdHostStandardRegisters) == 0x100);
|
||||
|
||||
constexpr inline size_t SdmaBufferBoundary = 512_KB;
|
||||
|
||||
#define SD_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (SD_HOST_STANDARD, NAME)
|
||||
#define SD_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (SD_HOST_STANDARD, NAME, VALUE)
|
||||
#define SD_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (SD_HOST_STANDARD, NAME, ENUM)
|
||||
#define SD_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(SD_HOST_STANDARD, NAME, __COND__, TRUE_ENUM, FALSE_ENUM)
|
||||
|
||||
#define DEFINE_SD_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (SD_HOST_STANDARD, NAME, __OFFSET__, __WIDTH__)
|
||||
#define DEFINE_SD_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (SD_HOST_STANDARD, NAME, __OFFSET__, ZERO, ONE)
|
||||
#define DEFINE_SD_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (SD_HOST_STANDARD, NAME, __OFFSET__, ZERO, ONE, TWO, THREE)
|
||||
#define DEFINE_SD_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(SD_HOST_STANDARD, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN)
|
||||
#define DEFINE_SD_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (SD_HOST_STANDARD, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN)
|
||||
|
||||
DEFINE_SD_REG(BLOCK_SIZE_TRANSFER_BLOCK_SIZE, 0, 12);
|
||||
DEFINE_SD_REG_THREE_BIT_ENUM(BLOCK_SIZE_SDMA_BUFFER_BOUNDARY, 12, 4_KB, 8_KB, 16_KB, 32_KB, 64_KB, 128_KB, 256_KB, 512_KB);
|
||||
constexpr inline size_t SdHostStandardBlockSizeTransferBlockSizeMax = 0xFFF;
|
||||
|
||||
DEFINE_SD_REG_BIT_ENUM(TRANSFER_MODE_DMA_ENABLE, 0, DISABLE, ENABLE);
|
||||
DEFINE_SD_REG_BIT_ENUM(TRANSFER_MODE_BLOCK_COUNT_ENABLE, 1, DISABLE, ENABLE);
|
||||
DEFINE_SD_REG_TWO_BIT_ENUM(TRANSFER_MODE_AUTO_CMD_ENABLE, 2, DISABLE, CMD12_ENABLE, CMD23_ENABLE, AUTO_SELECT);
|
||||
DEFINE_SD_REG_BIT_ENUM(TRANSFER_MODE_DATA_TRANSFER_DIRECTION, 4, WRITE, READ);
|
||||
DEFINE_SD_REG_BIT_ENUM(TRANSFER_MODE_MULTI_BLOCK_SELECT, 5, SINGLE_BLOCK, MULTI_BLOCK);
|
||||
|
||||
DEFINE_SD_REG_TWO_BIT_ENUM(COMMAND_RESPONSE_TYPE, 0, NO_RESPONSE, RESPONSE_LENGTH_136, RESPONSE_LENGTH_48, RESPONSE_LENGTH_48_CHECK_BUSY_AFTER_RESPONSE);
|
||||
DEFINE_SD_REG_BIT_ENUM(COMMAND_CRC_CHECK, 3, DISABLE, ENABLE);
|
||||
DEFINE_SD_REG_BIT_ENUM(COMMAND_INDEX_CHECK, 4, DISABLE, ENABLE);
|
||||
DEFINE_SD_REG_BIT_ENUM(COMMAND_DATA_PRESENT, 5, NO_DATA_PRESENT, DATA_PRESENT);
|
||||
DEFINE_SD_REG(COMMAND_COMMAND_INDEX, 8, 6);
|
||||
constexpr inline size_t SdHostStandardCommandIndexMax = 0x3F;
|
||||
|
||||
DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_COMMAND_INHIBIT_CMD, 0, READY, NOT_READY);
|
||||
DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_COMMAND_INHIBIT_DAT, 1, READY, NOT_READY);
|
||||
DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_DAT0_LINE_SIGNAL_LEVEL, 20, LOW, HIGH);
|
||||
DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_DAT1_LINE_SIGNAL_LEVEL, 21, LOW, HIGH);
|
||||
DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_DAT2_LINE_SIGNAL_LEVEL, 22, LOW, HIGH);
|
||||
DEFINE_SD_REG_BIT_ENUM(PRESENT_STATE_DAT3_LINE_SIGNAL_LEVEL, 23, LOW, HIGH);
|
||||
DEFINE_SD_REG(PRESENT_STATE_DAT0_3_LINE_SIGNAL_LEVEL, 20, 4);
|
||||
|
||||
DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL_DATA_TRANSFER_WIDTH, 1, ONE_BIT, FOUR_BIT);
|
||||
DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL_HIGH_SPEED_ENABLE, 2, NORMAL_SPEED, HIGH_SPEED);
|
||||
DEFINE_SD_REG_TWO_BIT_ENUM(HOST_CONTROL_DMA_SELECT, 3, SDMA, RESERVED1, ADMA2, ADMA2_OR_ADMA3);
|
||||
DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL_EXTENDED_DATA_TRANSFER_WIDTH, 5, USE_DATA_TRANSFER_WIDTH, EIGHT_BIT);
|
||||
|
||||
DEFINE_SD_REG_BIT_ENUM(POWER_CONTROL_SD_BUS_POWER_FOR_VDD1, 0, OFF, ON);
|
||||
DEFINE_SD_REG_THREE_BIT_ENUM(POWER_CONTROL_SD_BUS_VOLTAGE_SELECT_FOR_VDD1, 1, RESERVED0, RESERVED1, RESERVED2, RESERVED3, RESERVED4, 1_8V, 3_0V, 3_3V);
|
||||
|
||||
DEFINE_SD_REG_BIT_ENUM(CLOCK_CONTROL_INTERNAL_CLOCK_ENABLE, 0, STOP, OSCILLATE);
|
||||
DEFINE_SD_REG_BIT_ENUM(CLOCK_CONTROL_INTERNAL_CLOCK_STABLE, 1, NOT_READY, READY);
|
||||
DEFINE_SD_REG_BIT_ENUM(CLOCK_CONTROL_SD_CLOCK_ENABLE, 2, DISABLE, ENABLE);
|
||||
DEFINE_SD_REG_BIT_ENUM(CLOCK_CONTROL_CLOCK_GENERATOR_SELECT, 5, DIVIDED_CLOCK, PROGRAMMABLE_CLOCK);
|
||||
DEFINE_SD_REG(CLOCK_CONTROL_UPPER_BITS_OF_SDCLK_FREQUENCY_SELECT, 6, 2);
|
||||
DEFINE_SD_REG(CLOCK_CONTROL_SDCLK_FREQUENCY_SELECT, 8, 8);
|
||||
|
||||
DEFINE_SD_REG(TIMEOUT_CONTROL_DATA_TIMEOUT_COUNTER, 0, 4);
|
||||
|
||||
DEFINE_SD_REG_BIT_ENUM(SOFTWARE_RESET_FOR_ALL, 0, WORK, RESET);
|
||||
DEFINE_SD_REG_BIT_ENUM(SOFTWARE_RESET_FOR_CMD, 1, WORK, RESET);
|
||||
DEFINE_SD_REG_BIT_ENUM(SOFTWARE_RESET_FOR_DAT, 2, WORK, RESET);
|
||||
|
||||
DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_STATUS_COMMAND_COMPLETE, 0, NOT_COMPLETE, COMPLETE);
|
||||
DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_STATUS_TRANSFER_COMPLETE, 1, NOT_COMPLETE, COMPLETE);
|
||||
DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_STATUS_DMA_INTERRUPT, 3, NOT_GENERATED, GENERATED);
|
||||
DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_STATUS_ERROR_INTERRUPT, 15, NO_ERROR, ERROR);
|
||||
|
||||
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_TIMEOUT, 0, NO_ERROR, ERROR);
|
||||
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_CRC, 1, NO_ERROR, ERROR);
|
||||
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_END_BIT, 2, NO_ERROR, ERROR);
|
||||
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_COMMAND_INDEX, 3, NO_ERROR, ERROR);
|
||||
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_DATA_TIMEOUT, 4, NO_ERROR, ERROR);
|
||||
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_DATA_CRC, 5, NO_ERROR, ERROR);
|
||||
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_DATA_END_BIT, 6, NO_ERROR, ERROR);
|
||||
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_STATUS_AUTO_CMD, 8, NO_ERROR, ERROR);
|
||||
|
||||
DEFINE_SD_REG_BIT_ENUM(AUTO_CMD_ERROR_AUTO_CMD_TIMEOUT, 1, NO_ERROR, ERROR);
|
||||
DEFINE_SD_REG_BIT_ENUM(AUTO_CMD_ERROR_AUTO_CMD_CRC, 2, NO_ERROR, ERROR);
|
||||
DEFINE_SD_REG_BIT_ENUM(AUTO_CMD_ERROR_AUTO_CMD_END_BIT, 3, NO_ERROR, ERROR);
|
||||
DEFINE_SD_REG_BIT_ENUM(AUTO_CMD_ERROR_AUTO_CMD_INDEX, 4, NO_ERROR, ERROR);
|
||||
|
||||
DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_COMMAND_COMPLETE, 0, MASKED, ENABLED);
|
||||
DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_TRANSFER_COMPLETE, 1, MASKED, ENABLED);
|
||||
DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_DMA_INTERRUPT, 3, MASKED, ENABLED);
|
||||
DEFINE_SD_REG_BIT_ENUM(NORMAL_INTERRUPT_BUFFER_READ_READY, 5, MASKED, ENABLED);
|
||||
|
||||
#define SD_HOST_STANDARD_NORMAL_INTERRUPT_ENABLE_ISSUE_COMMAND(__ENUM__) \
|
||||
SD_REG_BITS_ENUM(NORMAL_INTERRUPT_COMMAND_COMPLETE, __ENUM__), \
|
||||
SD_REG_BITS_ENUM(NORMAL_INTERRUPT_TRANSFER_COMPLETE, __ENUM__), \
|
||||
SD_REG_BITS_ENUM(NORMAL_INTERRUPT_DMA_INTERRUPT, __ENUM__)
|
||||
|
||||
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_COMMAND_TIMEOUT_ERROR, 0, MASKED, ENABLED);
|
||||
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_COMMAND_CRC_ERROR, 1, MASKED, ENABLED);
|
||||
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_COMMAND_END_BIT_ERROR, 2, MASKED, ENABLED);
|
||||
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_COMMAND_INDEX_ERROR, 3, MASKED, ENABLED);
|
||||
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_DATA_TIMEOUT_ERROR, 4, MASKED, ENABLED);
|
||||
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_DATA_CRC_ERROR, 5, MASKED, ENABLED);
|
||||
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_DATA_END_BIT_ERROR, 6, MASKED, ENABLED);
|
||||
DEFINE_SD_REG_BIT_ENUM(ERROR_INTERRUPT_AUTO_CMD_ERROR, 8, MASKED, ENABLED);
|
||||
|
||||
#define SD_HOST_STANDARD_ERROR_INTERRUPT_ENABLE_ISSUE_COMMAND(__ENUM__) \
|
||||
SD_REG_BITS_ENUM(ERROR_INTERRUPT_COMMAND_TIMEOUT_ERROR, __ENUM__), \
|
||||
SD_REG_BITS_ENUM(ERROR_INTERRUPT_COMMAND_CRC_ERROR, __ENUM__), \
|
||||
SD_REG_BITS_ENUM(ERROR_INTERRUPT_COMMAND_END_BIT_ERROR, __ENUM__), \
|
||||
SD_REG_BITS_ENUM(ERROR_INTERRUPT_COMMAND_INDEX_ERROR, __ENUM__), \
|
||||
SD_REG_BITS_ENUM(ERROR_INTERRUPT_DATA_TIMEOUT_ERROR, __ENUM__), \
|
||||
SD_REG_BITS_ENUM(ERROR_INTERRUPT_DATA_CRC_ERROR, __ENUM__), \
|
||||
SD_REG_BITS_ENUM(ERROR_INTERRUPT_DATA_END_BIT_ERROR, __ENUM__), \
|
||||
SD_REG_BITS_ENUM(ERROR_INTERRUPT_AUTO_CMD_ERROR, __ENUM__)
|
||||
|
||||
|
||||
DEFINE_SD_REG_THREE_BIT_ENUM(HOST_CONTROL2_UHS_MODE_SELECT, 0, SDR12, SDR25, SDR50, SDR104, DDR50, HS400, RSVD6, UHS_II);
|
||||
|
||||
constexpr inline auto SD_HOST_STANDARD_HOST_CONTROL2_UHS_MODE_SELECT_HS200 = SD_HOST_STANDARD_HOST_CONTROL2_UHS_MODE_SELECT_SDR104;
|
||||
|
||||
DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_1_8V_SIGNALING_ENABLE, 3, 3_3V_SIGNALING, 1_8V_SIGNALING);
|
||||
DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_EXECUTE_TUNING, 6, TUNING_COMPLETED, EXECUTE_TUNING);
|
||||
DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_SAMPLING_CLOCK, 7, USING_FIXED_CLOCK, USING_TUNED_CLOCK);
|
||||
DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_HOST_VERSION_4_ENABLE, 12, VERSION_300_COMPATIBLE, VERSION_4);
|
||||
DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_64_BIT_ADDRESSING, 13, 32_BIT_ADDRESSING, 64_BIT_ADDRESSING);
|
||||
DEFINE_SD_REG_BIT_ENUM(HOST_CONTROL2_PRESET_VALUE_ENABLE, 15, HOST_DRIVER, AUTOMATIC_SELECTION);
|
||||
|
||||
DEFINE_SD_REG_BIT_ENUM(CAPABILITIES_64_BIT_SYSTEM_ADDRESS_SUPPORT_FOR_V3, 28, NOT_SUPPORTED, SUPPORTED);
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,682 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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.hpp>
|
||||
#include "sdmmc_sd_host_standard_controller.hpp"
|
||||
#include "sdmmc_clock_reset_controller.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
bool IsSocMariko();
|
||||
|
||||
constexpr inline size_t SdmmcRegistersSize = 0x200;
|
||||
|
||||
constexpr inline dd::PhysicalAddress ApbMiscRegistersPhysicalAddress = UINT64_C(0x70000000);
|
||||
constexpr inline size_t ApbMiscRegistersSize = 16_KB;
|
||||
|
||||
class SdmmcController : public SdHostStandardController {
|
||||
private:
|
||||
struct SdmmcRegisters {
|
||||
/* Standard registers. */
|
||||
volatile SdHostStandardRegisters sd_host_standard_registers;
|
||||
|
||||
/* Vendor specific registers */
|
||||
volatile uint32_t vendor_clock_cntrl;
|
||||
volatile uint32_t vendor_sys_sw_cntrl;
|
||||
volatile uint32_t vendor_err_intr_status;
|
||||
volatile uint32_t vendor_cap_overrides;
|
||||
volatile uint32_t vendor_boot_cntrl;
|
||||
volatile uint32_t vendor_boot_ack_timeout;
|
||||
volatile uint32_t vendor_boot_dat_timeout;
|
||||
volatile uint32_t vendor_debounce_count;
|
||||
volatile uint32_t vendor_misc_cntrl;
|
||||
volatile uint32_t max_current_override;
|
||||
volatile uint32_t max_current_override_hi;
|
||||
volatile uint32_t _0x12c[0x20];
|
||||
volatile uint32_t vendor_io_trim_cntrl;
|
||||
|
||||
/* Start of sdmmc2/sdmmc4 only */
|
||||
volatile uint32_t vendor_dllcal_cfg;
|
||||
volatile uint32_t vendor_dll_ctrl0;
|
||||
volatile uint32_t vendor_dll_ctrl1;
|
||||
volatile uint32_t vendor_dllcal_cfg_sta;
|
||||
/* End of sdmmc2/sdmmc4 only */
|
||||
|
||||
volatile uint32_t vendor_tuning_cntrl0;
|
||||
volatile uint32_t vendor_tuning_cntrl1;
|
||||
volatile uint32_t vendor_tuning_status0;
|
||||
volatile uint32_t vendor_tuning_status1;
|
||||
volatile uint32_t vendor_clk_gate_hysteresis_count;
|
||||
volatile uint32_t vendor_preset_val0;
|
||||
volatile uint32_t vendor_preset_val1;
|
||||
volatile uint32_t vendor_preset_val2;
|
||||
volatile uint32_t sdmemcomppadctrl;
|
||||
volatile uint32_t auto_cal_config;
|
||||
volatile uint32_t auto_cal_interval;
|
||||
volatile uint32_t auto_cal_status;
|
||||
volatile uint32_t io_spare;
|
||||
volatile uint32_t sdmmca_mccif_fifoctrl;
|
||||
volatile uint32_t timeout_wcoal_sdmmca;
|
||||
volatile uint32_t _0x1fc;
|
||||
};
|
||||
static_assert(sizeof(SdmmcRegisters) == SdmmcRegistersSize);
|
||||
private:
|
||||
SdmmcRegisters *m_sdmmc_registers;
|
||||
bool m_is_shutdown;
|
||||
bool m_is_awake;
|
||||
SpeedMode m_current_speed_mode;
|
||||
BusPower m_bus_power_before_sleep;
|
||||
BusWidth m_bus_width_before_sleep;
|
||||
SpeedMode m_speed_mode_before_sleep;
|
||||
u8 m_tap_value_before_sleep;
|
||||
bool m_is_powersaving_enable_before_sleep;
|
||||
u8 m_tap_value_for_hs_400;
|
||||
bool m_is_valid_tap_value_for_hs_400;
|
||||
Result m_drive_strength_calibration_status;
|
||||
private:
|
||||
void ReleaseReset(SpeedMode speed_mode);
|
||||
void AssertReset();
|
||||
Result StartupCore(BusPower bus_power);
|
||||
Result SetClockTrimmer(SpeedMode speed_mode, u8 tap_value);
|
||||
u8 GetCurrentTapValue();
|
||||
Result CalibrateDll();
|
||||
Result SetSpeedModeWithTapValue(SpeedMode speed_mode, u8 tap_value);
|
||||
Result IssueTuningCommand(u32 command_index);
|
||||
protected:
|
||||
void SetDriveCodeOffsets(BusPower bus_power);
|
||||
void CalibrateDriveStrength(BusPower bus_power);
|
||||
|
||||
virtual void SetPad() = 0;
|
||||
|
||||
virtual ClockResetController::Module GetClockResetModule() const = 0;
|
||||
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
virtual int GetInterruptNumber() const = 0;
|
||||
virtual os::InterruptEventType *GetInterruptEvent() const = 0;
|
||||
#endif
|
||||
|
||||
virtual bool IsNeedPeriodicDriveStrengthCalibration() = 0;
|
||||
virtual void ClearPadParked() = 0;
|
||||
virtual Result PowerOn(BusPower bus_power) = 0;
|
||||
virtual void PowerOff() = 0;
|
||||
virtual Result LowerBusPower() = 0;
|
||||
virtual void SetSchmittTrigger(BusPower bus_power) = 0;
|
||||
virtual u8 GetOutboundTapValue() const = 0;
|
||||
virtual u8 GetDefaultInboundTapValue() const = 0;
|
||||
virtual u8 GetVrefSelValue() const = 0;
|
||||
virtual void SetSlewCodes() = 0;
|
||||
virtual void GetAutoCalOffsets(u8 *out_auto_cal_pd_offset, u8 *out_auto_cal_pu_offset, BusPower bus_power) const = 0;
|
||||
virtual void SetDriveStrengthToDefaultValues(BusPower bus_power) = 0;
|
||||
public:
|
||||
explicit SdmmcController(dd::PhysicalAddress registers_phys_addr) : SdHostStandardController(registers_phys_addr, SdmmcRegistersSize) {
|
||||
/* Set sdmmc registers. */
|
||||
static_assert(AMS_OFFSETOF(SdmmcRegisters, sd_host_standard_registers) == 0);
|
||||
m_sdmmc_registers = reinterpret_cast<SdmmcRegisters *>(m_registers);
|
||||
|
||||
m_is_shutdown = true;
|
||||
m_is_awake = true;
|
||||
m_is_valid_tap_value_for_hs_400 = false;
|
||||
m_drive_strength_calibration_status = sdmmc::ResultDriveStrengthCalibrationNotCompleted();
|
||||
m_tap_value_for_hs_400 = 0;
|
||||
m_current_speed_mode = SpeedMode_MmcIdentification;
|
||||
m_bus_power_before_sleep = BusPower_Off;
|
||||
m_bus_width_before_sleep = BusWidth_1Bit;
|
||||
m_speed_mode_before_sleep = SpeedMode_MmcIdentification;
|
||||
m_tap_value_before_sleep = 0;
|
||||
m_is_powersaving_enable_before_sleep = false;
|
||||
}
|
||||
|
||||
virtual void Initialize() override {
|
||||
/* Set pad. */
|
||||
this->SetPad();
|
||||
|
||||
/* Initialize our clock/reset module. */
|
||||
ClockResetController::Initialize(this->GetClockResetModule());
|
||||
|
||||
/* If necessary, initialize our interrupt event. */
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
{
|
||||
os::InterruptEventType *interrupt_event = this->GetInterruptEvent();
|
||||
os::InitializeInterruptEvent(interrupt_event, this->GetInterruptNumber(), os::EventClearMode_ManualClear);
|
||||
SdHostStandardController::PreSetInterruptEvent(interrupt_event);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Perform base initialization. */
|
||||
SdHostStandardController::Initialize();
|
||||
}
|
||||
|
||||
virtual void Finalize() override {
|
||||
/* Perform base finalization. */
|
||||
SdHostStandardController::Finalize();
|
||||
|
||||
/* If necessary, finalize our interrupt event. */
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
{
|
||||
os::FinalizeInterruptEvent(this->GetInterruptEvent());
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Finalize our clock/reset module. */
|
||||
ClockResetController::Finalize(this->GetClockResetModule());
|
||||
}
|
||||
|
||||
virtual Result Startup(BusPower bus_power, BusWidth bus_width, SpeedMode speed_mode, bool power_saving_enable) override;
|
||||
virtual void Shutdown() override;
|
||||
virtual void PutToSleep() override;
|
||||
virtual Result Awaken() override;
|
||||
virtual Result SwitchToSdr12() override;
|
||||
virtual Result SetSpeedMode(SpeedMode speed_mode) override;
|
||||
|
||||
virtual SpeedMode GetSpeedMode() const override {
|
||||
return m_current_speed_mode;
|
||||
}
|
||||
|
||||
virtual void SetPowerSaving(bool en) override;
|
||||
virtual void EnableDeviceClock() override;
|
||||
|
||||
virtual Result IssueCommand(const Command *command, TransferData *xfer_data, u32 *out_num_transferred_blocks) override;
|
||||
virtual Result IssueStopTransmissionCommand(u32 *out_response) override;
|
||||
|
||||
virtual bool IsSupportedTuning() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual Result Tuning(SpeedMode speed_mode, u32 command_index) override;
|
||||
virtual void SaveTuningStatusForHs400() override;
|
||||
|
||||
virtual Result GetInternalStatus() const override {
|
||||
return m_drive_strength_calibration_status;
|
||||
}
|
||||
};
|
||||
|
||||
constexpr inline dd::PhysicalAddress Sdmmc1RegistersPhysicalAddress = UINT64_C(0x700B0000);
|
||||
|
||||
class Sdmmc1Controller : public SdmmcController {
|
||||
private:
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
static constinit inline os::InterruptEventType s_interrupt_event{};
|
||||
#endif
|
||||
|
||||
/* NOTE: This is a fascimile of pcv's Sdmmc1PowerController. */
|
||||
class PowerController {
|
||||
NON_COPYABLE(PowerController);
|
||||
NON_MOVEABLE(PowerController);
|
||||
private:
|
||||
BusPower m_current_bus_power;
|
||||
private:
|
||||
Result ControlVddioSdmmc1(BusPower bus_power);
|
||||
void SetSdmmcIoMode(bool is_3_3V);
|
||||
void ControlRailSdmmc1Io(bool is_power_on);
|
||||
public:
|
||||
PowerController();
|
||||
~PowerController();
|
||||
|
||||
Result PowerOn(BusPower bus_power);
|
||||
Result PowerOff();
|
||||
Result LowerBusPower();
|
||||
};
|
||||
private:
|
||||
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
|
||||
/* TODO: pinmux::PinmuxSession m_pinmux_session; */
|
||||
#endif
|
||||
BusPower m_current_bus_power;
|
||||
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
|
||||
bool m_is_pcv_control;
|
||||
#endif
|
||||
util::TypedStorage<PowerController> m_power_controller_storage;
|
||||
PowerController *m_power_controller;
|
||||
private:
|
||||
Result PowerOnForRegisterControl(BusPower bus_power);
|
||||
void PowerOffForRegisterControl();
|
||||
Result LowerBusPowerForRegisterControl();
|
||||
void SetSchmittTriggerForRegisterControl(BusPower bus_power);
|
||||
|
||||
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
|
||||
Result PowerOnForPcvControl(BusPower bus_power);
|
||||
void PowerOffForPcvControl();
|
||||
Result LowerBusPowerForPcvControl();
|
||||
void SetSchmittTriggerForPcvControl(BusPower bus_power);
|
||||
#endif
|
||||
protected:
|
||||
virtual void SetPad() override {
|
||||
/* Nothing is needed here. */
|
||||
}
|
||||
|
||||
virtual ClockResetController::Module GetClockResetModule() const override {
|
||||
return ClockResetController::Module_Sdmmc1;
|
||||
}
|
||||
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
virtual int GetInterruptNumber() const override {
|
||||
return 46;
|
||||
}
|
||||
|
||||
virtual os::InterruptEventType *GetInterruptEvent() const override {
|
||||
return std::addressof(s_interrupt_event);
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual bool IsNeedPeriodicDriveStrengthCalibration() override {
|
||||
return !IsSocMariko();
|
||||
}
|
||||
|
||||
virtual void ClearPadParked() override {
|
||||
/* Nothing is needed here. */
|
||||
}
|
||||
|
||||
virtual Result PowerOn(BusPower bus_power) override;
|
||||
virtual void PowerOff() override;
|
||||
virtual Result LowerBusPower() override;
|
||||
|
||||
virtual void SetSchmittTrigger(BusPower bus_power) override;
|
||||
|
||||
virtual u8 GetOutboundTapValue() const override {
|
||||
if (IsSocMariko()) {
|
||||
return 0xE;
|
||||
} else {
|
||||
return 0x2;
|
||||
}
|
||||
}
|
||||
|
||||
virtual u8 GetDefaultInboundTapValue() const override {
|
||||
if (IsSocMariko()) {
|
||||
return 0xB;
|
||||
} else {
|
||||
return 0x4;
|
||||
}
|
||||
}
|
||||
|
||||
virtual u8 GetVrefSelValue() const override {
|
||||
if (IsSocMariko()) {
|
||||
return 0x0;
|
||||
} else {
|
||||
return 0x7;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void SetSlewCodes() override {
|
||||
if (IsSocMariko()) {
|
||||
/* Do nothing. */
|
||||
} else {
|
||||
/* Ensure that we can control registers. */
|
||||
SdHostStandardController::EnsureControl();
|
||||
|
||||
/* Get the apb registers address. */
|
||||
const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize);
|
||||
|
||||
/* Write the slew values to APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL. */
|
||||
reg::ReadWrite(apb_address + APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_CLK_CFG_CAL_DRVDN_SLWR, 0x1),
|
||||
APB_MISC_REG_BITS_VALUE(GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_CLK_CFG_CAL_DRVDN_SLWF, 0x1));
|
||||
|
||||
/* Read to be sure our config takes. */
|
||||
reg::Read(apb_address + APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void GetAutoCalOffsets(u8 *out_auto_cal_pd_offset, u8 *out_auto_cal_pu_offset, BusPower bus_power) const override {
|
||||
/* Ensure that we can write the offsets. */
|
||||
AMS_ABORT_UNLESS(out_auto_cal_pd_offset != nullptr);
|
||||
AMS_ABORT_UNLESS(out_auto_cal_pu_offset != nullptr);
|
||||
|
||||
/* Set the offsets. */
|
||||
if (IsSocMariko()) {
|
||||
switch (bus_power) {
|
||||
case BusPower_1_8V:
|
||||
*out_auto_cal_pd_offset = 6;
|
||||
*out_auto_cal_pu_offset = 6;
|
||||
break;
|
||||
case BusPower_3_3V:
|
||||
*out_auto_cal_pd_offset = 0;
|
||||
*out_auto_cal_pu_offset = 0;
|
||||
break;
|
||||
case BusPower_Off:
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
} else {
|
||||
switch (bus_power) {
|
||||
case BusPower_1_8V:
|
||||
*out_auto_cal_pd_offset = 0x7B;
|
||||
*out_auto_cal_pu_offset = 0x7B;
|
||||
break;
|
||||
case BusPower_3_3V:
|
||||
*out_auto_cal_pd_offset = 0x7D;
|
||||
*out_auto_cal_pu_offset = 0;
|
||||
break;
|
||||
case BusPower_Off:
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void SetDriveStrengthToDefaultValues(BusPower bus_power) override {
|
||||
/* Ensure that we can control registers. */
|
||||
SdHostStandardController::EnsureControl();
|
||||
|
||||
/* Get the apb registers address. */
|
||||
const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize);
|
||||
|
||||
/* Determine the drive code values. */
|
||||
u8 drvdn, drvup;
|
||||
if (IsSocMariko()) {
|
||||
drvdn = 0x8;
|
||||
drvup = 0x8;
|
||||
} else {
|
||||
switch (bus_power) {
|
||||
case BusPower_1_8V:
|
||||
drvdn = 0xF;
|
||||
drvup = 0xB;
|
||||
break;
|
||||
case BusPower_3_3V:
|
||||
drvdn = 0xC;
|
||||
drvup = 0xC;
|
||||
break;
|
||||
case BusPower_Off:
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
/* Write the drv up/down values to APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL. */
|
||||
reg::ReadWrite(apb_address + APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_PAD_CAL_DRVDN, drvdn),
|
||||
APB_MISC_REG_BITS_VALUE(GP_SDMMC1_PAD_CFGPADCTRL_CFG2TMC_SDMMC1_PAD_CAL_DRVUP, drvup));
|
||||
|
||||
/* Read to be sure our config takes. */
|
||||
reg::Read(apb_address + APB_MISC_GP_SDMMC1_PAD_CFGPADCTRL);
|
||||
}
|
||||
public:
|
||||
Sdmmc1Controller() : SdmmcController(Sdmmc1RegistersPhysicalAddress) {
|
||||
m_current_bus_power = BusPower_Off;
|
||||
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
|
||||
m_is_pcv_control = false;
|
||||
#endif
|
||||
m_power_controller = nullptr;
|
||||
}
|
||||
|
||||
virtual void Initialize() override;
|
||||
virtual void Finalize() override;
|
||||
|
||||
void InitializeForRegisterControl();
|
||||
void FinalizeForRegisterControl();
|
||||
|
||||
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
|
||||
void InitializeForPcvControl();
|
||||
void FinalizeForPcvControl();
|
||||
#endif
|
||||
|
||||
virtual bool IsSupportedBusPower(BusPower bus_power) const override {
|
||||
switch (bus_power) {
|
||||
case BusPower_Off: return true;
|
||||
case BusPower_1_8V: return true;
|
||||
case BusPower_3_3V: return true;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool IsSupportedBusWidth(BusWidth bus_width) const override {
|
||||
switch (bus_width) {
|
||||
case BusWidth_1Bit: return true;
|
||||
case BusWidth_4Bit: return true;
|
||||
case BusWidth_8Bit: return false;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Sdmmc2And4Controller : public SdmmcController {
|
||||
protected:
|
||||
virtual bool IsNeedPeriodicDriveStrengthCalibration() override {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual Result PowerOn(BusPower bus_power) override {
|
||||
/* Power for SDMMC2/4 is assumed on, so we don't need to do anything. */
|
||||
AMS_UNUSED(bus_power);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
virtual void PowerOff() override {
|
||||
/* Power for SDMMC2/4 is assumed on, so we don't need to do anything. */
|
||||
}
|
||||
|
||||
virtual Result LowerBusPower() override {
|
||||
AMS_ABORT("Sdmmc2And4Controller cannot lower bus power\n");
|
||||
}
|
||||
|
||||
virtual void SetSchmittTrigger(BusPower bus_power) override {
|
||||
/* Do nothing. */
|
||||
AMS_UNUSED(bus_power);
|
||||
}
|
||||
|
||||
virtual u8 GetOutboundTapValue() const override {
|
||||
if (IsSocMariko()) {
|
||||
return 0xD;
|
||||
} else {
|
||||
return 0x8;
|
||||
}
|
||||
}
|
||||
|
||||
virtual u8 GetDefaultInboundTapValue() const override {
|
||||
if (IsSocMariko()) {
|
||||
return 0xB;
|
||||
} else {
|
||||
return 0x0;
|
||||
}
|
||||
}
|
||||
|
||||
virtual u8 GetVrefSelValue() const override {
|
||||
return 0x7;
|
||||
}
|
||||
|
||||
virtual void SetSlewCodes() override {
|
||||
/* Do nothing. */
|
||||
}
|
||||
|
||||
virtual void GetAutoCalOffsets(u8 *out_auto_cal_pd_offset, u8 *out_auto_cal_pu_offset, BusPower bus_power) const override {
|
||||
/* Ensure that we can write the offsets. */
|
||||
AMS_ABORT_UNLESS(out_auto_cal_pd_offset != nullptr);
|
||||
AMS_ABORT_UNLESS(out_auto_cal_pu_offset != nullptr);
|
||||
|
||||
/* Sdmmc2And4Controller only supports 1.8v. */
|
||||
AMS_ABORT_UNLESS(bus_power == BusPower_1_8V);
|
||||
|
||||
/* Set the offsets. */
|
||||
*out_auto_cal_pd_offset = 5;
|
||||
*out_auto_cal_pu_offset = 5;
|
||||
}
|
||||
public:
|
||||
explicit Sdmmc2And4Controller(dd::PhysicalAddress registers_phys_addr) : SdmmcController(registers_phys_addr) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
virtual bool IsSupportedBusPower(BusPower bus_power) const override {
|
||||
switch (bus_power) {
|
||||
case BusPower_Off: return true;
|
||||
case BusPower_1_8V: return true;
|
||||
case BusPower_3_3V: return false;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool IsSupportedBusWidth(BusWidth bus_width) const override {
|
||||
switch (bus_width) {
|
||||
case BusWidth_1Bit: return true;
|
||||
case BusWidth_4Bit: return true;
|
||||
case BusWidth_8Bit: return true;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
constexpr inline dd::PhysicalAddress Sdmmc2RegistersPhysicalAddress = UINT64_C(0x700B0200);
|
||||
|
||||
class Sdmmc2Controller : public Sdmmc2And4Controller {
|
||||
private:
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
static constinit inline os::InterruptEventType s_interrupt_event{};
|
||||
#endif
|
||||
protected:
|
||||
virtual void SetPad() override {
|
||||
/* Nothing is needed here. */
|
||||
}
|
||||
|
||||
virtual ClockResetController::Module GetClockResetModule() const override {
|
||||
return ClockResetController::Module_Sdmmc2;
|
||||
}
|
||||
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
virtual int GetInterruptNumber() const override {
|
||||
return 47;
|
||||
}
|
||||
|
||||
virtual os::InterruptEventType *GetInterruptEvent() const override {
|
||||
return std::addressof(s_interrupt_event);
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual void ClearPadParked() override {
|
||||
if (IsSocMariko()) {
|
||||
/* Nothing is needed here. */
|
||||
} else {
|
||||
/* Get the apb registers address. */
|
||||
const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize);
|
||||
|
||||
/* Clear all MISC2PMC_EMMC2_*_PARK bits. */
|
||||
reg::ReadWrite(apb_address + APB_MISC_GP_EMMC2_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_EMMC2_PAD_CFGPADCTRL_MISC2PMC_EMMC2_ALL_PARK, 0));
|
||||
|
||||
/* Read to be sure our config takes. */
|
||||
reg::Read(apb_address + APB_MISC_GP_EMMC2_PAD_CFGPADCTRL);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void SetDriveStrengthToDefaultValues(BusPower bus_power) override {
|
||||
/* SDMMC2 only supports 1.8v. */
|
||||
AMS_ABORT_UNLESS(bus_power == BusPower_1_8V);
|
||||
|
||||
/* Ensure that we can control registers. */
|
||||
SdHostStandardController::EnsureControl();
|
||||
|
||||
/* Get the apb registers address. */
|
||||
const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize);
|
||||
|
||||
if (IsSocMariko()) {
|
||||
/* Write the drv up/down values to APB_MISC_GP_SDMMC2_PAD_CFGPADCTRL. */
|
||||
reg::ReadWrite(apb_address + APB_MISC_GP_SDMMC2_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_SDMMC2_PAD_CFGPADCTRL_CFG2TMC_SDMMC2_PAD_CAL_DRVDN, 0xA),
|
||||
APB_MISC_REG_BITS_VALUE(GP_SDMMC2_PAD_CFGPADCTRL_CFG2TMC_SDMMC2_PAD_CAL_DRVUP, 0xA));
|
||||
|
||||
/* Read to be sure our config takes. */
|
||||
reg::Read(apb_address + APB_MISC_GP_SDMMC2_PAD_CFGPADCTRL);
|
||||
} else {
|
||||
/* Write the drv up/down values to APB_MISC_GP_EMMC4_PAD_CFGPADCTRL. */
|
||||
reg::ReadWrite(apb_address + APB_MISC_GP_EMMC2_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_EMMC2_PAD_CFGPADCTRL_CFG2TMC_EMMC2_PAD_DRVDN_COMP, 0x10),
|
||||
APB_MISC_REG_BITS_VALUE(GP_EMMC2_PAD_CFGPADCTRL_CFG2TMC_EMMC2_PAD_DRVUP_COMP, 0x10));
|
||||
|
||||
/* Read to be sure our config takes. */
|
||||
reg::Read(apb_address + APB_MISC_GP_EMMC2_PAD_CFGPADCTRL);
|
||||
}
|
||||
}
|
||||
public:
|
||||
Sdmmc2Controller() : Sdmmc2And4Controller(Sdmmc2RegistersPhysicalAddress) {
|
||||
/* ... */
|
||||
}
|
||||
};
|
||||
|
||||
constexpr inline dd::PhysicalAddress Sdmmc4RegistersPhysicalAddress = UINT64_C(0x700B0600);
|
||||
|
||||
class Sdmmc4Controller : public Sdmmc2And4Controller {
|
||||
private:
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
static constinit inline os::InterruptEventType s_interrupt_event{};
|
||||
#endif
|
||||
protected:
|
||||
virtual void SetPad() override {
|
||||
if (IsSocMariko()) {
|
||||
/* Get the apb registers address. */
|
||||
const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize);
|
||||
|
||||
/* Enable Schmitt Trigger in emmc4 iobrick. */
|
||||
reg::ReadWrite(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL, APB_MISC_REG_BITS_ENUM(GP_EMMC4_PAD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_E_SCH, ENABLE));
|
||||
|
||||
/* Clear CMD_PULLU, CLK_PULLD, DQS_PULLD. */
|
||||
reg::ReadWrite(apb_address + APB_MISC_GP_EMMC4_PAD_PUPD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_PUPD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_CMD_PUPD_PULLU, 0),
|
||||
APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_PUPD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_CLK_PUPD_PULLD, 0),
|
||||
APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_PUPD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_DQS_PUPD_PULLD, 0));
|
||||
|
||||
/* Read again to be sure our config takes. */
|
||||
reg::Read(apb_address + APB_MISC_GP_EMMC4_PAD_PUPD_CFGPADCTRL);
|
||||
} else {
|
||||
/* On Erista, we can just leave the reset value intact. */
|
||||
}
|
||||
}
|
||||
|
||||
virtual ClockResetController::Module GetClockResetModule() const override {
|
||||
return ClockResetController::Module_Sdmmc4;
|
||||
}
|
||||
|
||||
#if defined(AMS_SDMMC_USE_OS_EVENTS)
|
||||
virtual int GetInterruptNumber() const override {
|
||||
return 63;
|
||||
}
|
||||
|
||||
virtual os::InterruptEventType *GetInterruptEvent() const override {
|
||||
return std::addressof(s_interrupt_event);
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual void ClearPadParked() override {
|
||||
/* Get the apb registers address. */
|
||||
const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize);
|
||||
|
||||
/* Clear all MISC2PMC_EMMC4_*_PARK bits. */
|
||||
reg::ReadWrite(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_CFGPADCTRL_MISC2PMC_EMMC4_ALL_PARK, 0));
|
||||
|
||||
/* Read to be sure our config takes. */
|
||||
reg::Read(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL);
|
||||
}
|
||||
|
||||
virtual void SetDriveStrengthToDefaultValues(BusPower bus_power) override {
|
||||
/* SDMMC4 only supports 1.8v. */
|
||||
AMS_ABORT_UNLESS(bus_power == BusPower_1_8V);
|
||||
|
||||
/* Ensure that we can control registers. */
|
||||
SdHostStandardController::EnsureControl();
|
||||
|
||||
/* Get the apb registers address. */
|
||||
const uintptr_t apb_address = dd::QueryIoMapping(ApbMiscRegistersPhysicalAddress, ApbMiscRegistersSize);
|
||||
|
||||
/* Determine the drv up/down values. */
|
||||
u8 drvdn, drvup;
|
||||
if (IsSocMariko()) {
|
||||
drvdn = 0xA;
|
||||
drvup = 0xA;
|
||||
} else {
|
||||
drvdn = 0x10;
|
||||
drvup = 0x10;
|
||||
}
|
||||
|
||||
/* Write the drv up/down values to APB_MISC_GP_EMMC4_PAD_CFGPADCTRL. */
|
||||
reg::ReadWrite(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL, APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_DRVDN_COMP, drvdn),
|
||||
APB_MISC_REG_BITS_VALUE(GP_EMMC4_PAD_CFGPADCTRL_CFG2TMC_EMMC4_PAD_DRVUP_COMP, drvup));
|
||||
|
||||
/* Read to be sure our config takes. */
|
||||
reg::Read(apb_address + APB_MISC_GP_EMMC4_PAD_CFGPADCTRL);
|
||||
}
|
||||
public:
|
||||
Sdmmc4Controller() : Sdmmc2And4Controller(Sdmmc4RegistersPhysicalAddress) {
|
||||
/* ... */
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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.hpp>
|
||||
|
||||
#if defined(ATMOSPHERE_BOARD_NINTENDO_NX)
|
||||
|
||||
#include "sdmmc_sdmmc_controller.board.nintendo_nx.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
using SdmmcControllerForPortSdCard0 = Sdmmc1Controller;
|
||||
using SdmmcControllerForPortGcAsic0 = Sdmmc2Controller;
|
||||
using SdmmcControllerForPortMmc0 = Sdmmc4Controller;
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
#error "Unknown board for ams::sdmmc::SdmmcController"
|
||||
#endif
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
#include "sdmmc_timer.hpp"
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
namespace {
|
||||
|
||||
#if defined(AMS_SDMMC_USE_OS_TIMER)
|
||||
void SpinWaitMicroSeconds(u32 us) {
|
||||
const os::Tick timeout_tick = os::GetSystemTick() + os::ConvertToTick(TimeSpan::FromMicroSeconds(us)) + os::Tick(1);
|
||||
while (true) {
|
||||
if (os::GetSystemTick() > timeout_tick) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void DataSynchronizationBarrier() {
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64)
|
||||
__asm__ __volatile__("dsb sy" ::: "memory");
|
||||
#elif defined(ATMOSPHERE_ARCH_ARM)
|
||||
__asm__ __volatile__("dsb" ::: "memory");
|
||||
#elif defined(ATMOSPHERE_ARCH_X64) || defined(ATMOSPHERE_ARCH_X86)
|
||||
/* Nothing needed? */
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
#else
|
||||
#error "Unknown architecture for DataSynchronizationBarrier"
|
||||
#endif
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void InstructionSynchronizationBarrier() {
|
||||
#if defined(ATMOSPHERE_ARCH_ARM64) || defined(ATMOSPHERE_ARCH_ARM)
|
||||
__asm__ __volatile__("isb" ::: "memory");
|
||||
#elif defined(ATMOSPHERE_ARCH_X64) || defined(ATMOSPHERE_ARCH_X86)
|
||||
/* Nothing needed? */
|
||||
__asm__ __volatile__("" ::: "memory");
|
||||
#else
|
||||
#error "Unknown architecture for InstructionSynchronizationBarrier"
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void WaitMicroSeconds(u32 us) {
|
||||
#if defined(AMS_SDMMC_USE_OS_TIMER)
|
||||
/* Ensure that nothing is reordered before we wait. */
|
||||
DataSynchronizationBarrier();
|
||||
InstructionSynchronizationBarrier();
|
||||
|
||||
/* If the time is small, spinloop, otherwise pend ourselves. */
|
||||
if (us < 100) {
|
||||
SpinWaitMicroSeconds(us);
|
||||
} else {
|
||||
os::SleepThread(TimeSpan::FromMicroSeconds(us));
|
||||
}
|
||||
|
||||
/* Ensure that nothing is reordered after we wait. */
|
||||
DataSynchronizationBarrier();
|
||||
InstructionSynchronizationBarrier();
|
||||
#elif defined(AMS_SDMMC_USE_UTIL_TIMER)
|
||||
util::WaitMicroSeconds(us);
|
||||
#else
|
||||
#error "Unknown context for ams::sdmmc::impl::WaitMicroSeconds"
|
||||
#endif
|
||||
}
|
||||
|
||||
void WaitClocks(u32 num_clocks, u32 clock_frequency_khz) {
|
||||
AMS_ABORT_UNLESS(clock_frequency_khz > 0);
|
||||
WaitMicroSeconds(util::DivideUp(1000 * num_clocks, clock_frequency_khz));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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.hpp>
|
||||
|
||||
namespace ams::sdmmc::impl {
|
||||
|
||||
void WaitMicroSeconds(u32 us);
|
||||
void WaitClocks(u32 num_clocks, u32 clock_frequency_khz);
|
||||
|
||||
#if defined(AMS_SDMMC_USE_OS_TIMER)
|
||||
class ManualTimer {
|
||||
private:
|
||||
os::Tick m_timeout_tick;
|
||||
bool m_is_timed_out;
|
||||
public:
|
||||
explicit ManualTimer(u32 ms) {
|
||||
m_timeout_tick = os::GetSystemTick() + os::ConvertToTick(TimeSpan::FromMilliSeconds(ms));
|
||||
m_is_timed_out = false;
|
||||
}
|
||||
|
||||
bool Update() {
|
||||
if (m_is_timed_out) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_is_timed_out = os::GetSystemTick() > m_timeout_tick;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
#elif defined(AMS_SDMMC_USE_UTIL_TIMER)
|
||||
class ManualTimer {
|
||||
private:
|
||||
u32 m_timeout_us;
|
||||
bool m_is_timed_out;
|
||||
public:
|
||||
explicit ManualTimer(u32 ms) {
|
||||
m_timeout_us = util::GetMicroSeconds() + (ms * 1000);
|
||||
m_is_timed_out = false;
|
||||
}
|
||||
|
||||
bool Update() {
|
||||
if (m_is_timed_out) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_is_timed_out = util::GetMicroSeconds() > m_timeout_us;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
#else
|
||||
#error "Unknown context for ams::sdmmc::ManualTimer"
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
#include "impl/sdmmc_i_host_controller.hpp"
|
||||
#include "impl/sdmmc_i_device_accessor.hpp"
|
||||
#include "impl/sdmmc_clock_reset_controller.hpp"
|
||||
#include "impl/sdmmc_port_mmc0.hpp"
|
||||
#include "impl/sdmmc_port_sd_card0.hpp"
|
||||
#include "impl/sdmmc_port_gc_asic0.hpp"
|
||||
|
||||
namespace ams::sdmmc {
|
||||
|
||||
namespace {
|
||||
|
||||
impl::IHostController *GetHostController(Port port) {
|
||||
/* Get the controller. */
|
||||
impl::IHostController *host_controller = nullptr;
|
||||
switch (port) {
|
||||
case Port_Mmc0: host_controller = impl::GetHostControllerOfPortMmc0(); break;
|
||||
case Port_SdCard0: host_controller = impl::GetHostControllerOfPortSdCard0(); break;
|
||||
case Port_GcAsic0: host_controller = impl::GetHostControllerOfPortGcAsic0(); break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
/* Ensure it's valid */
|
||||
AMS_ABORT_UNLESS(host_controller != nullptr);
|
||||
return host_controller;
|
||||
}
|
||||
|
||||
impl::IDeviceAccessor *GetDeviceAccessor(Port port) {
|
||||
/* Get the accessor. */
|
||||
impl::IDeviceAccessor *device_accessor = nullptr;
|
||||
switch (port) {
|
||||
case Port_Mmc0: device_accessor = impl::GetDeviceAccessorOfPortMmc0(); break;
|
||||
case Port_SdCard0: device_accessor = impl::GetDeviceAccessorOfPortSdCard0(); break;
|
||||
case Port_GcAsic0: device_accessor = impl::GetDeviceAccessorOfPortGcAsic0(); break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
/* Ensure it's valid */
|
||||
AMS_ABORT_UNLESS(device_accessor != nullptr);
|
||||
return device_accessor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Initialize(Port port) {
|
||||
return GetDeviceAccessor(port)->Initialize();
|
||||
}
|
||||
|
||||
void Finalize(Port port) {
|
||||
return GetDeviceAccessor(port)->Finalize();
|
||||
}
|
||||
|
||||
#if defined(AMS_SDMMC_USE_PCV_CLOCK_RESET_CONTROL)
|
||||
void SwitchToPcvClockResetControl() {
|
||||
return impl::ClockResetController::SwitchToPcvControl();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(AMS_SDMMC_USE_DEVICE_VIRTUAL_ADDRESS)
|
||||
void RegisterDeviceVirtualAddress(Port port, uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) {
|
||||
return GetDeviceAccessor(port)->RegisterDeviceVirtualAddress(buffer, buffer_size, buffer_device_virtual_address);
|
||||
}
|
||||
|
||||
void UnregisterDeviceVirtualAddress(Port port, uintptr_t buffer, size_t buffer_size, ams::dd::DeviceVirtualAddress buffer_device_virtual_address) {
|
||||
return GetDeviceAccessor(port)->UnregisterDeviceVirtualAddress(buffer, buffer_size, buffer_device_virtual_address);
|
||||
}
|
||||
#endif
|
||||
|
||||
void ChangeCheckTransferInterval(Port port, u32 ms) {
|
||||
return GetHostController(port)->ChangeCheckTransferInterval(ms);
|
||||
}
|
||||
void SetDefaultCheckTransferInterval(Port port) {
|
||||
return GetHostController(port)->SetDefaultCheckTransferInterval();
|
||||
}
|
||||
|
||||
Result Activate(Port port) {
|
||||
R_RETURN(GetDeviceAccessor(port)->Activate());
|
||||
}
|
||||
|
||||
void Deactivate(Port port) {
|
||||
return GetDeviceAccessor(port)->Deactivate();
|
||||
}
|
||||
|
||||
Result Read(void *dst, size_t dst_size, Port port, u32 sector_index, u32 num_sectors) {
|
||||
R_RETURN(GetDeviceAccessor(port)->ReadWrite(sector_index, num_sectors, dst, dst_size, true));
|
||||
}
|
||||
|
||||
Result Write(Port port, u32 sector_index, u32 num_sectors, const void *src, size_t src_size) {
|
||||
R_RETURN(GetDeviceAccessor(port)->ReadWrite(sector_index, num_sectors, const_cast<void *>(src), src_size, false));
|
||||
}
|
||||
|
||||
Result CheckConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width, Port port) {
|
||||
R_RETURN(GetDeviceAccessor(port)->CheckConnection(out_speed_mode, out_bus_width));
|
||||
}
|
||||
|
||||
Result GetDeviceSpeedMode(SpeedMode *out, Port port) {
|
||||
R_RETURN(GetDeviceAccessor(port)->GetSpeedMode(out));
|
||||
}
|
||||
|
||||
Result GetDeviceMemoryCapacity(u32 *out_num_sectors, Port port) {
|
||||
R_RETURN(GetDeviceAccessor(port)->GetMemoryCapacity(out_num_sectors));
|
||||
}
|
||||
|
||||
Result GetDeviceStatus(u32 *out_device_status, Port port) {
|
||||
R_RETURN(GetDeviceAccessor(port)->GetDeviceStatus(out_device_status));
|
||||
}
|
||||
|
||||
Result GetDeviceCid(void *out, size_t out_size, Port port) {
|
||||
R_RETURN(GetDeviceAccessor(port)->GetCid(out, out_size));
|
||||
}
|
||||
|
||||
Result GetDeviceCsd(void *out, size_t out_size, Port port) {
|
||||
R_RETURN(GetDeviceAccessor(port)->GetCsd(out, out_size));
|
||||
}
|
||||
|
||||
void GetAndClearErrorInfo(ErrorInfo *out_error_info, size_t *out_log_size, char *out_log_buffer, size_t log_buffer_size, Port port) {
|
||||
return GetDeviceAccessor(port)->GetAndClearErrorInfo(out_error_info, out_log_size, out_log_buffer, log_buffer_size);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
#include "impl/sdmmc_gc_asic_device_accessor.hpp"
|
||||
#include "impl/sdmmc_port_mmc0.hpp"
|
||||
#include "impl/sdmmc_port_sd_card0.hpp"
|
||||
#include "impl/sdmmc_port_gc_asic0.hpp"
|
||||
|
||||
namespace ams::sdmmc {
|
||||
|
||||
namespace {
|
||||
|
||||
impl::GcAsicDeviceAccessor *GetGcAsicDeviceAccessor(Port port) {
|
||||
/* Get the accessor. */
|
||||
impl::GcAsicDeviceAccessor *gc_asic_device_accessor = nullptr;
|
||||
switch (port) {
|
||||
case Port_GcAsic0: gc_asic_device_accessor = impl::GetGcAsicDeviceAccessorOfPortGcAsic0(); break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
/* Ensure it's valid */
|
||||
AMS_ABORT_UNLESS(gc_asic_device_accessor != nullptr);
|
||||
return gc_asic_device_accessor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void PutGcAsicToSleep(Port port) {
|
||||
return GetGcAsicDeviceAccessor(port)->PutGcAsicToSleep();
|
||||
}
|
||||
|
||||
Result AwakenGcAsic(Port port) {
|
||||
R_RETURN(GetGcAsicDeviceAccessor(port)->AwakenGcAsic());
|
||||
}
|
||||
|
||||
Result WriteGcAsicOperation(Port port, const void *op_buf, size_t op_buf_size) {
|
||||
R_RETURN(GetGcAsicDeviceAccessor(port)->WriteGcAsicOperation(op_buf, op_buf_size));
|
||||
}
|
||||
|
||||
Result FinishGcAsicOperation(Port port) {
|
||||
R_RETURN(GetGcAsicDeviceAccessor(port)->FinishGcAsicOperation());
|
||||
}
|
||||
|
||||
Result AbortGcAsicOperation(Port port) {
|
||||
R_RETURN(GetGcAsicDeviceAccessor(port)->AbortGcAsicOperation());
|
||||
}
|
||||
|
||||
Result SleepGcAsic(Port port) {
|
||||
R_RETURN(GetGcAsicDeviceAccessor(port)->SleepGcAsic());
|
||||
}
|
||||
|
||||
Result UpdateGcAsicKey(Port port) {
|
||||
R_RETURN(GetGcAsicDeviceAccessor(port)->UpdateGcAsicKey());
|
||||
}
|
||||
|
||||
void SignalGcRemovedEvent(Port port) {
|
||||
return GetGcAsicDeviceAccessor(port)->SignalGcRemovedEvent();
|
||||
}
|
||||
|
||||
void ClearGcRemovedEvent(Port port) {
|
||||
return GetGcAsicDeviceAccessor(port)->ClearGcRemovedEvent();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
#include "impl/sdmmc_mmc_device_accessor.hpp"
|
||||
#include "impl/sdmmc_port_mmc0.hpp"
|
||||
#include "impl/sdmmc_port_sd_card0.hpp"
|
||||
#include "impl/sdmmc_port_gc_asic0.hpp"
|
||||
|
||||
namespace ams::sdmmc {
|
||||
|
||||
namespace {
|
||||
|
||||
impl::MmcDeviceAccessor *GetMmcDeviceAccessor(Port port) {
|
||||
/* Get the accessor. */
|
||||
impl::MmcDeviceAccessor *mmc_device_accessor = nullptr;
|
||||
switch (port) {
|
||||
case Port_Mmc0: mmc_device_accessor = impl::GetMmcDeviceAccessorOfPortMmc0(); break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
/* Ensure it's valid */
|
||||
AMS_ABORT_UNLESS(mmc_device_accessor != nullptr);
|
||||
return mmc_device_accessor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SetMmcWorkBuffer(Port port, void *buffer, size_t buffer_size) {
|
||||
return GetMmcDeviceAccessor(port)->SetMmcWorkBuffer(buffer, buffer_size);
|
||||
}
|
||||
|
||||
void PutMmcToSleep(Port port) {
|
||||
return GetMmcDeviceAccessor(port)->PutMmcToSleep();
|
||||
}
|
||||
|
||||
void AwakenMmc(Port port) {
|
||||
return GetMmcDeviceAccessor(port)->AwakenMmc();
|
||||
}
|
||||
|
||||
Result SelectMmcPartition(Port port, MmcPartition mmc_partition) {
|
||||
R_RETURN(GetMmcDeviceAccessor(port)->SelectMmcPartition(mmc_partition));
|
||||
}
|
||||
|
||||
Result EraseMmc(Port port) {
|
||||
R_RETURN(GetMmcDeviceAccessor(port)->EraseMmc());
|
||||
}
|
||||
|
||||
Result GetMmcBootPartitionCapacity(u32 *out_num_sectors, Port port) {
|
||||
R_RETURN(GetMmcDeviceAccessor(port)->GetMmcBootPartitionCapacity(out_num_sectors));
|
||||
}
|
||||
|
||||
Result GetMmcExtendedCsd(void *out_buffer, size_t buffer_size, Port port) {
|
||||
R_RETURN(GetMmcDeviceAccessor(port)->GetMmcExtendedCsd(out_buffer, buffer_size));
|
||||
}
|
||||
|
||||
Result CheckMmcConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width, Port port) {
|
||||
R_RETURN(GetMmcDeviceAccessor(port)->CheckConnection(out_speed_mode, out_bus_width));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#if defined(ATMOSPHERE_IS_STRATOSPHERE)
|
||||
#include <stratosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_MESOSPHERE)
|
||||
#include <mesosphere.hpp>
|
||||
#elif defined(ATMOSPHERE_IS_EXOSPHERE)
|
||||
#include <exosphere.hpp>
|
||||
#else
|
||||
#include <vapours.hpp>
|
||||
#endif
|
||||
#include "impl/sdmmc_sd_card_device_accessor.hpp"
|
||||
#include "impl/sdmmc_port_mmc0.hpp"
|
||||
#include "impl/sdmmc_port_sd_card0.hpp"
|
||||
#include "impl/sdmmc_port_gc_asic0.hpp"
|
||||
|
||||
namespace ams::sdmmc {
|
||||
|
||||
namespace {
|
||||
|
||||
impl::SdCardDeviceAccessor *GetSdCardDeviceAccessor(Port port) {
|
||||
/* Get the accessor. */
|
||||
impl::SdCardDeviceAccessor *sd_card_device_accessor = nullptr;
|
||||
switch (port) {
|
||||
case Port_SdCard0: sd_card_device_accessor = impl::GetSdCardDeviceAccessorOfPortSdCard0(); break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
/* Ensure it's valid */
|
||||
AMS_ABORT_UNLESS(sd_card_device_accessor != nullptr);
|
||||
return sd_card_device_accessor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SetSdCardWorkBuffer(Port port, void *buffer, size_t buffer_size) {
|
||||
return GetSdCardDeviceAccessor(port)->SetSdCardWorkBuffer(buffer, buffer_size);
|
||||
}
|
||||
|
||||
void PutSdCardToSleep(Port port) {
|
||||
return GetSdCardDeviceAccessor(port)->PutSdCardToSleep();
|
||||
}
|
||||
|
||||
void AwakenSdCard(Port port) {
|
||||
return GetSdCardDeviceAccessor(port)->AwakenSdCard();
|
||||
}
|
||||
|
||||
Result GetSdCardProtectedAreaCapacity(u32 *out_num_sectors, Port port) {
|
||||
R_RETURN(GetSdCardDeviceAccessor(port)->GetSdCardProtectedAreaCapacity(out_num_sectors));
|
||||
}
|
||||
|
||||
Result GetSdCardScr(void *dst, size_t dst_size, Port port) {
|
||||
R_RETURN(GetSdCardDeviceAccessor(port)->GetSdCardScr(dst, dst_size));
|
||||
}
|
||||
|
||||
Result GetSdCardSwitchFunctionStatus(void *dst, size_t dst_size, Port port, SdCardSwitchFunction switch_function) {
|
||||
R_RETURN(GetSdCardDeviceAccessor(port)->GetSdCardSwitchFunctionStatus(dst, dst_size, switch_function));
|
||||
}
|
||||
|
||||
Result GetSdCardCurrentConsumption(u16 *out_current_consumption, Port port, SpeedMode speed_mode) {
|
||||
R_RETURN(GetSdCardDeviceAccessor(port)->GetSdCardCurrentConsumption(out_current_consumption, speed_mode));
|
||||
}
|
||||
|
||||
Result GetSdCardSdStatus(void *dst, size_t dst_size, Port port) {
|
||||
R_RETURN(GetSdCardDeviceAccessor(port)->GetSdCardSdStatus(dst, dst_size));
|
||||
}
|
||||
|
||||
Result CheckSdCardConnection(SpeedMode *out_speed_mode, BusWidth *out_bus_width, Port port) {
|
||||
R_RETURN(GetSdCardDeviceAccessor(port)->CheckConnection(out_speed_mode, out_bus_width));
|
||||
}
|
||||
|
||||
bool IsSdCardInserted(Port port) {
|
||||
return GetSdCardDeviceAccessor(port)->IsSdCardInserted();
|
||||
}
|
||||
|
||||
bool IsSdCardRemoved(Port port) {
|
||||
return GetSdCardDeviceAccessor(port)->IsSdCardRemoved();
|
||||
}
|
||||
|
||||
|
||||
void RegisterSdCardDetectionEventCallback(Port port, DeviceDetectionEventCallback callback, void *arg) {
|
||||
return GetSdCardDeviceAccessor(port)->RegisterSdCardDetectionEventCallback(callback, arg);
|
||||
}
|
||||
|
||||
void UnregisterSdCardDetectionEventCallback(Port port) {
|
||||
return GetSdCardDeviceAccessor(port)->UnregisterSdCardDetectionEventCallback();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,232 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
|
||||
/* TODO: Define to enable tests? */
|
||||
#if 0
|
||||
|
||||
namespace ams::test {
|
||||
|
||||
template<typename T>
|
||||
concept IsRedBlackTreeTestNode = std::constructible_from<T, int> && requires (T &t, const T &ct) {
|
||||
{ ct.GetValue() } -> std::same_as<int>;
|
||||
{ t.GetNode() } -> std::same_as< util::IntrusiveRedBlackTreeNode &>;
|
||||
{ ct.GetNode() } -> std::same_as<const util::IntrusiveRedBlackTreeNode &>;
|
||||
};
|
||||
|
||||
template<typename T> requires IsRedBlackTreeTestNode<T>
|
||||
struct TestComparator {
|
||||
using RedBlackKeyType = int;
|
||||
|
||||
static constexpr int Compare(const T &lhs, const T &rhs) {
|
||||
if (lhs.GetValue() < rhs.GetValue()) {
|
||||
return -1;
|
||||
} else if (lhs.GetValue() > rhs.GetValue()) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr int Compare(const int &lhs, const T &rhs) {
|
||||
if (lhs < rhs.GetValue()) {
|
||||
return -1;
|
||||
} else if (lhs > rhs.GetValue()) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class TestBaseNode : public util::IntrusiveRedBlackTreeBaseNode<TestBaseNode> {
|
||||
private:
|
||||
const int m_value;
|
||||
public:
|
||||
constexpr TestBaseNode(int value) : m_value(value) { /* ... */ }
|
||||
|
||||
constexpr int GetValue() const { return m_value; }
|
||||
|
||||
constexpr util::IntrusiveRedBlackTreeNode &GetNode() { return static_cast< util::IntrusiveRedBlackTreeNode &>(*this); }
|
||||
constexpr const util::IntrusiveRedBlackTreeNode &GetNode() const { return static_cast<const util::IntrusiveRedBlackTreeNode &>(*this); }
|
||||
};
|
||||
static_assert(IsRedBlackTreeTestNode<TestBaseNode>);
|
||||
|
||||
class TestTreeTypes;
|
||||
|
||||
class TestMemberNode {
|
||||
private:
|
||||
friend class TestTreeTypes;
|
||||
private:
|
||||
const int m_value;
|
||||
util::IntrusiveRedBlackTreeNode m_node;
|
||||
public:
|
||||
constexpr TestMemberNode(int value) : m_value(value), m_node() { /* ... */ }
|
||||
|
||||
constexpr int GetValue() const { return m_value; }
|
||||
|
||||
constexpr util::IntrusiveRedBlackTreeNode &GetNode() { return m_node; }
|
||||
constexpr const util::IntrusiveRedBlackTreeNode &GetNode() const { return m_node; }
|
||||
};
|
||||
static_assert(IsRedBlackTreeTestNode<TestMemberNode>);
|
||||
|
||||
class TestTreeTypes {
|
||||
public:
|
||||
using BaseTree = util::IntrusiveRedBlackTreeBaseTraits<TestBaseNode>::TreeType<TestComparator<TestBaseNode>>;
|
||||
using MemberTree = util::IntrusiveRedBlackTreeMemberTraits<&TestMemberNode::m_node>::TreeType<TestComparator<TestMemberNode>>;
|
||||
};
|
||||
|
||||
using TestBaseTree = TestTreeTypes::BaseTree;
|
||||
using TestMemberTree = TestTreeTypes::MemberTree;
|
||||
|
||||
template<typename Tree, typename Node>
|
||||
consteval bool TestUsage() {
|
||||
constexpr int Values[] = { -3, 0, 5, 7, 11111111, 924, -100, 68, 70, 69, };
|
||||
|
||||
/* Get sorted array. */
|
||||
std::array<int, util::size(Values)> sorted_values{};
|
||||
std::copy(std::begin(Values), std::end(Values), std::begin(sorted_values));
|
||||
std::sort(std::begin(sorted_values), std::end(sorted_values));
|
||||
|
||||
/* Create the tree. */
|
||||
Tree tree{};
|
||||
AMS_ASSUME(tree.begin() == tree.end());
|
||||
|
||||
/* Create a node for each value. */
|
||||
/* TODO: GCC bug in constant evaluation fails if we use constexpr new/dynamically allocated nodes. */
|
||||
/* Check if this works in gcc 11. */
|
||||
std::array<Node, util::size(Values)> nodes = [&]<size_t... Ix>(std::index_sequence<Ix...>) {
|
||||
return std::array<Node, util::size(Values)> { Node(Values[Ix])... };
|
||||
}(std::make_index_sequence<util::size(Values)>());
|
||||
|
||||
/* Insert each node into the tree. */
|
||||
for (size_t i = 0; i < util::size(Values); ++i) {
|
||||
tree.insert(nodes[i]);
|
||||
if (std::distance(tree.begin(), tree.end()) != static_cast<int>(i + 1)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Verify that the nodes are in sorted order. */
|
||||
{
|
||||
size_t i = 0;
|
||||
for (const auto &node : tree) {
|
||||
if (node.GetValue() != sorted_values[i++]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Verify correctness with begin() */
|
||||
{
|
||||
size_t i = 0;
|
||||
for (auto it = tree.begin(); it != tree.end(); ++it) {
|
||||
if (it->GetValue() != sorted_values[i++]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Verify correctness with cbegin() */
|
||||
{
|
||||
size_t i = 0;
|
||||
for (auto it = tree.cbegin(); it != tree.cend(); ++it) {
|
||||
if (it->GetValue() != sorted_values[i++]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Verify min/max. */
|
||||
if (tree.front().GetValue() != sorted_values[0]) {
|
||||
return false;
|
||||
}
|
||||
if (tree.back().GetValue() != sorted_values[sorted_values.size() - 1]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Remove a value. */
|
||||
tree.erase(tree.iterator_to(nodes[3]));
|
||||
|
||||
/* Verify nodes are in sorted order. */
|
||||
{
|
||||
size_t i = 0;
|
||||
for (const auto &node : tree) {
|
||||
if (node.GetValue() == nodes[3].GetValue()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (node.GetValue() != sorted_values[i++]) {
|
||||
if (node.GetValue() != sorted_values[i++]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Add the node back. */
|
||||
tree.insert(nodes[3]);
|
||||
|
||||
/* Verify nodes are in sorted order. */
|
||||
{
|
||||
size_t i = 0;
|
||||
for (const auto &node : tree) {
|
||||
if (node.GetValue() != sorted_values[i++]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Verify that find works. */
|
||||
for (size_t i = 0; i < util::size(Values); ++i) {
|
||||
if (tree.find(Node(Values[i])) != tree.iterator_to(nodes[i])) {
|
||||
return false;
|
||||
}
|
||||
if (tree.nfind(Node(sorted_values[i]))->GetValue() != sorted_values[i]) {
|
||||
return false;
|
||||
}
|
||||
if (tree.find_key(Values[i]) != tree.iterator_to(nodes[i])) {
|
||||
return false;
|
||||
}
|
||||
if (tree.nfind_key(sorted_values[i])->GetValue() != sorted_values[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (tree.find(Node(std::numeric_limits<int>::min())) != tree.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Verify that nfind works. */
|
||||
for (size_t i = 0; i < util::size(Values) - 1; ++i) {
|
||||
if (tree.nfind(Node(sorted_values[i] + 1))->GetValue() != sorted_values[i + 1]) {
|
||||
return false;
|
||||
}
|
||||
if (tree.nfind_key(sorted_values[i] + 1)->GetValue() != sorted_values[i + 1]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static_assert(TestUsage<TestBaseTree, TestBaseNode>());
|
||||
static_assert(TestUsage<TestMemberTree, TestMemberNode>());
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,436 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
AMS_PRAGMA_BEGIN_OPTIMIZE("-Os")
|
||||
|
||||
namespace {
|
||||
|
||||
/* Useful definitions for our VSNPrintf implementation. */
|
||||
enum FormatSpecifierFlag : u32 {
|
||||
FormatSpecifierFlag_None = 0,
|
||||
FormatSpecifierFlag_EmptySign = (1 << 0),
|
||||
FormatSpecifierFlag_ForceSign = (1 << 1),
|
||||
FormatSpecifierFlag_Hash = (1 << 2),
|
||||
FormatSpecifierFlag_LeftJustify = (1 << 3),
|
||||
FormatSpecifierFlag_ZeroPad = (1 << 4),
|
||||
FormatSpecifierFlag_Char = (1 << 5),
|
||||
FormatSpecifierFlag_Short = (1 << 6),
|
||||
FormatSpecifierFlag_Long = (1 << 7),
|
||||
FormatSpecifierFlag_LongLong = (1 << 8),
|
||||
FormatSpecifierFlag_Uppercase = (1 << 9),
|
||||
FormatSpecifierFlag_HasPrecision = (1 << 10),
|
||||
};
|
||||
|
||||
using FormatSpecifierFlagStorage = std::underlying_type<FormatSpecifierFlag>::type;
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsDigit(char c) {
|
||||
return '0' <= c && c <= '9';
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE u32 ParseU32(const char *&str) {
|
||||
u32 value = 0;
|
||||
do {
|
||||
value = (value * 10) + static_cast<u32>(*(str++) - '0');
|
||||
} while (IsDigit(*str));
|
||||
return value;
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE size_t Strnlen(const char *str, size_t max) {
|
||||
const char *cur = str;
|
||||
while (*cur && max--) {
|
||||
cur++;
|
||||
}
|
||||
return static_cast<size_t>(cur - str);
|
||||
}
|
||||
|
||||
int TVSNPrintfImpl(char * const dst, const size_t dst_size, const char *format, ::std::va_list vl) {
|
||||
size_t dst_index = 0;
|
||||
|
||||
auto WriteCharacter = [dst, dst_size, &dst_index](char c) ALWAYS_INLINE_LAMBDA {
|
||||
if (const size_t i = (dst_index++); i < dst_size) {
|
||||
dst[i] = c;
|
||||
}
|
||||
};
|
||||
|
||||
/* Loop over every character in the string, looking for format specifiers. */
|
||||
while (*format) {
|
||||
if (const char c = *(format++); c != '%') {
|
||||
WriteCharacter(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We have to parse a format specifier. */
|
||||
/* Start by parsing flags. */
|
||||
FormatSpecifierFlagStorage flags = FormatSpecifierFlag_None;
|
||||
auto SetFlag = [&flags](FormatSpecifierFlag f) ALWAYS_INLINE_LAMBDA { flags |= f; };
|
||||
auto ClearFlag = [&flags](FormatSpecifierFlag f) ALWAYS_INLINE_LAMBDA { flags &= ~f; };
|
||||
auto HasFlag = [&flags](FormatSpecifierFlag f) ALWAYS_INLINE_LAMBDA { return (flags & f) != 0; };
|
||||
{
|
||||
bool parsed_flags = false;
|
||||
while (!parsed_flags) {
|
||||
switch (*format) {
|
||||
case ' ': SetFlag(FormatSpecifierFlag_EmptySign); format++; break;
|
||||
case '+': SetFlag(FormatSpecifierFlag_ForceSign); format++; break;
|
||||
case '#': SetFlag(FormatSpecifierFlag_Hash); format++; break;
|
||||
case '-': SetFlag(FormatSpecifierFlag_LeftJustify); format++; break;
|
||||
case '0': SetFlag(FormatSpecifierFlag_ZeroPad); format++; break;
|
||||
default:
|
||||
parsed_flags = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Next, parse width. */
|
||||
u32 width = 0;
|
||||
if (IsDigit(*format)) {
|
||||
/* Integer width. */
|
||||
width = ParseU32(format);
|
||||
} else if (*format == '*') {
|
||||
/* Dynamic width. */
|
||||
const int _width = va_arg(vl, int);
|
||||
if (_width >= 0) {
|
||||
width = static_cast<u32>(_width);
|
||||
} else {
|
||||
SetFlag(FormatSpecifierFlag_LeftJustify);
|
||||
width = static_cast<u32>(-_width);
|
||||
}
|
||||
format++;
|
||||
}
|
||||
|
||||
/* Next, parse precision if present. */
|
||||
u32 precision = 0;
|
||||
if (*format == '.') {
|
||||
SetFlag(FormatSpecifierFlag_HasPrecision);
|
||||
format++;
|
||||
|
||||
if (IsDigit(*format)) {
|
||||
/* Integer precision. */
|
||||
precision = ParseU32(format);
|
||||
} else if (*format == '*') {
|
||||
/* Dynamic precision. */
|
||||
const int _precision = va_arg(vl, int);
|
||||
if (_precision > 0) {
|
||||
precision = static_cast<u32>(_precision);
|
||||
}
|
||||
format++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse length. */
|
||||
constexpr bool SizeIsLong = sizeof(size_t) == sizeof(long);
|
||||
constexpr bool PointerIsLong = sizeof(uintptr_t) == sizeof(long);
|
||||
constexpr bool IntMaxIsLong = sizeof(intmax_t) == sizeof(long);
|
||||
constexpr bool PtrDiffIsLong = sizeof(ptrdiff_t) == sizeof(long);
|
||||
switch (*format) {
|
||||
case 'z':
|
||||
SetFlag(SizeIsLong ? FormatSpecifierFlag_Long : FormatSpecifierFlag_LongLong);
|
||||
format++;
|
||||
break;
|
||||
case 'j':
|
||||
SetFlag(IntMaxIsLong ? FormatSpecifierFlag_Long : FormatSpecifierFlag_LongLong);
|
||||
format++;
|
||||
break;
|
||||
case 't':
|
||||
SetFlag(PtrDiffIsLong ? FormatSpecifierFlag_Long : FormatSpecifierFlag_LongLong);
|
||||
format++;
|
||||
break;
|
||||
case 'h':
|
||||
SetFlag(FormatSpecifierFlag_Short);
|
||||
format++;
|
||||
if (*format == 'h') {
|
||||
SetFlag(FormatSpecifierFlag_Char);
|
||||
format++;
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
SetFlag(FormatSpecifierFlag_Long);
|
||||
format++;
|
||||
if (*format == 'l') {
|
||||
SetFlag(FormatSpecifierFlag_LongLong);
|
||||
format++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
const char specifier = *(format++);
|
||||
switch (specifier) {
|
||||
case 'p':
|
||||
if constexpr (PointerIsLong) {
|
||||
SetFlag(FormatSpecifierFlag_Long);
|
||||
} else {
|
||||
SetFlag(FormatSpecifierFlag_LongLong);
|
||||
}
|
||||
SetFlag(FormatSpecifierFlag_Hash);
|
||||
[[fallthrough]];
|
||||
case 'd':
|
||||
case 'i':
|
||||
case 'u':
|
||||
case 'b':
|
||||
case 'o':
|
||||
case 'x':
|
||||
case 'X':
|
||||
{
|
||||
/* Determine the base to print with. */
|
||||
u32 base;
|
||||
switch (specifier) {
|
||||
case 'b':
|
||||
base = 2;
|
||||
break;
|
||||
case 'o':
|
||||
base = 8;
|
||||
break;
|
||||
case 'X':
|
||||
SetFlag(FormatSpecifierFlag_Uppercase);
|
||||
[[fallthrough]];
|
||||
case 'p':
|
||||
case 'x':
|
||||
base = 16;
|
||||
break;
|
||||
default:
|
||||
base = 10;
|
||||
ClearFlag(FormatSpecifierFlag_Hash);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Precision implies no zero-padding. */
|
||||
if (HasFlag(FormatSpecifierFlag_HasPrecision)) {
|
||||
ClearFlag(FormatSpecifierFlag_ZeroPad);
|
||||
}
|
||||
|
||||
/* Unsigned types don't get signs. */
|
||||
const bool is_unsigned = base != 10 || specifier == 'u';
|
||||
if (is_unsigned) {
|
||||
ClearFlag(FormatSpecifierFlag_EmptySign);
|
||||
ClearFlag(FormatSpecifierFlag_ForceSign);
|
||||
}
|
||||
|
||||
auto PrintInteger = [&](bool negative, uintmax_t value) {
|
||||
constexpr size_t BufferSize = 64; /* Binary digits for 64-bit numbers may use 64 digits. */
|
||||
char buf[BufferSize];
|
||||
size_t len = 0;
|
||||
|
||||
/* No hash flag for zero. */
|
||||
if (value == 0) {
|
||||
ClearFlag(FormatSpecifierFlag_Hash);
|
||||
}
|
||||
|
||||
if (!HasFlag(FormatSpecifierFlag_HasPrecision) || value != 0) {
|
||||
do {
|
||||
const char digit = static_cast<char>(value % base);
|
||||
buf[len++] = (digit < 10) ? ('0' + digit) : ((HasFlag(FormatSpecifierFlag_Uppercase) ? 'A' : 'a') + digit - 10);
|
||||
value /= base;
|
||||
} while (value);
|
||||
}
|
||||
|
||||
/* Determine our prefix length. */
|
||||
size_t prefix_len = 0;
|
||||
const bool has_sign = negative || HasFlag(FormatSpecifierFlag_ForceSign) || HasFlag(FormatSpecifierFlag_EmptySign);
|
||||
if (has_sign) {
|
||||
prefix_len++;
|
||||
}
|
||||
if (HasFlag(FormatSpecifierFlag_Hash)) {
|
||||
prefix_len += (base != 8) ? 2 : 1;
|
||||
}
|
||||
|
||||
/* Determine zero-padding count. */
|
||||
size_t num_zeroes = (len < precision) ? precision - len : 0;
|
||||
if (!HasFlag(FormatSpecifierFlag_LeftJustify) && HasFlag(FormatSpecifierFlag_ZeroPad)) {
|
||||
num_zeroes = (len + prefix_len < width) ? width - len - prefix_len : 0;
|
||||
}
|
||||
|
||||
/* Print out left padding. */
|
||||
if (!HasFlag(FormatSpecifierFlag_LeftJustify)) {
|
||||
for (size_t i = len + prefix_len + num_zeroes; i < static_cast<size_t>(width); i++) {
|
||||
WriteCharacter(' ');
|
||||
}
|
||||
}
|
||||
|
||||
/* Print out sign. */
|
||||
if (negative) {
|
||||
WriteCharacter('-');
|
||||
} else if (HasFlag(FormatSpecifierFlag_ForceSign)) {
|
||||
WriteCharacter('+');
|
||||
} else if (HasFlag(FormatSpecifierFlag_EmptySign)) {
|
||||
WriteCharacter(' ');
|
||||
}
|
||||
|
||||
/* Print out base prefix. */
|
||||
if (HasFlag(FormatSpecifierFlag_Hash)) {
|
||||
WriteCharacter('0');
|
||||
if (base == 2) {
|
||||
WriteCharacter('b');
|
||||
} else if (base == 16) {
|
||||
WriteCharacter('x');
|
||||
}
|
||||
}
|
||||
|
||||
/* Print out zeroes. */
|
||||
for (size_t i = 0; i < num_zeroes; i++) {
|
||||
WriteCharacter('0');
|
||||
}
|
||||
|
||||
/* Print out digits. */
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
WriteCharacter(buf[len - 1 - i]);
|
||||
}
|
||||
|
||||
/* Print out right padding. */
|
||||
if (HasFlag(FormatSpecifierFlag_LeftJustify)) {
|
||||
for (size_t i = len + prefix_len + num_zeroes; i < static_cast<size_t>(width); i++) {
|
||||
WriteCharacter(' ');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Output the integer. */
|
||||
if (is_unsigned) {
|
||||
uintmax_t n = 0;
|
||||
if (HasFlag(FormatSpecifierFlag_LongLong)) {
|
||||
n = static_cast<unsigned long long>(va_arg(vl, unsigned long long));
|
||||
} else if (HasFlag(FormatSpecifierFlag_Long)) {
|
||||
n = static_cast<unsigned long>(va_arg(vl, unsigned long));
|
||||
} else if (HasFlag(FormatSpecifierFlag_Char)) {
|
||||
n = static_cast<unsigned char>(va_arg(vl, unsigned int));
|
||||
} else if (HasFlag(FormatSpecifierFlag_Short)) {
|
||||
n = static_cast<unsigned short>(va_arg(vl, unsigned int));
|
||||
} else {
|
||||
n = static_cast<unsigned int>(va_arg(vl, unsigned int));
|
||||
}
|
||||
if (specifier == 'p' && n == 0) {
|
||||
WriteCharacter('(');
|
||||
WriteCharacter('n');
|
||||
WriteCharacter('i');
|
||||
WriteCharacter('l');
|
||||
WriteCharacter(')');
|
||||
} else {
|
||||
PrintInteger(false, n);
|
||||
}
|
||||
} else {
|
||||
intmax_t n = 0;
|
||||
if (HasFlag(FormatSpecifierFlag_LongLong)) {
|
||||
n = static_cast<signed long long>(va_arg(vl, signed long long));
|
||||
} else if (HasFlag(FormatSpecifierFlag_Long)) {
|
||||
n = static_cast<signed long>(va_arg(vl, signed long));
|
||||
} else if (HasFlag(FormatSpecifierFlag_Char)) {
|
||||
n = static_cast<signed char>(va_arg(vl, signed int));
|
||||
} else if (HasFlag(FormatSpecifierFlag_Short)) {
|
||||
n = static_cast<signed short>(va_arg(vl, signed int));
|
||||
} else {
|
||||
n = static_cast<signed int>(va_arg(vl, signed int));
|
||||
}
|
||||
const bool negative = n < 0;
|
||||
const uintmax_t u = (negative) ? static_cast<uintmax_t>(-n) : static_cast<uintmax_t>(n);
|
||||
PrintInteger(negative, u);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
{
|
||||
size_t len = 1;
|
||||
if (!HasFlag(FormatSpecifierFlag_LeftJustify)) {
|
||||
while (len++ < width) {
|
||||
WriteCharacter(' ');
|
||||
}
|
||||
}
|
||||
WriteCharacter(static_cast<char>(va_arg(vl, int)));
|
||||
if (HasFlag(FormatSpecifierFlag_LeftJustify)) {
|
||||
while (len++ < width) {
|
||||
WriteCharacter(' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
{
|
||||
const char *str = va_arg(vl, char *);
|
||||
if (str == nullptr) {
|
||||
str = "(null)";
|
||||
}
|
||||
|
||||
size_t len = Strnlen(str, precision > 0 ? precision : std::numeric_limits<size_t>::max());
|
||||
if (HasFlag(FormatSpecifierFlag_HasPrecision)) {
|
||||
len = (len < precision) ? len : precision;
|
||||
}
|
||||
if (!HasFlag(FormatSpecifierFlag_LeftJustify)) {
|
||||
while (len++ < width) {
|
||||
WriteCharacter(' ');
|
||||
}
|
||||
}
|
||||
while (*str && (!HasFlag(FormatSpecifierFlag_HasPrecision) || (precision--) != 0)) {
|
||||
WriteCharacter(*(str++));
|
||||
}
|
||||
if (HasFlag(FormatSpecifierFlag_LeftJustify)) {
|
||||
while (len++ < width) {
|
||||
WriteCharacter(' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '%':
|
||||
default:
|
||||
WriteCharacter(specifier);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure null termination. */
|
||||
WriteCharacter('\0');
|
||||
if (dst_size > 0) {
|
||||
dst[dst_size - 1] = '\0';
|
||||
}
|
||||
|
||||
/* Return number of characters that would have been printed sans the null terminator. */
|
||||
return static_cast<int>(dst_index) - 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
AMS_PRAGMA_END_OPTIMIZE()
|
||||
|
||||
int TVSNPrintf(char *dst, size_t dst_size, const char *fmt, std::va_list vl) {
|
||||
return TVSNPrintfImpl(dst, dst_size, fmt, vl);
|
||||
}
|
||||
|
||||
int TSNPrintf(char *dst, size_t dst_size, const char *fmt, ...) {
|
||||
std::va_list vl;
|
||||
va_start(vl, fmt);
|
||||
const int len = TVSNPrintf(dst, dst_size, fmt, vl);
|
||||
va_end(vl);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int VSNPrintf(char *dst, size_t dst_size, const char *fmt, std::va_list vl) {
|
||||
/* TODO: floating point support? */
|
||||
return TVSNPrintfImpl(dst, dst_size, fmt, vl);
|
||||
}
|
||||
|
||||
int SNPrintf(char *dst, size_t dst_size, const char *fmt, ...) {
|
||||
std::va_list vl;
|
||||
va_start(vl, fmt);
|
||||
const int len = VSNPrintf(dst, dst_size, fmt, vl);
|
||||
va_end(vl);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 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/>.
|
||||
*/
|
||||
#include <vapours.hpp>
|
||||
|
||||
namespace ams::util {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline const u8 CodePointByteLengthTable[0x100] = {
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
constexpr ALWAYS_INLINE size_t GetCodePointByteLength(u8 c) {
|
||||
return CodePointByteLengthTable[c];
|
||||
}
|
||||
|
||||
constexpr ALWAYS_INLINE bool IsValidTail(u8 c) {
|
||||
return (c & 0xC0) == 0x80;
|
||||
}
|
||||
|
||||
constexpr inline bool VerifyCode(const u8 *code, size_t size) {
|
||||
switch (size) {
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
if (!IsValidTail(code[1])) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (code[0] == 0xE0 && (code[1] & 0x20) == 0x00) {
|
||||
return false;
|
||||
}
|
||||
if (code[0] == 0xED && (code[1] & 0x20) != 0x00) {
|
||||
return false;
|
||||
}
|
||||
if (!IsValidTail(code[1]) || !IsValidTail(code[2])) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (code[0] == 0xF0 && (code[1] & 0x30) == 0x00) {
|
||||
return false;
|
||||
}
|
||||
if (code[0] == 0xF4 && (code[1] & 0x30) != 0x00) {
|
||||
return false;
|
||||
}
|
||||
if (!IsValidTail(code[1]) || !IsValidTail(code[2]) || !IsValidTail(code[3])) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool VerifyUtf8String(const char *str, size_t size) {
|
||||
return GetCodePointCountOfUtf8String(str, size) != -1;
|
||||
}
|
||||
|
||||
int GetCodePointCountOfUtf8String(const char *str, size_t size) {
|
||||
/* Check pre-conditions. */
|
||||
AMS_ASSERT(str != nullptr);
|
||||
AMS_ASSERT(size > 0);
|
||||
|
||||
/* Parse codepoints. */
|
||||
int count = 0;
|
||||
|
||||
while (size > 0) {
|
||||
/* Get and check the current codepoint. */
|
||||
const u8 *code = reinterpret_cast<const u8 *>(str);
|
||||
const size_t code_size = GetCodePointByteLength(code[0]);
|
||||
|
||||
if (code_size > size || !VerifyCode(code, code_size)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Advance. */
|
||||
str += code_size;
|
||||
size -= code_size;
|
||||
|
||||
/* Increment count. */
|
||||
++count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user