Talk:BD Drive Reverse Engineering: Difference between revisions

From PS3 Developer wiki
Jump to navigation Jump to search
(Created page with "= Getting keys the easier way = First you need to get the sv_iso_spu_module.self individuals seed that can be found in the metadata header: <pre> 3E C2 XX XX XX XX XX XX XX...")
 
 
(5 intermediate revisions by 4 users not shown)
Line 1: Line 1:
= Getting keys the easier way =
= Getting keys the easier way =
== EID4 ==


First you need to get the sv_iso_spu_module.self individuals seed that can be found in the metadata header:  
First you need to get the sv_iso_spu_module.self individuals seed that can be found in the metadata header:  


<pre>
  3E C2 0C 17 02 19 01 97 8A 29 71 79 38 29 D3 08
3E C2 XX XX XX XX XX XX XX XX XX XX XX XX XX XX
  04 29 FA 84 E3 3E 7F 73 0C 1D 41 6E EA 25 CA FB
04 29 XX XX XX XX XX XX XX XX XX XX XX XX XX XX
  3D E0 2B C0 05 EA 49 0B 03 E9 91 98 F8 3F 10 1F
3D E0 XX XX XX XX XX XX XX XX XX XX XX XX XX XX
  1B A3 4B 50 58 94 28 AD D2 B3 EB 3F F4 C3 1A 58
1B A3 XX XX XX XX XX XX XX XX XX XX XX XX XX XX
</pre>


Using your console specific eid_root_key/iv (e.g. from metldr dump) and libeeid (insert the seed in keys.c) you can then decrypt EID4 directly by calling  
Using your console specific eid_root_key/iv (e.g. from metldr dump) and libeeid (insert the seed in keys.c) you can then decrypt EID4 directly by calling  


<syntaxhighlight lang="c">
u8 eid4[0x30] = {/* your EID4 */};
eid4_decrypt_buffer(eid4);
</syntaxhighlight>
Now you got the two keys you need in eid4+0x00 and eid4+0x10 :)
== EID2 ==
First you need to get the fdm_spu_module.self individuals seed that can be found in the metadata header:
  74 92 E5 7C 2C 7C 63 F4 49 42 26 8F B4 1C 58 ED
  66 83 41 F9 C9 7B 29 83 96 FA 9D 82 07 51 99 D8
  BC 1A 93 4B 37 4F A3 8D 46 AF 94 C7 C3 33 73 B3
  09 57 20 84 FE 2D E3 44 57 E0 F8 52 7A 34 75 3D
