PS2 Julian's various PS2 projects (Worklog)

Hm... Doesn't the MechaCon still have some small RAM? What about that?

Very interesting would be writing to registers!
The range is inside the RAM, but it is only +255 bytes from the start of the region where the model name is stored.
If you are talking about ARM registers, if it's not the LR register it's not very useful.

If you are talking about memory mapped I/O, it's outside the range.
 
If my assumption is correct, it should be possible to bitbang SPI from SSBUSC address/data/CS2 lines to DEV2.

Normally, the SPI process goes for one byte:
Enable CS
Disable SCLK
For each bit
->Set wanted value to MOSI
->Enable SCLK
->Read result value to MISO
->Disable SCLK
->Wait (if needed)
Disable CS

What I suspect will happen for SSBUSC bitbanging SPI (not sure of the ordering, if it is correct, or if it will work):

For each bit
->Enable CS
->Set wanted value to MOSI, Enable SCLK
->Disable CS
->Enable CS
->Read result value to MISO, Disable SCLK
->Disable CS
->Wait (if needed)

Presuming delay register -> 0x00183444
so configuration is:
WRDL = 0xf (write delay 16 cycles -> 432 ns)
RDDL = 0x4 (read delay 5 cycles -> 135 ns)
RECV = 0
HOLD = 0
FLOT = 1
STRB = 0
ATYP = 1 (16 bit)
AINC = 1 (enabled)
IOIS16 = 0
EXDL = 0
DECR = 0x18 (16777216 bytes / 16MB)
DMAT = 0
ADER = 0
DMAF = 0
WDMA = 0
WAIT = 0
(could probably optimize this further e.g. FLOT, ATYP, AINC, DECR)

A0 -> SCLK, A1 -> MOSI, D0 -> MISO, /CS2 -> CS

Code:
u8 val = in_val;
u8 out_val = 0;
*(vu16 *)(0xBE000000);
for (int i = 0; i < 8; i += 1)
{
    (void)*(vu16 *)(0xBE000001 | ((val & 0x80) ? 2 : 0));
    val <<= 1;
    out_val |= ((*(vu16 *)(0xBE000000) & 1) != 0) ? 1 : 0;
    out_val <<= 1;
    // Could probably mess with the RDDL value or add a busy loop here if we need more delay
}
return out_val
 
Last edited:
A note about 1103515245/0x41C64E6D and 123456789/0x75BCD15: these constants are just part of a rand implementation, usually used for clearing memory.

I don't think there is much point in it however since it increases entropy and makes it obvious that something else was supposed to be there
 
Notes on sio2man versions:


PsIIsio2man 13 (in ROM)
IRX ID: "sio2man", 1, 1
Export version: 1.1

PsIIsio2man 14
IRX ID: "sio2man", 1, 2
Export version: 1.2
Adds export 26 sio2_transfer_reset, mirrors of exports 4-24, multitap related function

PsIIsio2man 15
IRX ID: "sio2man", 2, 1
Export version: 1.2

PsIIsio2man 1600
IRX ID: "sio2man", 2, 1
Export version: 1.2

PsIIsio2man 2000
IRX ID: "sio2man", 2, 4
Export version: 2.3
Adds exports 55-60 related to multitap

PsIIsio2man 2100
IRX ID: "sio2man", 2, 4
Export version: 2.3

PsIIsio2man 2200
IRX ID: "sio2man", 2, 4
Export version: 2.3

PsIIsio2man 2300
IRX ID: "sio2man", 2, 4
Export version: 2.3

PsIIsio2man 2400
IRX ID: "sio2man", 2, 5
Export version: 2.3
Adds exports 61-62 (called from sio2d, libpad2, and libmc2)

PsIIsio2man 2500
IRX ID: "sio2man", 2, 5
Export version: 2.3

PsIIsio2man 2600
IRX ID: "sio2man", 3, 0
Export version: 2.4
Adds export 63 (called from sio2d, libmc2)

PsIIsio2man 2800
IRX ID: "sio2man", 3, 0
Export version: 2.4

PsIIsio2man 3000
IRX ID: "sio2man", 3, 1
Export version: 2.4

PsIIsio2man 3030
IRX ID: "sio2man", 3, 17
Export version: 2.7
Adds export 64 (users?), 65 (users?), 66 (users?), 67 (wait on interrupt sema, called from mcman/libmc2 fast function)
The locking mechanism for transfers has been changed from event flags to semaphores

PsIIsio2man 3100
IRX ID: "sio2man", 3, 17
Export version: 2.7
 
Last edited:
I'll probably implement sio2man/mtapman/padman/mcman/mcserv changes in stages:
1. Everything except the below
2. R/W fast code
3. Filesystem related code

Also, I'll try to get the existing libraries working with xfstests so that I can test and make sure it won't eat data
 
