Page 1 of 1

[Solved] Weird bug about qemu disk r/w

Posted: Thu May 05, 2022 2:11 pm
by michael
I've inserted a full 0 raw disk image file into qemu as hdd with parameter -hda disk.img. Then, I use following codes to test whether my driver works or not:

Code: Select all

unsigned char buf[512];
memset(buf,1,512);
transfer(ATA_WRITE_CMD,0x200,1,(unsigned char*)buf);//write one sector data from buf to LBA: 0x200 in disk
memset(buf,0,512);
transfer(ATA_READ_CMD,0x200,1,(unsigned char*)buf);//read one sector data from LBA: 0x200 to buf
for(int i=0;i<512;i++){
    printk(RED,YELLOW,"%x ",buf[i]);
}
The code runs well under any condition, output is always 512 bytes full 1 array no matter which address I choose to start. But when I check my image file (using hexedit disk.img), I cannot find any changes on it. After that I comment the write operation on the code and reload my kernel, the screen still shows 512 bytes full 1 array, but there're nothing changes on the image file.

PS: The only one exception this code works well is when LBA address start with 0, screen can print 512 bytes full 1 array and image file can also preserve the modifications on the first sector.

Here is core code for function transfer:

Code: Select all

switch(node->cmd){
		case ATA_WRITE_CMD:
			io_out8(PORT_DISK0_DEVICE,0x40);
			// 48 bits LBA address, should divide in to two part and send each of them
			io_out8(PORT_DISK0_ERR_FEATURE,0);
			io_out8(PORT_DISK0_SECTOR_CNT,	(node->count>>8)	&0xff);
			io_out8(PORT_DISK0_SECTOR_LOW,	(node->LBA>>24)		&0xff);
			io_out8(PORT_DISK0_SECTOR_MID,	(node->LBA>>32)		&0xff);
			io_out8(PORT_DISK0_SECTOR_HIGH,	(node->LBA>>40)		&0xff);

			io_out8(PORT_DISK0_ERR_FEATURE,0);
			io_out8(PORT_DISK0_SECTOR_CNT,	(node->count)		&0xff);
			io_out8(PORT_DISK0_SECTOR_LOW,	(node->LBA)			&0xff);
			io_out8(PORT_DISK0_SECTOR_MID,	(node->LBA>>8)		&0xff);
			io_out8(PORT_DISK0_SECTOR_HIGH,	(node->LBA>>16)		&0xff);

			//if disk not ready, wait
			while(!(io_in8(PORT_DISK0_STATUS_CMD)&DISK_STATUS_READY)){
				nop();
			}
			io_out8(PORT_DISK0_STATUS_CMD,node->cmd);
			//if disk not require for data, wait
			while(!(io_in8(PORT_DISK0_STATUS_CMD)&DISK_STATUS_REQ)){
				nop();
			}
			port_outsw(PORT_DISK0_DATA,node->buffer,256);
			break;

		case ATA_READ_CMD:
			io_out8(PORT_DISK0_DEVICE,0x40);

			io_out8(PORT_DISK0_ERR_FEATURE,0);
			io_out8(PORT_DISK0_SECTOR_CNT,	(node->count>>8)	&0xff);
			io_out8(PORT_DISK0_SECTOR_LOW,	(node->LBA>>24)		&0xff);
			io_out8(PORT_DISK0_SECTOR_MID,	(node->LBA>>32)		&0xff);
			io_out8(PORT_DISK0_SECTOR_HIGH,	(node->LBA>>40)		&0xff);

			io_out8(PORT_DISK0_ERR_FEATURE,0);
			io_out8(PORT_DISK0_SECTOR_CNT,	(node->count)		&0xff);
			io_out8(PORT_DISK0_SECTOR_LOW,	(node->LBA)			&0xff);
			io_out8(PORT_DISK0_SECTOR_MID,	(node->LBA>>8)		&0xff);
			io_out8(PORT_DISK0_SECTOR_HIGH,	(node->LBA>>16)		&0xff);

			//if disk not ready, wait
			while(!io_in8(PORT_DISK0_STATUS_CMD)&DISK_STATUS_READY){
				nop();
			}
			io_out8(PORT_DISK0_STATUS_CMD,node->cmd);
			break;

		default:
			printk(BLACK,WHITE,"ATA CMD ERROR\n");
			break;
	}
Here is the definition of function transfer:

Code: Select all

/*

cmd: ATA_READ_CMD or ATA_WRITE_CMD
blocks: LBA address
count: number of sectors
*buffer: I/O buffer address

*/
unsigned long transfer(long cmd, unsigned long blocks, long count, unsigned char *buffer){

Re: Weird bug about qemu disk r/w

Posted: Thu May 05, 2022 2:44 pm
by Octocontrabass
Your code to read the disk is missing the part that reads the disk.

How exactly do you calculate node->LBA?

Re: Weird bug about qemu disk r/w

Posted: Thu May 05, 2022 5:09 pm
by michael
Octocontrabass wrote: How exactly do you calculate node->LBA?
I directly put the "block" parameter from transfer into node->LBA.
Octocontrabass wrote: Your code to read the disk is missing the part that reads the disk.
The code to read the disk is here:

Code: Select all

void read_handler(unsigned long nr, unsigned long parameter){
	struct block_buffer_node *node=((struct request_queue*)parameter)->in_using;

	if(io_in8(PORT_DISK0_STATUS_CMD)&DISK_STATUS_ERROR){
		printk(RED,BLACK,"read_handler: %x\n",io_in8(PORT_DISK0_ERR_FEATURE));
	}else{
		port_insw(PORT_DISK0_DATA,node->buffer,256);
	}

	end_request(node);
}
And here is the disk interrupt handler:

Code: Select all

//Interrupt handler program for disk
void disk_handler(unsigned long nr, unsigned long parameter, struct pt_regs *regs){
    struct block_buffer_node *node=((struct request_queue*)parameter)->in_using;
	disk_request.queue_status=QUEUE_READY_4_NXT;
	node->end_handler(nr,parameter);//call read_handler() for disk here
}
This interrupt code should only be awakened after disk puts all the data into its buffer and send an interrupt.

I've already verified that it is the same node in disk_handler and the core code of transfer. I supposed it is the problem of some internal setting inside the qemu. Since the output is always correct, the only problem is that the changes on the disk doesn't seems like to "synchronized" with image file(except first sector).

Re: Weird bug about qemu disk r/w

Posted: Thu May 05, 2022 5:18 pm
by Octocontrabass
michael wrote:I directly put the "block" parameter from transfer into node->LBA.
How big is your disk image?
michael wrote:This interrupt code should only be awakened after disk puts all the data into its buffer and send an interrupt.
Where do you wait for the interrupt handler to fill the buffer before you print the buffer? Why aren't you using interrupts to write to the disk? How does your interrupt handler know that the interrupt is due to a read and not some other command?

Re: Weird bug about qemu disk r/w

Posted: Thu May 05, 2022 5:21 pm
by michael
Octocontrabass wrote:
michael wrote:I directly put the "block" parameter from transfer into node->LBA.
How big is your disk image?
michael wrote:This interrupt code should only be awakened after disk puts all the data into its buffer and send an interrupt.
Where do you wait for the interrupt handler to fill the buffer before you print the buffer? Why aren't you using interrupts to write to the disk? How does your interrupt handler know that the interrupt is due to a read and not some other command?
I've just found the data supposed to be written in 0x200 appear in 0x20000, probably know the reason
Sorry for disturb :)