Using your console specific eid_root_key/iv (e.g. from metldr dump) and libeeid (insert the seed, key and IV in keys.c) you can then decrypt EID2 directly by calling
<syntaxhighlight lang="c">
u8 eid2[0x730] = {/* your EID2 */};
p_block = eid2_generate_block_buffer(p_block_enc, EID2_BLOCKTYPE_P);
s_block = eid2_generate_block_buffer(s_block_enc, EID2_BLOCKTYPE_S);
eid2_decrypt_block(p_block + 0x10, 0x60);
eid2_decrypt_block(s_block + 0x10, 0x670);
</syntaxhighlight>
= SACD ripper =
source: [http://sacd-ripper.googlecode.com/svn-history/r58/trunk/sacdread/sacd_lv2_storage.c]
<pre>
<pre>
u8 eid4[0x30] = {/* your EID4 */};
 
eid4_decrypt_buffer(eid4);
/* The generic packet command opcodes for CD/DVD Logical Units,
* From Table 57 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */
#define GPCMD_GET_CONFIGURATION     0x46
#define GPCMD_GET_EVENT_STATUS_NOTIFICATION 0x4a
#define GPCMD_MODE_SELECT_10     0x55
#define GPCMD_MODE_SENSE_10     0x5a
#define GPCMD_READ_CD     0xbe
#define GPCMD_READ_DVD_STRUCTURE     0xad
#define GPCMD_READ_TRACK_RZONE_INFO     0x52
#define GPCMD_READ_TOC_PMA_ATIP     0x43
#define GPCMD_REPORT_KEY     0xa4
#define GPCMD_SEND_KEY     0xa3
 
#define LV2_STORAGE_SEND_ATAPI_COMMAND (1)
 
struct lv2_atapi_cmnd_block {
    uint8_t pkt[32]; /* packet command block          */
    uint32_t pktlen; /* should be 12 for ATAPI 8020    */
    uint32_t blocks;
    uint32_t block_size;
    uint32_t proto; /* transfer mode                  */
    uint32_t in_out; /* transfer direction            */
    uint32_t unknown;
} __attribute__((packed));
 
typedef struct {
    int fd;
    uint64_t device_id;
    sys_ppu_thread_t thread_id;
    sys_mutex_t read_async_mutex_id;
    sys_event_queue_t queue;
int io_buffer;
int io_buffer_piece;
} sac_accessor_driver_t;
 
enum lv2_atapi_proto {
    NON_DATA_PROTO = 0,
    PIO_DATA_IN_PROTO = 1,
    PIO_DATA_OUT_PROTO = 2,
    DMA_PROTO = 3
};
 
enum lv2_atapi_in_out {
    DIR_WRITE = 0, /* memory -> device */
    DIR_READ = 1 /* device -> memory */
};
 
 
static inline void init_atapi_cmnd_block(
        struct lv2_atapi_cmnd_block *atapi_cmnd
        , uint32_t block_size
        , uint32_t proto
        , uint32_t type) {
    memset(atapi_cmnd, 0, sizeof(struct lv2_atapi_cmnd_block));
    atapi_cmnd->pktlen = 12;
    atapi_cmnd->blocks = 1;
    atapi_cmnd->block_size = block_size; /* transfer size is block_size * blocks */
    atapi_cmnd->proto = proto;
    atapi_cmnd->in_out = type;
}
 
 
static inline int sys_storage_send_atapi_command(uint32_t fd, struct lv2_atapi_cmnd_block *atapi_cmnd, uint8_t *buffer) {
    uint64_t tag;
    system_call_7(SYS_STORAGE_EXECUTE_DEVICE_COMMAND
            , fd
            , LV2_STORAGE_SEND_ATAPI_COMMAND
            , (uint32_t) atapi_cmnd
            , sizeof (struct lv2_atapi_cmnd_block)
            , (uint32_t) buffer
            , atapi_cmnd->block_size
            , (uint32_t) & tag);
 
    return_to_user_prog(int);
}
 
 
int ps3rom_lv2_get_configuration(int fd, uint8_t *buffer) {
    int res;
    struct lv2_atapi_cmnd_block atapi_cmnd;
 
    init_atapi_cmnd_block(&atapi_cmnd, 0x10, PIO_DATA_IN_PROTO, DIR_READ);
    atapi_cmnd.pkt[0] = GPCMD_GET_CONFIGURATION;
    atapi_cmnd.pkt[1] = 0;
    atapi_cmnd.pkt[2] = 0xff;
    atapi_cmnd.pkt[3] = 0x41;
    atapi_cmnd.pkt[8] = 0x10;
 
    res = sys_storage_send_atapi_command(fd, &atapi_cmnd, buffer);
    // if (buffer[10] & 1 == 0) return 0xF000FFFF
    // if (buffer[0] & 1 != 0) exec_mode_sense
    return res;
}
 
int ps3rom_lv2_mode_sense(int fd, uint8_t *buffer) {
    int res;
    struct lv2_atapi_cmnd_block atapi_cmnd;
 
    init_atapi_cmnd_block(&atapi_cmnd, 0x10, PIO_DATA_IN_PROTO, DIR_READ);
 
    atapi_cmnd.pkt[0] = GPCMD_MODE_SENSE_10;
    atapi_cmnd.pkt[1] = 0x08;
    atapi_cmnd.pkt[2] = 0x03;
    atapi_cmnd.pkt[8] = 0x10;
 
    res = sys_storage_send_atapi_command(fd, &atapi_cmnd, buffer);
    // if (buffer[11] == 2) exec_mode_select
    return res;
}
 
int ps3rom_lv2_mode_select(int fd) {
    int res;
    struct lv2_atapi_cmnd_block atapi_cmnd;
    static uint8_t buffer[256];
    memset(buffer, 0, sizeof (buffer));
 
    buffer[1] = 0x0e;
    buffer[7] = 8;
    buffer[8] = 3;
    buffer[9] = 6;
    buffer[11] = 3; // ? 3 == SACD
    buffer[255] = 0x10;
 
    init_atapi_cmnd_block(&atapi_cmnd, 0x10, PIO_DATA_OUT_PROTO, DIR_WRITE);
 
    atapi_cmnd.pkt[0] = GPCMD_MODE_SELECT_10;
    atapi_cmnd.pkt[1] = 0x10; /* PF */
    atapi_cmnd.pkt[8] = 0x10;
 
    res = sys_storage_send_atapi_command(fd, &atapi_cmnd, buffer);
 
    return res;
}
 
int ps3rom_lv2_enable_encryption(int fd, uint8_t *buffer, uint32_t lba) {
    int res;
    struct lv2_atapi_cmnd_block atapi_cmnd;
 
    init_atapi_cmnd_block(&atapi_cmnd, 0x0a, PIO_DATA_IN_PROTO, DIR_READ);
 
    atapi_cmnd.pkt[0] = GPCMD_READ_DVD_STRUCTURE;
    atapi_cmnd.pkt[1] = 2;
   
atapi_cmnd.pkt[2] = lba >> 24;
atapi_cmnd.pkt[3] = lba >> 16;
atapi_cmnd.pkt[4] = lba >> 8;
atapi_cmnd.pkt[5] = lba & 0xff;
   
    atapi_cmnd.pkt[9] = 0x0a;
 
    res = sys_storage_send_atapi_command(fd, &atapi_cmnd, buffer);
 
    return res;
}
 
int ps3rom_lv2_get_event_status_notification(int fd, uint8_t *buffer) {
    int res;
    struct lv2_atapi_cmnd_block atapi_cmnd;
 
    init_atapi_cmnd_block(&atapi_cmnd, 0x08, PIO_DATA_IN_PROTO, DIR_READ);
 
    atapi_cmnd.pkt[0] = GPCMD_GET_EVENT_STATUS_NOTIFICATION;
    atapi_cmnd.pkt[1] = 1; /* IMMED */
    atapi_cmnd.pkt[4] = 4; /* media event */
    atapi_cmnd.pkt[8] = 0x08;
 
    res = sys_storage_send_atapi_command(fd, &atapi_cmnd, buffer);
 
    return res;
}
 
int ps3rom_lv2_report_key_start(int fd, uint8_t *buffer) {
    int res;
    struct lv2_atapi_cmnd_block atapi_cmnd;
 
    init_atapi_cmnd_block(&atapi_cmnd, 0x08, PIO_DATA_IN_PROTO, DIR_READ);
 
    atapi_cmnd.pkt[0] = GPCMD_REPORT_KEY;
 
    atapi_cmnd.pkt[2] = 0;
    atapi_cmnd.pkt[3] = 0;
    atapi_cmnd.pkt[4] = 0;
    atapi_cmnd.pkt[5] = 0x08;
 
    atapi_cmnd.pkt[6] = 0;
    atapi_cmnd.pkt[7] = 0x10;
 
    res = sys_storage_send_atapi_command(fd, &atapi_cmnd, buffer);
 
    return res;
}
 
int ps3rom_lv2_send_key(int fd, uint8_t agid, uint32_t key_size, uint8_t *key, uint8_t sequence) {
    int res;
    struct lv2_atapi_cmnd_block atapi_cmnd;
    uint32_t buffer_size;
    uint8_t buffer[256];
    uint8_t buffer_align = 0;
   
    if ((key_size & 3) != 0) {
      buffer_align = ~(key_size & 3) + 4 + 1;
    }
    buffer_size = key_size + 4 + buffer_align;
 
    init_atapi_cmnd_block(&atapi_cmnd, buffer_size, PIO_DATA_OUT_PROTO, DIR_WRITE);
 
    atapi_cmnd.pkt[0] = GPCMD_SEND_KEY;
 
atapi_cmnd.pkt[2] = buffer_size >> 24;
atapi_cmnd.pkt[3] = buffer_size >> 16;
atapi_cmnd.pkt[4] = buffer_size >> 8;
atapi_cmnd.pkt[5] = buffer_size & 0xff;
   
    atapi_cmnd.pkt[6] = sequence;
    atapi_cmnd.pkt[7] = 0x10;
    atapi_cmnd.pkt[10] = agid;
 
memset(buffer, 0, sizeof(buffer));
 
buffer[0] = key_size >> 24;
buffer[1] = key_size >> 16;
buffer[2] = key_size >> 8;
buffer[3] = key_size & 0xff;
    memcpy(buffer + 4, key, key_size);
 
if (buffer_align != 0) {
  memset(buffer + key_size + 4, 0, buffer_align);
  }
 
    res = sys_storage_send_atapi_command(fd, &atapi_cmnd, buffer);
 
    return res;
}
 
int ps3rom_lv2_report_key(int fd, uint8_t agid, uint32_t *key_size, uint8_t *key, uint8_t sequence) {
    int res;
    struct lv2_atapi_cmnd_block atapi_cmnd;
    uint32_t buffer_size;
    uint8_t buffer[256];
    uint8_t buffer_align = 0;
    uint32_t new_key_size, old_key_size = *key_size;
 
    memset(buffer, 0xff, sizeof(buffer));
   
    if ((old_key_size & 3) != 0) {
      buffer_align = ~(old_key_size & 3) + 4 + 1;
    }
    buffer_size = old_key_size + 4 + buffer_align;
 
    init_atapi_cmnd_block(&atapi_cmnd, buffer_size, PIO_DATA_IN_PROTO, DIR_READ);
 
    atapi_cmnd.pkt[0] = GPCMD_REPORT_KEY;
 
atapi_cmnd.pkt[2] = buffer_size >> 24;
atapi_cmnd.pkt[3] = buffer_size >> 16;
atapi_cmnd.pkt[4] = buffer_size >> 8;
atapi_cmnd.pkt[5] = buffer_size & 0xff;
 
    atapi_cmnd.pkt[6] = sequence;
    atapi_cmnd.pkt[7] = 0x10;
    atapi_cmnd.pkt[10] = agid;
   
    res = sys_storage_send_atapi_command(fd, &atapi_cmnd, buffer);
 
    new_key_size = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
    *key_size = new_key_size;
 
    memcpy(key, buffer + 4, (old_key_size > new_key_size ? new_key_size : old_key_size));
 
    return res;
}
 
int ps3rom_lv2_report_key_finish(int fd, uint8_t agid) {
    int res;
    struct lv2_atapi_cmnd_block atapi_cmnd;
 
    init_atapi_cmnd_block(&atapi_cmnd, 0, NON_DATA_PROTO, DIR_READ);
 
    atapi_cmnd.pkt[0] = GPCMD_REPORT_KEY;
 
    atapi_cmnd.pkt[6] = 0xff;
    atapi_cmnd.pkt[7] = 0x10;
    atapi_cmnd.pkt[10] = agid;
 
    res = sys_storage_send_atapi_command(fd, &atapi_cmnd, 0);
 
    return res;
}
 
 
int ps3rom_lv2_read_toc_header(int fd, uint8_t *buffer) {
    int res;
    struct lv2_atapi_cmnd_block atapi_cmnd;
 
    init_atapi_cmnd_block(&atapi_cmnd, 12, PIO_DATA_IN_PROTO, DIR_READ);
 
    atapi_cmnd.pkt[0] = GPCMD_READ_TOC_PMA_ATIP;
    atapi_cmnd.pkt[6] = 0;
    atapi_cmnd.pkt[8] = 12; /* LSB of length */
 
    res = sys_storage_send_atapi_command(fd, &atapi_cmnd, buffer);
 
    return res;
}
 
int ps3rom_lv2_read_toc_entry(int fd, uint8_t *buffer) {
    int res;
    struct lv2_atapi_cmnd_block atapi_cmnd;
 
    init_atapi_cmnd_block(&atapi_cmnd, 12, PIO_DATA_IN_PROTO, DIR_READ);
 
    atapi_cmnd.pkt[0] = GPCMD_READ_TOC_PMA_ATIP;
    atapi_cmnd.pkt[6] = 0x01;
    atapi_cmnd.pkt[8] = 12; /* LSB of length */
 
    res = sys_storage_send_atapi_command(fd, &atapi_cmnd, buffer);
 
    return res;
}
 
int ps3rom_lv2_read_track(int fd, uint8_t *buffer, uint8_t track) {
    int res;
    struct lv2_atapi_cmnd_block atapi_cmnd;
 
    init_atapi_cmnd_block(&atapi_cmnd, 48, PIO_DATA_IN_PROTO, DIR_READ);
 
    atapi_cmnd.pkt[0] = GPCMD_READ_TRACK_RZONE_INFO;
    atapi_cmnd.pkt[1] = 1;
    atapi_cmnd.pkt[5] = track; /* track */
    atapi_cmnd.pkt[8] = 48; /* LSB of length */
 
    res = sys_storage_send_atapi_command(fd, &atapi_cmnd, buffer);
 
    return res;
}
</pre>
</pre>
Now you got the two keys you need in eid4+0x00 and eid4+0x10 :)