Definitions from Linux kernel 2.2.14-5.0 that are used by the mrp and sam kernel modules:

Code:
typedef uint8_t u8;
typedef uint32_t u32;
typedef unsigned short     __kernel_mode_t;
typedef __kernel_mode_t          mode_t;
typedef long long     __kernel_loff_t;
typedef __kernel_loff_t          loff_t;
typedef unsigned long     __kernel_ino_t;
typedef __kernel_ino_t          ino_t;
typedef unsigned short     __kernel_uid_t;
typedef __kernel_uid_t          uid_t;
struct fown_struct {
     int pid;          /* pid or -pgrp where SIGIO should be sent */
     uid_t uid, euid;     /* uid/euid of process setting the owner */
     int signum;          /* posix.1b rt signal to be delivered on IO */
};
typedef int (*filldir_t)(void *, const char *, int, off_t, ino_t);
typedef unsigned short kdev_t;
typedef unsigned short     __kernel_nlink_t;
typedef __kernel_nlink_t     nlink_t;
typedef unsigned short     __kernel_gid_t;
typedef __kernel_gid_t          gid_t;
typedef long          __kernel_off_t;
typedef __kernel_off_t          off_t;

// Main types

struct file {
     struct file          *f_next, **f_pprev;
     struct dentry          *f_dentry;
     struct file_operations     *f_op;
     mode_t               f_mode;
     loff_t               f_pos;
     unsigned int           f_count, f_flags;
     unsigned long           f_reada, f_ramax, f_raend, f_ralen, f_rawin;
     struct fown_struct     f_owner;
     unsigned int          f_uid, f_gid;
     int               f_error;

     unsigned long          f_version;

     /* needed for tty driver, and maybe others */
     void               *private_data;
};
struct file_operations {
     loff_t (*llseek) (struct file *, loff_t, int);
     ssize_t (*read) (struct file *, char *, size_t, loff_t *);
     ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
     int (*readdir) (struct file *, void *, filldir_t);
     unsigned int (*poll) (struct file *, struct poll_table_struct *);
     int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
     int (*mmap) (struct file *, struct vm_area_struct *);
     int (*open) (struct inode *, struct file *);
     int (*flush) (struct file *);
     int (*release) (struct inode *, struct file *);
     int (*fsync) (struct file *, struct dentry *);
     int (*fasync) (int, struct file *, int);
     int (*check_media_change) (kdev_t dev);
     int (*revalidate) (kdev_t dev);
     int (*lock) (struct file *, int, struct file_lock *);
};

struct pci_dev {
     struct pci_bus     *bus;          /* bus this device is on */
     struct pci_dev     *sibling;     /* next device on this bus */
     struct pci_dev     *next;          /* chain of all devices */

     void          *sysdata;     /* hook for sys-specific extension */
     struct proc_dir_entry *procent;     /* device entry in /proc/bus/pci */

     unsigned int     devfn;          /* encoded device & function index */
     unsigned short     vendor;
     unsigned short     device;
     // Unofficial: the following member has been renamed
     unsigned int     class_;          /* 3 bytes: (base,sub,prog-if) */
     unsigned int     hdr_type;     /* PCI header type */
     unsigned int     master : 1;     /* set if device is master capable */
     /*
      * In theory, the irq level can be read from configuration
      * space and all would be fine.  However, old PCI chips don't
      * support these registers and return 0 instead.  For example,
      * the Vision864-P rev 0 chip can uses INTA, but returns 0 in
      * the interrupt line and pin registers.  pci_init()
      * initializes this field with the value at PCI_INTERRUPT_LINE
      * and it is the job of pcibios_fixup() to change it if
      * necessary.  The field must not be 0 unless the device
      * cannot generate interrupts at all.
      */
     unsigned int     irq;          /* irq generated by this device */

     /* Base registers for this device, can be adjusted by
      * pcibios_fixup() as necessary.
      */
     unsigned long     base_address[6];
     unsigned long     rom_address;
};

struct proc_dir_entry {
     unsigned short low_ino;
     unsigned short namelen;
     const char *name;
     mode_t mode;
     nlink_t nlink;
     uid_t uid;
     gid_t gid;
     unsigned long size;
     struct inode_operations * ops;
     int (*get_info)(char *, char **, off_t, int, int);
     void (*fill_inode)(struct inode *, int);
     struct proc_dir_entry *next, *parent, *subdir;
     void *data;
     int (*read_proc)(char *page, char **start, off_t off,
                int count, int *eof, void *data);
     int (*write_proc)(struct file *file, const char *buffer,
                 unsigned long count, void *data);
     int (*readlink_proc)(struct proc_dir_entry *de, char *page);
     unsigned int count;     /* use count */
     int deleted;          /* delete flag */
};
struct pt_regs {
     long ebx;
     long ecx;
     long edx;
     long esi;
     long edi;
     long ebp;
     long eax;
     int  xds;
     int  xes;
     long orig_eax;
     long eip;
     int  xcs;
     long eflags;
     long esp;
     int  xss;
};
struct timer_list {
     struct timer_list *next; /* MUST be first element */
     struct timer_list *prev;
     unsigned long expires;
     unsigned long data;
     void (*function)(unsigned long);
};
struct wait_queue {
     struct task_struct * task;
     struct wait_queue * next;
};

