diff options
author | Anthony Liguori <aliguori@us.ibm.com> | 2012-07-18 14:44:50 -0500 |
---|---|---|
committer | Anthony Liguori <aliguori@us.ibm.com> | 2012-07-18 14:44:50 -0500 |
commit | dfe1ce5d80cba603bafaac91b239d683abe19cf7 (patch) | |
tree | 15aa6b143325c83b61871216ccb44ebd545f9aaa | |
parent | 09f06a6c603ce64284287d32f6ffadaaa5064850 (diff) | |
parent | c3cdc1b0ff84d1cfed0c8052d2c83f8ecbf24166 (diff) |
Merge remote-tracking branch 'kwolf/for-anthony' into staging
* kwolf/for-anthony: (41 commits)
fdc-test: Clean up a bit
fdc-test: introduce test_relative_seek
fdc: fix relative seek
qemu-iotests: Valgrind support
coroutine-ucontext: Help valgrind understand coroutines
qemu-io: Fix memory leaks
hw/block-common: Factor out fall back to legacy -drive cyls=...
blockdev: Don't limit DriveInfo serial to 20 characters
hw/block-common: Factor out fall back to legacy -drive serial=...
hw/block-common: Move BlockConf & friends from block.h
Relax IDE CHS limits from 16383,16,63 to 65535,16,255
blockdev: Drop redundant CHS validation for if=ide
hd-geometry: Compute BIOS CHS translation in one place
qtest: Test we don't put hard disk info into CMOS for a CD-ROM
ide pc: Put hard disk info into CMOS only for hard disks
block: Geometry and translation hints are now useless, purge them
qtest: Cover qdev property for BIOS CHS translation
ide: qdev property for BIOS CHS translation
qdev: New property type chs-translation
qdev: Collect private helpers in one place
...
39 files changed, 1325 insertions, 697 deletions
@@ -996,12 +996,6 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest, bs_dest->block_timer = bs_src->block_timer; bs_dest->io_limits_enabled = bs_src->io_limits_enabled; - /* geometry */ - bs_dest->cyls = bs_src->cyls; - bs_dest->heads = bs_src->heads; - bs_dest->secs = bs_src->secs; - bs_dest->translation = bs_src->translation; - /* r/w error */ bs_dest->on_read_error = bs_src->on_read_error; bs_dest->on_write_error = bs_src->on_write_error; @@ -2132,148 +2126,6 @@ void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr) *nb_sectors_ptr = length; } -struct partition { - uint8_t boot_ind; /* 0x80 - active */ - uint8_t head; /* starting head */ - uint8_t sector; /* starting sector */ - uint8_t cyl; /* starting cylinder */ - uint8_t sys_ind; /* What partition type */ - uint8_t end_head; /* end head */ - uint8_t end_sector; /* end sector */ - uint8_t end_cyl; /* end cylinder */ - uint32_t start_sect; /* starting sector counting from 0 */ - uint32_t nr_sects; /* nr of sectors in partition */ -} QEMU_PACKED; - -/* try to guess the disk logical geometry from the MSDOS partition table. Return 0 if OK, -1 if could not guess */ -static int guess_disk_lchs(BlockDriverState *bs, - int *pcylinders, int *pheads, int *psectors) -{ - uint8_t buf[BDRV_SECTOR_SIZE]; - int i, heads, sectors, cylinders; - struct partition *p; - uint32_t nr_sects; - uint64_t nb_sectors; - - bdrv_get_geometry(bs, &nb_sectors); - - /** - * The function will be invoked during startup not only in sync I/O mode, - * but also in async I/O mode. So the I/O throttling function has to - * be disabled temporarily here, not permanently. - */ - if (bdrv_read_unthrottled(bs, 0, buf, 1) < 0) { - return -1; - } - /* test msdos magic */ - if (buf[510] != 0x55 || buf[511] != 0xaa) - return -1; - for(i = 0; i < 4; i++) { - p = ((struct partition *)(buf + 0x1be)) + i; - nr_sects = le32_to_cpu(p->nr_sects); - if (nr_sects && p->end_head) { - /* We make the assumption that the partition terminates on - a cylinder boundary */ - heads = p->end_head + 1; - sectors = p->end_sector & 63; - if (sectors == 0) - continue; - cylinders = nb_sectors / (heads * sectors); - if (cylinders < 1 || cylinders > 16383) - continue; - *pheads = heads; - *psectors = sectors; - *pcylinders = cylinders; -#if 0 - printf("guessed geometry: LCHS=%d %d %d\n", - cylinders, heads, sectors); -#endif - return 0; - } - } - return -1; -} - -void bdrv_guess_geometry(BlockDriverState *bs, int *pcyls, int *pheads, int *psecs) -{ - int translation, lba_detected = 0; - int cylinders, heads, secs; - uint64_t nb_sectors; - - /* if a geometry hint is available, use it */ - bdrv_get_geometry(bs, &nb_sectors); - bdrv_get_geometry_hint(bs, &cylinders, &heads, &secs); - translation = bdrv_get_translation_hint(bs); - if (cylinders != 0) { - *pcyls = cylinders; - *pheads = heads; - *psecs = secs; - } else { - if (guess_disk_lchs(bs, &cylinders, &heads, &secs) == 0) { - if (heads > 16) { - /* if heads > 16, it means that a BIOS LBA - translation was active, so the default - hardware geometry is OK */ - lba_detected = 1; - goto default_geometry; - } else { - *pcyls = cylinders; - *pheads = heads; - *psecs = secs; - /* disable any translation to be in sync with - the logical geometry */ - if (translation == BIOS_ATA_TRANSLATION_AUTO) { - bdrv_set_translation_hint(bs, - BIOS_ATA_TRANSLATION_NONE); - } - } - } else { - default_geometry: - /* if no geometry, use a standard physical disk geometry */ - cylinders = nb_sectors / (16 * 63); - - if (cylinders > 16383) - cylinders = 16383; - else if (cylinders < 2) - cylinders = 2; - *pcyls = cylinders; - *pheads = 16; - *psecs = 63; - if ((lba_detected == 1) && (translation == BIOS_ATA_TRANSLATION_AUTO)) { - if ((*pcyls * *pheads) <= 131072) { - bdrv_set_translation_hint(bs, - BIOS_ATA_TRANSLATION_LARGE); - } else { - bdrv_set_translation_hint(bs, - BIOS_ATA_TRANSLATION_LBA); - } - } - } - bdrv_set_geometry_hint(bs, *pcyls, *pheads, *psecs); - } -} - -void bdrv_set_geometry_hint(BlockDriverState *bs, - int cyls, int heads, int secs) -{ - bs->cyls = cyls; - bs->heads = heads; - bs->secs = secs; -} - -void bdrv_set_translation_hint(BlockDriverState *bs, int translation) -{ - bs->translation = translation; -} - -void bdrv_get_geometry_hint(BlockDriverState *bs, - int *pcyls, int *pheads, int *psecs) -{ - *pcyls = bs->cyls; - *pheads = bs->heads; - *psecs = bs->secs; -} - /* throttling disk io limits */ void bdrv_set_io_limits(BlockDriverState *bs, BlockIOLimit *io_limits) @@ -2282,112 +2134,6 @@ void bdrv_set_io_limits(BlockDriverState *bs, bs->io_limits_enabled = bdrv_io_limits_enabled(bs); } -/* Recognize floppy formats */ -typedef struct FDFormat { - FDriveType drive; - uint8_t last_sect; - uint8_t max_track; - uint8_t max_head; - FDriveRate rate; -} FDFormat; - -static const FDFormat fd_formats[] = { - /* First entry is default format */ - /* 1.44 MB 3"1/2 floppy disks */ - { FDRIVE_DRV_144, 18, 80, 1, FDRIVE_RATE_500K, }, - { FDRIVE_DRV_144, 20, 80, 1, FDRIVE_RATE_500K, }, - { FDRIVE_DRV_144, 21, 80, 1, FDRIVE_RATE_500K, }, - { FDRIVE_DRV_144, 21, 82, 1, FDRIVE_RATE_500K, }, - { FDRIVE_DRV_144, 21, 83, 1, FDRIVE_RATE_500K, }, - { FDRIVE_DRV_144, 22, 80, 1, FDRIVE_RATE_500K, }, - { FDRIVE_DRV_144, 23, 80, 1, FDRIVE_RATE_500K, }, - { FDRIVE_DRV_144, 24, 80, 1, FDRIVE_RATE_500K, }, - /* 2.88 MB 3"1/2 floppy disks */ - { FDRIVE_DRV_288, 36, 80, 1, FDRIVE_RATE_1M, }, - { FDRIVE_DRV_288, 39, 80, 1, FDRIVE_RATE_1M, }, - { FDRIVE_DRV_288, 40, 80, 1, FDRIVE_RATE_1M, }, - { FDRIVE_DRV_288, 44, 80, 1, FDRIVE_RATE_1M, }, - { FDRIVE_DRV_288, 48, 80, 1, FDRIVE_RATE_1M, }, - /* 720 kB 3"1/2 floppy disks */ - { FDRIVE_DRV_144, 9, 80, 1, FDRIVE_RATE_250K, }, - { FDRIVE_DRV_144, 10, 80, 1, FDRIVE_RATE_250K, }, - { FDRIVE_DRV_144, 10, 82, 1, FDRIVE_RATE_250K, }, - { FDRIVE_DRV_144, 10, 83, 1, FDRIVE_RATE_250K, }, - { FDRIVE_DRV_144, 13, 80, 1, FDRIVE_RATE_250K, }, - { FDRIVE_DRV_144, 14, 80, 1, FDRIVE_RATE_250K, }, - /* 1.2 MB 5"1/4 floppy disks */ - { FDRIVE_DRV_120, 15, 80, 1, FDRIVE_RATE_500K, }, - { FDRIVE_DRV_120, 18, 80, 1, FDRIVE_RATE_500K, }, - { FDRIVE_DRV_120, 18, 82, 1, FDRIVE_RATE_500K, }, - { FDRIVE_DRV_120, 18, 83, 1, FDRIVE_RATE_500K, }, - { FDRIVE_DRV_120, 20, 80, 1, FDRIVE_RATE_500K, }, - /* 720 kB 5"1/4 floppy disks */ - { FDRIVE_DRV_120, 9, 80, 1, FDRIVE_RATE_250K, }, - { FDRIVE_DRV_120, 11, 80, 1, FDRIVE_RATE_250K, }, - /* 360 kB 5"1/4 floppy disks */ - { FDRIVE_DRV_120, 9, 40, 1, FDRIVE_RATE_300K, }, - { FDRIVE_DRV_120, 9, 40, 0, FDRIVE_RATE_300K, }, - { FDRIVE_DRV_120, 10, 41, 1, FDRIVE_RATE_300K, }, - { FDRIVE_DRV_120, 10, 42, 1, FDRIVE_RATE_300K, }, - /* 320 kB 5"1/4 floppy disks */ - { FDRIVE_DRV_120, 8, 40, 1, FDRIVE_RATE_250K, }, - { FDRIVE_DRV_120, 8, 40, 0, FDRIVE_RATE_250K, }, - /* 360 kB must match 5"1/4 better than 3"1/2... */ - { FDRIVE_DRV_144, 9, 80, 0, FDRIVE_RATE_250K, }, - /* end */ - { FDRIVE_DRV_NONE, -1, -1, 0, 0, }, -}; - -void bdrv_get_floppy_geometry_hint(BlockDriverState *bs, int *nb_heads, - int *max_track, int *last_sect, - FDriveType drive_in, FDriveType *drive, - FDriveRate *rate) -{ - const FDFormat *parse; - uint64_t nb_sectors, size; - int i, first_match, match; - - bdrv_get_geometry(bs, &nb_sectors); - match = -1; - first_match = -1; - for (i = 0; ; i++) { - parse = &fd_formats[i]; - if (parse->drive == FDRIVE_DRV_NONE) { - break; - } - if (drive_in == parse->drive || - drive_in == FDRIVE_DRV_NONE) { - size = (parse->max_head + 1) * parse->max_track * - parse->last_sect; - if (nb_sectors == size) { - match = i; - break; - } - if (first_match == -1) { - first_match = i; - } - } - } - if (match == -1) { - if (first_match == -1) { - match = 1; - } else { - match = first_match; - } - parse = &fd_formats[match]; - } - *nb_heads = parse->max_head + 1; - *max_track = parse->max_track; - *last_sect = parse->last_sect; - *drive = parse->drive; - *rate = parse->rate; -} - -int bdrv_get_translation_hint(BlockDriverState *bs) -{ - return bs->translation; -} - void bdrv_set_on_error(BlockDriverState *bs, BlockErrorAction on_read_error, BlockErrorAction on_write_error) { @@ -178,7 +178,6 @@ int bdrv_truncate(BlockDriverState *bs, int64_t offset); int64_t bdrv_getlength(BlockDriverState *bs); int64_t bdrv_get_allocated_file_size(BlockDriverState *bs); void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr); -void bdrv_guess_geometry(BlockDriverState *bs, int *pcyls, int *pheads, int *psecs); int bdrv_commit(BlockDriverState *bs); int bdrv_commit_all(void); int bdrv_change_backing_file(BlockDriverState *bs, @@ -258,36 +257,6 @@ int bdrv_has_zero_init(BlockDriverState *bs); int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum); -#define BIOS_ATA_TRANSLATION_AUTO 0 -#define BIOS_ATA_TRANSLATION_NONE 1 -#define BIOS_ATA_TRANSLATION_LBA 2 -#define BIOS_ATA_TRANSLATION_LARGE 3 -#define BIOS_ATA_TRANSLATION_RECHS 4 - -void bdrv_set_geometry_hint(BlockDriverState *bs, - int cyls, int heads, int secs); -void bdrv_set_translation_hint(BlockDriverState *bs, int translation); -void bdrv_get_geometry_hint(BlockDriverState *bs, - int *pcyls, int *pheads, int *psecs); -typedef enum FDriveType { - FDRIVE_DRV_144 = 0x00, /* 1.44 MB 3"5 drive */ - FDRIVE_DRV_288 = 0x01, /* 2.88 MB 3"5 drive */ - FDRIVE_DRV_120 = 0x02, /* 1.2 MB 5"25 drive */ - FDRIVE_DRV_NONE = 0x03, /* No drive connected */ -} FDriveType; - -typedef enum FDriveRate { - FDRIVE_RATE_500K = 0x00, /* 500 Kbps */ - FDRIVE_RATE_300K = 0x01, /* 300 Kbps */ - FDRIVE_RATE_250K = 0x02, /* 250 Kbps */ - FDRIVE_RATE_1M = 0x03, /* 1 Mbps */ -} FDriveRate; - -void bdrv_get_floppy_geometry_hint(BlockDriverState *bs, int *nb_heads, - int *max_track, int *last_sect, - FDriveType drive_in, FDriveType *drive, - FDriveRate *rate); -int bdrv_get_translation_hint(BlockDriverState *bs); void bdrv_set_on_error(BlockDriverState *bs, BlockErrorAction on_read_error, BlockErrorAction on_write_error); BlockErrorAction bdrv_get_on_error(BlockDriverState *bs, int is_read); @@ -434,43 +403,4 @@ typedef enum { #define BLKDBG_EVENT(bs, evt) bdrv_debug_event(bs, evt) void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event); - -/* Convenience for block device models */ - -typedef struct BlockConf { - BlockDriverState *bs; - uint16_t physical_block_size; - uint16_t logical_block_size; - uint16_t min_io_size; - uint32_t opt_io_size; - int32_t bootindex; - uint32_t discard_granularity; -} BlockConf; - -static inline unsigned int get_physical_block_exp(BlockConf *conf) -{ - unsigned int exp = 0, size; - - for (size = conf->physical_block_size; - size > conf->logical_block_size; - size >>= 1) { - exp++; - } - - return exp; -} - -#define DEFINE_BLOCK_PROPERTIES(_state, _conf) \ - DEFINE_PROP_DRIVE("drive", _state, _conf.bs), \ - DEFINE_PROP_BLOCKSIZE("logical_block_size", _state, \ - _conf.logical_block_size, 512), \ - DEFINE_PROP_BLOCKSIZE("physical_block_size", _state, \ - _conf.physical_block_size, 512), \ - DEFINE_PROP_UINT16("min_io_size", _state, _conf.min_io_size, 0), \ - DEFINE_PROP_UINT32("opt_io_size", _state, _conf.opt_io_size, 0), \ - DEFINE_PROP_INT32("bootindex", _state, _conf.bootindex, -1), \ - DEFINE_PROP_UINT32("discard_granularity", _state, \ - _conf.discard_granularity, 0) - #endif - diff --git a/block/sheepdog.c b/block/sheepdog.c index 809df39d9e..a04ad99ead 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -498,26 +498,6 @@ success: return fd; } -static int send_req(int sockfd, SheepdogReq *hdr, void *data, - unsigned int *wlen) -{ - int ret; - - ret = qemu_send_full(sockfd, hdr, sizeof(*hdr), 0); - if (ret < sizeof(*hdr)) { - error_report("failed to send a req, %s", strerror(errno)); - return -errno; - } - - ret = qemu_send_full(sockfd, data, *wlen, 0); - if (ret < *wlen) { - error_report("failed to send a req, %s", strerror(errno)); - ret = -errno; - } - - return ret; -} - static coroutine_fn int send_co_req(int sockfd, SheepdogReq *hdr, void *data, unsigned int *wlen) { @@ -537,49 +517,6 @@ static coroutine_fn int send_co_req(int sockfd, SheepdogReq *hdr, void *data, return ret; } -static coroutine_fn int do_co_req(int sockfd, SheepdogReq *hdr, void *data, - unsigned int *wlen, unsigned int *rlen); - -static int do_req(int sockfd, SheepdogReq *hdr, void *data, - unsigned int *wlen, unsigned int *rlen) -{ - int ret; - - if (qemu_in_coroutine()) { - return do_co_req(sockfd, hdr, data, wlen, rlen); - } - - socket_set_block(sockfd); - ret = send_req(sockfd, hdr, data, wlen); - if (ret < 0) { - goto out; - } - - ret = qemu_recv_full(sockfd, hdr, sizeof(*hdr), 0); - if (ret < sizeof(*hdr)) { - error_report("failed to get a rsp, %s", strerror(errno)); - ret = -errno; - goto out; - } - - if (*rlen > hdr->data_length) { - *rlen = hdr->data_length; - } - - if (*rlen) { - ret = qemu_recv_full(sockfd, data, *rlen, 0); - if (ret < *rlen) { - error_report("failed to get the data, %s", strerror(errno)); - ret = -errno; - goto out; - } - } - ret = 0; -out: - socket_set_nonblock(sockfd); - return ret; -} - static void restart_co_req(void *opaque) { Coroutine *co = opaque; @@ -587,11 +524,26 @@ static void restart_co_req(void *opaque) qemu_coroutine_enter(co, NULL); } -static coroutine_fn int do_co_req(int sockfd, SheepdogReq *hdr, void *data, - unsigned int *wlen, unsigned int *rlen) +typedef struct SheepdogReqCo { + int sockfd; + SheepdogReq *hdr; + void *data; + unsigned int *wlen; + unsigned int *rlen; + int ret; + bool finished; +} SheepdogReqCo; + +static coroutine_fn void do_co_req(void *opaque) { int ret; Coroutine *co; + SheepdogReqCo *srco = opaque; + int sockfd = srco->sockfd; + SheepdogReq *hdr = srco->hdr; + void *data = srco->data; + unsigned int *wlen = srco->wlen; + unsigned int *rlen = srco->rlen; co = qemu_coroutine_self(); qemu_aio_set_fd_handler(sockfd, NULL, restart_co_req, NULL, co); @@ -627,7 +579,36 @@ static coroutine_fn int do_co_req(int sockfd, SheepdogReq *hdr, void *data, out: qemu_aio_set_fd_handler(sockfd, NULL, NULL, NULL, NULL); socket_set_nonblock(sockfd); - return ret; + + srco->ret = ret; + srco->finished = true; +} + +static int do_req(int sockfd, SheepdogReq *hdr, void *data, + unsigned int *wlen, unsigned int *rlen) +{ + Coroutine *co; + SheepdogReqCo srco = { + .sockfd = sockfd, + .hdr = hdr, + .data = data, + .wlen = wlen, + .rlen = rlen, + .ret = 0, + .finished = false, + }; + + if (qemu_in_coroutine()) { + do_co_req(&srco); + } else { + co = qemu_coroutine_create(do_co_req); + qemu_coroutine_enter(co, &srco); + while (!srco.finished) { + qemu_aio_wait(); + } + } + + return srco.ret; } static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req, @@ -1590,18 +1571,25 @@ static int coroutine_fn sd_co_rw_vector(void *p) len = MIN(total - done, SD_DATA_OBJ_SIZE - offset); - if (!inode->data_vdi_id[idx]) { - if (acb->aiocb_type == AIOCB_READ_UDATA) { + switch (acb->aiocb_type) { + case AIOCB_READ_UDATA: + if (!inode->data_vdi_id[idx]) { + qemu_iovec_memset(acb->qiov, done, 0, len); goto done; } - - create = 1; - } else if (acb->aiocb_type == AIOCB_WRITE_UDATA - && !is_data_obj_writable(inode, idx)) { - /* Copy-On-Write */ - create = 1; - old_oid = oid; - flags = SD_FLAG_CMD_COW; + break; + case AIOCB_WRITE_UDATA: + if (!inode->data_vdi_id[idx]) { + create = 1; + } else if (!is_data_obj_writable(inode, idx)) { + /* Copy-On-Write */ + create = 1; + old_oid = oid; + flags = SD_FLAG_CMD_COW; + } + break; + default: + break; } if (create) { @@ -1687,20 +1675,12 @@ static coroutine_fn int sd_co_readv(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) { SheepdogAIOCB *acb; - int i, ret; + int ret; acb = sd_aio_setup(bs, qiov, sector_num, nb_sectors, NULL, NULL); acb->aiocb_type = AIOCB_READ_UDATA; acb->aio_done_func = sd_finish_aiocb; - /* - * TODO: we can do better; we don't need to initialize - * blindly. - */ - for (i = 0; i < qiov->niov; i++) { - memset(qiov->iov[i].iov_base, 0, qiov->iov[i].iov_len); - } - ret = sd_co_rw_vector(acb); if (ret <= 0) { qemu_aio_release(acb); diff --git a/block/vvfat.c b/block/vvfat.c index 0fd3367d82..7b1dcee144 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -359,11 +359,12 @@ typedef struct BDRVVVFATState { * if the position is outside the specified geometry, fill maximum value for CHS * and return 1 to signal overflow. */ -static int sector2CHS(BlockDriverState* bs, mbr_chs_t * chs, int spos){ +static int sector2CHS(mbr_chs_t *chs, int spos, int cyls, int heads, int secs) +{ int head,sector; - sector = spos % (bs->secs); spos/= bs->secs; - head = spos % (bs->heads); spos/= bs->heads; - if(spos >= bs->cyls){ + sector = spos % secs; spos /= secs; + head = spos % heads; spos /= heads; + if (spos >= cyls) { /* Overflow, it happens if 32bit sector positions are used, while CHS is only 24bit. Windows/Dos is said to take 1023/255/63 as nonrepresentable CHS */ @@ -378,7 +379,7 @@ static int sector2CHS(BlockDriverState* bs, mbr_chs_t * chs, int spos){ return 0; } -static void init_mbr(BDRVVVFATState* s) +static void init_mbr(BDRVVVFATState *s, int cyls, int heads, int secs) { /* TODO: if the files mbr.img and bootsect.img exist, use them */ mbr_t* real_mbr=(mbr_t*)s->first_sectors; @@ -393,12 +394,15 @@ static void init_mbr(BDRVVVFATState* s) partition->attributes=0x80; /* bootable */ /* LBA is used when partition is outside the CHS geometry */ - lba = sector2CHS(s->bs, &partition->start_CHS, s->first_sectors_number-1); - lba|= sector2CHS(s->bs, &partition->end_CHS, s->sector_count); + lba = sector2CHS(&partition->start_CHS, s->first_sectors_number - 1, + cyls, heads, secs); + lba |= sector2CHS(&partition->end_CHS, s->bs->total_sectors - 1, + cyls, heads, secs); /*LBA partitions are identified only by start/length_sector_long not by CHS*/ - partition->start_sector_long =cpu_to_le32(s->first_sectors_number-1); - partition->length_sector_long=cpu_to_le32(s->sector_count - s->first_sectors_number+1); + partition->start_sector_long = cpu_to_le32(s->first_sectors_number - 1); + partition->length_sector_long = cpu_to_le32(s->bs->total_sectors + - s->first_sectors_number + 1); /* FAT12/FAT16/FAT32 */ /* DOS uses different types when partition is LBA, @@ -830,7 +834,7 @@ static inline off_t cluster2sector(BDRVVVFATState* s, uint32_t cluster_num) } static int init_directories(BDRVVVFATState* s, - const char* dirname) + const char *dirname, int heads, int secs) { bootsector_t* bootsector; mapping_t* mapping; @@ -957,8 +961,8 @@ static int init_directories(BDRVVVFATState* s, bootsector->media_type=(s->first_sectors_number>1?0xf8:0xf0); /* media descriptor (f8=hd, f0=3.5 fd)*/ s->fat.pointer[0] = bootsector->media_type; bootsector->sectors_per_fat=cpu_to_le16(s->sectors_per_fat); - bootsector->sectors_per_track=cpu_to_le16(s->bs->secs); - bootsector->number_of_heads=cpu_to_le16(s->bs->heads); + bootsector->sectors_per_track = cpu_to_le16(secs); + bootsector->number_of_heads = cpu_to_le16(heads); bootsector->hidden_sectors=cpu_to_le32(s->first_sectors_number==1?0:0x3f); bootsector->total_sectors=cpu_to_le32(s->sector_count>0xffff?s->sector_count:0); @@ -991,7 +995,7 @@ static void vvfat_rebind(BlockDriverState *bs) static int vvfat_open(BlockDriverState *bs, const char* dirname, int flags) { BDRVVVFATState *s = bs->opaque; - int i; + int i, cyls, heads, secs; #ifdef DEBUG vvv = s; @@ -1033,24 +1037,28 @@ DLOG(if (stderr == NULL) { /* 1.44MB or 2.88MB floppy. 2.88MB can be FAT12 (default) or FAT16. */ if (!s->fat_type) { s->fat_type = 12; - bs->secs = 36; + secs = 36; s->sectors_per_cluster=2; } else { - bs->secs=(s->fat_type == 12 ? 18 : 36); + secs = s->fat_type == 12 ? 18 : 36; s->sectors_per_cluster=1; } s->first_sectors_number = 1; - bs->cyls=80; bs->heads=2; + cyls = 80; + heads = 2; } else { /* 32MB or 504MB disk*/ if (!s->fat_type) { s->fat_type = 16; } - bs->cyls=(s->fat_type == 12 ? 64 : 1024); - bs->heads=16; bs->secs=63; + cyls = s->fat_type == 12 ? 64 : 1024; + heads = 16; + secs = 63; } + fprintf(stderr, "vvfat %s chs %d,%d,%d\n", + dirname, cyls, heads, secs); - s->sector_count=bs->cyls*bs->heads*bs->secs-(s->first_sectors_number-1); + s->sector_count = cyls * heads * secs - (s->first_sectors_number - 1); if (strstr(dirname, ":rw:")) { if (enable_write_target(s)) @@ -1066,18 +1074,16 @@ DLOG(if (stderr == NULL) { else dirname += i+1; - bs->total_sectors=bs->cyls*bs->heads*bs->secs; + bs->total_sectors = cyls * heads * secs; - if(init_directories(s, dirname)) + if (init_directories(s, dirname, heads, secs)) { return -1; + } s->sector_count = s->faked_sectors + s->sectors_per_cluster*s->cluster_count; - if(s->first_sectors_number==0x40) - init_mbr(s); - else { - /* MS-DOS does not like to know about CHS (?). */ - bs->heads = bs->cyls = bs->secs = 0; + if (s->first_sectors_number == 0x40) { + init_mbr(s, cyls, heads, secs); } // assert(is_consistent(s)); diff --git a/block_int.h b/block_int.h index 1fb5352d0e..d72317fbe3 100644 --- a/block_int.h +++ b/block_int.h @@ -320,7 +320,6 @@ struct BlockDriverState { /* NOTE: the following infos are only hints for real hardware drivers. They are not used by the block driver */ - int cyls, heads, secs, translation; BlockErrorAction on_read_error, on_write_error; bool iostatus_enabled; BlockDeviceIoStatus iostatus; diff --git a/blockdev.c b/blockdev.c index a85a429aef..3d7501565d 100644 --- a/blockdev.c +++ b/blockdev.c @@ -7,8 +7,8 @@ * later. See the COPYING file in the top-level directory. */ -#include "block.h" #include "blockdev.h" +#include "hw/block-common.h" #include "monitor.h" #include "qerror.h" #include "qemu-option.h" @@ -330,15 +330,15 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi) max_devs = if_max_devs[type]; if (cyls || heads || secs) { - if (cyls < 1 || (type == IF_IDE && cyls > 16383)) { + if (cyls < 1) { error_report("invalid physical cyls number"); return NULL; } - if (heads < 1 || (type == IF_IDE && heads > 16)) { + if (heads < 1) { error_report("invalid physical heads number"); return NULL; } - if (secs < 1 || (type == IF_IDE && secs > 63)) { + if (secs < 1) { error_report("invalid physical secs number"); return NULL; } @@ -530,11 +530,13 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi) dinfo->type = type; dinfo->bus = bus_id; dinfo->unit = unit_id; + dinfo->cyls = cyls; + dinfo->heads = heads; + dinfo->secs = secs; + dinfo->trans = translation; dinfo->opts = opts; dinfo->refcount = 1; - if (serial) { - pstrcpy(dinfo->serial, sizeof(dinfo->serial), serial); - } + dinfo->serial = serial; QTAILQ_INSERT_TAIL(&drives, dinfo, next); bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error); @@ -547,17 +549,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi) case IF_SCSI: case IF_XEN: case IF_NONE: - switch(media) { - case MEDIA_DISK: - if (cyls != 0) { - bdrv_set_geometry_hint(dinfo->bdrv, cyls, heads, secs); - bdrv_set_translation_hint(dinfo->bdrv, translation); - } - break; - case MEDIA_CDROM: - dinfo->media_cd = 1; - break; - } + dinfo->media_cd = media == MEDIA_CDROM; break; case IF_SD: case IF_FLOPPY: diff --git a/blockdev.h b/blockdev.h index 260e16b3c6..5f27b643be 100644 --- a/blockdev.h +++ b/blockdev.h @@ -17,8 +17,6 @@ void blockdev_mark_auto_del(BlockDriverState *bs); void blockdev_auto_del(BlockDriverState *bs); -#define BLOCK_SERIAL_STRLEN 20 - typedef enum { IF_DEFAULT = -1, /* for use with drive_add() only */ IF_NONE, @@ -35,8 +33,9 @@ struct DriveInfo { int unit; int auto_del; /* see blockdev_mark_auto_del() */ int media_cd; + int cyls, heads, secs, trans; QemuOpts *opts; - char serial[BLOCK_SERIAL_STRLEN + 1]; + const char *serial; QTAILQ_ENTRY(DriveInfo) next; int refcount; }; @@ -2871,6 +2871,22 @@ if compile_prog "" "" ; then fi ######################################## +# check if we have valgrind/valgrind.h + +valgrind_h=no +cat > $TMPC << EOF +#include <valgrind/valgrind.h> +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" +int main(void) { + VALGRIND_STACK_DEREGISTER(0); + return 0; +} +EOF +if compile_prog "" "" ; then + valgrind_h=yes +fi + +######################################## # check if environ is declared has_environ=no @@ -3379,6 +3395,10 @@ if test "$linux_magic_h" = "yes" ; then echo "CONFIG_LINUX_MAGIC_H=y" >> $config_host_mak fi +if test "$valgrind_h" = "yes" ; then + echo "CONFIG_VALGRIND_H=y" >> $config_host_mak +fi + if test "$has_environ" = "yes" ; then echo "CONFIG_HAS_ENVIRON=y" >> $config_host_mak fi diff --git a/coroutine-ucontext.c b/coroutine-ucontext.c index 5f43083af5..e3c450b322 100644 --- a/coroutine-ucontext.c +++ b/coroutine-ucontext.c @@ -30,6 +30,10 @@ #include "qemu-common.h" #include "qemu-coroutine-int.h" +#ifdef CONFIG_VALGRIND_H +#include <valgrind/valgrind.h> +#endif + enum { /* Maximum free pool size prevents holding too many freed coroutines */ POOL_MAX_SIZE = 64, @@ -43,6 +47,11 @@ typedef struct { Coroutine base; void *stack; jmp_buf env; + +#ifdef CONFIG_VALGRIND_H + unsigned int valgrind_stack_id; +#endif + } CoroutineUContext; /** @@ -159,6 +168,11 @@ static Coroutine *coroutine_new(void) uc.uc_stack.ss_size = stack_size; uc.uc_stack.ss_flags = 0; +#ifdef CONFIG_VALGRIND_H + co->valgrind_stack_id = + VALGRIND_STACK_REGISTER(co->stack, co->stack + stack_size); +#endif + arg.p = co; makecontext(&uc, (void (*)(void))coroutine_trampoline, @@ -185,6 +199,16 @@ Coroutine *qemu_coroutine_new(void) return co; } +#ifdef CONFIG_VALGRIND_H +/* Work around an unused variable in the valgrind.h macro... */ +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" +static inline void valgrind_stack_deregister(CoroutineUContext *co) +{ + VALGRIND_STACK_DEREGISTER(co->valgrind_stack_id); +} +#pragma GCC diagnostic error "-Wunused-but-set-variable" +#endif + void qemu_coroutine_delete(Coroutine *co_) { CoroutineUContext *co = DO_UPCAST(CoroutineUContext, base, co_); @@ -196,6 +220,10 @@ void qemu_coroutine_delete(Coroutine *co_) return; } +#ifdef CONFIG_VALGRIND_H + valgrind_stack_deregister(co); +#endif + g_free(co->stack); g_free(co); } diff --git a/hw/Makefile.objs b/hw/Makefile.objs index 9a350deafb..8327e55c03 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -138,7 +138,7 @@ common-obj-$(CONFIG_MAX111X) += max111x.o common-obj-$(CONFIG_DS1338) += ds1338.o common-obj-y += i2c.o smbus.o smbus_eeprom.o common-obj-y += eeprom93xx.o -common-obj-y += scsi-disk.o cdrom.o +common-obj-y += scsi-disk.o cdrom.o hd-geometry.o block-common.o common-obj-y += scsi-generic.o scsi-bus.o common-obj-y += hid.o common-obj-$(CONFIG_SSI) += ssi.o diff --git a/hw/block-common.c b/hw/block-common.c new file mode 100644 index 0000000000..f0196d78dc --- /dev/null +++ b/hw/block-common.c @@ -0,0 +1,64 @@ +/* + * Common code for block device models + * + * Copyright (C) 2012 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#include "blockdev.h" +#include "hw/block-common.h" +#include "qemu-error.h" + +void blkconf_serial(BlockConf *conf, char **serial) +{ + DriveInfo *dinfo; + + if (!*serial) { + /* try to fall back to value set with legacy -drive serial=... */ + dinfo = drive_get_by_blockdev(conf->bs); + if (dinfo->serial) { + *serial = g_strdup(dinfo->serial); + } + } +} + +int blkconf_geometry(BlockConf *conf, int *ptrans, + unsigned cyls_max, unsigned heads_max, unsigned secs_max) +{ + DriveInfo *dinfo; + + if (!conf->cyls && !conf->heads && !conf->secs) { + /* try to fall back to value set with legacy -drive cyls=... */ + dinfo = drive_get_by_blockdev(conf->bs); + conf->cyls = dinfo->cyls; + conf->heads = dinfo->heads; + conf->secs = dinfo->secs; + if (ptrans) { + *ptrans = dinfo->trans; + } + } + if (!conf->cyls && !conf->heads && !conf->secs) { + hd_geometry_guess(conf->bs, + &conf->cyls, &conf->heads, &conf->secs, + ptrans); + } else if (ptrans && *ptrans == BIOS_ATA_TRANSLATION_AUTO) { + *ptrans = hd_bios_chs_auto_trans(conf->cyls, conf->heads, conf->secs); + } + if (conf->cyls || conf->heads || conf->secs) { + if (conf->cyls < 1 || conf->cyls > cyls_max) { + error_report("cyls must be between 1 and %u", cyls_max); + return -1; + } + if (conf->heads < 1 || conf->heads > heads_max) { + error_report("heads must be between 1 and %u", heads_max); + return -1; + } + if (conf->secs < 1 || conf->secs > secs_max) { + error_report("secs must be between 1 and %u", secs_max); + return -1; + } + } + return 0; +} diff --git a/hw/block-common.h b/hw/block-common.h new file mode 100644 index 0000000000..bb808f7f56 --- /dev/null +++ b/hw/block-common.h @@ -0,0 +1,79 @@ +/* + * Common code for block device models + * + * Copyright (C) 2012 Red Hat, Inc. + * Copyright (c) 2003-2008 Fabrice Bellard + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#ifndef HW_BLOCK_COMMON_H +#define HW_BLOCK_COMMON_H + +#include "qemu-common.h" + +/* Configuration */ + +typedef struct BlockConf { + BlockDriverState *bs; + uint16_t physical_block_size; + uint16_t logical_block_size; + uint16_t min_io_size; + uint32_t opt_io_size; + int32_t bootindex; + uint32_t discard_granularity; + /* geometry, not all devices use this */ + uint32_t cyls, heads, secs; +} BlockConf; + +static inline unsigned int get_physical_block_exp(BlockConf *conf) +{ + unsigned int exp = 0, size; + + for (size = conf->physical_block_size; + size > conf->logical_block_size; + size >>= 1) { + exp++; + } + + return exp; +} + +#define DEFINE_BLOCK_PROPERTIES(_state, _conf) \ + DEFINE_PROP_DRIVE("drive", _state, _conf.bs), \ + DEFINE_PROP_BLOCKSIZE("logical_block_size", _state, \ + _conf.logical_block_size, 512), \ + DEFINE_PROP_BLOCKSIZE("physical_block_size", _state, \ + _conf.physical_block_size, 512), \ + DEFINE_PROP_UINT16("min_io_size", _state, _conf.min_io_size, 0), \ + DEFINE_PROP_UINT32("opt_io_size", _state, _conf.opt_io_size, 0), \ + DEFINE_PROP_INT32("bootindex", _state, _conf.bootindex, -1), \ + DEFINE_PROP_UINT32("discard_granularity", _state, \ + _conf.discard_granularity, 0) + +#define DEFINE_BLOCK_CHS_PROPERTIES(_state, _conf) \ + DEFINE_PROP_UINT32("cyls", _state, _conf.cyls, 0), \ + DEFINE_PROP_UINT32("heads", _state, _conf.heads, 0), \ + DEFINE_PROP_UINT32("secs", _state, _conf.secs, 0) + +/* Configuration helpers */ + +void blkconf_serial(BlockConf *conf, char **serial); +int blkconf_geometry(BlockConf *conf, int *trans, + unsigned cyls_max, unsigned heads_max, unsigned secs_max); + +/* Hard disk geometry */ + +#define BIOS_ATA_TRANSLATION_AUTO 0 +#define BIOS_ATA_TRANSLATION_NONE 1 +#define BIOS_ATA_TRANSLATION_LBA 2 +#define BIOS_ATA_TRANSLATION_LARGE 3 +#define BIOS_ATA_TRANSLATION_RECHS 4 + +void hd_geometry_guess(BlockDriverState *bs, + uint32_t *pcyls, uint32_t *pheads, uint32_t *psecs, + int *ptrans); +int hd_bios_chs_auto_trans(uint32_t cyls, uint32_t heads, uint32_t secs); + +#endif @@ -52,6 +52,113 @@ /********************************************************/ /* Floppy drive emulation */ +typedef enum FDriveRate { + FDRIVE_RATE_500K = 0x00, /* 500 Kbps */ + FDRIVE_RATE_300K = 0x01, /* 300 Kbps */ + FDRIVE_RATE_250K = 0x02, /* 250 Kbps */ + FDRIVE_RATE_1M = 0x03, /* 1 Mbps */ +} FDriveRate; + +typedef struct FDFormat { + FDriveType drive; + uint8_t last_sect; + uint8_t max_track; + uint8_t max_head; + FDriveRate rate; +} FDFormat; + +static const FDFormat fd_formats[] = { + /* First entry is default format */ + /* 1.44 MB 3"1/2 floppy disks */ + { FDRIVE_DRV_144, 18, 80, 1, FDRIVE_RATE_500K, }, + { FDRIVE_DRV_144, 20, 80, 1, FDRIVE_RATE_500K, }, + { FDRIVE_DRV_144, 21, 80, 1, FDRIVE_RATE_500K, }, + { FDRIVE_DRV_144, 21, 82, 1, FDRIVE_RATE_500K, }, + { FDRIVE_DRV_144, 21, 83, 1, FDRIVE_RATE_500K, }, + { FDRIVE_DRV_144, 22, 80, 1, FDRIVE_RATE_500K, }, + { FDRIVE_DRV_144, 23, 80, 1, FDRIVE_RATE_500K, }, + { FDRIVE_DRV_144, 24, 80, 1, FDRIVE_RATE_500K, }, + /* 2.88 MB 3"1/2 floppy disks */ + { FDRIVE_DRV_288, 36, 80, 1, FDRIVE_RATE_1M, }, + { FDRIVE_DRV_288, 39, 80, 1, FDRIVE_RATE_1M, }, + { FDRIVE_DRV_288, 40, 80, 1, FDRIVE_RATE_1M, }, + { FDRIVE_DRV_288, 44, 80, 1, FDRIVE_RATE_1M, }, + { FDRIVE_DRV_288, 48, 80, 1, FDRIVE_RATE_1M, }, + /* 720 kB 3"1/2 floppy disks */ + { FDRIVE_DRV_144, 9, 80, 1, FDRIVE_RATE_250K, }, + { FDRIVE_DRV_144, 10, 80, 1, FDRIVE_RATE_250K, }, + { FDRIVE_DRV_144, 10, 82, 1, FDRIVE_RATE_250K, }, + { FDRIVE_DRV_144, 10, 83, 1, FDRIVE_RATE_250K, }, + { FDRIVE_DRV_144, 13, 80, 1, FDRIVE_RATE_250K, }, + { FDRIVE_DRV_144, 14, 80, 1, FDRIVE_RATE_250K, }, + /* 1.2 MB 5"1/4 floppy disks */ + { FDRIVE_DRV_120, 15, 80, 1, FDRIVE_RATE_500K, }, + { FDRIVE_DRV_120, 18, 80, 1, FDRIVE_RATE_500K, }, + { FDRIVE_DRV_120, 18, 82, 1, FDRIVE_RATE_500K, }, + { FDRIVE_DRV_120, 18, 83, 1, FDRIVE_RATE_500K, }, + { FDRIVE_DRV_120, 20, 80, 1, FDRIVE_RATE_500K, }, + /* 720 kB 5"1/4 floppy disks */ + { FDRIVE_DRV_120, 9, 80, 1, FDRIVE_RATE_250K, }, + { FDRIVE_DRV_120, 11, 80, 1, FDRIVE_RATE_250K, }, + /* 360 kB 5"1/4 floppy disks */ + { FDRIVE_DRV_120, 9, 40, 1, FDRIVE_RATE_300K, }, + { FDRIVE_DRV_120, 9, 40, 0, FDRIVE_RATE_300K, }, + { FDRIVE_DRV_120, 10, 41, 1, FDRIVE_RATE_300K, }, + { FDRIVE_DRV_120, 10, 42, 1, FDRIVE_RATE_300K, }, + /* 320 kB 5"1/4 floppy disks */ + { FDRIVE_DRV_120, 8, 40, 1, FDRIVE_RATE_250K, }, + { FDRIVE_DRV_120, 8, 40, 0, FDRIVE_RATE_250K, }, + /* 360 kB must match 5"1/4 better than 3"1/2... */ + { FDRIVE_DRV_144, 9, 80, 0, FDRIVE_RATE_250K, }, + /* end */ + { FDRIVE_DRV_NONE, -1, -1, 0, 0, }, +}; + +static void pick_geometry(BlockDriverState *bs, int *nb_heads, + int *max_track, int *last_sect, + FDriveType drive_in, FDriveType *drive, + FDriveRate *rate) +{ + const FDFormat *parse; + uint64_t nb_sectors, size; + int i, first_match, match; + + bdrv_get_geometry(bs, &nb_sectors); + match = -1; + first_match = -1; + for (i = 0; ; i++) { + parse = &fd_formats[i]; + if (parse->drive == FDRIVE_DRV_NONE) { + break; + } + if (drive_in == parse->drive || + drive_in == FDRIVE_DRV_NONE) { + size = (parse->max_head + 1) * parse->max_track * + parse->last_sect; + if (nb_sectors == size) { + match = i; + break; + } + if (first_match == -1) { + first_match = i; + } + } + } + if (match == -1) { + if (first_match == -1) { + match = 1; + } else { + match = first_match; + } + parse = &fd_formats[match]; + } + *nb_heads = parse->max_head + 1; + *max_track = parse->max_track; + *last_sect = parse->last_sect; + *drive = parse->drive; + *rate = parse->rate; +} + #define GET_CUR_DRV(fdctrl) ((fdctrl)->cur_drv) #define SET_CUR_DRV(fdctrl, drive) ((fdctrl)->cur_drv = (drive)) @@ -187,8 +294,8 @@ static void fd_revalidate(FDrive *drv) FLOPPY_DPRINTF("revalidate\n"); if (drv->bs != NULL) { ro = bdrv_is_read_only(drv->bs); - bdrv_get_floppy_geometry_hint(drv->bs, &nb_heads, &max_track, - &last_sect, drv->drive, &drive, &rate); + pick_geometry(drv->bs, &nb_heads, &max_track, + &last_sect, drv->drive, &drive, &rate); if (!bdrv_is_inserted(drv->bs)) { FLOPPY_DPRINTF("No disk in drive\n"); } else { @@ -1695,7 +1802,7 @@ static void fdctrl_handle_drive_specification_command(FDCtrl *fdctrl, int direct } } -static void fdctrl_handle_relative_seek_out(FDCtrl *fdctrl, int direction) +static void fdctrl_handle_relative_seek_in(FDCtrl *fdctrl, int direction) { FDrive *cur_drv; @@ -1705,14 +1812,15 @@ static void fdctrl_handle_relative_seek_out(FDCtrl *fdctrl, int direction) fd_seek(cur_drv, cur_drv->head, cur_drv->max_track - 1, cur_drv->sect, 1); } else { - fd_seek(cur_drv, cur_drv->head, fdctrl->fifo[2], cur_drv->sect, 1); + fd_seek(cur_drv, cur_drv->head, + cur_drv->track + fdctrl->fifo[2], cur_drv->sect, 1); } fdctrl_reset_fifo(fdctrl); /* Raise Interrupt */ fdctrl_raise_irq(fdctrl, FD_SR0_SEEK); } -static void fdctrl_handle_relative_seek_in(FDCtrl *fdctrl, int direction) +static void fdctrl_handle_relative_seek_out(FDCtrl *fdctrl, int direction) { FDrive *cur_drv; @@ -1721,7 +1829,8 @@ static void fdctrl_handle_relative_seek_in(FDCtrl *fdctrl, int direction) if (fdctrl->fifo[2] > cur_drv->track) { fd_seek(cur_drv, cur_drv->head, 0, cur_drv->sect, 1); } else { - fd_seek(cur_drv, cur_drv->head, fdctrl->fifo[2], cur_drv->sect, 1); + fd_seek(cur_drv, cur_drv->head, + cur_drv->track - fdctrl->fifo[2], cur_drv->sect, 1); } fdctrl_reset_fifo(fdctrl); /* Raise Interrupt */ @@ -2054,18 +2163,13 @@ static int sun4m_fdc_init1(SysBusDevice *dev) return fdctrl_init_common(fdctrl); } -void fdc_get_bs(BlockDriverState *bs[], ISADevice *dev) +FDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i) { - FDCtrlISABus *isa = DO_UPCAST(FDCtrlISABus, busdev, dev); - FDCtrl *fdctrl = &isa->state; - int i; + FDCtrlISABus *isa = DO_UPCAST(FDCtrlISABus, busdev, fdc); - for (i = 0; i < MAX_FD; i++) { - bs[i] = fdctrl->drives[i].bs; - } + return isa->state.drives[i].drive; } - static const VMStateDescription vmstate_isa_fdc ={ .name = "fdc", .version_id = 2, @@ -6,11 +6,19 @@ /* fdc.c */ #define MAX_FD 2 +typedef enum FDriveType { + FDRIVE_DRV_144 = 0x00, /* 1.44 MB 3"5 drive */ + FDRIVE_DRV_288 = 0x01, /* 2.88 MB 3"5 drive */ + FDRIVE_DRV_120 = 0x02, /* 1.2 MB 5"25 drive */ + FDRIVE_DRV_NONE = 0x03, /* No drive connected */ +} FDriveType; + ISADevice *fdctrl_init_isa(ISABus *bus, DriveInfo **fds); void fdctrl_init_sysbus(qemu_irq irq, int dma_chann, target_phys_addr_t mmio_base, DriveInfo **fds); void sun4m_fdctrl_init(qemu_irq irq, target_phys_addr_t io_base, DriveInfo **fds, qemu_irq *fdc_tc); -void fdc_get_bs(BlockDriverState *bs[], ISADevice *dev); + +FDriveType isa_fdc_get_drive_type(ISADevice *fdc, int i); #endif diff --git a/hw/hd-geometry.c b/hw/hd-geometry.c new file mode 100644 index 0000000000..1cdb9fb753 --- /dev/null +++ b/hw/hd-geometry.c @@ -0,0 +1,157 @@ +/* + * Hard disk geometry utilities + * + * Copyright (C) 2012 Red Hat, Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * This file incorporates work covered by the following copyright and + * permission notice: + * + * Copyright (c) 2003 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "block.h" +#include "hw/block-common.h" +#include "trace.h" + +struct partition { + uint8_t boot_ind; /* 0x80 - active */ + uint8_t head; /* starting head */ + uint8_t sector; /* starting sector */ + uint8_t cyl; /* starting cylinder */ + uint8_t sys_ind; /* What partition type */ + uint8_t end_head; /* end head */ + uint8_t end_sector; /* end sector */ + uint8_t end_cyl; /* end cylinder */ + uint32_t start_sect; /* starting sector counting from 0 */ + uint32_t nr_sects; /* nr of sectors in partition */ +} QEMU_PACKED; + +/* try to guess the disk logical geometry from the MSDOS partition table. + Return 0 if OK, -1 if could not guess */ +static int guess_disk_lchs(BlockDriverState *bs, + int *pcylinders, int *pheads, int *psectors) +{ + uint8_t buf[BDRV_SECTOR_SIZE]; + int i, heads, sectors, cylinders; + struct partition *p; + uint32_t nr_sects; + uint64_t nb_sectors; + + bdrv_get_geometry(bs, &nb_sectors); + + /** + * The function will be invoked during startup not only in sync I/O mode, + * but also in async I/O mode. So the I/O throttling function has to + * be disabled temporarily here, not permanently. + */ + if (bdrv_read_unthrottled(bs, 0, buf, 1) < 0) { + return -1; + } + /* test msdos magic */ + if (buf[510] != 0x55 || buf[511] != 0xaa) { + return -1; + } + for (i = 0; i < 4; i++) { + p = ((struct partition *)(buf + 0x1be)) + i; + nr_sects = le32_to_cpu(p->nr_sects); + if (nr_sects && p->end_head) { + /* We make the assumption that the partition terminates on + a cylinder boundary */ + heads = p->end_head + 1; + sectors = p->end_sector & 63; + if (sectors == 0) { + continue; + } + cylinders = nb_sectors / (heads * sectors); + if (cylinders < 1 || cylinders > 16383) { + continue; + } + *pheads = heads; + *psectors = sectors; + *pcylinders = cylinders; + trace_hd_geometry_lchs_guess(bs, cylinders, heads, sectors); + return 0; + } + } + return -1; +} + +static void guess_chs_for_size(BlockDriverState *bs, + uint32_t *pcyls, uint32_t *pheads, uint32_t *psecs) +{ + uint64_t nb_sectors; + int cylinders; + + bdrv_get_geometry(bs, &nb_sectors); + + cylinders = nb_sectors / (16 * 63); + if (cylinders > 16383) { + cylinders = 16383; + } else if (cylinders < 2) { + cylinders = 2; + } + *pcyls = cylinders; + *pheads = 16; + *psecs = 63; +} + +void hd_geometry_guess(BlockDriverState *bs, + uint32_t *pcyls, uint32_t *pheads, uint32_t *psecs, + int *ptrans) +{ + int cylinders, heads, secs, translation; + + if (guess_disk_lchs(bs, &cylinders, &heads, &secs) < 0) { + /* no LCHS guess: use a standard physical disk geometry */ + guess_chs_for_size(bs, pcyls, pheads, psecs); + translation = hd_bios_chs_auto_trans(*pcyls, *pheads, *psecs); + } else if (heads > 16) { + /* LCHS guess with heads > 16 means that a BIOS LBA + translation was active, so a standard physical disk + geometry is OK */ + guess_chs_for_size(bs, pcyls, pheads, psecs); + translation = *pcyls * *pheads <= 131072 + ? BIOS_ATA_TRANSLATION_LARGE + : BIOS_ATA_TRANSLATION_LBA; + } else { + /* LCHS guess with heads <= 16: use as physical geometry */ + *pcyls = cylinders; + *pheads = heads; + *psecs = secs; + /* disable any translation to be in sync with + the logical geometry */ + translation = BIOS_ATA_TRANSLATION_NONE; + } + if (ptrans) { + *ptrans = translation; + } + trace_hd_geometry_guess(bs, *pcyls, *pheads, *psecs, translation); +} + +int hd_bios_chs_auto_trans(uint32_t cyls, uint32_t heads, uint32_t secs) +{ + return cyls <= 1024 && heads <= 16 && secs <= 63 + ? BIOS_ATA_TRANSLATION_NONE + : BIOS_ATA_TRANSLATION_LBA; +} @@ -29,7 +29,9 @@ void mmio_ide_init (target_phys_addr_t membase, target_phys_addr_t membase2, qemu_irq irq, int shift, DriveInfo *hd0, DriveInfo *hd1); -void ide_get_bs(BlockDriverState *bs[], BusState *qbus); +int ide_get_geometry(BusState *bus, int unit, + int16_t *cyls, int8_t *heads, int8_t *secs); +int ide_get_bios_chs_trans(BusState *bus, int unit); /* ide/core.c */ void ide_drive_get(DriveInfo **hd, int max_bus); diff --git a/hw/ide/core.c b/hw/ide/core.c index 71d4d7732a..d65ef3d58d 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -30,6 +30,7 @@ #include "qemu-timer.h" #include "sysemu.h" #include "dma.h" +#include "hw/block-common.h" #include "blockdev.h" #include <hw/ide/internal.h> @@ -1924,31 +1925,20 @@ static const BlockDevOps ide_cd_block_ops = { int ide_init_drive(IDEState *s, BlockDriverState *bs, IDEDriveKind kind, const char *version, const char *serial, const char *model, - uint64_t wwn) + uint64_t wwn, + uint32_t cylinders, uint32_t heads, uint32_t secs, + int chs_trans) { - int cylinders, heads, secs; uint64_t nb_sectors; s->bs = bs; s->drive_kind = kind; bdrv_get_geometry(bs, &nb_sectors); - bdrv_guess_geometry(bs, &cylinders, &heads, &secs); - if (cylinders < 1 || cylinders > 16383) { - error_report("cyls must be between 1 and 16383"); - return -1; - } - if (heads < 1 || heads > 16) { - error_report("heads must be between 1 and 16"); - return -1; - } - if (secs < 1 || secs > 63) { - error_report("secs must be between 1 and 63"); - return -1; - } s->cylinders = cylinders; s->heads = heads; s->sectors = secs; + s->chs_trans = chs_trans; s->nb_sectors = nb_sectors; s->wwn = wwn; /* The SMART values should be preserved across power cycles @@ -2075,17 +2065,39 @@ void ide_init2(IDEBus *bus, qemu_irq irq) void ide_init2_with_non_qdev_drives(IDEBus *bus, DriveInfo *hd0, DriveInfo *hd1, qemu_irq irq) { - int i; + int i, trans; DriveInfo *dinfo; + uint32_t cyls, heads, secs; for(i = 0; i < 2; i++) { dinfo = i == 0 ? hd0 : hd1; ide_init1(bus, i); if (dinfo) { + cyls = dinfo->cyls; + heads = dinfo->heads; + secs = dinfo->secs; + trans = dinfo->trans; + if (!cyls && !heads && !secs) { + hd_geometry_guess(dinfo->bdrv, &cyls, &heads, &secs, &trans); + } else if (trans == BIOS_ATA_TRANSLATION_AUTO) { + trans = hd_bios_chs_auto_trans(cyls, heads, secs); + } + if (cyls < 1 || cyls > 65535) { + error_report("cyls must be between 1 and 65535"); + exit(1); + } + if (heads < 1 || heads > 16) { + error_report("heads must be between 1 and 16"); + exit(1); + } + if (secs < 1 || secs > 255) { + error_report("secs must be between 1 and 255"); + exit(1); + } if (ide_init_drive(&bus->ifs[i], dinfo->bdrv, - dinfo->media_cd ? IDE_CD : IDE_HD, NULL, - *dinfo->serial ? dinfo->serial : NULL, - NULL, 0) < 0) { + dinfo->media_cd ? IDE_CD : IDE_HD, + NULL, dinfo->serial, NULL, 0, + cyls, heads, secs, trans) < 0) { error_report("Can't set up IDE drive %s", dinfo->id); exit(1); } diff --git a/hw/ide/internal.h b/hw/ide/internal.h index 1a02f57bf5..7170bd9cd0 100644 --- a/hw/ide/internal.h +++ b/hw/ide/internal.h @@ -11,6 +11,7 @@ #include "iorange.h" #include "dma.h" #include "sysemu.h" +#include "hw/block-common.h" #include "hw/scsi-defs.h" /* debug IDE devices */ @@ -344,7 +345,7 @@ struct IDEState { uint8_t unit; /* ide config */ IDEDriveKind drive_kind; - int cylinders, heads, sectors; + int cylinders, heads, sectors, chs_trans; int64_t nb_sectors; int mult_sectors; int identify_set; @@ -474,6 +475,7 @@ struct IDEDevice { DeviceState qdev; uint32_t unit; BlockConf conf; + int chs_trans; char *version; char *serial; char *model; @@ -545,7 +547,9 @@ uint32_t ide_data_readl(void *opaque, uint32_t addr); int ide_init_drive(IDEState *s, BlockDriverState *bs, IDEDriveKind kind, const char *version, const char *serial, const char *model, - uint64_t wwn); + uint64_t wwn, + uint32_t cylinders, uint32_t heads, uint32_t secs, + int chs_trans); void ide_init2(IDEBus *bus, qemu_irq irq); void ide_init2_with_non_qdev_drives(IDEBus *bus, DriveInfo *hd0, DriveInfo *hd1, qemu_irq irq); diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index c122395401..22e58dfc8a 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -21,6 +21,7 @@ #include "qemu-error.h" #include <hw/ide/internal.h> #include "blockdev.h" +#include "hw/block-common.h" #include "sysemu.h" /* --------------------------------- */ @@ -111,11 +112,24 @@ IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive) return DO_UPCAST(IDEDevice, qdev, dev); } -void ide_get_bs(BlockDriverState *bs[], BusState *qbus) +int ide_get_geometry(BusState *bus, int unit, + int16_t *cyls, int8_t *heads, int8_t *secs) { - IDEBus *bus = DO_UPCAST(IDEBus, qbus, qbus); - bs[0] = bus->master ? bus->master->conf.bs : NULL; - bs[1] = bus->slave ? bus->slave->conf.bs : NULL; + IDEState *s = &DO_UPCAST(IDEBus, qbus, bus)->ifs[unit]; + + if (s->drive_kind != IDE_HD || !s->bs) { + return -1; + } + + *cyls = s->cylinders; + *heads = s->heads; + *secs = s->sectors; + return 0; +} + +int ide_get_bios_chs_trans(BusState *bus, int unit) +{ + return DO_UPCAST(IDEBus, qbus, bus)->ifs[unit].chs_trans; } /* --------------------------------- */ @@ -128,25 +142,21 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind) { IDEBus *bus = DO_UPCAST(IDEBus, qbus, dev->qdev.parent_bus); IDEState *s = bus->ifs + dev->unit; - const char *serial; - DriveInfo *dinfo; if (dev->conf.discard_granularity && dev->conf.discard_granularity != 512) { error_report("discard_granularity must be 512 for ide"); return -1; } - serial = dev->serial; - if (!serial) { - /* try to fall back to value set with legacy -drive serial=... */ - dinfo = drive_get_by_blockdev(dev->conf.bs); - if (*dinfo->serial) { - serial = dinfo->serial; - } + blkconf_serial(&dev->conf, &dev->serial); + if (blkconf_geometry(&dev->conf, &dev->chs_trans, 65536, 16, 255) < 0) { + return -1; } if (ide_init_drive(s, dev->conf.bs, kind, - dev->version, serial, dev->model, dev->wwn) < 0) { + dev->version, dev->serial, dev->model, dev->wwn, + dev->conf.cyls, dev->conf.heads, dev->conf.secs, + dev->chs_trans) < 0) { return -1; } @@ -189,6 +199,9 @@ static int ide_drive_initfn(IDEDevice *dev) static Property ide_hd_properties[] = { DEFINE_IDE_DEV_PROPERTIES(), + DEFINE_BLOCK_CHS_PROPERTIES(IDEDrive, dev.conf), + DEFINE_PROP_BIOS_CHS_TRANS("bios-chs-trans", + IDEDrive, dev.chs_trans, BIOS_ATA_TRANSLATION_AUTO), DEFINE_PROP_END_OF_LIST(), }; @@ -44,6 +44,7 @@ #include "kvm.h" #include "xen.h" #include "blockdev.h" +#include "hw/block-common.h" #include "ui/qemu-spice.h" #include "memory.h" #include "exec-memory.h" @@ -216,11 +217,9 @@ static int cmos_get_fd_drive_type(FDriveType fd0) return val; } -static void cmos_init_hd(int type_ofs, int info_ofs, BlockDriverState *hd, - ISADevice *s) +static void cmos_init_hd(ISADevice *s, int type_ofs, int info_ofs, + int16_t cylinders, int8_t heads, int8_t sectors) { - int cylinders, heads, sectors; - bdrv_get_geometry_hint(hd, &cylinders, &heads, §ors); rtc_set_memory(s, type_ofs, 47); rtc_set_memory(s, info_ofs, cylinders); rtc_set_memory(s, info_ofs + 1, cylinders >> 8); @@ -281,48 +280,42 @@ static int pc_boot_set(void *opaque, const char *boot_device) typedef struct pc_cmos_init_late_arg { ISADevice *rtc_state; - BusState *idebus0, *idebus1; + BusState *idebus[2]; } pc_cmos_init_late_arg; static void pc_cmos_init_late(void *opaque) { pc_cmos_init_late_arg *arg = opaque; ISADevice *s = arg->rtc_state; + int16_t cylinders; + int8_t heads, sectors; int val; - BlockDriverState *hd_table[4]; - int i; - - ide_get_bs(hd_table, arg->idebus0); - ide_get_bs(hd_table + 2, arg->idebus1); + int i, trans; - rtc_set_memory(s, 0x12, (hd_table[0] ? 0xf0 : 0) | (hd_table[1] ? 0x0f : 0)); - if (hd_table[0]) - cmos_init_hd(0x19, 0x1b, hd_table[0], s); - if (hd_table[1]) - cmos_init_hd(0x1a, 0x24, hd_table[1], s); + val = 0; + if (ide_get_geometry(arg->idebus[0], 0, + &cylinders, &heads, §ors) >= 0) { + cmos_init_hd(s, 0x19, 0x1b, cylinders, heads, sectors); + val |= 0xf0; + } + if (ide_get_geometry(arg->idebus[0], 1, + &cylinders, &heads, §ors) >= 0) { + cmos_init_hd(s, 0x1a, 0x24, cylinders, heads, sectors); + val |= 0x0f; + } + rtc_set_memory(s, 0x12, val); val = 0; for (i = 0; i < 4; i++) { - if (hd_table[i]) { - int cylinders, heads, sectors, translation; - /* NOTE: bdrv_get_geometry_hint() returns the physical - geometry. It is always such that: 1 <= sects <= 63, 1 - <= heads <= 16, 1 <= cylinders <= 16383. The BIOS - geometry can be different if a translation is done. */ - translation = bdrv_get_translation_hint(hd_table[i]); - if (translation == BIOS_ATA_TRANSLATION_AUTO) { - bdrv_get_geometry_hint(hd_table[i], &cylinders, &heads, §ors); - if (cylinders <= 1024 && heads <= 16 && sectors <= 63) { - /* No translation. */ - translation = 0; - } else { - /* LBA translation. */ - translation = 1; - } - } else { - translation--; - } - val |= translation << (i * 2); + /* NOTE: ide_get_geometry() returns the physical + geometry. It is always such that: 1 <= sects <= 63, 1 + <= heads <= 16, 1 <= cylinders <= 16383. The BIOS + geometry can be different if a translation is done. */ + if (ide_get_geometry(arg->idebus[i / 2], i % 2, + &cylinders, &heads, §ors) >= 0) { + trans = ide_get_bios_chs_trans(arg->idebus[i / 2], i % 2) - 1; + assert((trans & ~3) == 0); + val |= trans << (i * 2); } } rtc_set_memory(s, 0x39, val); @@ -335,10 +328,8 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, ISADevice *floppy, BusState *idebus0, BusState *idebus1, ISADevice *s) { - int val, nb, nb_heads, max_track, last_sect, i; + int val, nb, i; FDriveType fd_type[2] = { FDRIVE_DRV_NONE, FDRIVE_DRV_NONE }; - FDriveRate rate; - BlockDriverState *fd[MAX_FD]; static pc_cmos_init_late_arg arg; /* various important CMOS locations needed by PC/Bochs bios */ @@ -381,13 +372,8 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, /* floppy type */ if (floppy) { - fdc_get_bs(fd, floppy); for (i = 0; i < 2; i++) { - if (fd[i]) { - bdrv_get_floppy_geometry_hint(fd[i], &nb_heads, &max_track, - &last_sect, FDRIVE_DRV_NONE, - &fd_type[i], &rate); - } + fd_type[i] = isa_fdc_get_drive_type(floppy, i); } } val = (cmos_get_fd_drive_type(fd_type[0]) << 4) | @@ -418,8 +404,8 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size, /* hard drives */ arg.rtc_state = s; - arg.idebus0 = idebus0; - arg.idebus1 = idebus1; + arg.idebus[0] = idebus0; + arg.idebus[1] = idebus1; qemu_register_reset(pc_cmos_init_late, &arg); } diff --git a/hw/qdev-properties.c b/hw/qdev-properties.c index 3571cf3017..24b39e8db4 100644 --- a/hw/qdev-properties.c +++ b/hw/qdev-properties.c @@ -2,6 +2,7 @@ #include "qdev.h" #include "qerror.h" #include "blockdev.h" +#include "hw/block-common.h" void *qdev_get_prop_ptr(DeviceState *dev, Property *prop) { @@ -10,6 +11,78 @@ void *qdev_get_prop_ptr(DeviceState *dev, Property *prop) return ptr; } +static void get_pointer(Object *obj, Visitor *v, Property *prop, + const char *(*print)(void *ptr), + const char *name, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + void **ptr = qdev_get_prop_ptr(dev, prop); + char *p; + + p = (char *) (*ptr ? print(*ptr) : ""); + visit_type_str(v, &p, name, errp); +} + +static void set_pointer(Object *obj, Visitor *v, Property *prop, + int (*parse)(DeviceState *dev, const char *str, + void **ptr), + const char *name, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Error *local_err = NULL; + void **ptr = qdev_get_prop_ptr(dev, prop); + char *str; + int ret; + + if (dev->state != DEV_STATE_CREATED) { + error_set(errp, QERR_PERMISSION_DENIED); + return; + } + + visit_type_str(v, &str, name, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + if (!*str) { + g_free(str); + *ptr = NULL; + return; + } + ret = parse(dev, str, ptr); + error_set_from_qdev_prop_error(errp, ret, dev, prop, str); + g_free(str); +} + +static void get_enum(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + int *ptr = qdev_get_prop_ptr(dev, prop); + + visit_type_enum(v, ptr, prop->info->enum_table, + prop->info->name, prop->name, errp); +} + +static void set_enum(Object *obj, Visitor *v, void *opaque, + const char *name, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + int *ptr = qdev_get_prop_ptr(dev, prop); + + if (dev->state != DEV_STATE_CREATED) { + error_set(errp, QERR_PERMISSION_DENIED); + return; + } + + visit_type_enum(v, ptr, prop->info->enum_table, + prop->info->name, prop->name, errp); +} + +/* Bit */ + static uint32_t qdev_get_prop_mask(Property *prop) { assert(prop->info == &qdev_prop_bit); @@ -26,8 +99,6 @@ static void bit_prop_set(DeviceState *dev, Property *props, bool val) *p &= ~mask; } -/* Bit */ - static int print_bit(DeviceState *dev, Property *prop, char *dest, size_t len) { uint32_t *p = qdev_get_prop_ptr(dev, prop); @@ -435,48 +506,6 @@ static const char *print_drive(void *ptr) return bdrv_get_device_name(ptr); } -static void get_pointer(Object *obj, Visitor *v, Property *prop, - const char *(*print)(void *ptr), - const char *name, Error **errp) -{ - DeviceState *dev = DEVICE(obj); - void **ptr = qdev_get_prop_ptr(dev, prop); - char *p; - - p = (char *) (*ptr ? print(*ptr) : ""); - visit_type_str(v, &p, name, errp); -} - -static void set_pointer(Object *obj, Visitor *v, Property *prop, - int (*parse)(DeviceState *dev, const char *str, void **ptr), - const char *name, Error **errp) -{ - DeviceState *dev = DEVICE(obj); - Error *local_err = NULL; - void **ptr = qdev_get_prop_ptr(dev, prop); - char *str; - int ret; - - if (dev->state != DEV_STATE_CREATED) { - error_set(errp, QERR_PERMISSION_DENIED); - return; - } - - visit_type_str(v, &str, name, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - if (!*str) { - g_free(str); - *ptr = NULL; - return; - } - ret = parse(dev, str, ptr); - error_set_from_qdev_prop_error(errp, ret, dev, prop, str); - g_free(str); -} - static void get_drive(Object *obj, Visitor *v, void *opaque, const char *name, Error **errp) { @@ -735,7 +764,6 @@ PropertyInfo qdev_prop_macaddr = { .set = set_mac, }; - /* --- lost tick policy --- */ static const char *lost_tick_policy_table[LOST_TICK_MAX+1] = { @@ -748,33 +776,6 @@ static const char *lost_tick_policy_table[LOST_TICK_MAX+1] = { QEMU_BUILD_BUG_ON(sizeof(LostTickPolicy) != sizeof(int)); -static void get_enum(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - int *ptr = qdev_get_prop_ptr(dev, prop); - - visit_type_enum(v, ptr, prop->info->enum_table, - prop->info->name, prop->name, errp); -} - -static void set_enum(Object *obj, Visitor *v, void *opaque, - const char *name, Error **errp) -{ - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; - int *ptr = qdev_get_prop_ptr(dev, prop); - - if (dev->state != DEV_STATE_CREATED) { - error_set(errp, QERR_PERMISSION_DENIED); - return; - } - - visit_type_enum(v, ptr, prop->info->enum_table, - prop->info->name, prop->name, errp); -} - PropertyInfo qdev_prop_losttickpolicy = { .name = "LostTickPolicy", .enum_table = lost_tick_policy_table, @@ -782,6 +783,21 @@ PropertyInfo qdev_prop_losttickpolicy = { .set = set_enum, }; +/* --- BIOS CHS translation */ + +static const char *bios_chs_trans_table[] = { + [BIOS_ATA_TRANSLATION_AUTO] = "auto", + [BIOS_ATA_TRANSLATION_NONE] = "none", + [BIOS_ATA_TRANSLATION_LBA] = "lba", +}; + +PropertyInfo qdev_prop_bios_chs_trans = { + .name = "bios-chs-trans", + .enum_table = bios_chs_trans_table, + .get = get_enum, + .set = set_enum, +}; + /* --- pci address --- */ /* @@ -232,6 +232,7 @@ extern PropertyInfo qdev_prop_chr; extern PropertyInfo qdev_prop_ptr; extern PropertyInfo qdev_prop_macaddr; extern PropertyInfo qdev_prop_losttickpolicy; +extern PropertyInfo qdev_prop_bios_chs_trans; extern PropertyInfo qdev_prop_drive; extern PropertyInfo qdev_prop_netdev; extern PropertyInfo qdev_prop_vlan; @@ -299,6 +300,8 @@ extern PropertyInfo qdev_prop_pci_host_devaddr; #define DEFINE_PROP_LOSTTICKPOLICY(_n, _s, _f, _d) \ DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_losttickpolicy, \ LostTickPolicy) +#define DEFINE_PROP_BIOS_CHS_TRANS(_n, _s, _f, _d) \ + DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_bios_chs_trans, int) #define DEFINE_PROP_BLOCKSIZE(_n, _s, _f, _d) \ DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_blocksize, uint16_t) #define DEFINE_PROP_PCI_HOST_DEVADDR(_n, _s, _f) \ diff --git a/hw/s390-virtio-bus.c b/hw/s390-virtio-bus.c index 4d49b96f94..a245684692 100644 --- a/hw/s390-virtio-bus.c +++ b/hw/s390-virtio-bus.c @@ -402,6 +402,7 @@ static TypeInfo s390_virtio_net = { static Property s390_virtio_blk_properties[] = { DEFINE_BLOCK_PROPERTIES(VirtIOS390Device, blk.conf), + DEFINE_BLOCK_CHS_PROPERTIES(VirtIOS390Device, blk.conf), DEFINE_PROP_STRING("serial", VirtIOS390Device, blk.serial), #ifdef __linux__ DEFINE_PROP_BIT("scsi", VirtIOS390Device, blk.scsi, 0, true), diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c index 34336b1b58..525816cb76 100644 --- a/hw/scsi-disk.c +++ b/hw/scsi-disk.c @@ -34,6 +34,7 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0) #include "scsi-defs.h" #include "sysemu.h" #include "blockdev.h" +#include "hw/block-common.h" #include "dma.h" #ifdef __linux @@ -965,9 +966,6 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, [MODE_PAGE_AUDIO_CTL] = (1 << TYPE_ROM), [MODE_PAGE_CAPABILITIES] = (1 << TYPE_ROM), }; - - BlockDriverState *bdrv = s->qdev.conf.bs; - int cylinders, heads, secs; uint8_t *p = *p_outbuf; if ((mode_sense_valid[page] & (1 << s->qdev.type)) == 0) { @@ -989,19 +987,18 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, break; } /* if a geometry hint is available, use it */ - bdrv_guess_geometry(bdrv, &cylinders, &heads, &secs); - p[2] = (cylinders >> 16) & 0xff; - p[3] = (cylinders >> 8) & 0xff; - p[4] = cylinders & 0xff; - p[5] = heads & 0xff; + p[2] = (s->qdev.conf.cyls >> 16) & 0xff; + p[3] = (s->qdev.conf.cyls >> 8) & 0xff; + p[4] = s->qdev.conf.cyls & 0xff; + p[5] = s->qdev.conf.heads & 0xff; /* Write precomp start cylinder, disabled */ - p[6] = (cylinders >> 16) & 0xff; - p[7] = (cylinders >> 8) & 0xff; - p[8] = cylinders & 0xff; + p[6] = (s->qdev.conf.cyls >> 16) & 0xff; + p[7] = (s->qdev.conf.cyls >> 8) & 0xff; + p[8] = s->qdev.conf.cyls & 0xff; /* Reduced current start cylinder, disabled */ - p[9] = (cylinders >> 16) & 0xff; - p[10] = (cylinders >> 8) & 0xff; - p[11] = cylinders & 0xff; + p[9] = (s->qdev.conf.cyls >> 16) & 0xff; + p[10] = (s->qdev.conf.cyls >> 8) & 0xff; + p[11] = s->qdev.conf.cyls & 0xff; /* Device step rate [ns], 200ns */ p[12] = 0; p[13] = 200; @@ -1023,18 +1020,17 @@ static int mode_sense_page(SCSIDiskState *s, int page, uint8_t **p_outbuf, p[2] = 5000 >> 8; p[3] = 5000 & 0xff; /* if a geometry hint is available, use it */ - bdrv_guess_geometry(bdrv, &cylinders, &heads, &secs); - p[4] = heads & 0xff; - p[5] = secs & 0xff; + p[4] = s->qdev.conf.heads & 0xff; + p[5] = s->qdev.conf.secs & 0xff; p[6] = s->qdev.blocksize >> 8; - p[8] = (cylinders >> 8) & 0xff; - p[9] = cylinders & 0xff; + p[8] = (s->qdev.conf.cyls >> 8) & 0xff; + p[9] = s->qdev.conf.cyls & 0xff; /* Write precomp start cylinder, disabled */ - p[10] = (cylinders >> 8) & 0xff; - p[11] = cylinders & 0xff; + p[10] = (s->qdev.conf.cyls >> 8) & 0xff; + p[11] = s->qdev.conf.cyls & 0xff; /* Reduced current start cylinder, disabled */ - p[12] = (cylinders >> 8) & 0xff; - p[13] = cylinders & 0xff; + p[12] = (s->qdev.conf.cyls >> 8) & 0xff; + p[13] = s->qdev.conf.cyls & 0xff; /* Device step rate [100us], 100us */ p[14] = 0; p[15] = 1; @@ -1741,7 +1737,6 @@ static void scsi_disk_unit_attention_reported(SCSIDevice *dev) static int scsi_initfn(SCSIDevice *dev) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); - DriveInfo *dinfo; if (!s->qdev.conf.bs) { error_report("drive property not set"); @@ -1754,12 +1749,9 @@ static int scsi_initfn(SCSIDevice *dev) return -1; } - if (!s->serial) { - /* try to fall back to value set with legacy -drive serial=... */ - dinfo = drive_get_by_blockdev(s->qdev.conf.bs); - if (*dinfo->serial) { - s->serial = g_strdup(dinfo->serial); - } + blkconf_serial(&s->qdev.conf, &s->serial); + if (blkconf_geometry(&dev->conf, NULL, 65535, 255, 255) < 0) { + return -1; } if (!s->version) { @@ -1974,6 +1966,7 @@ static Property scsi_hd_properties[] = { DEFINE_PROP_BIT("dpofua", SCSIDiskState, features, SCSI_DISK_F_DPOFUA, false), DEFINE_PROP_HEX64("wwn", SCSIDiskState, wwn, 0), + DEFINE_BLOCK_CHS_PROPERTIES(SCSIDiskState, qdev.conf), DEFINE_PROP_END_OF_LIST(), }; @@ -3,6 +3,7 @@ #include "qdev.h" #include "block.h" +#include "hw/block-common.h" #include "sysemu.h" #define MAX_SCSI_DEVS 255 @@ -25,7 +25,6 @@ * THE SOFTWARE. */ -#include "block.h" #include "qdev.h" #include "qemu-queue.h" diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index 251e7de1cd..7fa8b83d2e 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -532,13 +532,14 @@ static int usb_msd_initfn(USBDevice *dev) { MSDState *s = DO_UPCAST(MSDState, dev, dev); BlockDriverState *bs = s->conf.bs; - DriveInfo *dinfo; if (!bs) { error_report("drive property not set"); return -1; } + blkconf_serial(&s->conf, &s->serial); + /* * Hack alert: this pretends to be a block device, but it's really * a SCSI bus that can serve only a single device, which it @@ -551,13 +552,6 @@ static int usb_msd_initfn(USBDevice *dev) bdrv_detach_dev(bs, &s->dev.qdev); s->conf.bs = NULL; - if (!s->serial) { - /* try to fall back to value set with legacy -drive serial=... */ - dinfo = drive_get_by_blockdev(bs); - if (*dinfo->serial) { - s->serial = strdup(dinfo->serial); - } - } if (s->serial) { usb_desc_set_string(dev, STR_SERIALNUMBER, s->serial); } else { diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c index fe0774617b..f21757ed55 100644 --- a/hw/virtio-blk.c +++ b/hw/virtio-blk.c @@ -14,6 +14,7 @@ #include "qemu-common.h" #include "qemu-error.h" #include "trace.h" +#include "hw/block-common.h" #include "blockdev.h" #include "virtio-blk.h" #include "scsi-defs.h" @@ -478,19 +479,17 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) VirtIOBlock *s = to_virtio_blk(vdev); struct virtio_blk_config blkcfg; uint64_t capacity; - int cylinders, heads, secs; int blk_size = s->conf->logical_block_size; bdrv_get_geometry(s->bs, &capacity); - bdrv_get_geometry_hint(s->bs, &cylinders, &heads, &secs); memset(&blkcfg, 0, sizeof(blkcfg)); stq_raw(&blkcfg.capacity, capacity); stl_raw(&blkcfg.seg_max, 128 - 2); - stw_raw(&blkcfg.cylinders, cylinders); + stw_raw(&blkcfg.cylinders, s->conf->cyls); stl_raw(&blkcfg.blk_size, blk_size); stw_raw(&blkcfg.min_io_size, s->conf->min_io_size / blk_size); stw_raw(&blkcfg.opt_io_size, s->conf->opt_io_size / blk_size); - blkcfg.heads = heads; + blkcfg.heads = s->conf->heads; /* * We must ensure that the block device capacity is a multiple of * the logical block size. If that is not the case, lets use @@ -502,10 +501,10 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) * divided by 512 - instead it is the amount of blk_size blocks * per track (cylinder). */ - if (bdrv_getlength(s->bs) / heads / secs % blk_size) { - blkcfg.sectors = secs & ~s->sector_mask; + if (bdrv_getlength(s->bs) / s->conf->heads / s->conf->secs % blk_size) { + blkcfg.sectors = s->conf->secs & ~s->sector_mask; } else { - blkcfg.sectors = secs; + blkcfg.sectors = s->conf->secs; } blkcfg.size_max = 0; blkcfg.physical_block_exp = get_physical_block_exp(s->conf); @@ -589,9 +588,7 @@ static const BlockDevOps virtio_block_ops = { VirtIODevice *virtio_blk_init(DeviceState *dev, VirtIOBlkConf *blk) { VirtIOBlock *s; - int cylinders, heads, secs; static int virtio_blk_id; - DriveInfo *dinfo; if (!blk->conf.bs) { error_report("drive property not set"); @@ -602,12 +599,9 @@ VirtIODevice *virtio_blk_init(DeviceState *dev, VirtIOBlkConf *blk) return NULL; } - if (!blk->serial) { - /* try to fall back to value set with legacy -drive serial=... */ - dinfo = drive_get_by_blockdev(blk->conf.bs); - if (*dinfo->serial) { - blk->serial = strdup(dinfo->serial); - } + blkconf_serial(&blk->conf, &blk->serial); + if (blkconf_geometry(&blk->conf, NULL, 65535, 255, 255) < 0) { + return NULL; } s = (VirtIOBlock *)virtio_common_init("virtio-blk", VIRTIO_ID_BLOCK, @@ -622,7 +616,6 @@ VirtIODevice *virtio_blk_init(DeviceState *dev, VirtIOBlkConf *blk) s->blk = blk; s->rq = NULL; s->sector_mask = (s->conf->logical_block_size / BDRV_SECTOR_SIZE) - 1; - bdrv_guess_geometry(s->bs, &cylinders, &heads, &secs); s->vq = virtio_add_queue(&s->vdev, 128, virtio_blk_handle_output); diff --git a/hw/virtio-blk.h b/hw/virtio-blk.h index d7850012bd..79ebccc95b 100644 --- a/hw/virtio-blk.h +++ b/hw/virtio-blk.h @@ -15,7 +15,7 @@ #define _QEMU_VIRTIO_BLK_H #include "virtio.h" -#include "block.h" +#include "hw/block-common.h" /* from Linux's linux/virtio_blk.h */ diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c index a66c946f48..4e03f0b42c 100644 --- a/hw/virtio-pci.c +++ b/hw/virtio-pci.c @@ -887,6 +887,7 @@ static int virtio_balloon_exit_pci(PCIDevice *pci_dev) static Property virtio_blk_properties[] = { DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0), DEFINE_BLOCK_PROPERTIES(VirtIOPCIProxy, blk.conf), + DEFINE_BLOCK_CHS_PROPERTIES(VirtIOPCIProxy, blk.conf), DEFINE_PROP_STRING("serial", VirtIOPCIProxy, blk.serial), #ifdef __linux__ DEFINE_PROP_BIT("scsi", VirtIOPCIProxy, blk.scsi, 0, true), diff --git a/hw/virtio.h b/hw/virtio.h index 96f4dbb8d9..f8b5535db1 100644 --- a/hw/virtio.h +++ b/hw/virtio.h @@ -18,7 +18,6 @@ #include "net.h" #include "qdev.h" #include "sysemu.h" -#include "block.h" #include "event_notifier.h" #ifdef CONFIG_LINUX #include "9p.h" @@ -670,6 +670,7 @@ static int readv_f(int argc, char **argv) print_report("read", &t2, offset, qiov.size, total, cnt, Cflag); out: + qemu_iovec_destroy(&qiov); qemu_io_free(buf); return 0; } @@ -928,6 +929,7 @@ static int writev_f(int argc, char **argv) t2 = tsub(t2, t1); print_report("wrote", &t2, offset, qiov.size, total, cnt, Cflag); out: + qemu_iovec_destroy(&qiov); qemu_io_free(buf); return 0; } @@ -1126,6 +1128,7 @@ static void aio_write_done(void *opaque, int ret) ctx->qiov.size, 1, ctx->Cflag); out: qemu_io_free(ctx->buf); + qemu_iovec_destroy(&ctx->qiov); g_free(ctx); } @@ -1166,6 +1169,7 @@ static void aio_read_done(void *opaque, int ret) ctx->qiov.size, 1, ctx->Cflag); out: qemu_io_free(ctx->buf); + qemu_iovec_destroy(&ctx->qiov); g_free(ctx); } diff --git a/tests/Makefile b/tests/Makefile index d687ecce3f..9675ba7c13 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -21,6 +21,7 @@ check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh # All QTests for now are POSIX-only, but the dependencies are # really in libqtest, not in the testcases themselves. check-qtest-i386-y = tests/fdc-test$(EXESUF) +check-qtest-i386-y += tests/hd-geo-test$(EXESUF) check-qtest-i386-y += tests/rtc-test$(EXESUF) check-qtest-x86_64-y = $(check-qtest-i386-y) check-qtest-sparc-y = tests/m48t59-test$(EXESUF) @@ -72,6 +73,7 @@ tests/test-visitor-serialization$(EXESUF): tests/test-visitor-serialization.o $( tests/rtc-test$(EXESUF): tests/rtc-test.o $(trace-obj-y) tests/m48t59-test$(EXESUF): tests/m48t59-test.o $(trace-obj-y) tests/fdc-test$(EXESUF): tests/fdc-test.o tests/libqtest.o $(trace-obj-y) +tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o tests/libqtest.o $(trace-obj-y) # QTest rules diff --git a/tests/fdc-test.c b/tests/fdc-test.c index 585fb0e343..fa7441110d 100644 --- a/tests/fdc-test.c +++ b/tests/fdc-test.c @@ -47,9 +47,11 @@ enum { }; enum { - CMD_SENSE_INT = 0x08, - CMD_SEEK = 0x0f, - CMD_READ = 0xe6, + CMD_SENSE_INT = 0x08, + CMD_SEEK = 0x0f, + CMD_READ = 0xe6, + CMD_RELATIVE_SEEK_OUT = 0x8f, + CMD_RELATIVE_SEEK_IN = 0xcf, }; enum { @@ -91,12 +93,20 @@ static uint8_t floppy_recv(void) return inb(FLOPPY_BASE + reg_fifo); } -static void ack_irq(void) +/* pcn: Present Cylinder Number */ +static void ack_irq(uint8_t *pcn) { + uint8_t ret; + g_assert(get_irq(FLOPPY_IRQ)); floppy_send(CMD_SENSE_INT); floppy_recv(); - floppy_recv(); + + ret = floppy_recv(); + if (pcn != NULL) { + *pcn = ret; + } + g_assert(!get_irq(FLOPPY_IRQ)); } @@ -156,7 +166,7 @@ static uint8_t send_read_command(void) return ret; } -static void send_step_pulse(int cyl) +static void send_seek(int cyl) { int drive = 0; int head = 0; @@ -165,7 +175,7 @@ static void send_step_pulse(int cyl) floppy_send(head << 2 | drive); g_assert(!get_irq(FLOPPY_IRQ)); floppy_send(cyl); - ack_irq(); + ack_irq(NULL); } static uint8_t cmos_read(uint8_t reg) @@ -192,7 +202,7 @@ static void test_no_media_on_start(void) assert_bit_set(dir, DSKCHG); dir = inb(FLOPPY_BASE + reg_dir); assert_bit_set(dir, DSKCHG); - send_step_pulse(1); + send_seek(1); dir = inb(FLOPPY_BASE + reg_dir); assert_bit_set(dir, DSKCHG); dir = inb(FLOPPY_BASE + reg_dir); @@ -223,14 +233,14 @@ static void test_media_change(void) dir = inb(FLOPPY_BASE + reg_dir); assert_bit_set(dir, DSKCHG); - send_step_pulse(0); + send_seek(0); dir = inb(FLOPPY_BASE + reg_dir); assert_bit_set(dir, DSKCHG); dir = inb(FLOPPY_BASE + reg_dir); assert_bit_set(dir, DSKCHG); /* Step to next track should clear DSKCHG bit. */ - send_step_pulse(1); + send_seek(1); dir = inb(FLOPPY_BASE + reg_dir); assert_bit_clear(dir, DSKCHG); dir = inb(FLOPPY_BASE + reg_dir); @@ -246,13 +256,13 @@ static void test_media_change(void) dir = inb(FLOPPY_BASE + reg_dir); assert_bit_set(dir, DSKCHG); - send_step_pulse(0); + send_seek(0); dir = inb(FLOPPY_BASE + reg_dir); assert_bit_set(dir, DSKCHG); dir = inb(FLOPPY_BASE + reg_dir); assert_bit_set(dir, DSKCHG); - send_step_pulse(1); + send_seek(1); dir = inb(FLOPPY_BASE + reg_dir); assert_bit_set(dir, DSKCHG); dir = inb(FLOPPY_BASE + reg_dir); @@ -281,6 +291,35 @@ static void test_sense_interrupt(void) floppy_recv(); } +static void test_relative_seek(void) +{ + uint8_t drive = 0; + uint8_t head = 0; + uint8_t cyl = 1; + uint8_t pcn; + + /* Send seek to track 0 */ + send_seek(0); + + /* Send relative seek to increase track by 1 */ + floppy_send(CMD_RELATIVE_SEEK_IN); + floppy_send(head << 2 | drive); + g_assert(!get_irq(FLOPPY_IRQ)); + floppy_send(cyl); + + ack_irq(&pcn); + g_assert(pcn == 1); + + /* Send relative seek to decrease track by 1 */ + floppy_send(CMD_RELATIVE_SEEK_OUT); + floppy_send(head << 2 | drive); + g_assert(!get_irq(FLOPPY_IRQ)); + floppy_send(cyl); + + ack_irq(&pcn); + g_assert(pcn == 0); +} + /* success if no crash or abort */ static void fuzz_registers(void) { @@ -329,6 +368,7 @@ int main(int argc, char **argv) qtest_add_func("/fdc/read_without_media", test_read_without_media); qtest_add_func("/fdc/media_change", test_media_change); qtest_add_func("/fdc/sense_interrupt", test_sense_interrupt); + qtest_add_func("/fdc/relative_seek", test_relative_seek); qtest_add_func("/fdc/fuzz-registers", fuzz_registers); ret = g_test_run(); diff --git a/tests/hd-geo-test.c b/tests/hd-geo-test.c new file mode 100644 index 0000000000..9a31e8587f --- /dev/null +++ b/tests/hd-geo-test.c @@ -0,0 +1,428 @@ +/* + * Hard disk geometry test cases. + * + * Copyright (c) 2012 Red Hat Inc. + * + * Authors: + * Markus Armbruster <armbru@redhat.com>, + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +/* + * Covers only IDE and tests only CMOS contents. Better than nothing. + * Improvements welcome. + */ + +#include <glib.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "qemu-common.h" +#include "libqtest.h" + +static const char test_image[] = "/tmp/qtest.XXXXXX"; + +static char *create_test_img(int secs) +{ + char *template = strdup("/tmp/qtest.XXXXXX"); + int fd, ret; + + fd = mkstemp(template); + g_assert(fd >= 0); + ret = ftruncate(fd, (off_t)secs * 512); + g_assert(ret == 0); + close(fd); + return template; +} + +typedef struct { + int cyls, heads, secs, trans; +} CHST; + +typedef enum { + mbr_blank, mbr_lba, mbr_chs, + mbr_last +} MBRcontents; + +typedef enum { + /* order is relevant */ + backend_small, backend_large, backend_empty, + backend_last +} Backend; + +static const int img_secs[backend_last] = { + [backend_small] = 61440, + [backend_large] = 8388608, + [backend_empty] = -1, +}; + +static const CHST hd_chst[backend_last][mbr_last] = { + [backend_small] = { + [mbr_blank] = { 60, 16, 63, 0 }, + [mbr_lba] = { 60, 16, 63, 2 }, + [mbr_chs] = { 60, 16, 63, 0 } + }, + [backend_large] = { + [mbr_blank] = { 8322, 16, 63, 1 }, + [mbr_lba] = { 8322, 16, 63, 1 }, + [mbr_chs] = { 8322, 16, 63, 0 } + }, +}; + +static const char *img_file_name[backend_last]; + +static const CHST *cur_ide[4]; + +static bool is_hd(const CHST *expected_chst) +{ + return expected_chst && expected_chst->cyls; +} + +static void test_cmos_byte(int reg, int expected) +{ + enum { cmos_base = 0x70 }; + int actual; + + outb(cmos_base + 0, reg); + actual = inb(cmos_base + 1); + g_assert(actual == expected); +} + +static void test_cmos_bytes(int reg0, int n, uint8_t expected[]) +{ + int i; + + for (i = 0; i < 9; i++) { + test_cmos_byte(reg0 + i, expected[i]); + } +} + +static void test_cmos_disk_data(void) +{ + test_cmos_byte(0x12, + (is_hd(cur_ide[0]) ? 0xf0 : 0) | + (is_hd(cur_ide[1]) ? 0x0f : 0)); +} + +static void test_cmos_drive_cyl(int reg0, const CHST *expected_chst) +{ + if (is_hd(expected_chst)) { + int c = expected_chst->cyls; + int h = expected_chst->heads; + int s = expected_chst->secs; + uint8_t expected_bytes[9] = { + c & 0xff, c >> 8, h, 0xff, 0xff, 0xc0 | ((h > 8) << 3), + c & 0xff, c >> 8, s + }; + test_cmos_bytes(reg0, 9, expected_bytes); + } else { + int i; + + for (i = 0; i < 9; i++) { + test_cmos_byte(reg0 + i, 0); + } + } +} + +static void test_cmos_drive1(void) +{ + test_cmos_byte(0x19, is_hd(cur_ide[0]) ? 47 : 0); + test_cmos_drive_cyl(0x1b, cur_ide[0]); +} + +static void test_cmos_drive2(void) +{ + test_cmos_byte(0x1a, is_hd(cur_ide[1]) ? 47 : 0); + test_cmos_drive_cyl(0x24, cur_ide[1]); +} + +static void test_cmos_disktransflag(void) +{ + int val, i; + + val = 0; + for (i = 0; i < ARRAY_SIZE(cur_ide); i++) { + if (is_hd(cur_ide[i])) { + val |= cur_ide[i]->trans << (2 * i); + } + } + test_cmos_byte(0x39, val); +} + +static void test_cmos(void) +{ + test_cmos_disk_data(); + test_cmos_drive1(); + test_cmos_drive2(); + test_cmos_disktransflag(); +} + +static int append_arg(int argc, char *argv[], int argv_sz, char *arg) +{ + g_assert(argc + 1 < argv_sz); + argv[argc++] = arg; + argv[argc] = NULL; + return argc; +} + +static int setup_common(char *argv[], int argv_sz) +{ + memset(cur_ide, 0, sizeof(cur_ide)); + return append_arg(0, argv, argv_sz, + g_strdup("-nodefaults -display none")); +} + +static void setup_mbr(int img_idx, MBRcontents mbr) +{ + static const uint8_t part_lba[16] = { + /* chs 0,1,1 (lba 63) to chs 0,127,63 (8001 sectors) */ + 0x80, 1, 1, 0, 6, 127, 63, 0, 63, 0, 0, 0, 0x41, 0x1F, 0, 0, + }; + static const uint8_t part_chs[16] = { + /* chs 0,1,1 (lba 63) to chs 7,15,63 (8001 sectors) */ + 0x80, 1, 1, 0, 6, 15, 63, 7, 63, 0, 0, 0, 0x41, 0x1F, 0, 0, + }; + uint8_t buf[512]; + int fd, ret; + + memset(buf, 0, sizeof(buf)); + + if (mbr != mbr_blank) { + buf[0x1fe] = 0x55; + buf[0x1ff] = 0xAA; + memcpy(buf + 0x1BE, mbr == mbr_lba ? part_lba : part_chs, 16); + } + + fd = open(img_file_name[img_idx], O_WRONLY); + g_assert(fd >= 0); + ret = write(fd, buf, sizeof(buf)); + g_assert(ret == sizeof(buf)); + close(fd); +} + +static int setup_ide(int argc, char *argv[], int argv_sz, + int ide_idx, const char *dev, int img_idx, + MBRcontents mbr, const char *opts) +{ + char *s1, *s2, *s3; + + s1 = g_strdup_printf("-drive id=drive%d,if=%s", + ide_idx, dev ? "none" : "ide"); + s2 = dev ? g_strdup("") : g_strdup_printf(",index=%d", ide_idx); + + if (img_secs[img_idx] >= 0) { + setup_mbr(img_idx, mbr); + s3 = g_strdup_printf(",file=%s", img_file_name[img_idx]); + } else { + s3 = g_strdup(",media=cdrom"); + } + argc = append_arg(argc, argv, argv_sz, + g_strdup_printf("%s%s%s%s", s1, s2, s3, opts)); + g_free(s1); + g_free(s2); + g_free(s3); + + if (dev) { + argc = append_arg(argc, argv, argv_sz, + g_strdup_printf("-device %s,drive=drive%d," + "bus=ide.%d,unit=%d", + dev, ide_idx, + ide_idx / 2, ide_idx % 2)); + } + return argc; +} + +/* + * Test case: no IDE devices + */ +static void test_ide_none(void) +{ + char *argv[256]; + + setup_common(argv, ARRAY_SIZE(argv)); + qtest_start(g_strjoinv(" ", argv)); + test_cmos(); + qtest_quit(global_qtest); +} + +static void test_ide_mbr(bool use_device, MBRcontents mbr) +{ + char *argv[256]; + int argc; + Backend i; + const char *dev; + + argc = setup_common(argv, ARRAY_SIZE(argv)); + for (i = 0; i < backend_last; i++) { + cur_ide[i] = &hd_chst[i][mbr]; + dev = use_device ? (is_hd(cur_ide[i]) ? "ide-hd" : "ide-cd") : NULL; + argc = setup_ide(argc, argv, ARRAY_SIZE(argv), i, dev, i, mbr, ""); + } + qtest_start(g_strjoinv(" ", argv)); + test_cmos(); + qtest_quit(global_qtest); +} + +/* + * Test case: IDE devices (if=ide) with blank MBRs + */ +static void test_ide_drive_mbr_blank(void) +{ + test_ide_mbr(false, mbr_blank); +} + +/* + * Test case: IDE devices (if=ide) with MBRs indicating LBA is in use + */ +static void test_ide_drive_mbr_lba(void) +{ + test_ide_mbr(false, mbr_lba); +} + +/* + * Test case: IDE devices (if=ide) with MBRs indicating CHS is in use + */ +static void test_ide_drive_mbr_chs(void) +{ + test_ide_mbr(false, mbr_chs); +} + +/* + * Test case: IDE devices (if=none) with blank MBRs + */ +static void test_ide_device_mbr_blank(void) +{ + test_ide_mbr(true, mbr_blank); +} + +/* + * Test case: IDE devices (if=none) with MBRs indicating LBA is in use + */ +static void test_ide_device_mbr_lba(void) +{ + test_ide_mbr(true, mbr_lba); +} + +/* + * Test case: IDE devices (if=none) with MBRs indicating CHS is in use + */ +static void test_ide_device_mbr_chs(void) +{ + test_ide_mbr(true, mbr_chs); +} + +static void test_ide_drive_user(const char *dev, bool trans) +{ + char *argv[256], *opts; + int argc; + int secs = img_secs[backend_small]; + const CHST expected_chst = { secs / (4 * 32) , 4, 32, trans }; + + argc = setup_common(argv, ARRAY_SIZE(argv)); + opts = g_strdup_printf("%s,%s%scyls=%d,heads=%d,secs=%d", + dev ?: "", + trans && dev ? "bios-chs-" : "", + trans ? "trans=lba," : "", + expected_chst.cyls, expected_chst.heads, + expected_chst.secs); + cur_ide[0] = &expected_chst; + argc = setup_ide(argc, argv, ARRAY_SIZE(argv), + 0, dev ? opts : NULL, backend_small, mbr_chs, + dev ? "" : opts); + g_free(opts); + qtest_start(g_strjoinv(" ", argv)); + test_cmos(); + qtest_quit(global_qtest); +} + +/* + * Test case: IDE device (if=ide) with explicit CHS + */ +static void test_ide_drive_user_chs(void) +{ + test_ide_drive_user(NULL, false); +} + +/* + * Test case: IDE device (if=ide) with explicit CHS and translation + */ +static void test_ide_drive_user_chst(void) +{ + test_ide_drive_user(NULL, true); +} + +/* + * Test case: IDE device (if=none) with explicit CHS + */ +static void test_ide_device_user_chs(void) +{ + test_ide_drive_user("ide-hd", false); +} + +/* + * Test case: IDE device (if=none) with explicit CHS and translation + */ +static void test_ide_device_user_chst(void) +{ + test_ide_drive_user("ide-hd", true); +} + +/* + * Test case: IDE devices (if=ide), but use index=0 for CD-ROM + */ +static void test_ide_drive_cd_0(void) +{ + char *argv[256]; + int argc, ide_idx; + Backend i; + + argc = setup_common(argv, ARRAY_SIZE(argv)); + for (i = 0; i <= backend_empty; i++) { + ide_idx = backend_empty - i; + cur_ide[ide_idx] = &hd_chst[i][mbr_blank]; + argc = setup_ide(argc, argv, ARRAY_SIZE(argv), + ide_idx, NULL, i, mbr_blank, ""); + } + qtest_start(g_strjoinv(" ", argv)); + test_cmos(); + qtest_quit(global_qtest); +} + +int main(int argc, char **argv) +{ + Backend i; + int ret; + + g_test_init(&argc, &argv, NULL); + + for (i = 0; i < backend_last; i++) { + if (img_secs[i] >= 0) { + img_file_name[i] = create_test_img(img_secs[i]); + } else { + img_file_name[i] = NULL; + } + } + + qtest_add_func("hd-geo/ide/none", test_ide_none); + qtest_add_func("hd-geo/ide/drive/mbr/blank", test_ide_drive_mbr_blank); + qtest_add_func("hd-geo/ide/drive/mbr/lba", test_ide_drive_mbr_lba); + qtest_add_func("hd-geo/ide/drive/mbr/chs", test_ide_drive_mbr_chs); + qtest_add_func("hd-geo/ide/drive/user/chs", test_ide_drive_user_chs); + qtest_add_func("hd-geo/ide/drive/user/chst", test_ide_drive_user_chst); + qtest_add_func("hd-geo/ide/drive/cd_0", test_ide_drive_cd_0); + qtest_add_func("hd-geo/ide/device/mbr/blank", test_ide_device_mbr_blank); + qtest_add_func("hd-geo/ide/device/mbr/lba", test_ide_device_mbr_lba); + qtest_add_func("hd-geo/ide/device/mbr/chs", test_ide_device_mbr_chs); + qtest_add_func("hd-geo/ide/device/user/chs", test_ide_device_user_chs); + qtest_add_func("hd-geo/ide/device/user/chst", test_ide_device_user_chst); + + ret = g_test_run(); + + for (i = 0; i < backend_last; i++) { + unlink(img_file_name[i]); + } + + return ret; +} diff --git a/tests/qemu-iotests/common b/tests/qemu-iotests/common index eeb70cbcdc..1f6fdf5c56 100644 --- a/tests/qemu-iotests/common +++ b/tests/qemu-iotests/common @@ -41,6 +41,7 @@ sortme=false expunge=true have_test_arg=false randomize=false +valgrind=false rm -f $tmp.list $tmp.tmp $tmp.sed export IMGFMT=raw @@ -212,6 +213,11 @@ testlist options xpand=false ;; + -valgrind) + valgrind=true + xpand=false + ;; + -g) # -g group ... pick from group file group=true xpand=false @@ -345,3 +351,8 @@ fi [ "$QEMU" = "" ] && _fatal "qemu not found" [ "$QEMU_IMG" = "" ] && _fatal "qemu-img not found" [ "$QEMU_IO" = "" ] && _fatal "qemu-img not found" + +if $valgrind; then + export REAL_QEMU_IO="$QEMU_IO_PROG" + export QEMU_IO_PROG=valgrind_qemu_io +fi diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index e535874e4c..5e3a524bc8 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -53,6 +53,16 @@ else TEST_IMG=$IMGPROTO:$TEST_DIR/t.$IMGFMT fi +function valgrind_qemu_io() +{ + valgrind --log-file=/tmp/$$.valgrind --error-exitcode=99 $REAL_QEMU_IO "$@" + if [ $? != 0 ]; then + cat /tmp/$$.valgrind + fi + rm -f /tmp/$$.valgrind +} + + _optstr_add() { if [ -n "$1" ]; then diff --git a/trace-events b/trace-events index fa0c883456..2a5f074137 100644 --- a/trace-events +++ b/trace-events @@ -141,6 +141,10 @@ ecc_mem_readl_ecr1(uint32_t ret) "Read event count 2 %08x" ecc_diag_mem_writeb(uint64_t addr, uint32_t val) "Write diagnostic %"PRId64" = %02x" ecc_diag_mem_readb(uint64_t addr, uint32_t ret) "Read diagnostic %"PRId64"= %02x" +# hw/hd-geometry.c +hd_geometry_lchs_guess(void *bs, int cyls, int heads, int secs) "bs %p LCHS %d %d %d" +hd_geometry_guess(void *bs, uint32_t cyls, uint32_t heads, uint32_t secs, int trans) "bs %p CHS %u %u %u trans %d" + # hw/jazz-led.c jazz_led_read(uint64_t addr, uint8_t val) "read addr=0x%"PRIx64": 0x%x" jazz_led_write(uint64_t addr, uint8_t new) "write addr=0x%"PRIx64": 0x%x" @@ -130,8 +130,8 @@ int main(int argc, char **argv) #include "qemu-timer.h" #include "qemu-char.h" #include "cache-utils.h" -#include "block.h" #include "blockdev.h" +#include "hw/block-common.h" #include "block-migration.h" #include "dma.h" #include "audio/audio.h" |