diff --git a/include/sm_mount_defs.h b/include/sm_mount_defs.h index 088a8ab..b78ab99 100644 --- a/include/sm_mount_defs.h +++ b/include/sm_mount_defs.h @@ -9,36 +9,46 @@ #define UFS_ATTACH_USE_MDCTL 0 // --- LVD Definitions --- -// - ioctl: ATTACH=0xC0286D00, DETACH=0xC0286D01, ATTACH2 path=0xC0286D09. -// - single-image path uses raw option flags 0x8/0x9 -> normalized 0x14/0x1C. -// - DownloadData/LWFS path (imgtype=7) uses normalized options 0x16/0x1E. -// - image_type values accepted by validator: 0..0xC (13 values total). -// this code uses image_type=7 for UFS (DownloadData-like), 0 for generic path. -// - layer source_type observed: 1=file, 2=char/block-like source (/dev/sbram0). -// - layer entry flag bit0 is "no bitmap file specified". +// Kernel exposes base attach (V0), extended attach (V1/Attach2) and detach. +// This code only builds V0 layer descriptors, so it uses the base attach path. +// Wrapper-side raw attach presets: +// - 0x8/0x9 -> single-image/save-data family (normalized 0x14/0x1C) +// - 0xC/0xD -> download-data/LWFS family (normalized 0x16/0x1E) +// raw bit3 (0x8) sets the DONT_MAP_BM-style mode, raw bit2 (0x4) switches to +// the DD/LWFS family, and raw bit0 (0x1) selects the alternate preset inside a +// family. Firmware pairs bit0 with read-only mount variants, but this is not a +// standalone LVD readonly bit by itself. +// image_type values accepted by validator: 0..0xC (13 values total). +// layer source_type observed: 1=file, 2=device/special source (/dev/sbram0, char/block). +// layer descriptor flag bit0 is "no bitmap file specified". #define LVD_CTRL_PATH "/dev/lvdctl" #define MD_CTRL_PATH "/dev/mdctl" -#define SCE_LVD_IOC_ATTACH 0xC0286D00 +#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 1 -#define LVD_ATTACH_OPTION_FLAGS_DEFAULT 0x9 -#define LVD_ATTACH_OPTION_FLAGS_RW 0x8 -#define LVD_ATTACH_OPTION_NORM_DD_RO 0x1E -#define LVD_ATTACH_OPTION_NORM_DD_RW 0x16 +#define LVD_ATTACH_IO_VERSION_V0 0u +#define LVD_ATTACH_IO_VERSION_V1 1u +#define LVD_ATTACH_RAW_FLAGS_SINGLE_RO 0x9 +#define LVD_ATTACH_RAW_FLAGS_SINGLE_RW 0x8 +#define LVD_ATTACH_RAW_FLAGS_DD_RO 0xD +#define LVD_ATTACH_RAW_FLAGS_DD_RW 0xC +# #define LVD_SECTOR_SIZE_EXFAT 512u #define LVD_SECTOR_SIZE_UFS 4096u -#define LVD_SECTOR_SIZE_PFS 32768u +#define LVD_SECTOR_SIZE_PFS 4096u +#define LVD_SECONDARY_UNIT_SINGLE_IMAGE 0x10000u #define MD_SECTOR_SIZE_EXFAT 512u #define MD_SECTOR_SIZE_UFS 512u // Raw option bits are normalized by sceFsLvdAttachCommon before validation: // raw:0x1->norm:0x08, raw:0x2->norm:0x80, raw:0x4->norm:0x02, raw:0x8->norm:0x10. // The normalized masks are then checked against validator constraints (0x82/0x92). -#define LVD_ATTACH_IMAGE_TYPE 0 +#define LVD_ATTACH_IMAGE_TYPE_SINGLE 0 #define LVD_ATTACH_IMAGE_TYPE_UFS_DOWNLOAD_DATA 7 -#define LVD_ATTACH_IMAGE_TYPE_PFS_SAVE_DATA 0 // also works 5 +#define LVD_ATTACH_IMAGE_TYPE_PFS_SAVE_DATA 5 #define LVD_ATTACH_LAYER_COUNT 1 #define LVD_ATTACH_LAYER_ARRAY_SIZE 3 #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 diff --git a/include/sm_types.h b/include/sm_types.h index 6c4b63a..c4aecce 100644 --- a/include/sm_types.h +++ b/include/sm_types.h @@ -29,14 +29,12 @@ typedef struct { } devpfs_mount_profile_t; typedef struct { - // Source object class (observed: 1=file, 2=device-like source). + // Source object class (observed: 1=file, 2=device-like/special source). uint16_t source_type; - // Layer behavior flags (observed bit0 = no bitmap file path). - uint8_t entry_flags; + // Layer descriptor flags (observed bit0 = no bitmap file path). + uint16_t flags; // Must be zero. - uint8_t reserved0; - // Must be zero. - uint32_t reserved1; + uint32_t reserved0; // Backing file or device path. const char *path; // Data start offset in backing object (bytes). @@ -49,28 +47,28 @@ typedef struct { uint64_t bitmap_offset; // Bitmap size (bytes), 0 when bitmap is unused. uint64_t bitmap_size; -} lvd_kernel_layer_t; +} lvd_ioctl_layer_v0_t; typedef struct { - // Protocol version for /dev/lvdctl ioctl payload (valid <=1). + // Protocol version for /dev/lvdctl ioctl payload (0 = V0/base attach). uint32_t io_version; // Input: usually -1 for auto-assign. Output: created lvd unit id. int32_t device_id; - // Sector-like size fields used by LVD attach request validation. - // In refs these are populated from statfs and clamped to <= 4096. - uint32_t sector_size_0; - uint32_t sector_size_1; - // Encoded option length derived from option flags (0x14 for 0x8, 0x1C for 0x9). - uint16_t option_len; - // LVD image type id (validator accepts 0..0xC; this code uses 0). + // User-visible sector size exported by /dev/lvdN. + uint32_t sector_size; + // Secondary unit/granularity validated against sector_size. + uint32_t secondary_unit; + // Normalized attach flags produced from wrapper raw options. + uint16_t flags; + // LVD image type id (validator accepts 0..0xC). uint16_t image_type; // Number of valid entries in layers_ptr. uint32_t layer_count; // Total exported virtual size (bytes). uint64_t device_size; - // Pointer to layer array in user payload. - lvd_kernel_layer_t *layers_ptr; -} lvd_ioctl_attach_t; + // Pointer to V0 layer array in user payload. + lvd_ioctl_layer_v0_t *layers_ptr; +} lvd_ioctl_attach_v0_t; typedef struct { // Must be zero. diff --git a/src/sm_image.c b/src/sm_image.c index 6dcf089..9be2aeb 100644 --- a/src/sm_image.c +++ b/src/sm_image.c @@ -23,7 +23,7 @@ static bool wait_for_dev_node_state(const char *devname, bool should_exist) { return false; } -static uint32_t get_lvd_sector_size(image_fs_type_t fs_type) { +static uint32_t get_lvd_sector_size_fallback(image_fs_type_t fs_type) { const runtime_config_t *cfg = runtime_config(); switch (fs_type) { case IMAGE_FS_UFS: @@ -36,6 +36,31 @@ static uint32_t get_lvd_sector_size(image_fs_type_t fs_type) { } } +static uint32_t get_lvd_sector_size(const char *path, image_fs_type_t fs_type) { + uint32_t fallback = get_lvd_sector_size_fallback(fs_type); + if (!path) + return fallback; + + struct statfs sfs; + if (statfs(path, &sfs) != 0) + return fallback; + + uint64_t fs_cluster_size = (uint64_t)sfs.f_bsize; + if (fs_cluster_size == 0) + fs_cluster_size = (uint64_t)sfs.f_iosize; + if (fs_cluster_size == 0 || fs_cluster_size >= (uint64_t)fallback) + return fallback; + + return (uint32_t)fs_cluster_size; +} + +static uint32_t get_lvd_secondary_unit(const char *path, + image_fs_type_t fs_type) { + if (fs_type == IMAGE_FS_EXFAT) + return LVD_SECONDARY_UNIT_SINGLE_IMAGE; + return get_lvd_sector_size(path, fs_type); +} + static uint32_t get_md_sector_size(image_fs_type_t fs_type) { const runtime_config_t *cfg = runtime_config(); switch (fs_type) { @@ -54,14 +79,14 @@ static unsigned int get_md_attach_options(bool mount_read_only) { return options; } -static uint16_t get_lvd_attach_option(image_fs_type_t fs_type, - bool mount_read_only) { - if (fs_type == IMAGE_FS_UFS) { - return mount_read_only ? LVD_ATTACH_OPTION_NORM_DD_RO - : LVD_ATTACH_OPTION_NORM_DD_RW; +static uint16_t get_lvd_attach_raw_flags(image_fs_type_t fs_type, + bool mount_read_only) { + if (fs_type == IMAGE_FS_UFS) { + return mount_read_only ? LVD_ATTACH_RAW_FLAGS_DD_RO + : LVD_ATTACH_RAW_FLAGS_DD_RW; } - return mount_read_only ? LVD_ATTACH_OPTION_FLAGS_DEFAULT - : LVD_ATTACH_OPTION_FLAGS_RW; + return mount_read_only ? LVD_ATTACH_RAW_FLAGS_SINGLE_RO + : LVD_ATTACH_RAW_FLAGS_SINGLE_RW; } static unsigned int get_nmount_flags(image_fs_type_t fs_type, @@ -77,15 +102,38 @@ static unsigned int get_nmount_flags(image_fs_type_t fs_type, return mount_read_only ? MNT_RDONLY : 0; } -static uint16_t lvd_option_len_from_flags(uint16_t options) { - if ((options & 0x800Eu) != 0u) { - uint32_t raw = (uint32_t)options; +static uint16_t normalize_lvd_raw_flags(uint16_t raw_flags) { + if ((raw_flags & 0x800Eu) != 0u) { + uint32_t raw = (uint32_t)raw_flags; uint32_t len = (raw & 0xFFFF8000u) + ((raw & 2u) << 6) + (8u * (raw & 1u)) + (2u * ((raw >> 2) & 1u)) + (2u * (raw & 8u)) + 4u; return (uint16_t)len; } - return (uint16_t)(8u * ((uint32_t)options & 1u) + 4u); + return (uint16_t)(8u * ((uint32_t)raw_flags & 1u) + 4u); +} + +static uint16_t get_lvd_image_type(image_fs_type_t fs_type) { + if (fs_type == IMAGE_FS_UFS) + return LVD_ATTACH_IMAGE_TYPE_UFS_DOWNLOAD_DATA; + if (fs_type == IMAGE_FS_PFS) + return LVD_ATTACH_IMAGE_TYPE_PFS_SAVE_DATA; + return LVD_ATTACH_IMAGE_TYPE_SINGLE; +} + +static uint16_t get_lvd_source_type(const char *path) { + if (!path) + return LVD_ENTRY_TYPE_FILE; + + if (strncmp(path, "/dev/sbram0", strlen("/dev/sbram0")) == 0) + return LVD_ENTRY_TYPE_SPECIAL; + + struct stat st; + if (stat(path, &st) == 0) { + if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) + return LVD_ENTRY_TYPE_SPECIAL; + } + return LVD_ENTRY_TYPE_FILE; } // --- Image Path and Naming Helpers --- @@ -271,49 +319,42 @@ static bool attach_lvd_backend(const char *file_path, image_fs_type_t fs_type, return false; } - lvd_kernel_layer_t *layers = - calloc(LVD_ATTACH_LAYER_ARRAY_SIZE, sizeof(*layers)); - if (!layers) { - log_debug(" [IMG] calloc layers failed"); - close(lvd_fd); - return false; - } - - layers[0].source_type = LVD_ENTRY_TYPE_FILE; - layers[0].entry_flags = LVD_ENTRY_FLAG_NO_BITMAP; + lvd_ioctl_layer_v0_t layers[LVD_ATTACH_LAYER_COUNT]; + memset(layers, 0, sizeof(layers)); + layers[0].source_type = get_lvd_source_type(file_path); + layers[0].flags = LVD_ENTRY_FLAG_NO_BITMAP; layers[0].path = file_path; layers[0].offset = 0; layers[0].size = (uint64_t)file_size; - lvd_ioctl_attach_t req; + uint32_t sector_size = get_lvd_sector_size(file_path, fs_type); + uint32_t secondary_unit = get_lvd_secondary_unit(file_path, fs_type); + uint16_t raw_flags = get_lvd_attach_raw_flags(fs_type, mount_read_only); + uint16_t normalized_flags = normalize_lvd_raw_flags(raw_flags); + + lvd_ioctl_attach_v0_t req; memset(&req, 0, sizeof(req)); - req.io_version = LVD_ATTACH_IO_VERSION; - req.image_type = (fs_type == IMAGE_FS_UFS) - ? LVD_ATTACH_IMAGE_TYPE_UFS_DOWNLOAD_DATA - : (fs_type == IMAGE_FS_PFS) - ? LVD_ATTACH_IMAGE_TYPE_PFS_SAVE_DATA - : LVD_ATTACH_IMAGE_TYPE; + req.io_version = LVD_ATTACH_IO_VERSION_V0; + req.image_type = get_lvd_image_type(fs_type); req.layer_count = LVD_ATTACH_LAYER_COUNT; req.device_size = (uint64_t)file_size; req.layers_ptr = layers; - req.sector_size_0 = get_lvd_sector_size(fs_type); - req.sector_size_1 = req.sector_size_0; - - uint16_t attach_option = get_lvd_attach_option(fs_type, mount_read_only); - req.option_len = (fs_type == IMAGE_FS_UFS) ? attach_option - : lvd_option_len_from_flags(attach_option); + req.sector_size = sector_size; + req.secondary_unit = secondary_unit; + req.flags = normalized_flags; req.device_id = -1; int last_errno = 0; - log_debug(" [IMG][%s] attach try: ver=%u sec=%u options=0x%x len=0x%x", + log_debug(" [IMG][%s] attach try: ver=%u sec=%u sec2=%u raw=0x%x " + "flags=0x%x img=%u", attach_backend_name(ATTACH_BACKEND_LVD), req.io_version, - req.sector_size_0, attach_option, req.option_len); - int ret = ioctl(lvd_fd, SCE_LVD_IOC_ATTACH, &req); + req.sector_size, req.secondary_unit, raw_flags, req.flags, + req.image_type); + int ret = ioctl(lvd_fd, SCE_LVD_IOC_ATTACH_V0, &req); if (ret != 0) last_errno = errno; close(lvd_fd); int unit_id = req.device_id; - free(layers); if (ret != 0) { errno = last_errno; @@ -580,7 +621,8 @@ static bool validate_mounted_image(const char *file_path, image_fs_type_t fs_typ uint32_t min_device_sector = (attach_backend == ATTACH_BACKEND_MD) ? get_md_sector_size(fs_type) - : get_lvd_sector_size(fs_type); + : get_lvd_sector_size(file_path, + fs_type); uint64_t fs_block_size = (uint64_t)mounted_sfs.f_bsize; if (fs_block_size >= (uint64_t)min_device_sector) return true;