Latest revision as of 17:40, 24 December 2015

Getting keys the easier way[edit source]

EID4[edit source]

First you need to get the sv_iso_spu_module.self individuals seed that can be found in the metadata header:

 3E C2 0C 17 02 19 01 97 8A 29 71 79 38 29 D3 08
 04 29 FA 84 E3 3E 7F 73 0C 1D 41 6E EA 25 CA FB
 3D E0 2B C0 05 EA 49 0B 03 E9 91 98 F8 3F 10 1F
 1B A3 4B 50 58 94 28 AD D2 B3 EB 3F F4 C3 1A 58

Using your console specific eid_root_key/iv (e.g. from metldr dump) and libeeid (insert the seed in keys.c) you can then decrypt EID4 directly by calling

 u8 eid4[0x30] = {/* your EID4 */};
 eid4_decrypt_buffer(eid4);

Now you got the two keys you need in eid4+0x00 and eid4+0x10 :)

EID2[edit source]

First you need to get the fdm_spu_module.self individuals seed that can be found in the metadata header:

 74 92 E5 7C 2C 7C 63 F4 49 42 26 8F B4 1C 58 ED
 66 83 41 F9 C9 7B 29 83 96 FA 9D 82 07 51 99 D8
 BC 1A 93 4B 37 4F A3 8D 46 AF 94 C7 C3 33 73 B3
 09 57 20 84 FE 2D E3 44 57 E0 F8 52 7A 34 75 3D

