Core:EXTLINUX: applies the path from hpa to EXTLINUX
authorLiu Aleaxander <Aleaxander@gmail.com>
Sat, 20 Jun 2009 07:34:12 +0000 (15:34 +0800)
committerLiu Aleaxander <Aleaxander@gmail.com>
Sat, 20 Jun 2009 07:34:12 +0000 (15:34 +0800)
I just added it to EXTLINUX first, and I will consider if I should added it
SYLSINUX and ISOLINUX and so on tomorrow (I'm not sure for now).

And I added a new structure disk to store the disk specific information, so
some changes happened on the vfs-like interface. Hope you will like it, hap;)

the new code broken on the non-standard disk geometry, like the exttest package
given by hpa.

core/diskio.c [new file with mode: 0644]
core/diskstart.inc
core/ext2.c
core/fs.c
core/include/disk.h
core/include/fs.h

diff --git a/core/diskio.c b/core/diskio.c
new file mode 100644 (file)
index 0000000..8b33d8a
--- /dev/null
@@ -0,0 +1,295 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <klibc/compiler.h>
+#include "core.h"
+#include "fs.h"
+#include "disk.h"
+
+#define RETRY_COUNT 6
+
+extern uint16_t MaxTransfer;
+
+static int chs_rdwr_sectors(struct disk *disk, void *buf,
+                           sector_t lba, size_t count, bool is_write)
+{
+    char *ptr = buf;
+    char *tptr;
+    size_t chunk, freeseg;
+    int sector_size  = disk->sector_size;
+    int sector_shift = disk->sector_shift;
+    uint32_t xlba = lba;       /* Truncated LBA (CHS is << 2 TB) */
+    uint32_t t;
+    uint16_t c, h, s;
+    com32sys_t ireg, oreg;
+    size_t done = 0;
+    size_t bytes;
+    int retry;
+
+    memset(&ireg, 0, sizeof ireg);
+
+    ireg.eax.b[1] = 0x02 + is_write;
+    ireg.edx.b[0] = disk->disk_number;
+
+    while (count) {
+       chunk = count;
+       if (chunk > MaxTransfer)
+           chunk = MaxTransfer;
+
+       freeseg = (0x10000 - ((size_t)ptr & 0xffff)) >> sector_shift;
+
+       if ((size_t)buf <= 0xf0000 && freeseg) {
+           /* Can do a direct load */
+           tptr = ptr;
+       } else {
+           /* Either accessing high memory or we're crossing a 64K line */
+           tptr = core_xfer_buf;
+           freeseg = (0x10000 - ((size_t)tptr & 0xffff)) >> sector_shift;
+       }
+       if (chunk > freeseg)
+           chunk = freeseg;
+
+       bytes = chunk << sector_shift;
+
+       if (tptr != ptr && is_write)
+           memcpy(tptr, ptr, bytes);
+
+       s = xlba % disk->s;
+       t = xlba / disk->s;
+       h = t % disk->h;
+       c = t / disk->h;
+
+       ireg.eax.b[0] = chunk;
+       ireg.ecx.b[1] = c;
+       ireg.ecx.b[0] = ((c & 0x300) >> 2) | (s+1);
+       ireg.edx.b[1] = h;
+       ireg.ebx.w[0] = OFFS(tptr);
+       ireg.es       = SEG(tptr);
+
+       retry = RETRY_COUNT;
+
+       for (;;) {
+           __intcall(0x13, &ireg, &oreg);
+           if (!(oreg.eflags.l & EFLAGS_CF))
+               break;
+           if (retry--)
+               continue;
+           chunk >>= 1;
+           if (chunk) {
+               MaxTransfer = chunk;
+               retry = RETRY_COUNT;
+               continue;
+           }
+           return done;        /* Failure */
+       }
+
+       bytes = chunk << sector_shift;
+
+       if (tptr != ptr && !is_write)
+           memcpy(ptr, tptr, bytes);
+
+       ptr   += bytes;
+       xlba  += chunk;
+       count -= chunk;
+       done  += chunk;
+    }
+    return done;
+}
+
+struct edd_rdwr_packet {
+    uint16_t size;
+    uint16_t blocks;
+    far_ptr_t buf;
+    uint64_t lba;
+};
+
+static int edd_rdwr_sectors(struct disk *disk, void *buf,
+                           sector_t lba, size_t count, bool is_write)
+{
+    static __lowmem struct edd_rdwr_packet pkt;
+    char *ptr = buf;
+    char *tptr;
+    size_t chunk, freeseg;
+    int sector_size  = disk->sector_size;
+    int sector_shift = disk->sector_shift;
+    com32sys_t ireg, oreg;
+    size_t done = 0;
+    size_t bytes;
+    int retry;
+
+    memset(&ireg, 0, sizeof ireg);
+
+    ireg.eax.b[1] = 0x42 + is_write;
+    ireg.edx.b[0] = disk->disk_number;
+    ireg.ds       = SEG(&pkt);
+    ireg.esi.w[0] = OFFS(&pkt);
+
+    while (count) {
+       chunk = count;
+       if (chunk > MaxTransfer)
+           chunk = MaxTransfer;
+
+       freeseg = (0x10000 - ((size_t)ptr & 0xffff)) >> sector_shift;
+
+       if ((size_t)buf <= 0xf0000 && freeseg) {
+           /* Can do a direct load */
+           tptr = ptr;
+       } else {
+           /* Either accessing high memory or we're crossing a 64K line */
+           tptr = core_xfer_buf;
+           freeseg = (0x10000 - ((size_t)tptr & 0xffff)) >> sector_shift;
+       }
+       if (chunk > freeseg)
+           chunk = freeseg;
+
+       bytes = chunk << sector_shift;
+
+       if (tptr != ptr && is_write)
+           memcpy(tptr, ptr, bytes);
+
+       pkt.size   = sizeof pkt;
+       pkt.blocks = chunk;
+       pkt.buf    = FAR_PTR(tptr);
+       pkt.lba    = lba;
+
+       retry = RETRY_COUNT;
+
+       for (;;) {
+           __intcall(0x13, &ireg, &oreg);
+           if (!(oreg.eflags.l & EFLAGS_CF))
+               break;
+           if (retry--)
+               continue;
+           chunk >>= 1;
+           if (chunk) {
+               MaxTransfer = chunk;
+               retry = RETRY_COUNT;
+               continue;
+           }
+           /*** XXX: Consider falling back to CHS here?! ***/
+           return done;        /* Failure */
+       }
+
+       bytes = chunk << sector_shift;
+
+       if (tptr != ptr && !is_write)
+           memcpy(ptr, tptr, bytes);
+
+       ptr   += bytes;
+       lba   += chunk;
+       count -= chunk;
+       done  += chunk;
+    }
+    return done;
+}
+struct edd_disk_params {
+    uint16_t  len;
+    uint16_t  flags;
+    uint32_t  phys_c;
+    uint32_t  phys_h;
+    uint32_t  phys_s;
+    uint64_t  sectors;
+    uint16_t  sector_size;
+    far_ptr_t dpte;
+    uint16_t  devpath_key;
+    uint8_t   devpath_len;
+    uint8_t   _pad1[3];
+    char      bus_type[4];
+    char      if_type[8];
+    uint8_t   if_path[8];
+    uint8_t   dev_path[8];
+    uint8_t   _pad2;
+    uint8_t   devpath_csum;
+} __attribute__((packed));
+
+static inline bool is_power_of_2(uint32_t x)
+{
+    return !(x & (x-1));
+}
+
+int ilog2(uint32_t num)
+{
+    int i = 0;
+    
+    if (!is_power_of_2(num)) {
+        printf("ERROR: the num must be power of 2 when conveting to log2\n");
+        return 0;
+    }
+    while (num >>= 1)
+        i++;
+    return i;
+}
+
+void dump_disk(struct disk *disk)
+{
+    printf("drive number: 0x%x\n", disk->disk_number);
+    printf("disk type: %s(%d)\n", disk->type ? "EDD" : "CHS", disk->type);
+    printf("sector size: %d(%d)\n", disk->sector_size, disk->sector_shift);
+    printf("h: %d\ts: %d\n", disk->h, disk->s);
+    printf("offset: %d\n", disk->part_start);
+    printf("%s\n", disk->rdwr_sectors == edd_rdwr_sectors ? "EDD_RDWR_SECTORS" :
+           "CHS_RDWR_SECTORS");
+}
+
+struct disk *disk_init(uint8_t devno, bool cdrom, sector_t part_start,
+                       uint16_t bsHeads, uint16_t bsSecPerTrack)
+{
+    static struct disk disk;
+    static __lowmem struct edd_disk_params edd_params;
+    com32sys_t ireg, oreg;
+    bool ebios = cdrom;
+    int sector_size = cdrom ? 2048 : 512;
+
+    memset(&ireg, 0, sizeof ireg);
+
+    /* Get EBIOS support */
+    ireg.eax.b[1] = 0x41;
+    ireg.ebx.w[0] = 0x55aa;
+    ireg.edx.b[0] = devno;
+    ireg.eflags.b[0] = 0x3;    /* CF set */
+
+    __intcall(0x13, &ireg, &oreg);
+
+    if (cdrom || (!(oreg.eflags.l & EFLAGS_CF) &&
+                 oreg.ebx.w[0] == 0xaa55 && (oreg.ecx.b[0] & 1))) {
+       /* Query EBIOS parameters */
+       ireg.eax.b[1] = 0x48;
+       ireg.ds = SEG(&edd_params);
+       ireg.esi.w[0] = OFFS(&edd_params);
+       __intcall(0x13, &ireg, &oreg);
+
+       if (!(oreg.eflags.l & EFLAGS_CF) && oreg.eax.b[1] == 0) {
+           ebios = true;
+           if (edd_params.sector_size >= 512 &&
+               is_power_of_2(edd_params.sector_size))
+               sector_size = edd_params.sector_size;
+       }
+    }
+
+    /* CBIOS parameters */
+    disk.h = bsHeads;
+    disk.s = bsSecPerTrack;
+
+    if ((int8_t)devno < 0) {
+       /* Get hard disk geometry from BIOS */
+
+       ireg.eax.b[1] = 0x08;
+       __intcall(0x13, &ireg, &oreg);
+
+       if (!(oreg.eflags.l & EFLAGS_CF)) {
+           disk.h = oreg.edx.b[1] + 1;
+           disk.s = oreg.ecx.b[0] & 63;
+       }
+    }
+
+    disk.disk_number   = devno;
+    disk.type          = ebios;
+    disk.sector_size   = sector_size;
+    disk.sector_shift  = ilog2(sector_size);
+    disk.part_start    = part_start;
+    disk.rdwr_sectors  = ebios ? edd_rdwr_sectors : chs_rdwr_sectors;
+
+    dump_disk(&disk);
+
+    return &disk;
+}
index a08aa28..e6cd2ab 100644 (file)
@@ -455,6 +455,7 @@ bailmsg:    db 'Boot error', 0Dh, 0Ah, 0
                zb 1F8h-($-$$)
 
 FirstSector    dd 0xDEADBEEF                   ; Location of sector 1
