From: Liu Aleaxander Date: Sat, 20 Jun 2009 07:34:12 +0000 (+0800) Subject: Core:EXTLINUX: applies the path from hpa to EXTLINUX X-Git-Tag: syslinux-4.00-pre1~20 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=f6528168f826c8e1d8acf45f8b1443000314be58;p=profile%2Fivi%2Fsyslinux.git Core:EXTLINUX: applies the path from hpa to EXTLINUX 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. --- diff --git a/core/diskio.c b/core/diskio.c new file mode 100644 index 0000000..8b33d8a --- /dev/null +++ b/core/diskio.c @@ -0,0 +1,295 @@ +#include +#include +#include +#include +#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; +} diff --git a/core/diskstart.inc b/core/diskstart.inc index a08aa28..e6cd2ab 100644 --- a/core/diskstart.inc +++ b/core/diskstart.inc @@ -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 diff --git a/core/ext2.c b/core/ext2.c index ae30021..0ddda8e 100644 --- a/core/ext2.c +++ b/core/ext2.c @@ -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; diff --git a/core/fs.c b/core/fs.c index d2a82c3..35a11e9 100644 --- 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); } diff --git a/core/include/disk.h b/core/include/disk.h index 1eb9f03..376052d 100644 --- a/core/include/disk.h +++ b/core/include/disk.h @@ -1,15 +1,40 @@ #ifndef DISK_H #define DISK_H +#include #include +#include #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 */ diff --git a/core/include/fs.h b/core/include/fs.h index 0c837bd..92f3e7f 100644 --- a/core/include/fs.h +++ b/core/include/fs.h @@ -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;