umd-livepatch/io_funcs.c
2025-04-04 18:11:02 +02:00

155 lines
4.3 KiB
C

/**
* @file io_funcs.c
* @author Karim Vergnes <me@thesola.io>
* @copyright GPLv2
* @brief Functions to interpose with UMD driver
*
* Functions to parse and manipulate devctl requests to the UMD driver,
* allowing for live redirection of read requests to a patch file.
*/
#include "io_funcs.h"
#include <string.h>
extern PspIoDrvFuncs reserveUmdFuncs;
static char umd_id[11] = {0};
static char first_read = 1;
static char hdr[ISO_SECTOR_SIZE];
/**
* @brief Convert layout-based address (LBA) to an absolute offset.
*
* @param param The LBA parameter data, as obtained from a read devctl.
*/
static inline u32
_impl_lp_UmdLba_toAddr(lp_UmdLba *param)
{
u32 offset;
if (!param->byte_size_start) {
offset = param->lba_top * ISO_SECTOR_SIZE;
} else if (param->byte_size_centre) {
offset = param->lba_top * ISO_SECTOR_SIZE - param->byte_size_start + ISO_SECTOR_SIZE;
} else if (!param->byte_size_last) {
offset = param->lba_top * ISO_SECTOR_SIZE + param->byte_size_start;
} else {
offset = param->lba_top * ISO_SECTOR_SIZE - param->byte_size_start + ISO_SECTOR_SIZE;
}
return offset;
}
/**
* @brief Read the header sector for the disc to memory.
*
* This function is meant to be read in response to an existing read devctl.
* The parameters below should be patched through from a previous devctl call.
*
* @param arg The devctl file argument to be passed through to the driver.
* @param devname The device name argument to be passed through to the driver.
*/
static inline int
_impl_lp_readDiscHeader(PspIoDrvFileArg *arg, const char *devname)
{
int ret;
lp_UmdLba param = {
.unknown1 = 0,
.cmd = 0, /* read */
.lba_top = 0x8000 / ISO_SECTOR_SIZE,
.lba_size = 1,
.byte_size_total = ISO_SECTOR_SIZE,
.byte_size_centre = ISO_SECTOR_SIZE,
.byte_size_start = 0,
.byte_size_last = 0
};
ret = reserveUmdFuncs.IoDevctl(arg, devname, lp_UmdDevctl_READ_SECTORS,
&param, sizeof param, &hdr, ISO_SECTOR_SIZE);
return ret;
}
/**
* @brief The effective low-level read command.
*
* This function is called whenever a devctl to read data off disc is
* intercepted. All parameters are provided from the @ref PspIoDrvFuncs::IoDevctl
* function call made by the caller, to allow seamless passthrough to the
* original driver.
*
* @param param The parsed {@ref lp_UmdLba} layout-based address.
* @param outdata The memory address where read data is expected.
* @param outlen The amount of data expected to be read.
*
* @see _impl_lp_lbaToAddr to convert the LBA into an absolute bytes offset.
*/
static int
_impl_lp_devctlRead(PspIoDrvFileArg *arg, const char *devname,
unsigned int cmd, lp_UmdLba *param, int inlen,
void *outdata, int outlen)
{
u32 offset = _impl_lp_UmdLba_toAddr(param);
int ret;
if (first_read) {
ret = reserveUmdFuncs.IoDevctl(arg, devname, cmd, param, inlen, outdata, outlen);
_impl_lp_readDiscHeader(arg, devname);
strncpy(umd_id, hdr + 0x373, 10);
umd_id[10] = 0;
Kprintf("Disc ID obtained: '%s'\n", umd_id);
first_read = 0;
return ret;
}
passthru:
return reserveUmdFuncs.IoDevctl(arg, devname, cmd, param, inlen, outdata, outlen);
}
int
patched_IoDevctl(PspIoDrvFileArg *arg, const char *devname,
unsigned int cmd, void *indata, int inlen,
void *outdata, int outlen)
{
switch ((lp_UmdDevctl) cmd) {
case lp_UmdDevctl_READ_GENERAL:
case lp_UmdDevctl_READ_CACHE:
case lp_UmdDevctl_READ_SECTORS:
return _impl_lp_devctlRead(arg, devname, cmd, (lp_UmdLba *) indata,
inlen, outdata, outlen);
default:
goto passthru;
}
passthru:
return reserveUmdFuncs.IoDevctl(arg, devname, cmd, indata, inlen, outdata, outlen);
}
int
patched_IoRead(PspIoDrvFileArg *arg, char *data, int len)
{
Kprintf("Reading UMD data, hum dee dum...\n");
return reserveUmdFuncs.IoRead(arg, data, len);
}
int
patched_IoOpen(PspIoDrvFileArg *arg, char *file, int flags, SceMode mode)
{
Kprintf("Opening UMD.\n");
return reserveUmdFuncs.IoOpen(arg, file, flags, mode);
}
void
lp_pingDiscRemoved(void)
{
first_read = 1;
}
// vim: ft=c.doxygen