typedef struct poll_table_struct {
    struct poll_table_struct * next;
    unsigned int nr;
    struct poll_table_entry * entry;
} poll_table;

typedef struct { unsigned long pgprot; } pgprot_t;

// Prototypes

int /* asmlinkage */ printk(const char * fmt, ...) /* __attribute__ ((format (printf, 1, 2))) */;
int __verify_write(const void * addr, unsigned long size);
int del_timer(struct timer_list * timer);
int pci_read_config_byte(struct pci_dev *dev, u8 where, u8 *val);
int pci_read_config_dword(struct pci_dev *dev, u8 where, u32 *val);
int pcibios_find_device (unsigned short vendor, unsigned short dev_id, unsigned short index, unsigned char *bus, unsigned char *dev_fn);
int pcibios_present (void);
int pcibios_read_config_byte (unsigned char bus, unsigned char dev_fn, unsigned char where, unsigned char *val);
int pcibios_read_config_dword (unsigned char bus, unsigned char dev_fn, unsigned char where, unsigned int *val);
int pcibios_write_config_dword (unsigned char bus, unsigned char dev_fn, unsigned char where, unsigned int val);
int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp);
int proc_unregister(struct proc_dir_entry * dir, int ino);
int register_chrdev(unsigned int major, const char * name, struct file_operations * fops);
int remap_page_range(unsigned long from, unsigned long to, unsigned long size, pgprot_t prot);
int request_irq(unsigned int irq, void (*handler)(int irq, void * dev, struct pt_regs * regs), unsigned long flags, const char *device, void *dev_id);
int sprintf(char * buf, const char * fmt, ...);
int unregister_chrdev(unsigned int major, const char * name);
struct pci_dev * pci_find_device (unsigned int vendor, unsigned int device, struct pci_dev *from);
unsigned long __generic_copy_from_user(void * to, const void * from, unsigned long n);
unsigned long __generic_copy_to_user(void * to, const void * from, unsigned long n);
void * __ioremap(unsigned long offset, unsigned long size, unsigned long flags);
void * kmalloc(size_t size, int flags);
void /* __fastcall */ __wake_up(struct wait_queue ** p, unsigned int mode);
void /* __fastcall */ interruptible_sleep_on(struct wait_queue ** p);
void __const_udelay(unsigned long usecs);
void __get_user_1(void);
void __pollwait(struct file * filp, struct wait_queue ** wait_address, poll_table *p);
void __put_user_1(void);
void __put_user_4(void);
void __udelay(unsigned long usecs);
void add_timer(struct timer_list * timer);
void free_irq(unsigned int irq, void *dev_id);
void iounmap(void *addr);
void kfree_s(const void *objp, size_t size);
 
Some notes regarding .text section changes:

dev9
changes 3000->3020
no changes 3020->3100

atad
changes 3000->3020
changes 3020->3100

pfs
changes 3000->3020
changes 3020->3100

hdd
changes 3000->3020
no changes 3020->3100
 
I'm looking at some USB IR blasters for remote power/reset on PS2, and I see a lot of Sony IRB-BR1 on eBay.

It doesn't seem like it has been RE'd, so I went ahead and ordered one.

This is what I have found so far

Part numbers:
1-845-283-21
1-845-283-12
Product ID PID 2212 / 0x8a4
Vendor ID VID 1356 / 0x54c
054c:08a4
Device name: Sony Corp. IRB0136747

I extracted the rootfs of the Sony TV that was posted here: https://xdaforums.com/t/sony-android-tv-users.3096901/page-85

USB serial with custom PID/VID. Not sure if it is generic or cp210x. Definitely not pl2303.
Haven't found how the custom PID/VID gets automatically detected as a USB serial device by a quick grep for the PID/VID in the root filesystem.

Code in IRBlaster.apk (com.sony.dtv.irblaster) and IRBRecommendation.apk (com.sony.dtv.irbrecommendation)
is UI related.
Code in IrbService.apk (com.sony.dtv.irbservice) checks for PID/VID as mentioned above, and the USB serial device to appear in /dev/ttyUSB0 or /dev/ttyUSB1. Baud rate is 19200.
This appears to rely on QuickSetSDK from UEI (QuicksetSDK_EU.apk / com.uei.quicksetsdk.sony). Dalvik binaries are obfuscated and relies on some native armv7 binaries.