Using your console specific eid_root_key/iv (e.g. from metldr dump) and libeeid (insert the seed, key and IV in keys.c) you can then decrypt EID2 directly by calling

 u8 eid2[0x730] = {/* your EID2 */};
 p_block = eid2_generate_block_buffer(p_block_enc, EID2_BLOCKTYPE_P);
 s_block = eid2_generate_block_buffer(s_block_enc, EID2_BLOCKTYPE_S);
 eid2_decrypt_block(p_block + 0x10, 0x60);
 eid2_decrypt_block(s_block + 0x10, 0x670);

SACD ripper[edit source]

source: [1]


/* The generic packet command opcodes for CD/DVD Logical Units,
 * From Table 57 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */
#define GPCMD_GET_CONFIGURATION		    0x46
#define GPCMD_GET_EVENT_STATUS_NOTIFICATION 0x4a
#define GPCMD_MODE_SELECT_10		    0x55
#define GPCMD_MODE_SENSE_10		    0x5a
#define GPCMD_READ_CD			    0xbe
#define GPCMD_READ_DVD_STRUCTURE	    0xad
#define GPCMD_READ_TRACK_RZONE_INFO	    0x52
#define GPCMD_READ_TOC_PMA_ATIP		    0x43
#define GPCMD_REPORT_KEY		    0xa4
#define GPCMD_SEND_KEY			    0xa3

