mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-24 03:59:52 +00:00
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1 iQIcBAABAgAGBQJV/Ca2AAoJEH3vgQaq/DkOcjoP/0lTqs2rD3xjP/anxJFhLLU2 EcJiKh7hamVM18Yufdw2FkZK5KlCLSiosGn+6nViKnNho9C0xdCBfmEDDgS+bz5P eIcGjPecB+7fvYAtJt4Bm60l/UZWJ8mm5BaWByVwgR2YhJByN5QT/RSqmdamT9BU GfiwzknI1f6ovQPkTJVnbXTRcrgiBRJQR9eSpyJKhTTbAXvMTdBSoMrSvaoLRrKs ih85iPvxQvqWSR4RG1IS1voHTAbq3R7BBm+rSoLgJpvTtwg7COZ73FCT88knvP1j 0ab9zcXElk0oLHg08n6zlWTgSpJMs9UMLsrPF24R4bdxqN7JkYXnXhym3uBs2E82 AvRYuBD4Jix9pMrWTzpwiWLxXBmq56odRNP7zW/6b88JoC+iWCZ+MA/5RTRAJQSa thSvZiW3V4BbktW+2AtfhJuL8rmvM5+9IHV10Ic0Ik2I97h73BeeduanAn2OpbMi y9o3cs4FZ3nqWPsJaiTb0Sh63+QKgGZ1get9vWoCEwXxpSReAycAwk4g9gcYwo3Y hiM+kE42/mBl7QcmS9aFW4DvmpTVdUQOSjguQZHSX334Bv/GYPd4XYjb+hWLJDYc IXibCTl8x2B5WVFzNfpY1+++QsqKAojxMXJ3ICIe0HA1CFCCNGWAEHHhdzgJdFX9 mlPs8xueKnd3+VfBMp/e =y9MO -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/jnsnow/tags/ide-pull-request' into staging # gpg: Signature made Fri 18 Sep 2015 15:59:02 BST using RSA key ID AAFC390E # gpg: Good signature from "John Snow (John Huston) <jsnow@redhat.com>" * remotes/jnsnow/tags/ide-pull-request: ahci: clean up initial d2h semantics ahci: remove cmd_fis argument from write_fis_d2h ahci: fix signature generation ahci: remove dead reset code atapi: abort transfers with 0 byte limits ide: fix ATAPI command permissions ide-test: add cdrom dma test ide-test: add cdrom pio test qtest/ahci: export generate_pattern qtest/ahci: use generate_pattern everywhere ide: unify io_buffer_offset increments Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
a53efe9c47
@ -46,10 +46,9 @@ do { \
|
||||
static void check_cmd(AHCIState *s, int port);
|
||||
static int handle_cmd(AHCIState *s, int port, uint8_t slot);
|
||||
static void ahci_reset_port(AHCIState *s, int port);
|
||||
static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis);
|
||||
static bool ahci_write_fis_d2h(AHCIDevice *ad);
|
||||
static void ahci_init_d2h(AHCIDevice *ad);
|
||||
static int ahci_dma_prepare_buf(IDEDMA *dma, int32_t limit);
|
||||
static void ahci_commit_buf(IDEDMA *dma, uint32_t tx_bytes);
|
||||
static bool ahci_map_clb_address(AHCIDevice *ad);
|
||||
static bool ahci_map_fis_address(AHCIDevice *ad);
|
||||
static void ahci_unmap_clb_address(AHCIDevice *ad);
|
||||
@ -296,7 +295,6 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val)
|
||||
if ((pr->cmd & PORT_CMD_FIS_ON) &&
|
||||
!s->dev[port].init_d2h_sent) {
|
||||
ahci_init_d2h(&s->dev[port]);
|
||||
s->dev[port].init_d2h_sent = true;
|
||||
}
|
||||
|
||||
check_cmd(s, port);
|
||||
@ -539,20 +537,33 @@ static void ahci_check_cmd_bh(void *opaque)
|
||||
|
||||
static void ahci_init_d2h(AHCIDevice *ad)
|
||||
{
|
||||
uint8_t init_fis[20];
|
||||
IDEState *ide_state = &ad->port.ifs[0];
|
||||
AHCIPortRegs *pr = &ad->port_regs;
|
||||
|
||||
memset(init_fis, 0, sizeof(init_fis));
|
||||
|
||||
init_fis[4] = 1;
|
||||
init_fis[12] = 1;
|
||||
|
||||
if (ide_state->drive_kind == IDE_CD) {
|
||||
init_fis[5] = ide_state->lcyl;
|
||||
init_fis[6] = ide_state->hcyl;
|
||||
if (ad->init_d2h_sent) {
|
||||
return;
|
||||
}
|
||||
|
||||
ahci_write_fis_d2h(ad, init_fis);
|
||||
if (ahci_write_fis_d2h(ad)) {
|
||||
ad->init_d2h_sent = true;
|
||||
/* We're emulating receiving the first Reg H2D Fis from the device;
|
||||
* Update the SIG register, but otherwise proceed as normal. */
|
||||
pr->sig = (ide_state->hcyl << 24) |
|
||||
(ide_state->lcyl << 16) |
|
||||
(ide_state->sector << 8) |
|
||||
(ide_state->nsector & 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
static void ahci_set_signature(AHCIDevice *ad, uint32_t sig)
|
||||
{
|
||||
IDEState *s = &ad->port.ifs[0];
|
||||
s->hcyl = sig >> 24 & 0xFF;
|
||||
s->lcyl = sig >> 16 & 0xFF;
|
||||
s->sector = sig >> 8 & 0xFF;
|
||||
s->nsector = sig & 0xFF;
|
||||
|
||||
DPRINTF(ad->port_no, "set hcyl:lcyl:sect:nsect = 0x%08x\n", sig);
|
||||
}
|
||||
|
||||
static void ahci_reset_port(AHCIState *s, int port)
|
||||
@ -603,17 +614,11 @@ static void ahci_reset_port(AHCIState *s, int port)
|
||||
}
|
||||
|
||||
s->dev[port].port_state = STATE_RUN;
|
||||
if (!ide_state->blk) {
|
||||
pr->sig = 0;
|
||||
ide_state->status = SEEK_STAT | WRERR_STAT;
|
||||
} else if (ide_state->drive_kind == IDE_CD) {
|
||||
pr->sig = SATA_SIGNATURE_CDROM;
|
||||
ide_state->lcyl = 0x14;
|
||||
ide_state->hcyl = 0xeb;
|
||||
DPRINTF(port, "set lcyl = %d\n", ide_state->lcyl);
|
||||
if (ide_state->drive_kind == IDE_CD) {
|
||||
ahci_set_signature(d, SATA_SIGNATURE_CDROM);\
|
||||
ide_state->status = SEEK_STAT | WRERR_STAT | READY_STAT;
|
||||
} else {
|
||||
pr->sig = SATA_SIGNATURE_DISK;
|
||||
ahci_set_signature(d, SATA_SIGNATURE_DISK);
|
||||
ide_state->status = SEEK_STAT | WRERR_STAT;
|
||||
}
|
||||
|
||||
@ -749,7 +754,7 @@ static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len)
|
||||
ahci_trigger_irq(ad->hba, ad, PORT_IRQ_PIOS_FIS);
|
||||
}
|
||||
|
||||
static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis)
|
||||
static bool ahci_write_fis_d2h(AHCIDevice *ad)
|
||||
{
|
||||
AHCIPortRegs *pr = &ad->port_regs;
|
||||
uint8_t *d2h_fis;
|
||||
@ -757,7 +762,7 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis)
|
||||
IDEState *s = &ad->port.ifs[0];
|
||||
|
||||
if (!ad->res_fis || !(pr->cmd & PORT_CMD_FIS_RX)) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
d2h_fis = &ad->res_fis[RES_FIS_RFIS];
|
||||
@ -790,6 +795,7 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis)
|
||||
}
|
||||
|
||||
ahci_trigger_irq(ad->hba, ad, PORT_IRQ_D2H_REG_FIS);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int prdt_tbl_entry_size(const AHCI_SG *tbl)
|
||||
@ -1289,7 +1295,7 @@ out:
|
||||
s->data_ptr = s->data_end;
|
||||
|
||||
/* Update number of transferred bytes, destroy sglist */
|
||||
ahci_commit_buf(dma, size);
|
||||
dma_buf_commit(s, size);
|
||||
|
||||
s->end_transfer_func(s);
|
||||
|
||||
@ -1331,9 +1337,8 @@ static void ahci_restart(IDEDMA *dma)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called in DMA R/W chains to read the PRDT, utilizing ahci_populate_sglist.
|
||||
* Not currently invoked by PIO R/W chains,
|
||||
* which invoke ahci_populate_sglist via ahci_start_transfer.
|
||||
* Called in DMA and PIO R/W chains to read the PRDT.
|
||||
* Not shared with NCQ pathways.
|
||||
*/
|
||||
static int32_t ahci_dma_prepare_buf(IDEDMA *dma, int32_t limit)
|
||||
{
|
||||
@ -1352,21 +1357,16 @@ static int32_t ahci_dma_prepare_buf(IDEDMA *dma, int32_t limit)
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the scatter-gather list,
|
||||
* and updates the command header with a bytes-read value.
|
||||
* called explicitly via ahci_dma_rw_buf (ATAPI DMA),
|
||||
* and ahci_start_transfer (PIO R/W),
|
||||
* and called via callback from ide_dma_cb for DMA R/W paths.
|
||||
* Updates the command header with a bytes-read value.
|
||||
* Called via dma_buf_commit, for both DMA and PIO paths.
|
||||
* sglist destruction is handled within dma_buf_commit.
|
||||
*/
|
||||
static void ahci_commit_buf(IDEDMA *dma, uint32_t tx_bytes)
|
||||
{
|
||||
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
|
||||
IDEState *s = &ad->port.ifs[0];
|
||||
|
||||
tx_bytes += le32_to_cpu(ad->cur_cmd->status);
|
||||
ad->cur_cmd->status = cpu_to_le32(tx_bytes);
|
||||
|
||||
qemu_sglist_destroy(&s->sg);
|
||||
}
|
||||
|
||||
static int ahci_dma_rw_buf(IDEDMA *dma, int is_write)
|
||||
@ -1387,10 +1387,9 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write)
|
||||
}
|
||||
|
||||
/* free sglist, update byte count */
|
||||
ahci_commit_buf(dma, l);
|
||||
dma_buf_commit(s, l);
|
||||
|
||||
s->io_buffer_index += l;
|
||||
s->io_buffer_offset += l;
|
||||
|
||||
DPRINTF(ad->port_no, "len=%#x\n", l);
|
||||
|
||||
@ -1404,7 +1403,7 @@ static void ahci_cmd_done(IDEDMA *dma)
|
||||
DPRINTF(ad->port_no, "cmd done\n");
|
||||
|
||||
/* update d2h status */
|
||||
ahci_write_fis_d2h(ad, NULL);
|
||||
ahci_write_fis_d2h(ad);
|
||||
|
||||
if (!ad->check_bh) {
|
||||
/* maybe we still have something to process, check later */
|
||||
|
@ -1169,20 +1169,28 @@ enum {
|
||||
* 4.1.8)
|
||||
*/
|
||||
CHECK_READY = 0x02,
|
||||
|
||||
/*
|
||||
* Commands flagged with NONDATA do not in any circumstances return
|
||||
* any data via ide_atapi_cmd_reply. These commands are exempt from
|
||||
* the normal byte_count_limit constraints.
|
||||
* See ATA8-ACS3 "7.21.5 Byte Count Limit"
|
||||
*/
|
||||
NONDATA = 0x04,
|
||||
};
|
||||
|
||||
static const struct {
|
||||
void (*handler)(IDEState *s, uint8_t *buf);
|
||||
int flags;
|
||||
} atapi_cmd_table[0x100] = {
|
||||
[ 0x00 ] = { cmd_test_unit_ready, CHECK_READY },
|
||||
[ 0x00 ] = { cmd_test_unit_ready, CHECK_READY | NONDATA },
|
||||
[ 0x03 ] = { cmd_request_sense, ALLOW_UA },
|
||||
[ 0x12 ] = { cmd_inquiry, ALLOW_UA },
|
||||
[ 0x1b ] = { cmd_start_stop_unit, 0 }, /* [1] */
|
||||
[ 0x1e ] = { cmd_prevent_allow_medium_removal, 0 },
|
||||
[ 0x1b ] = { cmd_start_stop_unit, NONDATA }, /* [1] */
|
||||
[ 0x1e ] = { cmd_prevent_allow_medium_removal, NONDATA },
|
||||
[ 0x25 ] = { cmd_read_cdvd_capacity, CHECK_READY },
|
||||
[ 0x28 ] = { cmd_read, /* (10) */ CHECK_READY },
|
||||
[ 0x2b ] = { cmd_seek, CHECK_READY },
|
||||
[ 0x2b ] = { cmd_seek, CHECK_READY | NONDATA },
|
||||
[ 0x43 ] = { cmd_read_toc_pma_atip, CHECK_READY },
|
||||
[ 0x46 ] = { cmd_get_configuration, ALLOW_UA },
|
||||
[ 0x4a ] = { cmd_get_event_status_notification, ALLOW_UA },
|
||||
@ -1190,7 +1198,7 @@ static const struct {
|
||||
[ 0x5a ] = { cmd_mode_sense, /* (10) */ 0 },
|
||||
[ 0xa8 ] = { cmd_read, /* (12) */ CHECK_READY },
|
||||
[ 0xad ] = { cmd_read_dvd_structure, CHECK_READY },
|
||||
[ 0xbb ] = { cmd_set_speed, 0 },
|
||||
[ 0xbb ] = { cmd_set_speed, NONDATA },
|
||||
[ 0xbd ] = { cmd_mechanism_status, 0 },
|
||||
[ 0xbe ] = { cmd_read_cd, CHECK_READY },
|
||||
/* [1] handler detects and reports not ready condition itself */
|
||||
@ -1251,6 +1259,20 @@ void ide_atapi_cmd(IDEState *s)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Nondata commands permit the byte_count_limit to be 0.
|
||||
* If this is a data-transferring PIO command and BCL is 0,
|
||||
* we abort at the /ATA/ level, not the ATAPI level.
|
||||
* See ATA8 ACS3 section 7.17.6.49 and 7.21.5 */
|
||||
if (!(atapi_cmd_table[s->io_buffer[0]].flags & NONDATA)) {
|
||||
/* TODO: Check IDENTIFY data word 125 for default BCL (currently 0) */
|
||||
uint16_t byte_count_limit = s->lcyl | (s->hcyl << 8);
|
||||
if (!(byte_count_limit || s->atapi_dma)) {
|
||||
/* TODO: Move abort back into core.c and make static inline again */
|
||||
ide_abort_command(s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Execute the command */
|
||||
if (atapi_cmd_table[s->io_buffer[0]].handler) {
|
||||
atapi_cmd_table[s->io_buffer[0]].handler(s, buf);
|
||||
|
@ -457,7 +457,7 @@ BlockAIOCB *ide_issue_trim(BlockBackend *blk,
|
||||
return &iocb->common;
|
||||
}
|
||||
|
||||
static inline void ide_abort_command(IDEState *s)
|
||||
void ide_abort_command(IDEState *s)
|
||||
{
|
||||
ide_transfer_stop(s);
|
||||
s->status = READY_STAT | ERR_STAT;
|
||||
@ -591,7 +591,6 @@ static void ide_sector_read_cb(void *opaque, int ret)
|
||||
s->nsector -= n;
|
||||
/* Allow the guest to read the io_buffer */
|
||||
ide_transfer_start(s, s->io_buffer, n * BDRV_SECTOR_SIZE, ide_sector_read);
|
||||
s->io_buffer_offset += 512 * n;
|
||||
ide_set_irq(s->bus);
|
||||
}
|
||||
|
||||
@ -635,11 +634,12 @@ static void ide_sector_read(IDEState *s)
|
||||
ide_sector_read_cb, s);
|
||||
}
|
||||
|
||||
static void dma_buf_commit(IDEState *s, uint32_t tx_bytes)
|
||||
void dma_buf_commit(IDEState *s, uint32_t tx_bytes)
|
||||
{
|
||||
if (s->bus->dma->ops->commit_buf) {
|
||||
s->bus->dma->ops->commit_buf(s->bus->dma, tx_bytes);
|
||||
}
|
||||
s->io_buffer_offset += tx_bytes;
|
||||
qemu_sglist_destroy(&s->sg);
|
||||
}
|
||||
|
||||
@ -842,7 +842,6 @@ static void ide_sector_write_cb(void *opaque, int ret)
|
||||
n = s->req_nb_sectors;
|
||||
}
|
||||
s->nsector -= n;
|
||||
s->io_buffer_offset += 512 * n;
|
||||
|
||||
ide_set_sector(s, ide_get_sector(s) + n);
|
||||
if (s->nsector == 0) {
|
||||
@ -1747,11 +1746,11 @@ static const struct {
|
||||
} ide_cmd_table[0x100] = {
|
||||
/* NOP not implemented, mandatory for CD */
|
||||
[CFA_REQ_EXT_ERROR_CODE] = { cmd_cfa_req_ext_error_code, CFA_OK },
|
||||
[WIN_DSM] = { cmd_data_set_management, ALL_OK },
|
||||
[WIN_DSM] = { cmd_data_set_management, HD_CFA_OK },
|
||||
[WIN_DEVICE_RESET] = { cmd_device_reset, CD_OK },
|
||||
[WIN_RECAL] = { cmd_nop, HD_CFA_OK | SET_DSC},
|
||||
[WIN_READ] = { cmd_read_pio, ALL_OK },
|
||||
[WIN_READ_ONCE] = { cmd_read_pio, ALL_OK },
|
||||
[WIN_READ_ONCE] = { cmd_read_pio, HD_CFA_OK },
|
||||
[WIN_READ_EXT] = { cmd_read_pio, HD_CFA_OK },
|
||||
[WIN_READDMA_EXT] = { cmd_read_dma, HD_CFA_OK },
|
||||
[WIN_READ_NATIVE_MAX_EXT] = { cmd_read_native_max, HD_CFA_OK | SET_DSC },
|
||||
@ -1770,12 +1769,12 @@ static const struct {
|
||||
[CFA_TRANSLATE_SECTOR] = { cmd_cfa_translate_sector, CFA_OK },
|
||||
[WIN_DIAGNOSE] = { cmd_exec_dev_diagnostic, ALL_OK },
|
||||
[WIN_SPECIFY] = { cmd_nop, HD_CFA_OK | SET_DSC },
|
||||
[WIN_STANDBYNOW2] = { cmd_nop, ALL_OK },
|
||||
[WIN_IDLEIMMEDIATE2] = { cmd_nop, ALL_OK },
|
||||
[WIN_STANDBY2] = { cmd_nop, ALL_OK },
|
||||
[WIN_SETIDLE2] = { cmd_nop, ALL_OK },
|
||||
[WIN_CHECKPOWERMODE2] = { cmd_check_power_mode, ALL_OK | SET_DSC },
|
||||
[WIN_SLEEPNOW2] = { cmd_nop, ALL_OK },
|
||||
[WIN_STANDBYNOW2] = { cmd_nop, HD_CFA_OK },
|
||||
[WIN_IDLEIMMEDIATE2] = { cmd_nop, HD_CFA_OK },
|
||||
[WIN_STANDBY2] = { cmd_nop, HD_CFA_OK },
|
||||
[WIN_SETIDLE2] = { cmd_nop, HD_CFA_OK },
|
||||
[WIN_CHECKPOWERMODE2] = { cmd_check_power_mode, HD_CFA_OK | SET_DSC },
|
||||
[WIN_SLEEPNOW2] = { cmd_nop, HD_CFA_OK },
|
||||
[WIN_PACKETCMD] = { cmd_packet, CD_OK },
|
||||
[WIN_PIDENTIFY] = { cmd_identify_packet, CD_OK },
|
||||
[WIN_SMART] = { cmd_smart, HD_CFA_OK | SET_DSC },
|
||||
@ -1789,19 +1788,19 @@ static const struct {
|
||||
[WIN_WRITEDMA] = { cmd_write_dma, HD_CFA_OK },
|
||||
[WIN_WRITEDMA_ONCE] = { cmd_write_dma, HD_CFA_OK },
|
||||
[CFA_WRITE_MULTI_WO_ERASE] = { cmd_write_multiple, CFA_OK },
|
||||
[WIN_STANDBYNOW1] = { cmd_nop, ALL_OK },
|
||||
[WIN_IDLEIMMEDIATE] = { cmd_nop, ALL_OK },
|
||||
[WIN_STANDBY] = { cmd_nop, ALL_OK },
|
||||
[WIN_SETIDLE1] = { cmd_nop, ALL_OK },
|
||||
[WIN_CHECKPOWERMODE1] = { cmd_check_power_mode, ALL_OK | SET_DSC },
|
||||
[WIN_SLEEPNOW1] = { cmd_nop, ALL_OK },
|
||||
[WIN_STANDBYNOW1] = { cmd_nop, HD_CFA_OK },
|
||||
[WIN_IDLEIMMEDIATE] = { cmd_nop, HD_CFA_OK },
|
||||
[WIN_STANDBY] = { cmd_nop, HD_CFA_OK },
|
||||
[WIN_SETIDLE1] = { cmd_nop, HD_CFA_OK },
|
||||
[WIN_CHECKPOWERMODE1] = { cmd_check_power_mode, HD_CFA_OK | SET_DSC },
|
||||
[WIN_SLEEPNOW1] = { cmd_nop, HD_CFA_OK },
|
||||
[WIN_FLUSH_CACHE] = { cmd_flush_cache, ALL_OK },
|
||||
[WIN_FLUSH_CACHE_EXT] = { cmd_flush_cache, HD_CFA_OK },
|
||||
[WIN_IDENTIFY] = { cmd_identify, ALL_OK },
|
||||
[WIN_SETFEATURES] = { cmd_set_features, ALL_OK | SET_DSC },
|
||||
[IBM_SENSE_CONDITION] = { cmd_ibm_sense_condition, CFA_OK | SET_DSC },
|
||||
[CFA_WEAR_LEVEL] = { cmd_cfa_erase_sectors, HD_CFA_OK | SET_DSC },
|
||||
[WIN_READ_NATIVE_MAX] = { cmd_read_native_max, ALL_OK | SET_DSC },
|
||||
[WIN_READ_NATIVE_MAX] = { cmd_read_native_max, HD_CFA_OK | SET_DSC },
|
||||
};
|
||||
|
||||
static bool ide_cmd_permitted(IDEState *s, uint32_t cmd)
|
||||
|
@ -536,7 +536,9 @@ int64_t ide_get_sector(IDEState *s);
|
||||
void ide_set_sector(IDEState *s, int64_t sector_num);
|
||||
|
||||
void ide_start_dma(IDEState *s, BlockCompletionFunc *cb);
|
||||
void dma_buf_commit(IDEState *s, uint32_t tx_bytes);
|
||||
void ide_dma_error(IDEState *s);
|
||||
void ide_abort_command(IDEState *s);
|
||||
|
||||
void ide_atapi_cmd_ok(IDEState *s);
|
||||
void ide_atapi_cmd_error(IDEState *s, int sense_key, int asc);
|
||||
|
@ -71,32 +71,6 @@ static void string_bswap16(uint16_t *s, size_t bytes)
|
||||
}
|
||||
}
|
||||
|
||||
static void generate_pattern(void *buffer, size_t len, size_t cycle_len)
|
||||
{
|
||||
int i, j;
|
||||
unsigned char *tx = (unsigned char *)buffer;
|
||||
unsigned char p;
|
||||
size_t *sx;
|
||||
|
||||
/* Write an indicative pattern that varies and is unique per-cycle */
|
||||
p = rand() % 256;
|
||||
for (i = j = 0; i < len; i++, j++) {
|
||||
tx[i] = p;
|
||||
if (j % cycle_len == 0) {
|
||||
p = rand() % 256;
|
||||
}
|
||||
}
|
||||
|
||||
/* force uniqueness by writing an id per-cycle */
|
||||
for (i = 0; i < len / cycle_len; i++) {
|
||||
j = i * cycle_len;
|
||||
if (j + sizeof(*sx) <= len) {
|
||||
sx = (size_t *)&tx[j];
|
||||
*sx = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the transfer did not corrupt our state at all.
|
||||
*/
|
||||
@ -1155,7 +1129,6 @@ static void ahci_migrate_simple(uint8_t cmd_read, uint8_t cmd_write)
|
||||
size_t bufsize = 4096;
|
||||
unsigned char *tx = g_malloc(bufsize);
|
||||
unsigned char *rx = g_malloc0(bufsize);
|
||||
unsigned i;
|
||||
const char *uri = "tcp:127.0.0.1:1234";
|
||||
|
||||
src = ahci_boot_and_enable("-m 1024 -M q35 "
|
||||
@ -1171,9 +1144,7 @@ static void ahci_migrate_simple(uint8_t cmd_read, uint8_t cmd_write)
|
||||
ahci_port_clear(src, px);
|
||||
|
||||
/* create pattern */
|
||||
for (i = 0; i < bufsize; i++) {
|
||||
tx[i] = (bufsize - i);
|
||||
}
|
||||
generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE);
|
||||
|
||||
/* Write, migrate, then read. */
|
||||
ahci_io(src, px, cmd_write, tx, bufsize, 0);
|
||||
@ -1213,7 +1184,6 @@ static void ahci_halted_io_test(uint8_t cmd_read, uint8_t cmd_write)
|
||||
size_t bufsize = 4096;
|
||||
unsigned char *tx = g_malloc(bufsize);
|
||||
unsigned char *rx = g_malloc0(bufsize);
|
||||
unsigned i;
|
||||
uint64_t ptr;
|
||||
AHCICommand *cmd;
|
||||
|
||||
@ -1231,11 +1201,8 @@ static void ahci_halted_io_test(uint8_t cmd_read, uint8_t cmd_write)
|
||||
port = ahci_port_select(ahci);
|
||||
ahci_port_clear(ahci, port);
|
||||
|
||||
for (i = 0; i < bufsize; i++) {
|
||||
tx[i] = (bufsize - i);
|
||||
}
|
||||
|
||||
/* create DMA source buffer and write pattern */
|
||||
generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE);
|
||||
ptr = ahci_alloc(ahci, bufsize);
|
||||
g_assert(ptr);
|
||||
memwrite(ptr, tx, bufsize);
|
||||
@ -1282,7 +1249,6 @@ static void ahci_migrate_halted_io(uint8_t cmd_read, uint8_t cmd_write)
|
||||
size_t bufsize = 4096;
|
||||
unsigned char *tx = g_malloc(bufsize);
|
||||
unsigned char *rx = g_malloc0(bufsize);
|
||||
unsigned i;
|
||||
uint64_t ptr;
|
||||
AHCICommand *cmd;
|
||||
const char *uri = "tcp:127.0.0.1:1234";
|
||||
@ -1310,10 +1276,7 @@ static void ahci_migrate_halted_io(uint8_t cmd_read, uint8_t cmd_write)
|
||||
/* Initialize and prepare */
|
||||
port = ahci_port_select(src);
|
||||
ahci_port_clear(src, port);
|
||||
|
||||
for (i = 0; i < bufsize; i++) {
|
||||
tx[i] = (bufsize - i);
|
||||
}
|
||||
generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE);
|
||||
|
||||
/* create DMA source buffer and write pattern */
|
||||
ptr = ahci_alloc(src, bufsize);
|
||||
|
245
tests/ide-test.c
245
tests/ide-test.c
@ -45,8 +45,15 @@
|
||||
#define IDE_BASE 0x1f0
|
||||
#define IDE_PRIMARY_IRQ 14
|
||||
|
||||
#define ATAPI_BLOCK_SIZE 2048
|
||||
|
||||
/* How many bytes to receive via ATAPI PIO at one time.
|
||||
* Must be less than 0xFFFF. */
|
||||
#define BYTE_COUNT_LIMIT 5120
|
||||
|
||||
enum {
|
||||
reg_data = 0x0,
|
||||
reg_feature = 0x1,
|
||||
reg_nsectors = 0x2,
|
||||
reg_lba_low = 0x3,
|
||||
reg_lba_middle = 0x4,
|
||||
@ -80,6 +87,7 @@ enum {
|
||||
CMD_WRITE_DMA = 0xca,
|
||||
CMD_FLUSH_CACHE = 0xe7,
|
||||
CMD_IDENTIFY = 0xec,
|
||||
CMD_PACKET = 0xa0,
|
||||
|
||||
CMDF_ABORT = 0x100,
|
||||
CMDF_NO_BM = 0x200,
|
||||
@ -172,7 +180,8 @@ typedef struct PrdtEntry {
|
||||
#define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0)
|
||||
|
||||
static int send_dma_request(int cmd, uint64_t sector, int nb_sectors,
|
||||
PrdtEntry *prdt, int prdt_entries)
|
||||
PrdtEntry *prdt, int prdt_entries,
|
||||
void(*post_exec)(uint64_t sector, int nb_sectors))
|
||||
{
|
||||
QPCIDevice *dev;
|
||||
uint16_t bmdma_base;
|
||||
@ -189,6 +198,9 @@ static int send_dma_request(int cmd, uint64_t sector, int nb_sectors,
|
||||
|
||||
switch (cmd) {
|
||||
case CMD_READ_DMA:
|
||||
case CMD_PACKET:
|
||||
/* Assuming we only test data reads w/ ATAPI, otherwise we need to know
|
||||
* the SCSI command being sent in the packet, too. */
|
||||
from_dev = true;
|
||||
break;
|
||||
case CMD_WRITE_DMA:
|
||||
@ -217,14 +229,22 @@ static int send_dma_request(int cmd, uint64_t sector, int nb_sectors,
|
||||
outl(bmdma_base + bmreg_prdt, guest_prdt);
|
||||
|
||||
/* ATA DMA command */
|
||||
outb(IDE_BASE + reg_nsectors, nb_sectors);
|
||||
|
||||
outb(IDE_BASE + reg_lba_low, sector & 0xff);
|
||||
outb(IDE_BASE + reg_lba_middle, (sector >> 8) & 0xff);
|
||||
outb(IDE_BASE + reg_lba_high, (sector >> 16) & 0xff);
|
||||
if (cmd == CMD_PACKET) {
|
||||
/* Enables ATAPI DMA; otherwise PIO is attempted */
|
||||
outb(IDE_BASE + reg_feature, 0x01);
|
||||
} else {
|
||||
outb(IDE_BASE + reg_nsectors, nb_sectors);
|
||||
outb(IDE_BASE + reg_lba_low, sector & 0xff);
|
||||
outb(IDE_BASE + reg_lba_middle, (sector >> 8) & 0xff);
|
||||
outb(IDE_BASE + reg_lba_high, (sector >> 16) & 0xff);
|
||||
}
|
||||
|
||||
outb(IDE_BASE + reg_command, cmd);
|
||||
|
||||
if (post_exec) {
|
||||
post_exec(sector, nb_sectors);
|
||||
}
|
||||
|
||||
/* Start DMA transfer */
|
||||
outb(bmdma_base + bmreg_cmd, BM_CMD_START | (from_dev ? BM_CMD_WRITE : 0));
|
||||
|
||||
@ -278,7 +298,8 @@ static void test_bmdma_simple_rw(void)
|
||||
memset(buf, 0x55, len);
|
||||
memwrite(guest_buf, buf, len);
|
||||
|
||||
status = send_dma_request(CMD_WRITE_DMA, 0, 1, prdt, ARRAY_SIZE(prdt));
|
||||
status = send_dma_request(CMD_WRITE_DMA, 0, 1, prdt,
|
||||
ARRAY_SIZE(prdt), NULL);
|
||||
g_assert_cmphex(status, ==, BM_STS_INTR);
|
||||
assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
|
||||
|
||||
@ -286,14 +307,15 @@ static void test_bmdma_simple_rw(void)
|
||||
memset(buf, 0xaa, len);
|
||||
memwrite(guest_buf, buf, len);
|
||||
|
||||
status = send_dma_request(CMD_WRITE_DMA, 1, 1, prdt, ARRAY_SIZE(prdt));
|
||||
status = send_dma_request(CMD_WRITE_DMA, 1, 1, prdt,
|
||||
ARRAY_SIZE(prdt), NULL);
|
||||
g_assert_cmphex(status, ==, BM_STS_INTR);
|
||||
assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
|
||||
|
||||
/* Read and verify 0x55 pattern in sector 0 */
|
||||
memset(cmpbuf, 0x55, len);
|
||||
|
||||
status = send_dma_request(CMD_READ_DMA, 0, 1, prdt, ARRAY_SIZE(prdt));
|
||||
status = send_dma_request(CMD_READ_DMA, 0, 1, prdt, ARRAY_SIZE(prdt), NULL);
|
||||
g_assert_cmphex(status, ==, BM_STS_INTR);
|
||||
assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
|
||||
|
||||
@ -303,7 +325,7 @@ static void test_bmdma_simple_rw(void)
|
||||
/* Read and verify 0xaa pattern in sector 1 */
|
||||
memset(cmpbuf, 0xaa, len);
|
||||
|
||||
status = send_dma_request(CMD_READ_DMA, 1, 1, prdt, ARRAY_SIZE(prdt));
|
||||
status = send_dma_request(CMD_READ_DMA, 1, 1, prdt, ARRAY_SIZE(prdt), NULL);
|
||||
g_assert_cmphex(status, ==, BM_STS_INTR);
|
||||
assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
|
||||
|
||||
@ -328,13 +350,13 @@ static void test_bmdma_short_prdt(void)
|
||||
|
||||
/* Normal request */
|
||||
status = send_dma_request(CMD_READ_DMA, 0, 1,
|
||||
prdt, ARRAY_SIZE(prdt));
|
||||
prdt, ARRAY_SIZE(prdt), NULL);
|
||||
g_assert_cmphex(status, ==, 0);
|
||||
assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
|
||||
|
||||
/* Abort the request before it completes */
|
||||
status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 1,
|
||||
prdt, ARRAY_SIZE(prdt));
|
||||
prdt, ARRAY_SIZE(prdt), NULL);
|
||||
g_assert_cmphex(status, ==, 0);
|
||||
assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
|
||||
}
|
||||
@ -353,13 +375,13 @@ static void test_bmdma_one_sector_short_prdt(void)
|
||||
|
||||
/* Normal request */
|
||||
status = send_dma_request(CMD_READ_DMA, 0, 2,
|
||||
prdt, ARRAY_SIZE(prdt));
|
||||
prdt, ARRAY_SIZE(prdt), NULL);
|
||||
g_assert_cmphex(status, ==, 0);
|
||||
assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
|
||||
|
||||
/* Abort the request before it completes */
|
||||
status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 2,
|
||||
prdt, ARRAY_SIZE(prdt));
|
||||
prdt, ARRAY_SIZE(prdt), NULL);
|
||||
g_assert_cmphex(status, ==, 0);
|
||||
assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
|
||||
}
|
||||
@ -377,13 +399,13 @@ static void test_bmdma_long_prdt(void)
|
||||
|
||||
/* Normal request */
|
||||
status = send_dma_request(CMD_READ_DMA, 0, 1,
|
||||
prdt, ARRAY_SIZE(prdt));
|
||||
prdt, ARRAY_SIZE(prdt), NULL);
|
||||
g_assert_cmphex(status, ==, BM_STS_ACTIVE | BM_STS_INTR);
|
||||
assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
|
||||
|
||||
/* Abort the request before it completes */
|
||||
status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 1,
|
||||
prdt, ARRAY_SIZE(prdt));
|
||||
prdt, ARRAY_SIZE(prdt), NULL);
|
||||
g_assert_cmphex(status, ==, BM_STS_INTR);
|
||||
assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
|
||||
}
|
||||
@ -399,7 +421,7 @@ static void test_bmdma_no_busmaster(void)
|
||||
PrdtEntry prdt[4096] = { };
|
||||
|
||||
status = send_dma_request(CMD_READ_DMA | CMDF_NO_BM, 0, 512,
|
||||
prdt, ARRAY_SIZE(prdt));
|
||||
prdt, ARRAY_SIZE(prdt), NULL);
|
||||
|
||||
/* Not entirely clear what the expected result is, but this is what we get
|
||||
* in practice. At least we want to be aware of any changes. */
|
||||
@ -585,6 +607,191 @@ static void test_isa_retry_flush(const char *machine)
|
||||
test_retry_flush("isapc");
|
||||
}
|
||||
|
||||
typedef struct Read10CDB {
|
||||
uint8_t opcode;
|
||||
uint8_t flags;
|
||||
uint32_t lba;
|
||||
uint8_t reserved;
|
||||
uint16_t nblocks;
|
||||
uint8_t control;
|
||||
uint16_t padding;
|
||||
} __attribute__((__packed__)) Read10CDB;
|
||||
|
||||
static void send_scsi_cdb_read10(uint64_t lba, int nblocks)
|
||||
{
|
||||
Read10CDB pkt = { .padding = 0 };
|
||||
int i;
|
||||
|
||||
g_assert_cmpint(lba, <=, UINT32_MAX);
|
||||
g_assert_cmpint(nblocks, <=, UINT16_MAX);
|
||||
g_assert_cmpint(nblocks, >=, 0);
|
||||
|
||||
/* Construct SCSI CDB packet */
|
||||
pkt.opcode = 0x28;
|
||||
pkt.lba = cpu_to_be32(lba);
|
||||
pkt.nblocks = cpu_to_be16(nblocks);
|
||||
|
||||
/* Send Packet */
|
||||
for (i = 0; i < sizeof(Read10CDB)/2; i++) {
|
||||
outw(IDE_BASE + reg_data, ((uint16_t *)&pkt)[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void nsleep(int64_t nsecs)
|
||||
{
|
||||
const struct timespec val = { .tv_nsec = nsecs };
|
||||
nanosleep(&val, NULL);
|
||||
clock_set(nsecs);
|
||||
}
|
||||
|
||||
static uint8_t ide_wait_clear(uint8_t flag)
|
||||
{
|
||||
int i;
|
||||
uint8_t data;
|
||||
|
||||
/* Wait with a 5 second timeout */
|
||||
for (i = 0; i <= 12500000; i++) {
|
||||
data = inb(IDE_BASE + reg_status);
|
||||
if (!(data & flag)) {
|
||||
return data;
|
||||
}
|
||||
nsleep(400);
|
||||
}
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void ide_wait_intr(int irq)
|
||||
{
|
||||
int i;
|
||||
bool intr;
|
||||
|
||||
for (i = 0; i <= 12500000; i++) {
|
||||
intr = get_irq(irq);
|
||||
if (intr) {
|
||||
return;
|
||||
}
|
||||
nsleep(400);
|
||||
}
|
||||
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static void cdrom_pio_impl(int nblocks)
|
||||
{
|
||||
FILE *fh;
|
||||
int patt_blocks = MAX(16, nblocks);
|
||||
size_t patt_len = ATAPI_BLOCK_SIZE * patt_blocks;
|
||||
char *pattern = g_malloc(patt_len);
|
||||
size_t rxsize = ATAPI_BLOCK_SIZE * nblocks;
|
||||
uint16_t *rx = g_malloc0(rxsize);
|
||||
int i, j;
|
||||
uint8_t data;
|
||||
uint16_t limit;
|
||||
|
||||
/* Prepopulate the CDROM with an interesting pattern */
|
||||
generate_pattern(pattern, patt_len, ATAPI_BLOCK_SIZE);
|
||||
fh = fopen(tmp_path, "w+");
|
||||
fwrite(pattern, ATAPI_BLOCK_SIZE, patt_blocks, fh);
|
||||
fclose(fh);
|
||||
|
||||
ide_test_start("-drive if=none,file=%s,media=cdrom,format=raw,id=sr0,index=0 "
|
||||
"-device ide-cd,drive=sr0,bus=ide.0", tmp_path);
|
||||
qtest_irq_intercept_in(global_qtest, "ioapic");
|
||||
|
||||
/* PACKET command on device 0 */
|
||||
outb(IDE_BASE + reg_device, 0);
|
||||
outb(IDE_BASE + reg_lba_middle, BYTE_COUNT_LIMIT & 0xFF);
|
||||
outb(IDE_BASE + reg_lba_high, (BYTE_COUNT_LIMIT >> 8 & 0xFF));
|
||||
outb(IDE_BASE + reg_command, CMD_PACKET);
|
||||
/* HPD0: Check_Status_A State */
|
||||
nsleep(400);
|
||||
data = ide_wait_clear(BSY);
|
||||
/* HPD1: Send_Packet State */
|
||||
assert_bit_set(data, DRQ | DRDY);
|
||||
assert_bit_clear(data, ERR | DF | BSY);
|
||||
|
||||
/* SCSI CDB (READ10) -- read n*2048 bytes from block 0 */
|
||||
send_scsi_cdb_read10(0, nblocks);
|
||||
|
||||
/* HPD3: INTRQ_Wait */
|
||||
ide_wait_intr(IDE_PRIMARY_IRQ);
|
||||
|
||||
/* HPD2: Check_Status_B */
|
||||
data = ide_wait_clear(BSY);
|
||||
assert_bit_set(data, DRQ | DRDY);
|
||||
assert_bit_clear(data, ERR | DF | BSY);
|
||||
|
||||
/* Read data back: occurs in bursts of 'BYTE_COUNT_LIMIT' bytes.
|
||||
* If BYTE_COUNT_LIMIT is odd, we transfer BYTE_COUNT_LIMIT - 1 bytes.
|
||||
* We allow an odd limit only when the remaining transfer size is
|
||||
* less than BYTE_COUNT_LIMIT. However, SCSI's read10 command can only
|
||||
* request n blocks, so our request size is always even.
|
||||
* For this reason, we assume there is never a hanging byte to fetch. */
|
||||
g_assert(!(rxsize & 1));
|
||||
limit = BYTE_COUNT_LIMIT & ~1;
|
||||
for (i = 0; i < DIV_ROUND_UP(rxsize, limit); i++) {
|
||||
size_t offset = i * (limit / 2);
|
||||
size_t rem = (rxsize / 2) - offset;
|
||||
for (j = 0; j < MIN((limit / 2), rem); j++) {
|
||||
rx[offset + j] = inw(IDE_BASE + reg_data);
|
||||
}
|
||||
ide_wait_intr(IDE_PRIMARY_IRQ);
|
||||
}
|
||||
data = ide_wait_clear(DRQ);
|
||||
assert_bit_set(data, DRDY);
|
||||
assert_bit_clear(data, DRQ | ERR | DF | BSY);
|
||||
|
||||
g_assert_cmpint(memcmp(pattern, rx, rxsize), ==, 0);
|
||||
g_free(pattern);
|
||||
g_free(rx);
|
||||
test_bmdma_teardown();
|
||||
}
|
||||
|
||||
static void test_cdrom_pio(void)
|
||||
{
|
||||
cdrom_pio_impl(1);
|
||||
}
|
||||
|
||||
static void test_cdrom_pio_large(void)
|
||||
{
|
||||
/* Test a few loops of the PIO DRQ mechanism. */
|
||||
cdrom_pio_impl(BYTE_COUNT_LIMIT * 4 / ATAPI_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
|
||||
static void test_cdrom_dma(void)
|
||||
{
|
||||
static const size_t len = ATAPI_BLOCK_SIZE;
|
||||
char *pattern = g_malloc(ATAPI_BLOCK_SIZE * 16);
|
||||
char *rx = g_malloc0(len);
|
||||
uintptr_t guest_buf;
|
||||
PrdtEntry prdt[1];
|
||||
FILE *fh;
|
||||
|
||||
ide_test_start("-drive if=none,file=%s,media=cdrom,format=raw,id=sr0,index=0 "
|
||||
"-device ide-cd,drive=sr0,bus=ide.0", tmp_path);
|
||||
qtest_irq_intercept_in(global_qtest, "ioapic");
|
||||
|
||||
guest_buf = guest_alloc(guest_malloc, len);
|
||||
prdt[0].addr = cpu_to_le32(guest_buf);
|
||||
prdt[0].size = cpu_to_le32(len | PRDT_EOT);
|
||||
|
||||
generate_pattern(pattern, ATAPI_BLOCK_SIZE * 16, ATAPI_BLOCK_SIZE);
|
||||
fh = fopen(tmp_path, "w+");
|
||||
fwrite(pattern, ATAPI_BLOCK_SIZE, 16, fh);
|
||||
fclose(fh);
|
||||
|
||||
send_dma_request(CMD_PACKET, 0, 1, prdt, 1, send_scsi_cdb_read10);
|
||||
|
||||
/* Read back data from guest memory into local qtest memory */
|
||||
memread(guest_buf, rx, len);
|
||||
g_assert_cmpint(memcmp(pattern, rx, len), ==, 0);
|
||||
|
||||
g_free(pattern);
|
||||
g_free(rx);
|
||||
test_bmdma_teardown();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *arch = qtest_get_arch();
|
||||
@ -628,6 +835,10 @@ int main(int argc, char **argv)
|
||||
qtest_add_func("/ide/flush/retry_pci", test_pci_retry_flush);
|
||||
qtest_add_func("/ide/flush/retry_isa", test_isa_retry_flush);
|
||||
|
||||
qtest_add_func("/ide/cdrom/pio", test_cdrom_pio);
|
||||
qtest_add_func("/ide/cdrom/pio_large", test_cdrom_pio_large);
|
||||
qtest_add_func("/ide/cdrom/dma", test_cdrom_dma);
|
||||
|
||||
ret = g_test_run();
|
||||
|
||||
/* Cleanup */
|
||||
|
@ -212,3 +212,29 @@ void prepare_blkdebug_script(const char *debug_fn, const char *event)
|
||||
ret = fclose(debug_file);
|
||||
g_assert(ret == 0);
|
||||
}
|
||||
|
||||
void generate_pattern(void *buffer, size_t len, size_t cycle_len)
|
||||
{
|
||||
int i, j;
|
||||
unsigned char *tx = (unsigned char *)buffer;
|
||||
unsigned char p;
|
||||
size_t *sx;
|
||||
|
||||
/* Write an indicative pattern that varies and is unique per-cycle */
|
||||
p = rand() % 256;
|
||||
for (i = 0; i < len; i++) {
|
||||
tx[i] = p++ % 256;
|
||||
if (i % cycle_len == 0) {
|
||||
p = rand() % 256;
|
||||
}
|
||||
}
|
||||
|
||||
/* force uniqueness by writing an id per-cycle */
|
||||
for (i = 0; i < len / cycle_len; i++) {
|
||||
j = i * cycle_len;
|
||||
if (j + sizeof(*sx) <= len) {
|
||||
sx = (size_t *)&tx[j];
|
||||
*sx = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ void mkqcow2(const char *file, unsigned size_mb);
|
||||
void set_context(QOSState *s);
|
||||
void migrate(QOSState *from, QOSState *to, const char *uri);
|
||||
void prepare_blkdebug_script(const char *debug_fn, const char *event);
|
||||
void generate_pattern(void *buffer, size_t len, size_t cycle_len);
|
||||
|
||||
static inline uint64_t qmalloc(QOSState *q, size_t bytes)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user