-
-
Notifications
You must be signed in to change notification settings - Fork 294
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement XDVD security challenge responses #1659
base: master
Are you sure you want to change the base?
Changes from all commits
99dd9dd
36c97d3
f039a21
43c2173
c0d833f
f7fe9e2
c68b546
4fabcf1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,6 +32,18 @@ | |
#define ATAPI_SECTOR_BITS (2 + BDRV_SECTOR_BITS) | ||
#define ATAPI_SECTOR_SIZE (1 << ATAPI_SECTOR_BITS) | ||
|
||
#ifdef XBOX | ||
#include "ui/xemu-settings.h" | ||
#include "hw/xbox/xdvd/xdvd.h" | ||
#endif | ||
|
||
// #define DEBUG | ||
#ifdef DEBUG | ||
# define XBOX_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) | ||
#else | ||
# define XBOX_DPRINTF(format, ...) do { } while (0) | ||
#endif | ||
|
||
static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret); | ||
|
||
static void padstr8(uint8_t *buf, int buf_size, const char *src) | ||
|
@@ -61,11 +73,27 @@ static inline int media_present(IDEState *s) | |
/* XXX: DVDs that could fit on a CD will be reported as a CD */ | ||
static inline int media_is_dvd(IDEState *s) | ||
{ | ||
#ifdef XBOX | ||
// We need to prevent small XISOs from being reported as CD-ROMs | ||
// If the DVD security path is set, we can assume it is an XISO. | ||
const char *dvd_security_path = g_config.sys.files.dvd_security_path; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should probably make this a qdev property, like disc file path, media type, etc. so it can play nicely with other systems |
||
if (strlen(dvd_security_path) > 0) { | ||
mborgerson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return media_present(s); | ||
} | ||
#endif | ||
return (media_present(s) && s->nb_sectors > CD_MAX_SECTORS); | ||
} | ||
|
||
static inline int media_is_cd(IDEState *s) | ||
{ | ||
#ifdef XBOX | ||
// We need to prevent small XISOs from being reported as CD-ROMs | ||
// If the DVD security path is set, we can assume it is an XISO. | ||
const char *dvd_security_path = g_config.sys.files.dvd_security_path; | ||
if (strlen(dvd_security_path) > 0) { | ||
return 0; | ||
} | ||
#endif | ||
return (media_present(s) && s->nb_sectors <= CD_MAX_SECTORS); | ||
} | ||
|
||
|
@@ -485,6 +513,24 @@ static int ide_dvd_read_structure(IDEState *s, int format, | |
{ | ||
int layer = packet[6]; | ||
uint64_t total_sectors; | ||
#ifdef XBOX | ||
int block_number = ldl_be_p(buf + 2); | ||
|
||
// Challenge table is located on disk runout, on Xbox these locations are always set to these for this command it looks like | ||
if (layer == XDVD_STRUCTURE_LAYER && block_number == XDVD_STRUCTURE_BLOCK_NUMBER) | ||
{ | ||
if (xdvd_get_encrypted_challenge_table(s->xdvd_challenges_encrypted)) | ||
{ | ||
xdvd_get_decrypted_responses(s->xdvd_challenges_encrypted, s->xdvd_challenges_decrypted); | ||
memcpy(buf, s->xdvd_challenges_encrypted, XDVD_STRUCTURE_LEN); | ||
return XDVD_STRUCTURE_LEN; | ||
} | ||
} | ||
|
||
// We deferred clearing this earlier so do it now | ||
int max_len = lduw_be_p(buf + 8); | ||
memset(buf, 0, MIN(max_len, IDE_DMA_BUF_SECTORS * BDRV_SECTOR_SIZE + 4)); | ||
#endif | ||
|
||
if (layer != 0) | ||
return -ASC_INV_FIELD_IN_CMD_PACKET; | ||
|
@@ -855,6 +901,56 @@ static void cmd_get_configuration(IDEState *s, uint8_t *buf) | |
ide_atapi_cmd_reply(s, len, max_len); | ||
} | ||
|
||
#ifdef XBOX | ||
static void cmd_mode_select_cb(void *opaque, int ret) | ||
{ | ||
IDEState *s = opaque; | ||
uint8_t *buf = s->io_buffer; | ||
|
||
if (ret < 0) { | ||
block_acct_failed(blk_get_stats(s->blk), &s->acct); | ||
ide_set_inactive(s, false); | ||
return; | ||
} | ||
|
||
if (s->bus->dma->ops->rw_buf(s->bus->dma, 0)) | ||
{ | ||
s->status = READY_STAT | SEEK_STAT; | ||
s->nsector = (s->nsector & ~7) | ATAPI_INT_REASON_IO | ATAPI_INT_REASON_CD; | ||
ide_set_irq(s->bus); | ||
} | ||
block_acct_done(blk_get_stats(s->blk), &s->acct); | ||
ide_set_inactive(s, false); | ||
|
||
int max_len = lduw_be_p(buf) + 2; | ||
if (max_len == XDVD_SECURITY_PAGE_LEN && buf[8] == MODE_PAGE_XBOX_SECURITY) | ||
{ | ||
memcpy(&s->xdvd_security, buf, XDVD_SECURITY_PAGE_LEN); | ||
XBOX_DPRINTF("Authenticated: %d, Partition: %d\n", s->xdvd_security.page.Authenticated, s->xdvd_security.page.Partition); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My understanding is that the drive requires the full unlock challenge-response sequence to happen before it will unlock and we are not yet fully modeling this state machine. If this is correct and you don't plan to implement this, can you leave a comment so we can remember to come back to it? |
||
s->xdvd_security.page.Authenticated = 1; | ||
ide_atapi_cmd_ok(s); | ||
} | ||
} | ||
|
||
static void cmd_mode_select(IDEState *s, uint8_t *buf) | ||
{ | ||
int parameter_list_len = lduw_be_p(buf + 7); | ||
|
||
// Does MODE SELECT contain more data? Read it out | ||
if (parameter_list_len > 0) | ||
{ | ||
s->lba = -1; | ||
s->packet_transfer_size = parameter_list_len; | ||
s->io_buffer_size = parameter_list_len; | ||
s->elementary_transfer_size = 0; | ||
|
||
block_acct_start(blk_get_stats(s->blk), &s->acct, parameter_list_len, BLOCK_ACCT_WRITE); | ||
s->status = READY_STAT | SEEK_STAT | DRQ_STAT; | ||
ide_start_dma(s, cmd_mode_select_cb); | ||
} | ||
} | ||
#endif | ||
|
||
static void cmd_mode_sense(IDEState *s, uint8_t *buf) | ||
{ | ||
int action, code; | ||
|
@@ -941,6 +1037,32 @@ static void cmd_mode_sense(IDEState *s, uint8_t *buf) | |
buf[29] = 0; | ||
ide_atapi_cmd_reply(s, 30, max_len); | ||
break; | ||
#ifdef XBOX | ||
case MODE_PAGE_XBOX_SECURITY: | ||
// If this is the first response, get the default security page | ||
if (s->xdvd_security.page.PageCode != MODE_PAGE_XBOX_SECURITY) { | ||
xdvd_get_default_security_page(&s->xdvd_security); | ||
} | ||
|
||
XBOX_DPRINTF("Got MODE_SENSE: PAGE_XBOX_SECURITY. Responding with ss "); | ||
|
||
s->xdvd_security.page.ResponseValue = | ||
xdvd_get_challenge_response(s->xdvd_challenges_decrypted, | ||
s->xdvd_security.page.ChallengeID); | ||
|
||
XBOX_DPRINTF("Challenge value %08x, ID %02x, Response %08x\n", | ||
s->xdvd_security.page.ChallengeValue, | ||
s->xdvd_security.page.ChallengeID, | ||
s->xdvd_security.page.ResponseValue); | ||
XBOX_DPRINTF("Authenticated: %d, Partition: %d\n", | ||
s->xdvd_security.page.Authenticated, | ||
s->xdvd_security.page.Partition); | ||
|
||
memcpy(buf, &s->xdvd_security, sizeof(s->xdvd_security)); | ||
|
||
ide_atapi_cmd_reply(s, sizeof(XBOX_DVD_SECURITY), max_len); | ||
#endif | ||
break; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This |
||
default: | ||
goto error_cmd; | ||
} | ||
|
@@ -993,6 +1115,12 @@ static void cmd_read(IDEState *s, uint8_t* buf) | |
} | ||
|
||
lba = ldl_be_p(buf + 2); | ||
|
||
#ifdef XBOX | ||
lba = xdvd_get_lba_offset(&s->xdvd_security, total_sectors, lba); | ||
total_sectors = xdvd_get_sector_cnt(&s->xdvd_security, total_sectors); | ||
#endif | ||
|
||
if (lba >= total_sectors || lba + nb_sectors - 1 >= total_sectors) { | ||
ide_atapi_cmd_error(s, ILLEGAL_REQUEST, ASC_LOGICAL_BLOCK_OOR); | ||
return; | ||
|
@@ -1149,6 +1277,10 @@ static void cmd_read_cdvd_capacity(IDEState *s, uint8_t* buf) | |
{ | ||
uint64_t total_sectors = s->nb_sectors >> 2; | ||
|
||
#ifdef XBOX | ||
total_sectors = xdvd_get_sector_cnt(&s->xdvd_security, total_sectors); | ||
#endif | ||
|
||
/* NOTE: it is really the number of sectors minus 1 */ | ||
stl_be_p(buf, total_sectors - 1); | ||
stl_be_p(buf + 4, 2048); | ||
|
@@ -1206,8 +1338,12 @@ static void cmd_read_dvd_structure(IDEState *s, uint8_t* buf) | |
} | ||
} | ||
|
||
// Still some important info we don't want to lose so we disable clearing | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since this is one of the few "not defined" conditionals, it's perhaps nicer to change it into an |
||
// on Xbox for now | ||
#ifndef XBOX | ||
memset(buf, 0, max_len > IDE_DMA_BUF_SECTORS * BDRV_SECTOR_SIZE + 4 ? | ||
IDE_DMA_BUF_SECTORS * BDRV_SECTOR_SIZE + 4 : max_len); | ||
#endif | ||
|
||
switch (format) { | ||
case 0x00 ... 0x7f: | ||
|
@@ -1291,6 +1427,9 @@ static const struct AtapiCmd { | |
[ 0x46 ] = { cmd_get_configuration, ALLOW_UA }, | ||
[ 0x4a ] = { cmd_get_event_status_notification, ALLOW_UA }, | ||
[ 0x51 ] = { cmd_read_disc_information, CHECK_READY }, | ||
#ifdef XBOX | ||
[ 0x55 ] = { cmd_mode_select,/* (10) */ 0 }, | ||
#endif | ||
[ 0x5a ] = { cmd_mode_sense, /* (10) */ 0 }, | ||
[ 0xa8 ] = { cmd_read, /* (12) */ CHECK_READY }, | ||
[ 0xad ] = { cmd_read_dvd_structure, CHECK_READY }, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,3 +18,4 @@ specific_ss.add(files( | |
)) | ||
subdir('nv2a') | ||
subdir('mcpx') | ||
subdir('xdvd') |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
xdvd_ss = ss.source_set() | ||
xdvd_ss.add(files( | ||
'xdvd.c', | ||
)) | ||
|
||
specific_ss.add_all(xdvd_ss) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could be moved inside the above
#ifdef XBOX
conditional block.