#define LV2_STORAGE_SEND_ATAPI_COMMAND	(1)

struct lv2_atapi_cmnd_block {
    uint8_t pkt[32]; /* packet command block           */
    uint32_t pktlen; /* should be 12 for ATAPI 8020    */
    uint32_t blocks;
    uint32_t block_size;
    uint32_t proto; /* transfer mode                  */
    uint32_t in_out; /* transfer direction             */
    uint32_t unknown;
} __attribute__((packed));

typedef struct {
    int fd;
    uint64_t device_id;
    sys_ppu_thread_t thread_id;
    sys_mutex_t read_async_mutex_id;
    sys_event_queue_t queue;
	int io_buffer;
	int io_buffer_piece;
} sac_accessor_driver_t;

enum lv2_atapi_proto {
    NON_DATA_PROTO = 0,
    PIO_DATA_IN_PROTO = 1,
    PIO_DATA_OUT_PROTO = 2,
    DMA_PROTO = 3
};

enum lv2_atapi_in_out {
    DIR_WRITE = 0, /* memory -> device */
    DIR_READ = 1 /* device -> memory */
};


static inline void init_atapi_cmnd_block(
        struct lv2_atapi_cmnd_block *atapi_cmnd
        , uint32_t block_size
        , uint32_t proto
        , uint32_t type) {
    memset(atapi_cmnd, 0, sizeof(struct lv2_atapi_cmnd_block));
    atapi_cmnd->pktlen = 12;
    atapi_cmnd->blocks = 1;
    atapi_cmnd->block_size = block_size; /* transfer size is block_size * blocks */
    atapi_cmnd->proto = proto;
    atapi_cmnd->in_out = type;
}