These magic bytes appear to be sent to initialize the controller (libqs.so:QS_get_codeset_binary("u_rst", outbuf, outlen)):
00020E64A6C53A780C03573F49D02BE5BA35D31A2E0914BE8CA55AA5

The device appears to be "P5MiniQS".

These magic bytes appear to be sent to get the version after initialization:
4f696e69
 
Last edited:
My take on the EE memory controller bug (mirrored here: https://gist.github.com/uyjulian/2c79789ec437511b726909783a56c7f6 )

DTL-T10000 has 8 RDRAM chips (16MB each for a total of 128MB), but retail models have 2 chips (16MB each for a total of 32MB)
For DESR, S148, and S256, they also have 2 chips but are higher capacity (32MB each for a total of 64MB)

What I suspect is that the DMA controller can only handle 2 chips properly. Any higher count and the bug condition can occur.

If I know exactly what causes the bug and how to detect it, it shouldn't be too hard to test out on DESR and see if the same bug also occurs there.
 
Here is some pcode for updating DVRP.
Untested, not responsible for breaking your system, etc.

Code:
// xFlash_Dvrp_UpdateFirmware p-code
const char *path_to_firmware = "host:path/firmware.udm"; // needs to be absolute path parsable by ioman(X)
u8 tmpbuf[2];

LoadStartModule("dvr.irx", ...);
LoadStartModule("dvrmisc.irx", ...);
if (devctl("dvr:", 0x5646, 0, 0, tmpbuf, sizeof(tmpbuf)) < 0) goto fail; // pre_update_a
if (devctl("dvr:", 0x5647, tmpbuf, sizeof(tmpbuf), 0, 0) < 0) goto fail; // pre_update_b
if (devctl("dvr_misc:", 0x5676, path_to_firmware, strlen(path_to_firmware) + 1, 0, 0) < 0) goto fail; // update_dvrp_firmware
for (;;)
{
	u32 status_arg;
	usleep(500000); // .5 seconds
	if (devctl("dvr_misc:", 0x5677, 0, 0, &status_arg, sizeof(status_arg)) < 0) goto fail; // flash_write_status
	if ((status_arg & 0xFFFF) == 0xFFFF) goto fail;
	if ((status_arg & 0xFFFF) == 2)
	{
		if (unk_param)
		{
			char inarg[1];
			char outarg[16];
			inarg[0] = 0;
			// Sets 0x2000238 to 1 in mechacon firmware 5.14. Reboot related?
			sceCdApplySCmd(61 /* 0x3D */, inarg, sizeof(inarg), outarg, sizeof(outarg));
		}
		return 0;
	}
}
fail:
return -1;
 
Some functions in bn to take special note of:

_sce_dnas2_symbol60010 - Script (.bnx -> .amx) decryption
Script__Abort - sysUpdateGetUrl - "/opt0/bn/sysconf/BNUPDATE" file decryption
Script__Abort2 - Sound (.bde -> .bd; .hde -> .hd; .sqe -> .sq) decryption
Script__Abort3 - Model (.bmo -> .gmo) decryption
_sce_dnas_pwrap_main - self code decryption
bnReboot - return to osdsys/launch game
 
Some ways I'm aware of to capture toslink, optical out, s/pdif audio without needing DAC or ADC

* USB to TOSLINK in/RX (not USB to TOSLINK out/TX)
* Chain a HDMI embedder/inserter that has TOSLINK input (not output) with a HDMI capture card
* Older Macs after a certain period have TOSLINK input (with an adapter)
 
Last edited:
Some DESR HDD cryptoanalysis:

Usually, in APA partition layout, __mbr partition at 0x4 is "APA\x00" and next partition at 0x1404 is "APA\x00".
The __mbr partition also has "Sony Computer Entertainment Inc." at 0x100, while next partition at 0x1500 is just NULL bytes.

The encryption appears to differ for sector % (mod) 2 (unconfirmed).
Additionally, the __mbr partition and next partition 0x100, 0x1500 contents are the same, implying that the byte offsets have shifted or are being rearranged.

Encryption is done in 16 byte intervals, and is not simple xorpad. The relation appears to be for one set of decrypted data, there exists one set of encrypted data.
AES-ECB matches this behavior.
Bruteforcing a 128 bit (16 byte) key would be infeasible, but with partial key overwrite it should be able to make it feasible (presumably 64 bit (8 byte) are known, but their positions/usage are unknown).
This pattern also makes it so that HDD dumps are somewhat more compressible since the encryption mapping is the same no matter which 16-byte unit position is being encrypted.
 
Last edited:

Similar threads

Back
Top