+               global MaxTransfer
 MaxTransfer    dw 0x007F                       ; Max transfer size
 
 ; This field will be filled in 0xAA55 by the installer, but we abuse it
@@ -713,7 +714,8 @@ expand_super:
 %include "init.inc"
 %include "cpuinit.inc"
 
-
+               
+               pushad
 %if IS_PXELINUX
              extern pxe_fs_ops
              mov eax,pxe_fs_ops
@@ -729,7 +731,11 @@ expand_super:
              mov eax,iso_fs_ops
  %endif
              mov dl,[DriveNumber]
+               mov dh,0               ; we are boot from disk not CDROM
              mov ecx,[bsHidden]
              mov ebx,[bsHidden+4]
+               mov si,[bsHeads]
+               mov di,[bsSecPerTrack]
 %endif
-              pm_call fs_init
+               pm_call fs_init
+               popad
index ae30021..0ddda8e 100644 (file)
@@ -442,7 +442,8 @@ inline struct ext2_dir_entry *ext2_next_entry(struct ext2_dir_entry *p)
  * n ext2 block pointer, i.e. anything *except the superblock
  *
  */
-void getlinsec_ext(char *buf, sector_t sector, int sector_cnt)
+void getlinsec_ext(struct fs_info *fs, char *buf, 
+                   sector_t sector, int sector_cnt)
 {
     int ext_cnt = 0;
     
@@ -454,7 +455,7 @@ void getlinsec_ext(char *buf, sector_t sector, int sector_cnt)
     
     sector += ext_cnt;
     sector_cnt -= ext_cnt;
-    read_sectors(buf, sector, sector_cnt);
+    fs->fs_dev->disk->rdwr_sectors(fs->fs_dev->disk, buf, sector, sector_cnt, 0);
 }
 
 /**
@@ -516,7 +517,7 @@ uint32_t ext2_getfssec(struct fs_info *fs, char *buf,
         printf("You are reading stores at sector --0x%x--0x%x\n", 
                frag_start, frag_start + con_sec_cnt -1);
 #endif        
-        getlinsec_ext(buf, frag_start, con_sec_cnt);
+        getlinsec_ext(fs, buf, frag_start, con_sec_cnt);
         buf += con_sec_cnt << 9;
         file->file_sector += con_sec_cnt;  /* next sector index */
     }while(sectors);