static inline int sys_storage_send_atapi_command(uint32_t fd, struct lv2_atapi_cmnd_block *atapi_cmnd, uint8_t *buffer) {
    uint64_t tag;
    system_call_7(SYS_STORAGE_EXECUTE_DEVICE_COMMAND
            , fd
            , LV2_STORAGE_SEND_ATAPI_COMMAND
            , (uint32_t) atapi_cmnd
            , sizeof (struct lv2_atapi_cmnd_block)
            , (uint32_t) buffer
            , atapi_cmnd->block_size
            , (uint32_t) & tag);

    return_to_user_prog(int);
}


int ps3rom_lv2_get_configuration(int fd, uint8_t *buffer) {
    int res;
    struct lv2_atapi_cmnd_block atapi_cmnd;

    init_atapi_cmnd_block(&atapi_cmnd, 0x10, PIO_DATA_IN_PROTO, DIR_READ);
    atapi_cmnd.pkt[0] = GPCMD_GET_CONFIGURATION;
    atapi_cmnd.pkt[1] = 0;
    atapi_cmnd.pkt[2] = 0xff;
    atapi_cmnd.pkt[3] = 0x41;
    atapi_cmnd.pkt[8] = 0x10;

    res = sys_storage_send_atapi_command(fd, &atapi_cmnd, buffer);
    // if (buffer[10] & 1 == 0) return 0xF000FFFF
    // if (buffer[0] & 1 != 0) exec_mode_sense
    return res;
}

int ps3rom_lv2_mode_sense(int fd, uint8_t *buffer) {
    int res;
    struct lv2_atapi_cmnd_block atapi_cmnd;

    init_atapi_cmnd_block(&atapi_cmnd, 0x10, PIO_DATA_IN_PROTO, DIR_READ);

    atapi_cmnd.pkt[0] = GPCMD_MODE_SENSE_10;
    atapi_cmnd.pkt[1] = 0x08;
    atapi_cmnd.pkt[2] = 0x03;
    atapi_cmnd.pkt[8] = 0x10;

    res = sys_storage_send_atapi_command(fd, &atapi_cmnd, buffer);
    // if (buffer[11] == 2) exec_mode_select
    return res;
}

int ps3rom_lv2_mode_select(int fd) {
    int res;
    struct lv2_atapi_cmnd_block atapi_cmnd;
    static uint8_t buffer[256];
    memset(buffer, 0, sizeof (buffer));

    buffer[1] = 0x0e;
    buffer[7] = 8;
    buffer[8] = 3;
    buffer[9] = 6;
    buffer[11] = 3; // ? 3 == SACD
    buffer[255] = 0x10;

    init_atapi_cmnd_block(&atapi_cmnd, 0x10, PIO_DATA_OUT_PROTO, DIR_WRITE);

    atapi_cmnd.pkt[0] = GPCMD_MODE_SELECT_10;
    atapi_cmnd.pkt[1] = 0x10; /* PF */
    atapi_cmnd.pkt[8] = 0x10;

    res = sys_storage_send_atapi_command(fd, &atapi_cmnd, buffer);

    return res;
}

