mirror of
https://github.com/drakmor/ShadowMountPlus.git
synced 2026-10-22 06:32:25 -05:00
328 lines
17 KiB
C
328 lines
17 KiB
C
#ifndef SM_MOUNT_DEFS_H
|
|
#define SM_MOUNT_DEFS_H
|
|
|
|
#include <stdint.h>
|
|
|
|
#define IMAGE_MOUNT_READ_ONLY 1
|
|
|
|
// Build-time backend defaults. runtime config.ini can override both values.
|
|
// 1 = legacy /dev/mdctl, 0 = /dev/lvdctl.
|
|
#define EXFAT_ATTACH_USE_MDCTL 0
|
|
#define UFS_ATTACH_USE_MDCTL 0
|
|
|
|
/*
|
|
* Mount profiles and cache layers
|
|
* ================================================================
|
|
*
|
|
* LVD geometry
|
|
* ------------
|
|
* sector_size is the logical sector exported by /dev/lvdN. It controls BIO
|
|
* alignment and is not a filesystem allocation block. ShadowMount defaults to
|
|
* 512 bytes for exFAT and 4096 bytes for UFS/PFS.
|
|
*
|
|
* secondary_unit is the LVD mapping/split granularity. The optimized profile
|
|
* uses 64 KiB for all supported image filesystems. The legacy 1.6 profile uses
|
|
* sector_size except for exFAT, which already used 64 KiB. The value must be
|
|
* divisible by sector_size; 64 KiB is required by the type-5 BFS sdimg path.
|
|
*
|
|
* LVD attach ABI and image types
|
|
* ------------------------------
|
|
* The kernel exposes base attach V0, extended attach V1/Attach2 and detach.
|
|
* ShadowMount builds one complete V0 layer and marks it NO_BITMAP. source_type
|
|
* 1 is a regular file; source_type 2 is a device/special source.
|
|
*
|
|
* Accepted image types and kernel names:
|
|
* 0 Dfl, 1 GmI, 2 GmO, 3 PtcI, 4 PtcO, 5 Sv, 6 Trp, 7 Dwl,
|
|
* 8 AcI, 9 AcO, 10 MaI, 11 MaO, 12 Pcdwl.
|
|
*
|
|
* Types 0/5/6/7/12 use lvdstart_vnode_direct. Types 1-4/8-11 use
|
|
* lvdstart_vnode and resolve each secondary unit through layered bitmap and
|
|
* optional OTBL metadata. Optimized ShadowMount profiles are:
|
|
*
|
|
* .exfat type 5 Sv, direct + BFS sdimg batching
|
|
* .ffpkg (UFS) type 5 Sv, direct + BFS sdimg batching
|
|
* standalone .ffpfs type 5 Sv, direct
|
|
* outer .ffpfsc type 9 AcO, layered package outer
|
|
* inner .ffpfs/pfs_image.dat type 8 AcI, layered package inner
|
|
*
|
|
* exFAT, UFS/.ffpkg and standalone PFS/.ffpfs use the optimized profiles above
|
|
* only when their direct source path is under /data or /user. Other direct
|
|
* sources use the version 1.6 profiles: type 0 for exFAT, type 7 for UFS and
|
|
* the sector-size mapping unit for standalone PFS. The outer .ffpfsc container
|
|
* and its nested .ffpfs/pfs_image.dat images use the version 1.6 sector-size
|
|
* mapping unit regardless of the container's source path.
|
|
*
|
|
* ShadowMount deliberately uses type 5 with single/save attach flags for UFS
|
|
* images so a standalone .ffpkg on BFS can use sdimg batching. Type 7 with
|
|
* Download Data instead favors HDD request isolation. UFS nmount has
|
|
* no separate download-data flag: 0x10000000 is the generic MNT_NOATIME bit,
|
|
* not an UFS or DD mode. Do not substitute GmO/PtcO only to request the UFS
|
|
* block-map cache: that replaces direct dispatch with layered dispatch. The
|
|
* kernel requests that cache only for type 2/4, an HDD-class regular backing
|
|
* vnode, and a backing filesystem that is UFS.
|
|
*
|
|
* Type-5 BFS sdimg fast path
|
|
* -------------------------
|
|
* Type 5 labels the LVD provider "Ssd" and may enable BFS sdimg when the image
|
|
* is a regular file on internal BFS and secondary_unit is exactly 64 KiB. The
|
|
* kernel also applies platform/storage-mode and lower-provider guards.
|
|
*
|
|
* When enabled, lvd_worker accepts only requests whose length is exactly
|
|
* 64 KiB and whose backing-file offset is 64-KiB aligned. It collects the
|
|
* initial request plus up to 30 matching queued requests and submits them via
|
|
* bfs_iosession_rw_sdimg/BfsSdimg. All other requests fall back to the normal
|
|
* vnode path. This is block-I/O batching, not an exFAT cache or decompressor.
|
|
*
|
|
* For exFAT keep sector_size at 512 and create the filesystem with a 64-KiB
|
|
* allocation unit. Setting sector_size to 65536 does not request this mode;
|
|
* the relevant LVD value is secondary_unit. The path is unavailable through
|
|
* MD or when the image is backed by USB/UFS/PFS/exFAT instead of BFS.
|
|
*
|
|
* Raw flags and validator
|
|
* -----------------------
|
|
* Download-data flags exist only in the LVD attach ABI. ShadowMount calls
|
|
* /dev/lvdctl directly and normalizes wrapper-style presets:
|
|
* raw 0x8/0x9 -> single/save-data family 0x14/0x1c;
|
|
* raw 0xc/0xd -> download-data family 0x16/0x1e.
|
|
* Firmware pairs raw bit 0 with read-only variants; it is not an independent
|
|
* LVD read-only flag. The normalized validator masks are:
|
|
*
|
|
* type 0,5,6,8-11: (flags & 0x92) == 0x10
|
|
* type 1: (flags & 0x82) == 0x80
|
|
* type 2,3,4: (flags & 0x82) == 0
|
|
* type 7,12: (flags & 0x92) == 0x12
|
|
*
|
|
* Download Data HDD worker policy
|
|
* -------------------------------
|
|
* Download Data is normalized LVD bit 0x02 and is required by types 7 and 12.
|
|
* On an HDD-class provider it makes LVD create two request workers instead of
|
|
* one: worker 0 is labelled System (lvdhdsw), worker 1 Game (lvdhdgw). The LVD
|
|
* start routine routes each BIO using the kernel classification byte at
|
|
* BIO + 0x172: value 1 selects Game and every other value selects System. LVD
|
|
* also tracks and drains the workers separately during shutdown. The flag is
|
|
* therefore an HDD queue-isolation/QoS policy, not an on-disk-format property.
|
|
*
|
|
* Download Data does not select direct BIO dispatch (image_type does), enable
|
|
* bitmap/OTBL interpretation, populate the UFS block-map cache (types 2/4),
|
|
* unlock BFS sdimg batching (type 5), or enable PFS/compression caches. For a
|
|
* standalone image, type 7 + Download Data is the HDD-oriented choice when
|
|
* System/Game queue separation matters. Type 5 + Single/Save is preferable for
|
|
* a BFS sdimg-backed .ffpkg when maximum sequential/batched throughput matters
|
|
* and the two-queue HDD policy is not needed.
|
|
*
|
|
* Layer metadata and independent caches
|
|
* -------------------------------------
|
|
* LVD bitmap says whether a logical unit is present in a layer. V1 OTBL maps a
|
|
* logical unit to a physical unit inside the layer file. It is unrelated to a
|
|
* PFSC compressed-offset table.
|
|
*
|
|
* LVD bitmap/OTBL image-layer presence/remapping
|
|
* UFS block-map cache backing UFS file blocks -> physical UFS blocks
|
|
* PFS GDDR5 ioctl PFSC compressed offsets and optional full ICV
|
|
*
|
|
* The UFS cache depends on the filesystem containing the image, not UFS stored
|
|
* inside .ffpkg, and is unavailable for BFS/PFS/exFAT backing files. The PFS
|
|
* metadata ioctl applies to every nested image backed by pfs, ppr_pfs,
|
|
* transaction_pfs, or a forwarding overlay; the filesystem inside the image
|
|
* does not need to be PFS.
|
|
*
|
|
* PFS/PPR compression dispatch
|
|
* ----------------------------
|
|
* Classic pfs and ppr_pfs are distinct kernel read paths. A classic compressed
|
|
* PFS block uses the classic PFSC header/table interpretation and the pfs path
|
|
* dispatches its compressed payload to zlib. The PPR variant interprets the
|
|
* PPR block metadata and can dispatch Kraken. LVD image_type, the nested
|
|
* selector and PFS_GDDR5_CACHE_IOCTL do not select a decompressor and cannot
|
|
* turn a classic zlib block header into a PPR/Kraken block. The image creator
|
|
* must emit the matching on-disk block records and offset table.
|
|
*
|
|
* Compression is decided per block. Files excluded from compression and blocks
|
|
* that are not smaller after compression are stored as raw blocks but remain
|
|
* represented in the same image offset mapping. This is independent of the
|
|
* mount-time caches described below.
|
|
*
|
|
* PFS compression caches and big-app reserve
|
|
* ------------------------------------------
|
|
* Three independent mechanisms have similar historical GDDR5/SBRAM cache
|
|
* names and must not be treated as one allocation:
|
|
*
|
|
* 1. A compressed PFS/PFSC mount can allocate its own cmp_offtbl cache. The
|
|
* kernel message
|
|
*
|
|
* pfs_allocate_cmp_offtbl_cache(): cmp_offtbl is cached
|
|
*
|
|
* confirms this mount-local compressed-block offset table. It is created
|
|
* by the PFS mount path and is not evidence that a big-app memory reserve
|
|
* exists.
|
|
*
|
|
* 2. PFS_GDDR5_CACHE_IOCTL can be issued on a nested image's backing vnode
|
|
* before LVD attach when nested_pfs_index_cache=1.
|
|
* The ioctl asks the containing PFS/PPR read path to cache the
|
|
* compressed-offset table used to reach that vnode's data; it does not
|
|
* inspect or impose a format on the bytes stored inside the file. Thus the
|
|
* nested file may contain PFS, UFS, exFAT or arbitrary block data and need
|
|
* not itself be compressed. The hard requirement for the offset request is
|
|
* that the containing PFS/PPR image has its compressed-image flag set. On
|
|
* an uncompressed containing image the kernel logs "NOT a compressed
|
|
* image" and accepts the request as a compatibility no-op.
|
|
*
|
|
* cmp_cache_units is a count of shared-cache allocation units, not bytes.
|
|
* The compressed-offset pool uses 0x10000-byte units and has 600 units
|
|
* (37.5 MiB) with at most eight simultaneous slots. A zero count makes the
|
|
* kernel derive the required count from the on-disk offset-table geometry;
|
|
* this is the preferred full-table/default request. An explicit nonzero
|
|
* count limits the reservation and can force more LRU refills. EBUSY is
|
|
* non-fatal: it means that this vnode already has a cache or that all eight
|
|
* slots are occupied. ENOMEM means that the requested units exceed the
|
|
* remaining total or contiguous shared-cache space.
|
|
*
|
|
* The optional full-ICV request is independent. It requires the containing
|
|
* image's signed-image flag and usable ICV metadata; otherwise the kernel
|
|
* rejects that half of the request. It also has its own eight-slot pool.
|
|
* Neither half selects zlib/Kraken or changes the block-record format.
|
|
*
|
|
* 3. The 180-MiB big-app reserve is application-lifecycle memory. It is not
|
|
* allocated by the ioctl, LVD, PFS nmount, EnsureBigAppBudget, or a visible
|
|
* /dev/sbram device. During BEGIN_APP_MOUNT_EVENT the kernel allocates
|
|
* 0x0B400000 bytes aligned to 0x200000 for selected category values:
|
|
*
|
|
* 0x0000F000, 0x0000F100, 0x0001F000, 0x0001F300,
|
|
* 0x0002F000, 0x0200F100.
|
|
*
|
|
* The allocation belongs to the big-app/application context. PFS can bind
|
|
* to it through the application lifecycle callbacks; userland is not given
|
|
* a buffer pointer. EndAppMount tears the context down and releases the
|
|
* reserve after the application pre-delete path. Consequently a managed
|
|
* image stack must remain mounted until the matching EndAppMount hook, and
|
|
* no explicit metadata-cache clear should run while its PFS vnode is live.
|
|
*
|
|
* For unsigned ShadowMount PFS profiles sigverify=0, so request only compressed
|
|
* offsets (cache_full_icv=0). Signing does not make ordinary reads faster: it
|
|
* enables integrity verification and can add work; a cached full ICV table
|
|
* only reduces metadata lookup cost when verification is actually enabled.
|
|
* Both compressed-offset caches are properties of the outer PFS read path.
|
|
* They can accelerate an LVD-backed nested exFAT/UFS/PFS image because the
|
|
* inner filesystem format is irrelevant at that layer.
|
|
*
|
|
* Backend and nmount policy
|
|
* -------------------------
|
|
* LVD is the default for every format. MD is a compatibility backend for
|
|
* UFS/exFAT; it uses AUTOUNIT|ASYNC and configurable logical sectors (modern
|
|
* UFS default 4096, exFAT default 512). Read-only state
|
|
* is applied to the backing device and nmount. The shared nmount profiles are:
|
|
*
|
|
* UFS ufs + budgetid + async + noatime + automounted
|
|
* exFAT exfatfs + large + static-timezone + async + noatime + ignoreacl
|
|
* PFS pfs + AC + game + sigverify/playgo/disc=0 + zero ekpfs
|
|
* + async + noatime
|
|
*
|
|
* The optimized profile passes only MNT_RDONLY (0x1) in nmount's numeric flags
|
|
* argument. Legacy UFS also passes MNT_NOATIME, matching version 1.6. Generic
|
|
* named options are decoded by the kernel as async=MNT_ASYNC (0x40),
|
|
* noatime=MNT_NOATIME (0x10000000), and automounted=MNT_AUTOMOUNTED
|
|
* (0x200000000). UFS options compressedfile, iochunk, largewrite and related
|
|
* zone-aware modes are separate filesystem features and are not DD flags.
|
|
*
|
|
* force is appended only for explicit recovery. Unsigned PFS keeps signature
|
|
* verification disabled. The source path selects one profile before attach;
|
|
*/
|
|
|
|
#define LVD_CTRL_PATH "/dev/lvdctl"
|
|
#define MD_CTRL_PATH "/dev/mdctl"
|
|
#define SCE_LVD_IOC_ATTACH_V0 0xC0286D00
|
|
#define SCE_LVD_IOC_ATTACH_V1 0xC0286D09
|
|
#define SCE_LVD_IOC_DETACH 0xC0286D01
|
|
#define LVD_ATTACH_IO_VERSION_V0 0u
|
|
#define LVD_ATTACH_IO_VERSION_V1 1u
|
|
#define LVD_ATTACH_RAW_BIT_RO_VARIANT 0x0001u
|
|
#define LVD_ATTACH_RAW_BIT_INNER_PROFILE 0x0002u
|
|
#define LVD_ATTACH_RAW_BIT_DOWNLOAD_DATA_PROFILE 0x0004u
|
|
#define LVD_ATTACH_RAW_BIT_SINGLE_SAVE_PROFILE 0x0008u
|
|
#define LVD_ATTACH_RAW_PASSTHROUGH_MASK 0x8000u
|
|
|
|
#define LVD_ATTACH_FLAG_DOWNLOAD_DATA_PROFILE 0x0002u
|
|
#define LVD_ATTACH_FLAG_BASE 0x0004u
|
|
#define LVD_ATTACH_FLAG_RO_VARIANT 0x0008u
|
|
#define LVD_ATTACH_FLAG_SINGLE_SAVE_PROFILE 0x0010u
|
|
#define LVD_ATTACH_FLAG_INNER_PROFILE 0x0080u
|
|
|
|
#define LVD_ATTACH_RAW_FLAGS_SINGLE_RW LVD_ATTACH_RAW_BIT_SINGLE_SAVE_PROFILE
|
|
#define LVD_ATTACH_RAW_FLAGS_SINGLE_RO \
|
|
(LVD_ATTACH_RAW_FLAGS_SINGLE_RW | LVD_ATTACH_RAW_BIT_RO_VARIANT)
|
|
#define LVD_ATTACH_RAW_FLAGS_DD_RW \
|
|
(LVD_ATTACH_RAW_BIT_SINGLE_SAVE_PROFILE | \
|
|
LVD_ATTACH_RAW_BIT_DOWNLOAD_DATA_PROFILE)
|
|
#define LVD_ATTACH_RAW_FLAGS_DD_RO \
|
|
(LVD_ATTACH_RAW_FLAGS_DD_RW | LVD_ATTACH_RAW_BIT_RO_VARIANT)
|
|
|
|
#define LVD_ATTACH_FLAGS_SINGLE_RW \
|
|
(LVD_ATTACH_FLAG_BASE | LVD_ATTACH_FLAG_SINGLE_SAVE_PROFILE)
|
|
#define LVD_ATTACH_FLAGS_SINGLE_RO \
|
|
(LVD_ATTACH_FLAGS_SINGLE_RW | LVD_ATTACH_FLAG_RO_VARIANT)
|
|
#define LVD_ATTACH_FLAGS_DD_RW \
|
|
(LVD_ATTACH_FLAGS_SINGLE_RW | LVD_ATTACH_FLAG_DOWNLOAD_DATA_PROFILE)
|
|
#define LVD_ATTACH_FLAGS_DD_RO \
|
|
(LVD_ATTACH_FLAGS_DD_RW | LVD_ATTACH_FLAG_RO_VARIANT)
|
|
#define LVD_SECTOR_SIZE_EXFAT 512u
|
|
#define LVD_SECTOR_SIZE_UFS 4096u
|
|
#define LVD_SECTOR_SIZE_PFS 4096u
|
|
#define LVD_SECONDARY_UNIT_IMAGE_IO 0x10000u
|
|
#define MD_SECTOR_SIZE_EXFAT 512u
|
|
#define MD_SECTOR_SIZE_UFS_OPTIMIZED 4096u
|
|
_Static_assert(LVD_SECONDARY_UNIT_IMAGE_IO % LVD_SECTOR_SIZE_EXFAT == 0,
|
|
"exFAT LVD geometry must be aligned");
|
|
_Static_assert(LVD_SECONDARY_UNIT_IMAGE_IO % LVD_SECTOR_SIZE_UFS == 0,
|
|
"UFS LVD geometry must be aligned");
|
|
_Static_assert(LVD_SECONDARY_UNIT_IMAGE_IO % LVD_SECTOR_SIZE_PFS == 0,
|
|
"PFS LVD geometry must be aligned");
|
|
#define LVD_ATTACH_IMAGE_TYPE_SINGLE 0
|
|
#define LVD_ATTACH_IMAGE_TYPE_SAVE_DATA 5
|
|
#define LVD_ATTACH_IMAGE_TYPE_DOWNLOAD_DATA 7
|
|
#define PFS_NESTED_OUTER_IMG_TYPE 0x02u
|
|
#define PFS_NESTED_INNER_IMG_TYPE 0x82u
|
|
#define LVD_ATTACH_LAYER_COUNT 1
|
|
#define LVD_ENTRY_TYPE_FILE 1
|
|
#define LVD_ENTRY_TYPE_SPECIAL 2
|
|
#define LVD_ENTRY_FLAG_NO_BITMAP 0x1
|
|
#define LVD_NODE_WAIT_US 100000
|
|
#define LVD_NODE_WAIT_RETRIES 100
|
|
// PFS nmount values used by ShadowMount. Other firmware flows also expose
|
|
// transaction_pfs/ppr_pfs, SD/GD key modes and system budget IDs.
|
|
#define DEVPFS_BUDGET_GAME "game"
|
|
#define DEVPFS_BUDGET_SYSTEM "system"
|
|
#define DEVPFS_MKEYMODE_SD "SD"
|
|
#define DEVPFS_MKEYMODE_GD "GD"
|
|
#define DEVPFS_MKEYMODE_AC "AC"
|
|
#define PFS_MOUNT_BUDGET_ID DEVPFS_BUDGET_GAME
|
|
#define PFS_MOUNT_MKEYMODE DEVPFS_MKEYMODE_AC
|
|
#define PFS_MOUNT_SIGVERIFY 0
|
|
#define PFS_MOUNT_PLAYGO 0
|
|
#define PFS_MOUNT_DISC 0
|
|
#define PFS_GDDR5_CACHE_IOCTL 0x80209168
|
|
|
|
// 0x80209168 is an IOC_IN request with a 0x20-byte payload and is accepted on a
|
|
// read-only fd. Despite the userland "GDDR5 cache" name, the pfs and ppr_pfs
|
|
// vnode handlers use it for two metadata caches belonging to the containing
|
|
// filesystem: compressed offsets (only when its compressed-image flag is set)
|
|
// and, optionally, full ICV (only for a signed image). The nested file's own
|
|
// magic, filesystem and compression method are not inspected. Counts are
|
|
// allocation units rather than bytes; zero asks the kernel to derive the full
|
|
// size. The caller must have the kernel privilege checked as privilege 0x2ab.
|
|
typedef struct pfs_gddr5_cache_request {
|
|
uint8_t cache_cmp_offsets;
|
|
uint8_t reserved01[7];
|
|
uint64_t cmp_cache_units; // 0 = auto; each cmp unit is 0x10000 bytes
|
|
uint8_t cache_full_icv;
|
|
uint8_t reserved11[7];
|
|
uint64_t icv_cache_units; // 0 = auto; independent signed-image ICV pool
|
|
} pfs_gddr5_cache_request_t;
|
|
|
|
_Static_assert(sizeof(pfs_gddr5_cache_request_t) == 0x20,
|
|
"PFS GDDR5 cache request must be 0x20 bytes");
|
|
|
|
// 4x64-bit PFS key encoded as 64 hex chars.
|
|
#define PFS_ZERO_EKPFS_KEY_HEX \
|
|
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
|
|
#endif
|