qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH] vvfat mbr fixes
@ 2007-09-22 21:43 Ivan Kalvachev
  2007-09-22 22:48 ` Johannes Schindelin
  0 siblings, 1 reply; 14+ messages in thread
From: Ivan Kalvachev @ 2007-09-22 21:43 UTC (permalink / raw)
  To: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 2774 bytes --]

Hello,

I've been having problems using vvfat virtual block device.
Even linux fdisk was able to find problems with it.
The reason turned out to be simple, MBR have bogus parameters.

1. Partition size in sectors is threated as partition end,
causing the partition to have the same size as the device,
while starting 63 sectors inside it.

2. The disk CHS geometry was set to maximum allowed values,
not the maximum sizes of parameters.

Cylinders = 1024 -> 0 .. 1023
Heads     = 16   -> 0 .. 15
Sectors   = 63   -> 1 .. 63

By historical reasons sectors maximum allowed value and size match.
I've changed it to correct values. Now disks are at 504MB limit.

3. The size of partition was fixed with pre-calculated value based on
the wrong geometry.
I've implemented dynamic calculation.

4. The size of partition have been precalculated to fill the maximum geometry.
However usually this means that there is incomplete cluster at the
end of it.
I've implemented proper shrinking of the partition after init_directories().

5. WinNT clones refuse to mount disk that doesn't have NT-ID.
I set it to 'qemu'. :)

As a bonus I reworked the rest of init_mbr code, so it can handle
disk outside the CHS limits in a way that all modern systems do, using LBA.
Because I couldn't find sector->CHS function I wrote one and to simplify
its calling I changed the 3 separate bytes for CHS into an array of 3.
Also when LBA mode is detected it sets the proper FAT partition types.

I haven't touched any of the directory/boot_record/etc code.
(I haven't done automatic fat/partition/disk growing, so far it all
have to fit into the maximum CHS geometry).

I also moved the ":rw:" handling after other option parsing, as some of them
change the sector_count that is used to initialize the snapshot device.
However in qemu-0.9.0 and qemu-cvs RW mode doesn't work for me, it fails
at bdrv_open() in enable_write_target(). I'll try to debug that and
if I find solution I would send additional patch.

The attached patch is to the current CVS, it should apply to
qemu-0.9.0 with `patch -l`, as there have been only whitespace
changes.


Here is some simple table of the virtual disk, that would help you
understand the code changes.

====
MBR           - 0
---
partition_start
boot          - first_sectors_number-1
FAT1          - first_sectors_number
FAT2          - first_sectors_number+sectors_per_fat
DIR_ROOT      - first_sectors_number+2*sectors_per_fat = faked_sectors
...data...
partition_end - cluster_count*sectors_per_cluster + faked_sectors = sector_count
====

P.S.
Maybe it is good idea to use 80/2/18 CHS for 1.44MB floppy,
instead of 80/2/36 CHS for 2.88MB. Linux doesn't seem to autodetect them.
The problem is noticed first by Thomas Schwinge in mail to qemu-dev at 28 Mart.

[-- Attachment #2: qemu_vvfat_mbr.patch --]
[-- Type: application/octet-stream, Size: 4874 bytes --]

--- oldqemu/block-vvfat.c	2007-09-17 11:09:43.000000000 +0300
+++ newqemu/block-vvfat.c	2007-09-22 22:58:17.044333017 +0300
@@ -244,15 +244,11 @@ typedef struct bootsector_t {
 
 typedef struct partition_t {
     uint8_t attributes; /* 0x80 = bootable */
-    uint8_t start_head;
-    uint8_t start_sector;
-    uint8_t start_cylinder;
-    uint8_t fs_type; /* 0x1 = FAT12, 0x6 = FAT16, 0xb = FAT32 */
-    uint8_t end_head;
-    uint8_t end_sector;
-    uint8_t end_cylinder;
+    uint8_t start_CHS[3];
+    uint8_t fs_type; /* 0x1 = FAT12, 0x6 = FAT16, 0xe = FAT16_LBA, 0xb = FAT32, 0xc = FAT32_LBA */
+    uint8_t end_CHS[3];
     uint32_t start_sector_long;
-    uint32_t end_sector_long;
+    uint32_t length_sector_long;
 } __attribute__((packed)) partition_t;
 
 typedef struct mbr_t {
@@ -350,26 +346,52 @@ typedef struct BDRVVVFATState {
     int downcase_short_names;
 } BDRVVVFATState;
 
+static int convert_sector2CHS(BlockDriverState* bs, uint8_t * CHS, int spos){
+    int head,sector;
+    sector   = spos % (bs->secs);  spos/= bs->secs;
+    head     = spos % (bs->heads); spos/= bs->heads;
+    if(spos >= bs->cyls){
+        /* Windows/Dos is seid to take 1023/255/63 as nonrepresentable CHS */
+        CHS[0] = 0xFF;
+        CHS[1] = 0xFF;
+        CHS[2] = 0xFF;
+        return 1;
+    }
+    CHS[0] = (uint8_t)head;//head
+    CHS[1] = (uint8_t)( (sector+1) | ((spos>>8)<<6) ); //sector+hi(cylinder)
+    CHS[2] = (uint8_t)spos;//cylinder
+    return 0;
+}
 
 static void init_mbr(BDRVVVFATState* s)
 {
     /* TODO: if the files mbr.img and bootsect.img exist, use them */
     mbr_t* real_mbr=(mbr_t*)s->first_sectors;
     partition_t* partition=&(real_mbr->partition[0]);
+    int lba;
 
     memset(s->first_sectors,0,512);
 
+    /* Win NT Disk Signature */
+    real_mbr->ignored[0x1b8]='q';
+    real_mbr->ignored[0x1b9]='e';
+    real_mbr->ignored[0x1ba]='m';
+    real_mbr->ignored[0x1bb]='u';
+   
     partition->attributes=0x80; /* bootable */
-    partition->start_head=1;
-    partition->start_sector=1;
-    partition->start_cylinder=0;
+    
+    lba =convert_sector2CHS(s->bs, partition->start_CHS, s->first_sectors_number-1);
+    lba|=convert_sector2CHS(s->bs, partition->end_CHS, s->sector_count);
+
+    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);
+
     /* FAT12/FAT16/FAT32 */
-    partition->fs_type=(s->fat_type==12?0x1:s->fat_type==16?0x6:0xb);
-    partition->end_head=s->bs->heads-1;
-    partition->end_sector=0xff; /* end sector & upper 2 bits of cylinder */;
-    partition->end_cylinder=0xff; /* lower 8 bits of end cylinder */;
-    partition->start_sector_long=cpu_to_le32(s->bs->secs);
-    partition->end_sector_long=cpu_to_le32(s->sector_count);
+    if(lba)
+        /*LBA partitions are identified by start/ength_sector_long not by CHS*/
+        partition->fs_type=(s->fat_type==12?0x1:s->fat_type==16?0xe:0xc);
+    else
+        partition->fs_type=(s->fat_type==12?0x1:s->fat_type==16?0x6:0xb);
 
     real_mbr->magic[0]=0x55; real_mbr->magic[1]=0xaa;
 }
@@ -973,10 +995,10 @@ DLOG(if (stderr == NULL) {
 
     s->fat_type=16;
     /* LATER TODO: if FAT32, adjust */
-    s->sector_count=0xec04f;
     s->sectors_per_cluster=0x10;
-    /* LATER TODO: this could be wrong for FAT32 */
-    bs->cyls=1023; bs->heads=15; bs->secs=63;
+    /* Keep this geometry for disk +512MB and use LBA model*/
+    bs->cyls=1024; bs->heads=16; bs->secs=63;
+
 
     s->current_cluster=0xffffffff;
 
@@ -991,11 +1013,6 @@ DLOG(if (stderr == NULL) {
     if (!strstart(dirname, "fat:", NULL))
 	return -1;
 
-    if (strstr(dirname, ":rw:")) {
-	if (enable_write_target(s))
-	    return -1;
-	bs->read_only = 0;
-    }
 
     if (strstr(dirname, ":floppy:")) {
 	floppy = 1;
@@ -1005,6 +1022,8 @@ DLOG(if (stderr == NULL) {
 	bs->cyls = 80; bs->heads = 2; bs->secs = 36;
     }
 
+    s->sector_count=bs->cyls*bs->heads*bs->secs;
+
     if (strstr(dirname, ":32:")) {
 	fprintf(stderr, "Big fat greek warning: FAT32 has not been tested. You are welcome to do so!\n");
 	s->fat_type = 32;
@@ -1015,6 +1034,12 @@ DLOG(if (stderr == NULL) {
 	s->sector_count=2880;
     }
 
+    if (strstr(dirname, ":rw:")) {
+	if (enable_write_target(s))
+	    return -1;
+	bs->read_only = 0;
+    }
+
     i = strrchr(dirname, ':') - dirname;
     assert(i >= 3);
     if (dirname[i-2] == ':' && isalpha(dirname[i-1]))
@@ -1024,11 +1049,12 @@ DLOG(if (stderr == NULL) {
 	dirname += i+1;
 
     bs->total_sectors=bs->cyls*bs->heads*bs->secs;
-    if (s->sector_count > bs->total_sectors)
-	s->sector_count = bs->total_sectors;
+
     if(init_directories(s, dirname))
 	return -1;
 
+    s->sector_count = s->faked_sectors + s->sectors_per_cluster*s->cluster_count;
+
     if(s->first_sectors_number==0x40)
 	init_mbr(s);
 

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2007-09-24 21:59 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-09-22 21:43 [Qemu-devel] [PATCH] vvfat mbr fixes Ivan Kalvachev
2007-09-22 22:48 ` Johannes Schindelin
2007-09-23  7:31   ` [Qemu-devel] " Lorenzo Campedelli
2007-09-23 11:34     ` Johannes Schindelin
2007-09-23 15:55       ` Lorenzo Campedelli
2007-09-23 19:18         ` Johannes Schindelin
2007-09-23 20:17           ` Lorenzo Campedelli
2007-09-24 10:50   ` [Qemu-devel] " Ivan Kalvachev
2007-09-24 11:18     ` Johannes Schindelin
2007-09-24 14:12       ` Ivan Kalvachev
2007-09-24 15:32         ` Johannes Schindelin
2007-09-24 17:23           ` Ivan Kalvachev
2007-09-24 19:19           ` [Qemu-devel] " Lorenzo Campedelli
2007-09-24 21:58             ` Johannes Schindelin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).