int ps3rom_lv2_enable_encryption(int fd, uint8_t *buffer, uint32_t lba) {
    int res;
    struct lv2_atapi_cmnd_block atapi_cmnd;

    init_atapi_cmnd_block(&atapi_cmnd, 0x0a, PIO_DATA_IN_PROTO, DIR_READ);

    atapi_cmnd.pkt[0] = GPCMD_READ_DVD_STRUCTURE;
    atapi_cmnd.pkt[1] = 2;
    
		atapi_cmnd.pkt[2] = lba >> 24; 
		atapi_cmnd.pkt[3] = lba >> 16;
		atapi_cmnd.pkt[4] = lba >> 8;
		atapi_cmnd.pkt[5] = lba & 0xff;
    
    atapi_cmnd.pkt[9] = 0x0a;

    res = sys_storage_send_atapi_command(fd, &atapi_cmnd, buffer);

    return res;
}

int ps3rom_lv2_get_event_status_notification(int fd, uint8_t *buffer) {
    int res;
    struct lv2_atapi_cmnd_block atapi_cmnd;

    init_atapi_cmnd_block(&atapi_cmnd, 0x08, PIO_DATA_IN_PROTO, DIR_READ);

    atapi_cmnd.pkt[0] = GPCMD_GET_EVENT_STATUS_NOTIFICATION;
    atapi_cmnd.pkt[1] = 1; /* IMMED */
    atapi_cmnd.pkt[4] = 4; /* media event */
    atapi_cmnd.pkt[8] = 0x08;

    res = sys_storage_send_atapi_command(fd, &atapi_cmnd, buffer);

    return res;
}

int ps3rom_lv2_report_key_start(int fd, uint8_t *buffer) {
    int res;
    struct lv2_atapi_cmnd_block atapi_cmnd;

    init_atapi_cmnd_block(&atapi_cmnd, 0x08, PIO_DATA_IN_PROTO, DIR_READ);

    atapi_cmnd.pkt[0] = GPCMD_REPORT_KEY;

    atapi_cmnd.pkt[2] = 0;
    atapi_cmnd.pkt[3] = 0;
    atapi_cmnd.pkt[4] = 0;
    atapi_cmnd.pkt[5] = 0x08;

    atapi_cmnd.pkt[6] = 0;
    atapi_cmnd.pkt[7] = 0x10;

    res = sys_storage_send_atapi_command(fd, &atapi_cmnd, buffer);

    return res;
}

int ps3rom_lv2_send_key(int fd, uint8_t agid, uint32_t key_size, uint8_t *key, uint8_t sequence) {
    int res;
    struct lv2_atapi_cmnd_block atapi_cmnd;
    uint32_t buffer_size;
    uint8_t buffer[256];
    uint8_t buffer_align = 0;
    
    if ((key_size & 3) != 0) {
      buffer_align = ~(key_size & 3) + 4 + 1;
    }
    buffer_size = key_size + 4 + buffer_align;

    init_atapi_cmnd_block(&atapi_cmnd, buffer_size, PIO_DATA_OUT_PROTO, DIR_WRITE);

    atapi_cmnd.pkt[0] = GPCMD_SEND_KEY;

		atapi_cmnd.pkt[2] = buffer_size >> 24; 
		atapi_cmnd.pkt[3] = buffer_size >> 16;
		atapi_cmnd.pkt[4] = buffer_size >> 8;
		atapi_cmnd.pkt[5] = buffer_size & 0xff;
    
    atapi_cmnd.pkt[6] = sequence;
    atapi_cmnd.pkt[7] = 0x10;
    atapi_cmnd.pkt[10] = agid;

		memset(buffer, 0, sizeof(buffer));

		buffer[0] = key_size >> 24; 
		buffer[1] = key_size >> 16;
		buffer[2] = key_size >> 8;
		buffer[3] = key_size & 0xff;
    memcpy(buffer + 4, key, key_size);

		if (buffer_align != 0) {
  		memset(buffer + key_size + 4, 0, buffer_align);
  	}

    res = sys_storage_send_atapi_command(fd, &atapi_cmnd, buffer);

    return res;
}