@@ -748,10 +749,10 @@ void ext2_load_config(com32sys_t *regs)
 /**
  * init. the fs meta data, return the block size bits.
  */
-int ext2_fs_init(void)
+int ext2_fs_init(struct fs_info *fs)
 {
     /* read the super block */
-    read_sectors((char *)&sb, 2, 2);
+    fs->fs_dev->disk->rdwr_sectors(fs->fs_dev->disk, (void *)&sb, 2, 2, 0);
     
     ClustByteShift = sb.s_log_block_size + 10;
     ClustSize = 1 << ClustByteShift;
index d2a82c3..35a11e9 100644 (file)
--- a/core/fs.c
+++ b/core/fs.c
@@ -7,7 +7,6 @@
 /* The this fs pointer */
 struct fs_info *this_fs;
 struct fs_info fs;
-struct device dev;
 
 
 void load_config(com32sys_t *regs)
@@ -105,12 +104,12 @@ uint8_t detect_edd(uint8_t device_num)
 /* 
  * initialize the device structure 
  */
-void device_init(struct device *dev, uint8_t device_num, sector_t offset)
+struct device * device_init(uint8_t devno, bool cdrom, sector_t part_start,
+                            uint16_t bsHeads, uint16_t bsSecPerTrack)
 {
-    dev->device_number = device_num;
-    dev->part_start = offset;
+    static struct device dev;
 
-    dev->type = detect_edd(device_num);
+    dev.disk = disk_init(devno, cdrom, part_start, bsHeads, bsSecPerTrack);
         
     /* 
      * check if we use cache or not, for now I just know ISO fs 
@@ -118,21 +117,23 @@ void device_init(struct device *dev, uint8_t device_num, sector_t offset)
      * it correctly.
      *
      */    
-    if ( USE_CACHE(dev->device_number) ) {
+    if ( USE_CACHE(dev.disk->disk_number) ) {
         /* I can't use __lowmem here, 'cause it will cause the error:
            "auxseg/lowmem region collides with xfer_buf_seg" */
         //static __lowmem char cache_buf[65536];
-        dev->cache_data = core_cache_buf;
-        dev->cache_size = sizeof core_cache_buf;
+        dev.cache_data = core_cache_buf;
+        dev.cache_size = sizeof core_cache_buf;
     } else 
-        dev->cache_data = NULL;
+        dev.cache_data = NULL;
+
+    return &dev;
 }
 
 
 /* debug function */
 void dump_dev(struct device *dev)
 {
-    printf("device type:%s\n", dev->type ? "CHS" : "EDD");
+    printf("device type:%s\n", dev->disk->type ? "EDD" : "CHS");
     printf("cache_data: %p\n", dev->cache_data);
     printf("cache_head: %p\n", dev->cache_head);
     printf("cache_block_size: %d\n", dev->cache_block_size);
@@ -153,18 +154,18 @@ void fs_init(com32sys_t *regs)
     int blk_shift;
     struct fs_ops *ops = (struct fs_ops*)regs->eax.l;
     
-    device_init(&dev, regs->edx.b[0], regs->ecx.l);
-    
     /* set up the fs stucture */    
     fs.fs_name = ops->fs_name;
     fs.fs_ops = ops;
-    fs.fs_dev = &dev;
+    fs.fs_dev = device_init(regs->edx.b[0], regs->edx.b[1], regs->ecx.l,
+                            regs->esi.w[0], regs->edi.w[0]);
     this_fs = &fs;
 
     /* invoke the fs-specific init code */
-    blk_shift = fs.fs_ops->fs_init();    
+    blk_shift = fs.fs_ops->fs_init(&fs);    
 
     /* initialize the cache */
-    if (dev.cache_data)
-        cache_init(&dev, blk_shift);
+    if (fs.fs_dev->cache_data)
+        cache_init(fs.fs_dev, blk_shift);
+    dump_dev(fs.fs_dev);
 }
index 1eb9f03..376052d 100644 (file)
@@ -1,15 +1,40 @@
 #ifndef DISK_H
 #define DISK_H
 
+#include <stddef.h>
 #include <stdint.h>
+#include <stdbool.h>
 
 #define SECTOR_SHIFT     9
 #define SECTOR_SIZE      (1 << SECTOR_SHIFT)
 
-typedef uint64_t sector_t;
+/* I do want it be simple for now */
+typedef uint32_t sector_t;
 typedef uint32_t block_t;
 
+
+/*
+ * struct disk: contains the information about a specific disk and also
+ * contains the I/O function.
+ */
+struct disk {
+    uint8_t  disk_number;  /* in BIOS style */
+    uint8_t  type;         /* CHS or EDD */
+    uint16_t sector_size;  /* gener512B or 2048B */
+    uint8_t  sector_shift;
+    
+    uint8_t h, s;          /* CHS geometry */
+    uint8_t pad;
+
+    sector_t part_start;   /* the start address of this partition(in sectors) */
+    
+    int (*rdwr_sectors)(struct disk *, void *, sector_t, size_t, bool);
+};
+
 extern void read_sectors(char *, sector_t, int);
 extern void getoneblk(char *, block_t, int);
 
+/* diskio.c */
+struct disk *disk_init(uint8_t, bool, sector_t, uint16_t, uint16_t);
+
 #endif /* DISK_H */
index 0c837bd..92f3e7f 100644 (file)
@@ -28,7 +28,7 @@ struct fs_ops {
     /* in fact, we use fs_ops structure to find the right fs */
     char *fs_name;
     
-    int      (*fs_init)(void);
+    int      (*fs_init)(struct fs_info *);
     void     (*searchdir)(char *, struct file *);
     uint32_t (*getfssec)(struct fs_info *, char *, void * , int, int *);
     void     (*mangle_name)(char *, char *);
@@ -37,47 +37,18 @@ struct fs_ops {
 };
 
 enum dev_type {CHS, EDD};
-
+    
 /*
- * Struct device should have all the information about a specific disk
- * structure, and contain either a pointer to the metadata cache or 
- * actually contain the cache itself.
- * 
- * All the information in this case is stuff like BIOS device number, 
- * type of access (CHS, EDD, ...), geometry, partition offset, and 
- * sector size.
- * 
- * It would be usefull and much easier to implement the C version getlinsec
- * later(I have not much time to implement it now, so I will leave it for 
- * a while, maybe a long while).
+ * Struct device contains:
+ *     the pointer points to the disk structure,
+ *     the cache stuff.
  */
 struct device {
-    /* the device numger (in BIOS style ) */
-    uint8_t device_number;
-    
-    /* type of access (CHS or EDD ) */
-    uint8_t type;
-
-    /* the sector size, 512B for disk and floppy, 2048B for CD */
-    uint16_t sector_size;
-    uint8_t sector_shift;
-
-    /* CHS geometry */
-    uint8_t h, s;
-    uint8_t pad1;
-
-    /* the start address of this partition(in sectors) */
-    sector_t part_start;
-       
-    int (*rdwr_sectors)(struct device *, void *, sector_t, size_t, bool);
+    struct disk *disk;
 
-    /* 
-     * I think we still need the cache_data filed here, 'cause hpa said 
-     * different device has diffrent cache buffer, and the following filed
-     * are quite for cache parts. 
-     */
-    char*    cache_data;
-    struct  cache_struct *cache_head;
+    /* the cache stuff */
+    char* cache_data;
+    struct cache_struct* cache_head;
     uint16_t cache_block_size;
     uint16_t cache_entries;
     uint32_t cache_size;