aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLiu Aleaxander <Aleaxander@gmail.com>2009-06-20 15:34:12 +0800
committerLiu Aleaxander <Aleaxander@gmail.com>2009-06-20 15:34:12 +0800
commitf6528168f826c8e1d8acf45f8b1443000314be58 (patch)
tree6296bea1b32c7f44b80215731469ee207d1da6a1
parent6a0cf5f86ec1efd9ea05e6449f7ec0f97d28c594 (diff)
downloadsyslinux-f6528168f826c8e1d8acf45f8b1443000314be58.tar.gz
syslinux-f6528168f826c8e1d8acf45f8b1443000314be58.tar.xz
syslinux-f6528168f826c8e1d8acf45f8b1443000314be58.zip
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.
-rw-r--r--core/diskio.c295
-rw-r--r--core/diskstart.inc10
-rw-r--r--core/ext2.c11
-rw-r--r--core/fs.c33
-rw-r--r--core/include/disk.h27
-rw-r--r--core/include/fs.h47
6 files changed, 361 insertions, 62 deletions
diff --git a/core/diskio.c b/core/diskio.c
new file mode 100644
index 00000000..8b33d8a7
--- /dev/null
+++ b/core/diskio.c
@@ -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;
+}
diff --git a/core/diskstart.inc b/core/diskstart.inc
index a08aa281..e6cd2ab1 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 ae300218..0ddda8ee 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 d2a82c36..35a11e9e 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 1eb9f038..376052dd 100644
--- a/core/include/disk.h
+++ b/core/include/disk.h
@@ -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 */
diff --git a/core/include/fs.h b/core/include/fs.h
index 0c837bd7..92f3e7fd 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;