int ps3rom_lv2_report_key(int fd, uint8_t agid, uint32_t *key_size, uint8_t *key, uint8_t sequence) {
    int res;
    struct lv2_atapi_cmnd_block atapi_cmnd;
    uint32_t buffer_size;
    uint8_t buffer[256];
    uint8_t buffer_align = 0;
    uint32_t new_key_size, old_key_size = *key_size;

    memset(buffer, 0xff, sizeof(buffer));
    
    if ((old_key_size & 3) != 0) {
      buffer_align = ~(old_key_size & 3) + 4 + 1;
    }
    buffer_size = old_key_size + 4 + buffer_align;

    init_atapi_cmnd_block(&atapi_cmnd, buffer_size, PIO_DATA_IN_PROTO, DIR_READ);

    atapi_cmnd.pkt[0] = GPCMD_REPORT_KEY;

		atapi_cmnd.pkt[2] = buffer_size >> 24; 
		atapi_cmnd.pkt[3] = buffer_size >> 16;
		atapi_cmnd.pkt[4] = buffer_size >> 8;
		atapi_cmnd.pkt[5] = buffer_size & 0xff;

    atapi_cmnd.pkt[6] = sequence;
    atapi_cmnd.pkt[7] = 0x10;
    atapi_cmnd.pkt[10] = agid;
    
    res = sys_storage_send_atapi_command(fd, &atapi_cmnd, buffer);

    new_key_size = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
    *key_size = new_key_size;

    memcpy(key, buffer + 4, (old_key_size > new_key_size ? new_key_size : old_key_size));

    return res;
}

int ps3rom_lv2_report_key_finish(int fd, uint8_t agid) {
    int res;
    struct lv2_atapi_cmnd_block atapi_cmnd;

    init_atapi_cmnd_block(&atapi_cmnd, 0, NON_DATA_PROTO, DIR_READ);

    atapi_cmnd.pkt[0] = GPCMD_REPORT_KEY;

    atapi_cmnd.pkt[6] = 0xff;
    atapi_cmnd.pkt[7] = 0x10;
    atapi_cmnd.pkt[10] = agid;

    res = sys_storage_send_atapi_command(fd, &atapi_cmnd, 0);

    return res;
}


int ps3rom_lv2_read_toc_header(int fd, uint8_t *buffer) {
    int res;
    struct lv2_atapi_cmnd_block atapi_cmnd;

    init_atapi_cmnd_block(&atapi_cmnd, 12, PIO_DATA_IN_PROTO, DIR_READ);

    atapi_cmnd.pkt[0] = GPCMD_READ_TOC_PMA_ATIP;
    atapi_cmnd.pkt[6] = 0;
    atapi_cmnd.pkt[8] = 12; /* LSB of length */

    res = sys_storage_send_atapi_command(fd, &atapi_cmnd, buffer);

    return res;
}

int ps3rom_lv2_read_toc_entry(int fd, uint8_t *buffer) {
    int res;
    struct lv2_atapi_cmnd_block atapi_cmnd;

    init_atapi_cmnd_block(&atapi_cmnd, 12, PIO_DATA_IN_PROTO, DIR_READ);

    atapi_cmnd.pkt[0] = GPCMD_READ_TOC_PMA_ATIP;
    atapi_cmnd.pkt[6] = 0x01;
    atapi_cmnd.pkt[8] = 12; /* LSB of length */

    res = sys_storage_send_atapi_command(fd, &atapi_cmnd, buffer);

    return res;
}

int ps3rom_lv2_read_track(int fd, uint8_t *buffer, uint8_t track) {
    int res;
    struct lv2_atapi_cmnd_block atapi_cmnd;

    init_atapi_cmnd_block(&atapi_cmnd, 48, PIO_DATA_IN_PROTO, DIR_READ);

    atapi_cmnd.pkt[0] = GPCMD_READ_TRACK_RZONE_INFO;
    atapi_cmnd.pkt[1] = 1;
    atapi_cmnd.pkt[5] = track; /* track */
    atapi_cmnd.pkt[8] = 48; /* LSB of length */

    res = sys_storage_send_atapi_command(fd, &atapi_cmnd, buffer);

    return res;
}