sys_execv源码分析

因为最近工作涉及到linux的反汇编,做了一阵子Java搜索引擎的工作,再回到C很不适应,因此借着看源码回忆一点linux的知识,上一篇博文分析了_dl_runtime_resolve的源码,本章从头开始研究在linux下程序装载、运行的全部过程,因此从glibc回到linux的源码,来看看linux的系统调用sys_execv是如何装载程序的,后面会回到glibc陆续分析_dl_start、_dl_main函数的源码。

因为linux源码涉及到的知识太多,本文在尽量保证代码原样的情况下删除一些不重要的代码,并且不过多深入其他知识领域,例如文件系统、负载均衡、安全等等,以后有时间了再分析其他的源码。

fs/exec.c
sys_execv

SYSCALL_DEFINE3(execve,
        const char __user *, filename,
        const char __user *const __user *, argv,
        const char __user *const __user *, envp)
{
    return do_execve(getname(filename), argv, envp);
}

SYSCALL_DEFINE3宏定义展开就是sys_execv函数,getname函数进而调用getname_flags函数,该函数会在内核空间分配一块内存用于存储用户空间传入的文件名filename。

fs/exec.c
sys_execv->do_execve

int do_execve(struct filename *filename,
    const char __user *const __user *__argv,
    const char __user *const __user *__envp)
{
    struct user_arg_ptr argv = { .ptr.native = __argv };
    struct user_arg_ptr envp = { .ptr.native = __envp };
    return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
}

user_arg_ptr是对普通指针的一种封装,用于表示用户空间的指针。这里分别封装了程序的调用参数__argv和环境变量__envp。
AT_FDCWD标志意味着文件的搜索路径不是从进程文件系统的根路径开始,而是从进程当前路径开始,在后面打开文件时会用到。

do_execveat_common函数

fs/exec.c
sys_execv->do_execve->do_execveat_common

static int do_execveat_common(int fd, struct filename *filename,
                  struct user_arg_ptr argv,
                  struct user_arg_ptr envp,
                  int flags)
{
    char *pathbuf = NULL;
    struct linux_binprm *bprm;
    struct file *file;
    struct files_struct *displaced;
    int retval;

    if (IS_ERR(filename))
        return PTR_ERR(filename);

    if ((current->flags & PF_NPROC_EXCEEDED) &&
        atomic_read(&current_user()->processes) > rlimit(RLIMIT_NPROC)) {
        retval = -EAGAIN;
        goto out_ret;
    }

    current->flags &= ~PF_NPROC_EXCEEDED;
    retval = unshare_files(&displaced);

    retval = -ENOMEM;
    bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
    if (!bprm)
        goto out_files;

    retval = prepare_bprm_creds(bprm);
    if (retval)
        goto out_free;

    check_unsafe_exec(bprm);
    current->in_execve = 1;

    file = do_open_execat(fd, filename, flags);
    retval = PTR_ERR(file);
    if (IS_ERR(file))
        goto out_unmark;

    sched_exec();

    bprm->file = file;
    if (fd == AT_FDCWD || filename->name[0] == '/') {
        bprm->filename = filename->name;
    } else {
        ...
    }
    bprm->interp = bprm->filename;

    retval = bprm_mm_init(bprm);
    if (retval)
        goto out_unmark;

    bprm->argc = count(argv, MAX_ARG_STRINGS);
    if ((retval = bprm->argc) < 0)
        goto out;

    bprm->envc = count(envp, MAX_ARG_STRINGS);
    if ((retval = bprm->envc) < 0)
        goto out;

    retval = prepare_binprm(bprm);
    if (retval < 0)
        goto out;

    retval = copy_strings_kernel(1, &bprm->filename, bprm);
    if (retval < 0)
        goto out;

    bprm->exec = bprm->p;
    retval = copy_strings(bprm->envc, envp, bprm);
    if (retval < 0)
        goto out;

    retval = copy_strings(bprm->argc, argv, bprm);
    if (retval < 0)
        goto out;

    retval = exec_binprm(bprm);
    if (retval < 0)
        goto out;

    ...

}

参数user_arg_ptr是对用户空间指针的一种分装,linux内核在特定时候(例如sparse)会检查该指针的合法性。
IS_ERR宏用来检测文件名指针是否合法,判断该指针是否指向内核空间的最后一个页面,即错误页面,如果是则直接返回。
接下来根据PF_NPROC_EXCEEDED标志位检查当前用户的进程数是否超过限制,如果超过限制就不能再创建新的进程了,直接返回。
unshare_files函数用于备份当前进程的文件表至displaced中,即当前进程的files_struct结构,当出错或者返回时用于恢复当前进程的文件表。
接下来创建linux_binprm并分配内存空间,后面就要对该结构进行初始化。
prepare_bprm_creds函数会从当前进程内复制一份cred结构,封装了进程的安全信息。
check_unsafe_exec检查程序执行后是否存在潜在的风险,进而设置linux_binprm的unsafe标志位。
do_open_execat函数内部通过do_filp_open函数打开文件,返回file结构。
sched_exec函数在多核计算机中找到最小负载的CPU,用来执行该二进制文件。
再往下通过bprm_mm_init函数分配新进程的内存空间mm_struct。
然后计算用户和环境变量的字符串个数,分别赋值给bprm的argc和envc。count函数内部会通过get_user函数检查用户空间指针的合法性(根据thread_info中的addr_limit变量进行检查)。
prepare_binprm用于设置进程的授权,并将可执行文件的内容读取到bprm的buf缓存中。
再往下的copy_strings_kernel和copy_strings函数都是讲用户空间的数据拷贝到内核空间,其内部通过kmap系统调用获取内核空间的页面。
最后通过exec_binprm函数开始执行新进程。

kernel/fork.c
sys_execv->do_execve->do_execveat_common->unshare_files

int unshare_files(struct files_struct **displaced)
{
    struct task_struct *task = current;
    struct files_struct *copy = NULL;

    unshare_fd(CLONE_FILES, &copy);
    *displaced = task->files;
    task->files = copy;
    return 0;
}

static int unshare_fd(unsigned long unshare_flags, struct files_struct **new_fdp)
{
    struct files_struct *fd = current->files;

    if ((unshare_flags & CLONE_FILES) &&
        (fd && atomic_read(&fd->count) > 1)) {
        *new_fdp = dup_fd(fd, &error);
    }

    return 0;
}

unshare_files继而通过unshare_fd函数复制当前进程current的文件表files,赋值操作通过dup_fd函数执行,这里就不往下看了。

kernel/cred.c
sys_execv->do_execve->do_execveat_common->prepare_exec_creds

int prepare_bprm_creds(struct linux_binprm *bprm)
{
    bprm->cred = prepare_exec_creds();
    if (likely(bprm->cred))
        return 0;
    return -ENOMEM;
}

struct cred *prepare_exec_creds(void)
{
    return prepare_creds();
}

struct cred *prepare_creds(void)
{
    struct task_struct *task = current;
    const struct cred *old;
    struct cred *new;

    validate_process_creds();

    new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
    if (!new)
        return NULL;

    old = task->cred;
    memcpy(new, old, sizeof(struct cred));

    atomic_set(&new->usage, 1);
    set_cred_subscribers(new, 0);
    get_group_info(new->group_info);
    get_uid(new->user);
    get_user_ns(new->user_ns);

    key_get(new->session_keyring);
    key_get(new->process_keyring);
    key_get(new->thread_keyring);
    key_get(new->request_key_auth);
    new->security = NULL;

    if (security_prepare_creds(new, old, GFP_KERNEL) < 0)
        goto error;
    validate_creds(new);
    return new;

error:
    abort_creds(new);
    return NULL;
}

prepare_exec_creds用于复制当前进程的cred结构,并相应地增加引用计数,验证后返回。linux后面的版本将安全信息封装在cred结构里,例如uid不再和进程结构task_struct粘在一起,而是抽象出来。

fs/exec.c
sys_execv->do_execve->do_execveat_common->check_unsafe_exec

static void check_unsafe_exec(struct linux_binprm *bprm)
{
    struct task_struct *p = current, *t;
    unsigned n_fs;

    if (p->ptrace) {
        if (p->ptrace & PT_PTRACE_CAP)
            bprm->unsafe |= LSM_UNSAFE_PTRACE_CAP;
        else
            bprm->unsafe |= LSM_UNSAFE_PTRACE;
    }

    if (task_no_new_privs(current))
        bprm->unsafe |= LSM_UNSAFE_NO_NEW_PRIVS;

    t = p;
    n_fs = 1;
    spin_lock(&p->fs->lock);
    rcu_read_lock();
    while_each_thread(p, t) {
        if (t->fs == p->fs)
            n_fs++;
    }
    rcu_read_unlock();

    if (p->fs->users > n_fs)
        bprm->unsafe |= LSM_UNSAFE_SHARE;
    else
        p->fs->in_exec = 1;
    spin_unlock(&p->fs->lock);
}

ptrace标志位决定了该进程是否被跟踪。
task_no_new_privs是个宏定义,定义在include/linux/sched.h中,用于检测task_struct结构中atomic_flags的标志位PFA_NO_NEW_PRIVS。
while_each_thread遍历同一个线程组中的其他线程,查找具有相同fs_struct结构的线程,检查fs_struct结构体的使用者计数(即current->fs->users)是否超过线程组中所有线程的数量,若是,则标记bprm->unsafe为LSM_UNSAFE_SHARE,若不是,则标记current->fs->in_exec为1。

fs/exec.c
sys_execv->do_execve->do_execveat_common->do_open_execat

static struct file *do_open_execat(int fd, struct filename *name, int flags)
{
    struct file *file;
    struct open_flags open_exec_flags = {
        .open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC,
        .acc_mode = MAY_EXEC | MAY_OPEN,
        .intent = LOOKUP_OPEN,
        .lookup_flags = LOOKUP_FOLLOW,
    };

    file = do_filp_open(fd, name, &open_exec_flags);

    ...

    return file;
}

因为将要打开的是可执行文件,因此要设置相应的标志位,然后调用do_filp_open打开文件,下面涉及到文件系统的知识了,不往下看了。

kernel/sched/core.c
sys_execv->do_execve->do_execveat_common->sched_exec

void sched_exec(void)
{
    struct task_struct *p = current;
    unsigned long flags;
    int dest_cpu;

    raw_spin_lock_irqsave(&p->pi_lock, flags);
    dest_cpu = p->sched_class->select_task_rq(p, task_cpu(p), SD_BALANCE_EXEC, 0);
    if (dest_cpu == smp_processor_id())
        goto unlock;

    if (likely(cpu_active(dest_cpu))) {
        struct migration_arg arg = { p, dest_cpu };

        raw_spin_unlock_irqrestore(&p->pi_lock, flags);
        stop_one_cpu(task_cpu(p), migration_cpu_stop, &arg);
        return;
    }
unlock:
    raw_spin_unlock_irqrestore(&p->pi_lock, flags);
}

select_task_rq函数选择负载最小的cpu,其内部通过select_task_rq_fair函数进行筛选,最后通过stop_one_cpu函数执行切换。

fs/exec.c
sys_execv->do_execve->do_execveat_common->bprm_mm_init

static int bprm_mm_init(struct linux_binprm *bprm)
{
    struct mm_struct *mm = NULL;
    bprm->mm = mm = mm_alloc();
    __bprm_mm_init(bprm);
    return 0;
}

static int __bprm_mm_init(struct linux_binprm *bprm)
{
    int err;
    struct vm_area_struct *vma = NULL;
    struct mm_struct *mm = bprm->mm;

    bprm->vma = vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);

    down_write(&mm->mmap_sem);
    vma->vm_mm = mm;

    vma->vm_end = STACK_TOP_MAX;
    vma->vm_start = vma->vm_end - PAGE_SIZE;
    vma->vm_flags = VM_SOFTDIRTY | VM_STACK_FLAGS | VM_STACK_INCOMPLETE_SETUP;
    vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
    INIT_LIST_HEAD(&vma->anon_vma_chain);
    insert_vm_struct(mm, vma);

    mm->stack_vm = mm->total_vm = 1;
    arch_bprm_mm_init(mm, vma);
    up_write(&mm->mmap_sem);
    bprm->p = vma->vm_end - sizeof(void *);
    return 0;
err:
    up_write(&mm->mmap_sem);
    bprm->vma = NULL;
    kmem_cache_free(vm_area_cachep, vma);
    return err;
}

bprm_mm_init函数首先通过mm_alloc创建新进程的mm_struct结构,然后执行__bprm_mm_init继续进行初始化。
__bprm_mm_init函数创建虚拟内存结构vm_area_struct,STACK_TOP_MAX在64位的计算机上定义为

#define TASK_SIZE_MAX   ((1UL << 47) - PAGE_SIZE)

因此是用户空间的结束地址减去一个保护页面PAGE_SIZE,即0x0000,7fff,ffff,f000。
vm_end即虚拟空间的结束地址指向STACK_TOP_MAX。
vm_start即虚拟内存的开始地址指向vm_end向下一个页面的地址,即0x0000,7fff,ffff,e000。
insert_vm_struct将分配的虚拟内存vm_area_struct插入进程的内存管理结构mm_struct中。
然后通过arch_bprm_mm_init函数对具体的计算机类型执行相应的初始化操作。
最后设置堆栈起始指针,大小为将STACK_TOP_MAX减去一个指针的大小,即8个字节,因此最后的地址为0x0000,7fff,ffff,eff8。

fs/exec.c
sys_execv->do_execve->do_execveat_common->prepare_binprm

int prepare_binprm(struct linux_binprm *bprm)
{
    int retval;

    bprm_fill_uid(bprm);
    retval = security_bprm_set_creds(bprm);
    if (retval)
        return retval;
    bprm->cred_prepared = 1;

    memset(bprm->buf, 0, BINPRM_BUF_SIZE);
    return kernel_read(bprm->file, 0, bprm->buf, BINPRM_BUF_SIZE);
}

bprm_fill_uid设置即将运行的进程的uid和gid。security_bprm_set_creds函数设置授权。最后通过kernel_read函数将file中的内容读取到bprm的缓存buf中。

fs/exec.c
sys_execv->do_execve->do_execveat_common->prepare_binprm->bprm_fill_uid

static void bprm_fill_uid(struct linux_binprm *bprm)
{
    struct inode *inode;
    unsigned int mode;
    kuid_t uid;
    kgid_t gid;

    bprm->cred->euid = current_euid();
    bprm->cred->egid = current_egid();

    if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
        return;

    if (task_no_new_privs(current))
        return;

    inode = file_inode(bprm->file);
    mode = READ_ONCE(inode->i_mode);
    if (!(mode & (S_ISUID|S_ISGID)))
        return;

    mutex_lock(&inode->i_mutex);

    mode = inode->i_mode;
    uid = inode->i_uid;
    gid = inode->i_gid;
    mutex_unlock(&inode->i_mutex);

    if (!kuid_has_mapping(bprm->cred->user_ns, uid) ||
         !kgid_has_mapping(bprm->cred->user_ns, gid))
        return;

    if (mode & S_ISUID) {
        bprm->per_clear |= PER_CLEAR_ON_SETID;
        bprm->cred->euid = uid;
    }

    if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
        bprm->per_clear |= PER_CLEAR_ON_SETID;
        bprm->cred->egid = gid;
    }
}

首先设置euid和egid未当前进程的euid和egid。
在文件模式字mode中,有两个位bit10和bit11分别称为设置用户组ID位和设置用户ID位,分别有两个测试常量S_ISGID和S_ISUID与其对应。若此文件为可执行文件:当设置用户组ID位为1,则文件执行时,内核将其进程的有效用户组ID设置为文件的所有组ID。当设置用户ID位为1,则文件执行时,内核将其进程的有效用户ID设置为文件所有者的用户ID。

fs/exec.c
sys_execv->do_execve->do_execveat_common->exec_binprm

static int exec_binprm(struct linux_binprm *bprm)
{
    int ret;

    ...

    ret = search_binary_handler(bprm);

    ...

    return ret;
}

int search_binary_handler(struct linux_binprm *bprm)
{
    struct linux_binfmt *fmt;
    int retval = -ENOENT;
    list_for_each_entry(fmt, &formats, lh) {
        bprm->recursion_depth++;
        retval = fmt->load_binary(bprm);
        put_binfmt(fmt);
        bprm->recursion_depth--;
    }

    return retval;
}

exec_binprm主要调用search_binary_handler处理可执行文件,search_binary_handler函数遍历format格式,对于linux下的elf格式的可执行文件而言,会找到elf_format,调用其load_binary函数,最终其实调用的是load_elf_binary函数,下面开始重点分析这个函数。

load_elf_binary

fs/binfmt_elf.c
load_elf_binary第一部分

static int load_elf_binary(struct linux_binprm *bprm)
{
    struct file *interpreter = NULL; 
    unsigned long load_addr = 0, load_bias = 0;
    int load_addr_set = 0;
    char * elf_interpreter = NULL;
    unsigned long error;
    struct elf_phdr *elf_ppnt, *elf_phdata, *interp_elf_phdata = NULL;
    unsigned long elf_bss, elf_brk;
    int retval, i;
    unsigned long elf_entry;
    unsigned long interp_load_addr = 0;
    unsigned long start_code, end_code, start_data, end_data;
    unsigned long reloc_func_desc __maybe_unused = 0;
    int executable_stack = EXSTACK_DEFAULT;
    struct pt_regs *regs = current_pt_regs();
    struct {
        struct elfhdr elf_ex;
        struct elfhdr interp_elf_ex;
    } *loc;
    struct arch_elf_state arch_state = INIT_ARCH_ELF_STATE;

    loc = kmalloc(sizeof(*loc), GFP_KERNEL);
    if (!loc) {
        retval = -ENOMEM;
        goto out_ret;
    }

    loc->elf_ex = *((struct elfhdr *)bprm->buf);

    retval = -ENOEXEC;
    if (memcmp(loc->elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
        goto out;

    if (loc->elf_ex.e_type != ET_EXEC && loc->elf_ex.e_type != ET_DYN)
        goto out;
    if (!elf_check_arch(&loc->elf_ex))
        goto out;
    if (!bprm->file->f_op->mmap)
        goto out;

    elf_phdata = load_elf_phdrs(&loc->elf_ex, bprm->file);
    if (!elf_phdata)
        goto out;

    ...
}

第一部分代码主要是检查elf的文件头信息。
首先检查e_ident,即前4个字节,魔数。
接下来查看类型e_type是否为ET_EXEC(2)和ET_DYN(3)的其中一种。

#define elf_check_arch(x)           \
    ((x)->e_machine == EM_X86_64)

elf_check_arch检查e_machine 的值是否为EM_X86_64(0x3E)。
load_elf_phdrs函数读取elf文件中的Segment头信息到elf_phdata中。
为了方便说明,下面首先看一下elf64文件的文件头elf64_hdr结构和Segment头elf64_phdr结构。

typedef struct elf64_hdr {
  unsigned char e_ident[EI_NIDENT];
  Elf64_Half e_type;
  Elf64_Half e_machine;
  Elf64_Word e_version;
  Elf64_Addr e_entry;
  Elf64_Off e_phoff;
  Elf64_Off e_shoff;
  Elf64_Word e_flags;
  Elf64_Half e_ehsize;
  Elf64_Half e_phentsize;
  Elf64_Half e_phnum;
  Elf64_Half e_shentsize;
  Elf64_Half e_shnum;
  Elf64_Half e_shstrndx;
} Elf64_Ehdr;

typedef struct elf64_phdr {
  Elf64_Word p_type;
  Elf64_Word p_flags;
  Elf64_Off p_offset;
  Elf64_Addr p_vaddr;
  Elf64_Addr p_paddr;
  Elf64_Xword p_filesz;
  Elf64_Xword p_memsz;
  Elf64_Xword p_align;
} Elf64_Phdr;

结构中成员的具体意义可以到网上查,不一一说明了,下面碰到重要的时会说明。

fs/binfmt_elf.c
load_elf_binary->load_elf_phdrs

static struct elf_phdr *load_elf_phdrs(struct elfhdr *elf_ex,
                       struct file *elf_file)
{
    struct elf_phdr *elf_phdata = NULL;
    int size;

    size = sizeof(struct elf_phdr) * elf_ex->e_phnum;
    elf_phdata = kmalloc(size, GFP_KERNEL);
    kernel_read(elf_file, elf_ex->e_phoff,
                 (char *)elf_phdata, size);

    return elf_phdata;
}

elf文件头的e_phnum表示Segment头的个数,e_phentsize是Segment头的大小,两者的乘积size即所有Segment头的大小和,所以要先分配size大小的内存空间,e_phoff是第一个Segment头在文件中的偏移。接着调用kernel_read从文件中读取Segment信息到elf_phdata中。

fs/binfmt_elf.c
load_elf_binary第二部分

static int load_elf_binary(struct linux_binprm *bprm)
{
    ...

    elf_ppnt = elf_phdata;
    elf_bss = 0;
    elf_brk = 0;

    start_code = ~0UL;
    end_code = 0;
    start_data = 0;
    end_data = 0;

    for (i = 0; i < loc->elf_ex.e_phnum; i++) {
        if (elf_ppnt->p_type == PT_INTERP) {
            if (elf_ppnt->p_filesz > PATH_MAX || 
                elf_ppnt->p_filesz < 2)
                goto out_free_ph;

            elf_interpreter = kmalloc(elf_ppnt->p_filesz,
                          GFP_KERNEL);

            retval = kernel_read(bprm->file, elf_ppnt->p_offset,
                         elf_interpreter,
                         elf_ppnt->p_filesz);

            interpreter = open_exec(elf_interpreter);
            retval = kernel_read(interpreter, 0, bprm->buf,
                         BINPRM_BUF_SIZE);

            loc->interp_elf_ex = *((struct elfhdr *)bprm->buf);
            break;
        }
        elf_ppnt++;
    }

    ...
}

load_elf_binary函数的第二部分主要是查找文件中的解释器信息。
首先遍历所有的Segment头信息,查看其p_type是否为PT_INTERP(3)。
对于解释器信息的Segment头,p_filesz其实是解释器的路径,p_offset是路径信息在elf文件中的偏移,接下来检查该路径信息是否合理。
再往下分配用于存储解释器路径的内存空间elf_interpreter,并通过kernel_read函数从文件中读取路径信息到elf_interpreter中。
然后通过open_exec函数打开解释器,返回一个file结构,open_exec函数内部也是调用do_open_execat函数打开文件。
通过kernel_read函数将解释器的头部128个字节BINPRM_BUF_SIZE写入bprm的buf结构中,因为之前已经将文件头读取出来,这里覆盖了bprm的buf。
然后将loc的interp_elf_ex设置为解释器的头部在缓存buf中的起始地址。

fs/binfmt_elf.c
load_elf_binary第三部分

static int load_elf_binary(struct linux_binprm *bprm)
{
    ...

    elf_ppnt = elf_phdata;
    for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++)
        switch (elf_ppnt->p_type) {
        case PT_GNU_STACK:
            if (elf_ppnt->p_flags & PF_X)
                executable_stack = EXSTACK_ENABLE_X;
            else
                executable_stack = EXSTACK_DISABLE_X;
            break;

        case PT_LOPROC ... PT_HIPROC:
            retval = arch_elf_pt_proc(&loc->elf_ex, elf_ppnt,
                          bprm->file, false,
                          &arch_state);
            if (retval)
                goto out_free_dentry;
            break;
        }

    ...
}

这部分代码首先通过查找类型为PT_GNU_STACK的Segment,检查堆栈的可执行性,设置在executable_stack中。PT_LOPROC和PT_HIPROC类型的Segment用来提供给特定的计算机体系进行检查,这里不往下看了。

fs/binfmt_elf.c
load_elf_binary第四部分

static int load_elf_binary(struct linux_binprm *bprm)
{
    ...

    if (elf_interpreter) {
        retval = -ELIBBAD;

        if (memcmp(loc->interp_elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
            goto out_free_dentry;

        if (!elf_check_arch(&loc->interp_elf_ex))
            goto out_free_dentry;

        interp_elf_phdata = load_elf_phdrs(&loc->interp_elf_ex,
                           interpreter);

        elf_ppnt = interp_elf_phdata;
        for (i = 0; i < loc->interp_elf_ex.e_phnum; i++, elf_ppnt++)
            switch (elf_ppnt->p_type) {
            case PT_LOPROC ... PT_HIPROC:
                retval = arch_elf_pt_proc(&loc->interp_elf_ex,
                              elf_ppnt, interpreter,
                              true, &arch_state);
                if (retval)
                    goto out_free_dentry;
                break;
            }
    }

    ...
}

因为解释器也是elf文件,被读取到内存中后,这部分代码首先检查解释器的elf文件头信息,然后再次调用load_elf_phdrs函数读取解释器文件的Segment信息,找到类型为PT_LOPROC和PT_HIPROC的Segment以供特定的体系结构检查。

fs/binfmt_elf.c
load_elf_binary第五部分

static int load_elf_binary(struct linux_binprm *bprm)
{
    ...

    flush_old_exec(bprm);

    SET_PERSONALITY2(loc->elf_ex, &arch_state);
    if (elf_read_implies_exec(loc->elf_ex, executable_stack))
        current->personality |= READ_IMPLIES_EXEC;

    if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
        current->flags |= PF_RANDOMIZE;

    setup_new_exec(bprm);
    retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP),
                 executable_stack);
    if (retval < 0)
        goto out_free_dentry;

    current->mm->start_stack = bprm->p;

    ...
}

flush_old_exec主要用来进行新进程地址空间的替换,并删除同线程组中的其他线程。
再往下继续设置进程task_struct的personality。
然后通过setup_new_exec函数对刚刚替换的地址空间进行简单的初始化。
randomize_stack_top对栈的指针进行随机的移动,然后通过setup_arg_pages函数随机调整堆栈的位置。
最后记录栈的起始地址至mm_struct结构中。

fs/exec.c
load_elf_binary->flush_old_exec

int flush_old_exec(struct linux_binprm * bprm)
{
    de_thread(current);
    set_mm_exe_file(bprm->mm, bprm->file);
    exec_mmap(bprm->mm);

    bprm->mm = NULL;
    set_fs(USER_DS);
    current->flags &= ~(PF_RANDOMIZE | PF_FORKNOEXEC | PF_KTHREAD |
                    PF_NOFREEZE | PF_NO_SETAFFINITY);
    flush_thread();
    current->personality &= ~bprm->per_clear;
    return 0;
}

因为即将要替换新进程的地址空间,所以首先通过de_thread函数用来删除同线程组中的其他线程。
set_mm_exe_file函数设置新进程的路径,即mm_struct中的exe_file成员变量。
再往下就通过exec_mmap函数将新进程的地址空间设置为bprm中创建并设置好的地址空间。
flush_thread函数主要用来初始化thread_struct中的TLS元数据信息。
最后设置进程的标志位flags和personality,personality用来兼容linux的旧版本或者BSD等其他版本。

fs/exec.c
load_elf_binary->flush_old_exec->de_thread

static int de_thread(struct task_struct *tsk)
{
    struct signal_struct *sig = tsk->signal;
    struct sighand_struct *oldsighand = tsk->sighand;
    spinlock_t *lock = &oldsighand->siglock;

    if (thread_group_empty(tsk))
        goto no_thread_group;

    if (signal_group_exit(sig)) {
        spin_unlock_irq(lock);
        return -EAGAIN;
    }

    sig->group_exit_task = tsk;
    sig->notify_count = zap_other_threads(tsk);
    if (!thread_group_leader(tsk))
        sig->notify_count--;

    while (sig->notify_count) {
        __set_current_state(TASK_KILLABLE);
        spin_unlock_irq(lock);
        schedule();
        if (unlikely(__fatal_signal_pending(tsk)))
            goto killed;
        spin_lock_irq(lock);
    }
    spin_unlock_irq(lock);

    if (!thread_group_leader(tsk)) {
        ...
    }

    sig->group_exit_task = NULL;
    sig->notify_count = 0;

no_thread_group:
    tsk->exit_signal = SIGCHLD;

    exit_itimers(sig);
    flush_itimer_signals();

    if (atomic_read(&oldsighand->count) != 1) {
        struct sighand_struct *newsighand;
        newsighand = kmem_cache_alloc(sighand_cachep, GFP_KERNEL);
        if (!newsighand)
            return -ENOMEM;

        atomic_set(&newsighand->count, 1);
        memcpy(newsighand->action, oldsighand->action,
               sizeof(newsighand->action));
        rcu_assign_pointer(tsk->sighand, newsighand);
        __cleanup_sighand(oldsighand);
    }
    return 0;

    ...

}

首先通过thread_group_empty函数检查线程组中是否有其他线程,如果没有就直接返回。
接下来通过signal_group_exit函数检查是否已经开始删除线程组,如果是,也直接返回。
zap_other_threads开始执行删除线程组的操作,其内部会向除了本线程外的所有其他线程发送SIGKILL信号。
接下来通过while循环等待其他线程的退出。
如果不是线程组leader,就要等待该leader的退出,省略的代码用来将当前task替换成线程组leader。
再往下为新的进程分配新的sighand_struct结构并初始化。

fs/exec.c
load_elf_binary->setup_new_exec

void setup_new_exec(struct linux_binprm * bprm)
{
    arch_pick_mmap_layout(current->mm);
    current->sas_ss_sp = current->sas_ss_size = 0;

    if (uid_eq(current_euid(), current_uid()) && gid_eq(current_egid(), current_gid()))
        set_dumpable(current->mm, SUID_DUMP_USER);
    else
        set_dumpable(current->mm, suid_dumpable);

    perf_event_exec();
    __set_task_comm(current, kbasename(bprm->filename), true);

    current->mm->task_size = TASK_SIZE;
    if (!uid_eq(bprm->cred->uid, current_euid()) ||
        !gid_eq(bprm->cred->gid, current_egid())) {
        current->pdeath_signal = 0;
    } else {
        would_dump(bprm, bprm->file);
        if (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)
            set_dumpable(current->mm, suid_dumpable);
    }

    current->self_exec_id++;
    flush_signal_handlers(current, 0);
    do_close_on_exec(current->files);
}

既然前面已经替换了新进程的mm_struct结构,下面就要对该结构进行设置。arch_pick_mmap_layout函数对设置了mmap的起始地址和分配函数。
然后更新mm的标志位,通过kbasename函数根据文件路径bprm->filename获得最后的文件名,再调用__set_task_comm函数设置进程的文件路径,最终设置到task_struct的comm变量中。
flush_signal_handlers用于清空信号的处理函数。最后调用do_close_on_exec关闭对应的文件。

arch/x86/mm/mmap.c
load_elf_binary->setup_new_exec->arch_pick_mmap_layout

void arch_pick_mmap_layout(struct mm_struct *mm)
{
    unsigned long random_factor = 0UL;

    if (current->flags & PF_RANDOMIZE)
        random_factor = arch_mmap_rnd();

    mm->mmap_legacy_base = mmap_legacy_base(random_factor);

    if (mmap_is_legacy()) {
        mm->mmap_base = mm->mmap_legacy_base;
        mm->get_unmapped_area = arch_get_unmapped_area;
    } else {
        mm->mmap_base = mmap_base(random_factor);
        mm->get_unmapped_area = arch_get_unmapped_area_topdown;
    }
}

arch_mmap_rnd获得线性区的随机起始地址,其为get_random_int() % (1<<28)。
mmap_legacy_base修改该地址,加上TASK_UNMAPPED_BASE,值为((1UL << 47) - PAGE_SIZE)/3。
最后设置mmap_base地址和get_unmapped_area函数指针,get_unmapped_area函数用于分配虚拟内存。

load_elf_binary->setup_arg_pages

int setup_arg_pages(struct linux_binprm *bprm,
            unsigned long stack_top,
            int executable_stack)
{
    unsigned long ret;
    unsigned long stack_shift;
    struct mm_struct *mm = current->mm;
    struct vm_area_struct *vma = bprm->vma;
    struct vm_area_struct *prev = NULL;
    unsigned long vm_flags;
    unsigned long stack_base;
    unsigned long stack_size;
    unsigned long stack_expand;
    unsigned long rlim_stack;

    stack_top = arch_align_stack(stack_top);
    stack_top = PAGE_ALIGN(stack_top);

    stack_shift = vma->vm_end - stack_top;

    bprm->p -= stack_shift;
    mm->arg_start = bprm->p;
    bprm->exec -= stack_shift;

    ...

    if (stack_shift) {
        shift_arg_pages(vma, stack_shift);
    }

    stack_expand = 131072UL;
    stack_size = vma->vm_end - vma->vm_start;

    rlim_stack = rlimit(RLIMIT_STACK) & PAGE_MASK;
    if (stack_size + stack_expand > rlim_stack)
        stack_base = vma->vm_end - rlim_stack;
    else
        stack_base = vma->vm_start - stack_expand;
    current->mm->start_stack = bprm->p;
    expand_stack(vma, stack_base);
}

传入的参数stack_top添加了随机因子,首先对该stack_top进行页对齐,然后计算位移stack_shift,再将该位移添加到栈的指针bprm->p也即当前参数的存放地址mm->arg_start。省略的部分是对标志位的修改,再往下既然修改了栈的指针,就要通过shift_arg_pages函数修改堆栈对应的虚拟内存了。最后需要通过expand_stack函数拓展栈的大小,默认为stack_expand即4个页面。

fs/binfmt_elf.c
load_elf_binary第六部分

前面已经根据elf文件头进行了相应的初始化和设置工作,并加载了解释器的Segment头,下面这部分代码要查找elf文件中类型为PT_LOAD的Segment,将其装载进内存。

static int load_elf_binary(struct linux_binprm *bprm)
{
    ...

    for(i = 0, elf_ppnt = elf_phdata;
        i < loc->elf_ex.e_phnum; i++, elf_ppnt++) {
        int elf_prot = 0, elf_flags;
        unsigned long k, vaddr;
        unsigned long total_size = 0;

        if (elf_ppnt->p_type != PT_LOAD)
            continue;

        if (unlikely (elf_brk > elf_bss)) {
            unsigned long nbyte;

            retval = set_brk(elf_bss + load_bias,
                     elf_brk + load_bias);
            if (retval)
                goto out_free_dentry;
            nbyte = ELF_PAGEOFFSET(elf_bss);
            if (nbyte) {
                nbyte = ELF_MIN_ALIGN - nbyte;
                if (nbyte > elf_brk - elf_bss)
                    nbyte = elf_brk - elf_bss;
                if (clear_user((void __user *)elf_bss +
                            load_bias, nbyte)) {
                }
            }
        }

        if (elf_ppnt->p_flags & PF_R)
            elf_prot |= PROT_READ;
        if (elf_ppnt->p_flags & PF_W)
            elf_prot |= PROT_WRITE;
        if (elf_ppnt->p_flags & PF_X)
            elf_prot |= PROT_EXEC;

        elf_flags = MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE;

        vaddr = elf_ppnt->p_vaddr;
        if (loc->elf_ex.e_type == ET_EXEC || load_addr_set) {
            elf_flags |= MAP_FIXED;
        } else if (loc->elf_ex.e_type == ET_DYN) {
            load_bias = ELF_ET_DYN_BASE - vaddr;
            if (current->flags & PF_RANDOMIZE)
                load_bias += arch_mmap_rnd();
            load_bias = ELF_PAGESTART(load_bias);
            total_size = total_mapping_size(elf_phdata,
                            loc->elf_ex.e_phnum);
        }

        elf_map(bprm->file, load_bias + vaddr, elf_ppnt,
                elf_prot, elf_flags, total_size);

        if (!load_addr_set) {
            load_addr_set = 1;
            load_addr = (elf_ppnt->p_vaddr - elf_ppnt->p_offset);
            if (loc->elf_ex.e_type == ET_DYN) {
                load_bias += error -
                             ELF_PAGESTART(load_bias + vaddr);
                load_addr += load_bias;
                reloc_func_desc = load_bias;
            }
        }
        k = elf_ppnt->p_vaddr;
        if (k < start_code)
            start_code = k;
        if (start_data < k)
            start_data = k;

        if (BAD_ADDR(k) || elf_ppnt->p_filesz > elf_ppnt->p_memsz ||
            elf_ppnt->p_memsz > TASK_SIZE ||
            TASK_SIZE - elf_ppnt->p_memsz < k) {
            retval = -EINVAL;
            goto out_free_dentry;
        }

        k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz;

        if (k > elf_bss)
            elf_bss = k;
        if ((elf_ppnt->p_flags & PF_X) && end_code < k)
            end_code = k;
        if (end_data < k)
            end_data = k;
        k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz;
        if (k > elf_brk)
            elf_brk = k;
    }

    loc->elf_ex.e_entry += load_bias;
    elf_bss += load_bias;
    elf_brk += load_bias;
    start_code += load_bias;
    end_code += load_bias;
    start_data += load_bias;
    end_data += load_bias;

    set_brk(elf_bss, elf_brk);

    ...
}

首先循环遍历Segment头,查找类型为PT_LOAD的头,
然后根据Segment头的flag标志位设置内存的标志位elf_prot。
接下来如果要加载的文件数据类型为ET_EXEC,则在固定地址上分配虚拟内存,因此要加上MAP_FIXED标志,而如果要加载的数据类型为ET_DYN,则需要从ELF_ET_DYN_BASE地址处开始映射时,在设置了PF_RANDOMIZE标志位时,需要加上arch_mmap_rnd()随机因子,将偏移记录到load_bias中。total_size为计算的需要映射的内存大小。
再往下就通过elf_map函数将文件映射到虚拟内存中。如果是第一次映射,则需要记录虚拟的elf文件装载地址load_addr,如果是ET_DYN类型的数据,需要加上偏移load_bias。
每次映射后,都要修改bss段、代码段、数据段、堆的起始位置,对同一个elf文件而言,start_code向上增长,start_data向下增长,elf_bss向上增长,end_code 向上增长,end_data 向上增长,elf_brk向上增长,因此从虚拟内存中看,从低地址到高地址依次为代码段,数据段,bss段和堆的起始地址。当装载完毕退出循环后需要将这些变量加上偏移load_bias。
最后通过set_brk在elf_bss到elf_brk之间分配内存空间。

fs/binfmt_elf.c
load_elf_binary->elf_map

static unsigned long elf_map(struct file *filep, unsigned long addr,
        struct elf_phdr *eppnt, int prot, int type,
        unsigned long total_size)
{
    unsigned long map_addr;
    unsigned long size = eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr);
    unsigned long off = eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr);
    addr = ELF_PAGESTART(addr);
    size = ELF_PAGEALIGN(size);

    if (!size)
        return addr;

    if (total_size) {
        total_size = ELF_PAGEALIGN(total_size);
        map_addr = vm_mmap(filep, addr, total_size, prot, type, off);
        if (!BAD_ADDR(map_addr))
            vm_munmap(map_addr+size, total_size-size);
    } else
        map_addr = vm_mmap(filep, addr, size, prot, type, off);

    return(map_addr);
}

传入的参数filep是文件指针,addr是即将映射的内存中的虚拟地址,size是文件映像的大小,off是映像在文件中的偏移。elf_map函数主要通过vm_mmap为文件申请虚拟空间并进行相应的映射,然后返回虚拟空间的起始地址map_addr。

fs/binfmt_elf.c
load_elf_binary第七部分

前面一部分将elf文件中类型为PT_LOAD的数据映射到虚拟内存中,这一部分代码采用相同的方法将解释器的数据映射到虚拟内存中,其内部都是通过elf_map函数进行映射,最后将程序的起始地址保存在elf_entry中,这里就不往下分析了。

static int load_elf_binary(struct linux_binprm *bprm)
{
    ...

    if (elf_interpreter) {
        unsigned long interp_map_addr = 0;

        elf_entry = load_elf_interp(&loc->interp_elf_ex,
                        interpreter,
                        &interp_map_addr,
                        load_bias, interp_elf_phdata);

        interp_load_addr = elf_entry;
        elf_entry += loc->interp_elf_ex.e_entry;
        reloc_func_desc = interp_load_addr;
        allow_write_access(interpreter);
        fput(interpreter);
        kfree(elf_interpreter);
    } else {
        elf_entry = loc->elf_ex.e_entry;
    }

    kfree(interp_elf_phdata);
    kfree(elf_phdata);

    ...
}

fs/binfmt_elf.c
load_elf_binary第八部分

static int load_elf_binary(struct linux_binprm *bprm)
{
    ...

    set_binfmt(&elf_format);

    install_exec_creds(bprm);
    create_elf_tables(bprm, &loc->elf_ex,
              load_addr, interp_load_addr);

    current->mm->end_code = end_code;
    current->mm->start_code = start_code;
    current->mm->start_data = start_data;
    current->mm->end_data = end_data;
    current->mm->start_stack = bprm->p;

    if ((current->flags & PF_RANDOMIZE) && (randomize_va_space > 1)) {
        current->mm->brk = current->mm->start_brk =
            arch_randomize_brk(current->mm);
    }

    start_thread(regs, elf_entry, bprm->p);
    ...
}

set_binfmt将elf_format记录到mm_struct的binfmt变量中。
install_exec_creds会设置新进程的cred结构。
create_elf_tables函数在将启动解释器或者程序前,向用户空间的堆栈添加一些额外信息,例如应用程序Segment头的起始地址,入口地址等等。
接着向新进程mm_struct结构中设置前面计算的代码段、数据段、bss段和堆的位置。
再往下根据标志位PF_RANDOMIZE选择是否要为堆起始地址添加随机变量。
最后调用start_thread函数将执行权交给解释器或者应用程序了。

arch/x86/kernel/process_64.c
load_elf_binary->start_thread

void
start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
{
    start_thread_common(regs, new_ip, new_sp,
                __USER_CS, __USER_DS, 0);
}

static void
start_thread_common(struct pt_regs *regs, unsigned long new_ip,
            unsigned long new_sp,
            unsigned int _cs, unsigned int _ss, unsigned int _ds)
{
    loadsegment(fs, 0);
    loadsegment(es, _ds);
    loadsegment(ds, _ds);
    load_gs_index(0);
    regs->ip        = new_ip;
    regs->sp        = new_sp;
    regs->cs        = _cs;
    regs->ss        = _ss;
    regs->flags     = X86_EFLAGS_IF;
    force_iret();
}

传入的参数regs为保存的寄存器,new_ip为解释器或者应用程序的起始代码地址,new_sp为用户空间的堆栈指针。设置完这些变量后,最后通过force_iret强制返回,跳到new_ip指向的地址处开始执行。对于glibc而言,最终就会跳转到_start函数中,下一章开始分析该函数。

XMGR RDISK and UIDE DOS Device Drivers 1 Description XMGR RDISK and UIDE are a group of DOS device drivers for a PC system with an 80386+ CPU and using MS DOS V5 0+ or equivalent XMGR is a DOS driver which works as an "XMS manager" and provides up to 4 GB of XMS memory XMGR has direct support for V3 70+ UMBPCI by Uwe Sieber After UMBPCI enables upper memory XMGR loads there and will provide both upper and XMS memory to a DOS system XMGR uses an "I O Catcher" with UMBPCI Disk diskette I O above 640K is trapped by XMGR and done using a low memory area as UMBPCI "Shadow RAM" cannot do DMA XMGR also runs with JEMM386 or MS DOS EMM386 With EMM drivers XMGR using its B switch first boots in temporary space When upper memory gets enabled by the EMM driver XMGR loads there with no B copies all its boot data and takes over XMS work For a small XMS only system XMGR can also run entirely in low memory RDISK is a DOS RAM disk driver It creates a "fast" disk drive using 2 Megabytes to 2 GIGABYTES of XMS memory It loads as a system driver in CONFIG SYS or it can load later in AUTOEXEC BAT or by user command DOS can copy critical programs and data to the RAMdisk where they will be read or written at memory speed If loaded after CONFIG SYS RDISK files can be assigned to any free DOS drive letter using its : switch RDISK runs with V2 0 or V3 0 XMS managers 60 MB maximum for V2 0 XMS It uses only 656 to 752 bytes of upper memory depending on the system and it can also load in 640K DOS memory RDISK is a simple and small RAMdisk driver for use when resizing or other features are not needed UIDE is a DOS "Universal IDE" caching driver It intercepts "Int 13h" BIOS I O requests and caches data for up to 30 BIOS disks including A: or B: diskettes and including hard disks of any size UIDE can handle 48 bit LBA or 24 bit CHS I O calls by new or old DOS systems It will handle up to 10 "Legacy" or "Native PCI" IDE controllers UIDE "calls the BIOS" for diskettes and intercepts I O for "Int 13h" drivers loaded first thus UIDE caches ALL drives on a DOS system "ASPI" and other "non Int 13h" drivers are unsupported UIDE also detects and runs up to 8 SATA IDE and old "PIO mode" CD DVD drives It can cache CD DVD data and directories for MUCH greater speed and it will play audio CDs and handle "raw" trackwriter input audio and "raw" input is uncached UIDE caches 5 Megabytes to 4 GIGABYTES of data It can set up to four separate caches of its own "Common" User 1" "User 2" and "CD DVD" and it also permits caching requests from user drivers to "bring along" their OWN caches See the UIDE TXT file for full details UIDE uses 4816 bytes of upper DOS memory for 1 to 4 caches of any size All its data or cache tables use XMS memory A "stand alone" UIDE B switch no cache or diskettes can be used in test or diagnostic work and takes 3664 bytes of upper DOS memory If its N2 switch is given UIDE will omit all CD DVD logic saving 1744 bytes Its "CD DVD" cache can then become a 3rd user driver cache if needed UIDE"s H switch will load most of the driver into "free HMA" thus using only 928 bytes of memory 832 "stand alone" The small UHDD and UDVD2 drivers are also available for those who want only non caching drivers or a smaller size driver set for use on "boot" diskettes etc UHDD can cache 26 SATA IDE disks of any size on up to 10 controllers A: or B: diskettes included It now has all four UIDE caches takes 3280 bytes for caching and it can set a 1408 byte "stand alone" driver no cache with its B switch UHDD can put most of its code in HMA space with its H switch taking only 832 bytes 640 "stand alone" UDVD2 handles up to 6 SATA IDE or old PIO mode CD DVD drives it tests up to 10 controllers on loading and takes 2000 bytes or 144 with its H switch Caching by UHDD adds 96 bytes and UDVD2 "shares" UHDD"s I O buffer in XMS for input unsuited to UltraDMA If UHDD is not used UDVD2 will take 128K of XMS as its buffer or it handles such input in PIO mode if XMS is not available UHDD + UDVD2 require only 10K of disk file space and provide most UIDE features The small RDISKON COM program can "re enable" a DOS drive used by RDISK if a "format" command is accidentally issued to it This disables the drive on some systems Entering RDISKON L at a DOS command prompt where L is the desired drive letter A to Z will re enable the drive The small CC COM "Clear Cache" program can help verify files written by UIDE Entering CC at the DOS command prompt sends a BIOS "reset" to all disks making UIDE flush its "Common" cache Data from the disk NOT data still in cache can then be compared to the original output 2 NO Warranties XMGR RDISK and UIDE are offered at no cost "as is" "use at your own risk" and with NO warranties not even the implied warranty of FITNESS for any particular purpose nor of MERCHANTABILITY Driver questions and comments may be addressed to the E Mail of Johnson Lam <johnsonlam hk@gmail com> 3 Revision Summary 19 Oct 14 UHDD now "overlaps" cache work during UltraDMA disk output and the disk sector "gap" at I O end for greater speed UHDD M switch deleted 256 byte binary search buffer is now permanent Other drivers unchanged re dated only 27 Sep 14 UHDD now sets all 4 UIDE caches New UHDD M switch sets a 512 byte binary search buffer for more speed 26 Jan 14 UIDE error handling CD DVD media changes for "stand alone" mode is fixed UHDD offers "Common" & "CD DVD" caches 12 Jan 14 UIDE UD switch deleted many problems UIDE now offers "User 1" and "User 2" caches "Stand alone" UHDD UDVD2 re added for use as needed 12 Dec 13 UHDD UDVD2 deleted low use UIDE N2 dismisses CD DVD logic UIDE C switch added user caching improved 21 Nov 13 UHDD old style "stand alone" driver re added 14 Nov 13 UHDD UDVD2 "private" caches deleted unneeded and unused 25 Sep 13 BAD error fixed in UDVD2 re: locating UHDD MANY Thanks to Japheth for his tests and exact analysis 9 Sep 13 Possible but unlikely UHDD exit errors corrected UDVD2 UIDE now use all 32 CD DVD LBA bits in caching calls 2 Sep 13 Possible UDVD2 "media change" error fixed UHDD N1 size reduced 26 Aug 13 UHDD now has its "Common" cache and handles "private" user driver caches UDVD2 etc can now set a private cache 28 Jul 13 UHDD UIDE binary search buffer and F switch deleted 30 Apr 13 UHDD UDVD2 can now run without XMS lower speed for tests and FreeDOS "scripts" UDVD2 can now do "raw" input 15 Oct 12 UHDD UIDE again detect A: and B: diskettes from BIOS data NOT from "Int 13h" calls that FAIL with an LS 120 drive 2 Aug 12 UHDD "disk only" caching driver added UDVD2 caches CD DVD data if UHDD is also loaded UIDEJR deleted New UD switch in UDVD2 UIDE for CD DVD directory caching 9 Jul 12 UIDE UIDEJR device select error for master + slave CD DVD units on one IDE channel is corrected Many Thanks to Doug Beneway for finding this error 25 Jun 12 UIDE2 deleted: Not enough added speed complex to use 17 Jun 12 UIDE UIDE2 UIDEJR A switch init of 2 "Old IDE" channels and CD audio "Q" status data corrected Many Thanks to Japheth for his research and audio test program 29 May 12 UIDE and UIDE2 check for diskettes via Int 13h avoid DPTE tests if no PCI BIOS let the BIOS do I O for disks with bad DPTE data all re: VirtualBox BUGS 24 Feb 12 UIDE UIDE2 "64K DMA boundary error" fixed may affect only year 2000 chips or older 16 Oct 11 UIDE M switch deleted search buffer is always 512 bytes UIDE SYS back to 7 5K UIDE S dropped UIDE2 improved 7 Oct 11 All UIDE drivers updated to avoid BIOS "DPTE" ERRORS: Bad DPTE data for USB sticks Many Thanks to Daniel Nice 9 Sep 11 UIDE2 re added UIDE S and UIDE2 handle 6 CD DVD drives 22 Jul 11 UIDE E switch added for DOS emulators VirtualBox etc 20 May 11 UIDE S "short" UIDE added for systems with limited HMA 25 Apr 11 BAD "code mods" init error corrected for UIDE UIDEJR and RDISK XMGR not affected 5 Dec 10 UIDE UIDEJR R15 and R63 switches added to handle old DOS "games" Thanks Guillermo Grana Gomez 28 Nov 10 Minor updates: UIDEJR audio track number error corrected XMGR faster in protected mode Added XMGR and UIDE Z 15 Aug 10 UIDE audio track number error corrected Thanks Nagatoshi Uehara 10 Aug 10 UIDE binary search buffer added Using $ in CD DVD names fixed in UIDE UIDEJR Thanks Japheth 4 Jul 10 README file update XMGR UIDE can use "Native IDE" mode same as "Legacy" "Compatibility" for AHCI mainboards 28 Jun 10 XMGR updated for AHCI see the README sec 7 for details 10 Jun 10 UIDE now ignores "removable HARD disks" size reduced 16 Nov 09 UIDE now caches 4 GIGABYTES of data 6 Oct 09 UIDE and UIDEJR H requests HMA use "at the user"s risk" 2 Sep 09 README file updated FreeDOS users who desire full upper memory must omit UMBPCI and load JEMM386 JEMMEX only 23 Jun 09 RDISK now a COM file RDISK : switch RDISKON program added Corrected UIDE CD DVD handling of VDS errors 9 Jun 09 UIDE UIDEJR N3 switch added for no XMS memory Override of D: name by UIDE$ UIDEJR$ added for no CD DVD drives 15 May 09 Added RDISK 6 May 09 Added UIDEJR 1 May 09 Fixed XMGR "Port 92h" logic error Added XMGR PA and PN switches to control use of "Port 92h" 25 Apr 09 XMGR UIDE license and FreeDOS prohibition deleted drivers and sources are again available to all 4 Switch Options XMGR usually needs only its B switch if "booting" with an EMM driver All XMGR switch options are as follows: B Specifies "boot" mode XMGR loads in temporary memory until upper memory is enabled Without B XMGR loads stand alone in low memory or direct to upper memory with UMBPCI See the CONFIG SYS examples in section 5 Mn Specifies a temporary area for loading XMGR in "boot" mode or for UMBPCI upper memory I O before DOS posts a "workspace" buffer Values are: M1 64K M3 192K M5 320K M7 448K M2 128K M4 256K M6 384K M8 512K Without M M5 is assumed and the 320K area will be used NOTE: DOS systems may NOT load at address 0 and may leave temporary data anywhere in memory Mn helps to find a "safe" area for XMGR to use M is ignored if XMGR loads stand alone Nnn Specifies how many XMS "Handles" can be used by DOS programs The value nn may be 48 80 or 128 If N is omitted 48 "Handles" are used A big system doing much XMS work may need 80 or 128 "Handles" PA Specifies use or non use of PS 2 Port 92h logic to handle the PN system"s "A20" line PA indicates "Always" use Port 92h logic PN indicates "Never" use it and handle "A20" via normal keyboard port logic If P is omitted XMGR "asks the BIOS" if the system has Port 92h logic If not XMGR will use normal "A20" logic NOTE: If "A20" was enabled by DOS before XMGR loads XMGR does not handle it at all Tn Specifies the BIOS requests to use in getting extended memory as follows: T0 No "E820h" nor "E801h" requests T1 Memory list requests only Int 15h AX E820h T2 A dual area request only Int 15h AX E801h T3 "E820h" requests first then an "E801h" request T can usually be omitted causing T3 to be assumed In addition XMGR always uses an old 64 MB request to get T0 memory or if the requests denoted by T1 thru T3 are not successful Users may need to test T1 or T2 separately to see if their BIOS takes them A pre 1994 BIOS may not ignore T1 thru T3 correctly and may require T0 instead For old "QHIMEM" users T4 thru T7 may still be used and work the same as T0 thru T3 W Specifies use of the DOS "workspace" buffer for upper memory I O if loading with UMBPCI If W is omitted or if the DOS system does not have proper workspace logic XMGR sets its own buffer in low memory With PC DOS or EDR DOS W must be omitted Without UMBPCI W is ignored Z See Z for UIDE below RDISK uses only S size and : drive letter switches: Sn Specifies a desired RAM disk size in megabytes of XMS memory Values may be any number from 2 to 2047 S1024 or more creates a 1 to 2 GIGABYTE RAM disk If S is omitted or invalid a 25 MB RAM disk is created by default For old V2 0 XMS managers ROM DOS etc only S2 through S60 may be used See section 5 below for more details :L Specifies the DOS drive letter desired to access RDISK files L may be any available drive letter from A to Z e g :N assigns drive N: to all RDISK files If the drive letter is too high or already in use RDISK will abort and users may need "LASTDRIVE " in CONFIG SYS to set up more drives If RDISK is loaded by CONFIG SYS or if : is omitted the next free drive letter will be used UIDE usually needs only a H switch to use HMA space and a S switch to specify its cache size All UIDE switches are as follows: A Specifies ALTERNATE addressing for "legacy IDE" controllers The first legacy controller uses 01E8h 0168h addresses and a second if present uses 01F0h 0170h addresses A is only for "odd" mainboards with REVERSED addressing for the two legacy IDE controllers Without A the first legacy controller uses 01F0h 0170H and a second uses 01E8h 0168h as is normal for most PC mainboards B Requests a "basic" UltraDMA driver for disks and CDs DVDs no caching or diskette handling This may help for tests or diagnostics The B driver can request 128K of XMS as an UltraDMA I O buffer and it can load in the HMA The N2 switch can be given with B to "dismiss" all CD DVD logic Cnn Sets a separate "CD DVD" cache for higher CD DVD performance Values for nn are the same as for the S switch and permit up to 4 GB caches The "CD DVD" cache can be used by any user driver devices on systems with no SATA or IDE CD DVD drives If C is omitted data for requests addressed to the "CD DVD" cache shall go into UIDE"s "Common" cache D: Specifies the "device name" used by the CD DVD Redirector to access CD DVD drives For example: D:CDROM1 D:SANYO1 etc If D: is not given or the name following a D: is missing invalid UDVD1 is set by default If no CD DVD drives were found UIDE$ overrides any D: name for use with FreeDOS autoloader scripts E Makes the driver call the BIOS for any hard disk I O request E avoids setup trouble on some DOS emulators VirtualBox etc that do not emulate all PC hardware logic E also allows using hard disks on 1994 or older PCs which have no PCI EDD BIOS E still caches disk data unlike N1 that removes ALL disk support If B is given E is ignored NOTE Use of E on protected mode systems JEMM386 etc may run VERY slow Many BIOS programs omit DOS "VDS" support for hard disks and in protected mode they must do "PIO mode" transfers not UltraDMA If E is required a PC should be run in real mode UMBPCI etc whenever possible H Loads most of the driver in "free HMA" space UIDE will use only 928 bytes of upper DOS memory 832 when B is given H must not be used with ROM DOS which has no HMA NOTE MS DOS kernels have ERRORS in posting free HMA space which can give CRASHES Specifying H is "At the user"s risk" No such crashes are noted with other DOS systems also HMA usage by UIDE is under 4K bytes Users should still test a PC system before H is given for any serious tasks with these drivers N1 Requests NO hard disk handling by the driver N2 Requests NO CD DVD handling by the driver N2 will dismiss all CD DVD routines and save 1744 bytes N3 Requests no XMS memory N3 sets UIDE"s B "basic" driver N3 requires loading in low memory or UIDE aborts N3 can LOSE much speed as misaligned or other I O not suited to UltraDMA requires "calling the BIOS" for disks or using "PIO mode" for CD DVD drives N4 See Z below Q Awaits a "data request" before doing UltraDMA disk transfers Q is for "old" systems and may be used only if the driver loads O K but seems unable to transfer data Q must be OMITTED with SATA to IDE adapters from Sabrent and others since they may not emulate "data request" from SATA disks Q does not affect CD DVD drives R15 Sets the driver"s XMS memory at 16 or 64 MB R15 reserves R63 15 MB of XMS and R63 reserves 63 MB of XMS for DOS game programs that require XMS memory below 16 or 64 MB The drivers must be able to reserve this memory reserve their own XMS above that and "free" the 15 63 MB XMS If not the drivers display "XMS init error" and abort R15 or R63 need the drivers to load after the XMS manager XMGR HIMEMX etc so another driver cannot take any XMS first and the reserved XMS is just beyond the HMA See section 7 below for further details Snn Specifies the desired "Common" cache size in megabytes of XMS memory UIDE"s "Common" cache holds data for hard disks diskettes and CD DVD drives when C above is not given Values for S can be 5 15 25 40 50 or any number from 80 to 4093 S1024 and up sets a 1 to 4 GIGABYTE cache Suggested S values are Below 128 MB memory: Use S5 S15 S25 or S40 With 128 MB memory: Use S25 S40 S50 or S80 With 256 MB memory: Use S80 up to S127 With 512 MB memory: Use S160 up to S255 With 1 GB memory: Use S320 up to S511 With 2 GB memory: Use S640 up to S1023 With 4 GB memory: Use S1280 up to S3072 Small systems may prefer S25 or S50 which set 1600 cache blocks and are more efficient If S is omitted invalid an 80 MB cache is set Except for 25 or 50 values below 80 are cut to 40 15 or 5 MB The drivers display "XMS init error" and abort when not enough XMS memory is free If so a smaller cache must be requested For older V2 0 XMS managers ROM DOS etc only S5 to S50 may be used UX Disables all CD DVD UltraDMA even for drives that can do it "PIO mode" then handles all CD DVD I O Except for a few unusual drives by Sony etc which do not follow all ATAPI "rules" UX is rarely needed UX does not affect hard disks Xnn Sets a separate "User 1" cache for user drivers Values for nn are the same as for S above If X is omitted data for requests addressed to the "User 1" cache shall go into UIDE"s "Common" cache Ynn Sets a separate "User 2" cache for user drivers Values for nn are the same as for S above If Y is omitted data for requests addressed to the "User 2" cache shall go into UIDE"s "Common" cache Z For XMGR UIDE UHDD limits XMS moves to 2K byte sections not 64K when in protected mode Z is unneeded for JEMM386 JEMMEX MS DOS EMM386 or real mode UMBPCI If other EMM VCPI or DPMI drivers are used systems must be tested to see if Z is required BAD schemes that allow not enough interrupts during XMS moves can still be in use UIDE"s old N4 switch works the same and can still be used The "stand alone" UHDD ignores N4 or Z and will call the XMS manager to do its XMS moves UHDD usually needs only a H switch to load in HMA space also C S X or Y switches to specify cache sizes A summary of all UHDD switches is as follows: A Sets ALTERNATE addressing for "Legacy" IDE controllers same as UIDE A above Rarely necessary B Requests a 1408 byte "stand alone" driver no caching same as UIDE B above Cnn Sets a "CD DVD" cache size for UDVD2 use same values as for UIDE S above If C is omitted or invalid CD DVD data will go in UHDD"s "Common" cache E Makes the driver "call the BIOS" for hard disk I O requests same as UIDE E above E dismisses UltraDMA disk logic and saves 496 bytes H Loads all but 832 bytes of the driver 640 with B into HMA space See the note for UIDE H above Q Awaits "data request" before beginning UltraDMA I O with old controllers same as UIDE Q above Rarely necessary R15 Reserves 15 MB or 63 MB of XMS for old DOS "game" programs R63 same as UIDE R above Rarely necessary Snn Sets a "Common" cache size same values as UIDE S above Xnn Sets the "User 1" cache size same values for UIDE S above If X is omitted invalid "User 1" data will go in UHDD"s "Common" cache Ynn Sets the "User 2" cache size same values for UIDE S above If Y is omitted invalid "User 2" data will go in UHDD"s "Common" cache Z See Z above UDVD2 normally needs only a H switch to use HMA space and a D: switch to specify a driver "device name" A summary of all UDVD2 switches is as follows: A Sets ALTERNATE addressing for "Legacy" IDE controllers same as UIDE A above Rarely necessary D: Sets a "device name" used by the CD DVD Redirector to access CD DVD drives same as UIDE D: above H Puts all but 144 bytes of the driver in HMA space See the note for UIDE H above Rnn Reserves 15 MB or 63 MB of XMS for old DOS "game" programs same as UIDE R above Rarely necessary UX Disables CD DVD UltraDMA same as UIDE UX above Rarely necessary For all switches in each driver a dash may replace the slash and lower case letters may be used if desired 5 Setup and Configuration XMGR RDISK and UIDE are all loaded using the CONFIG SYS file Your CONFIG SYS should have command lines similar to the following examples: DEVICE C: DOSDVRS XMGR SYS N128 B DEVICEHIGH C: DRIVERS RDISK COM S500 DEVICEHIGH C: SYSTEM UIDE SYS D:TOSHIBA1 S511 H DEVICEHIGH C: USERDVRS UHDD SYS S500 C80 H DEVICEHIGH C: MYDVRS UDVD2 SYS D:BLURAY1 H Note that "Int 13h" BIOS drivers must be loaded first so UIDE UHDD can intercept and cache their DOS Int 13h calls Also note that any user drivers that call UIDE to do caching must be loaded after UIDE so they will "find" UIDE in memory and can "link" to it This also applies if UHDD followed by UDVD2 are used in place of UIDE See the CONFIG SYS examples below With V3 70+ UMBPCI and XMGR a "boot" procedure is not needed UMBPCI loads first to enable upper memory then XMGR loads to offer it and XMS to DOS then other drivers may load For V6 22 V7 10 MS DOS JEMM386 can also be loaded to offer extra upper memory in the "video graphics" areas or if other JEMM386 features are desired NOTE: FreeDOS and some other DOS variants will NOT "add up" the memory found by both UMBPCI and JEMM386 like MS DOS does FreeDOS users who want extra upper memory or other items must omit UMBPCI and load JEMMEX or HIMEMX JEMM386 per their instructions or load XMGR JEMM386 as shown in the 3rd example below An example CONFIG SYS file using V3 70+ UMBPCI and XMGR is as follows: SHELL C: DOS COMMAND COM C: DOS E:512 P DEVICE C: BIN UMBPCI SYS DEVICE C: BIN XMGR SYS W DOS HIGH UMB DEVICE C: BIN JEMM386 EXE I B000 B7FF X C800 EFFF NOEMS ;Optional Int 13h drivers cached by UIDE load now DEVICEHIGH C: BIN UIDE SYS D:CDROM1 S511 C250 H ;Or UHDD plus ; UDVD2 here User drivers that call UIDE load now DEVICEHIGH C: BIN RDISK COM S250 ;Optional Etc XMGR can be used "stand alone" on a small XMS only system It must be the first DOS system driver to load and it must load in LOW memory as in the following example: SHELL C: DOS COMMAND COM C: DOS E:512 P DEVICE C: BIN XMGR SYS DOS HIGH Int 13h drivers cached by UHDD load now DEVICE C: BIN UHDD SYS S80 C15 ;Or UIDE in place DEVICE C: BIN UDVD2 SYS ; of UHDD + UDVD2 User drivers that call UHDD load now DEVICE C: BIN RDISK COM S20 ;Optional Etc With JEMM386 and XMGR XMGR loads first in "boot" mode then JEMM386 and then XMGR finally loads in upper memory JEMMEX can also be used and if so XMGR can be omitted An example CONFIG SYS file which uses the XMGR "boot" procedure is shown below Note that in this example UIDE sets a 2 GIGABYTE disk cache plus a 700 Megabyte CD DVD cache SHELL C: DOS COMMAND COM C: DOS E:512 P DEVICE C: BIN XMGR SYS B ; B for "boot" DOS HIGH UMB DEVICE C: DOS JEMM386 EXE I B000 B7FF NOEMS ;Or JEMMEX here DEVICEHIGH C: BIN XMGR SYS ;No "boot" here Int 13h drivers cached by UIDE load now DEVICEHIGH C: BIN UIDE SYS D:MYDVD S2047 C700 H ;Or UHDD plus ; UDVD2 here User drivers that call UIDE load now DEVICEHIGH C: BIN RDISK COM S500 ;Optional Etc After the above drivers are loaded further CONFIG SYS drivers SETVER ANSI SYS etc can then load in any desired order When a specific RDISK drive letter is required RDISK can now be loaded by AUTOEXEC BAT and its : switch can specify any "free" drive letter e g :Q assigns drive Q: for RDISK files Whenever RDISK is used AUTOEXEC BAT should also include commands which copy all RDISK programs and data up to the RAM disk This is required each time DOS loads as XMS memory is LOST when a system shuts down Such copies usually take little time If RDISK and UIDE UHDD are used users must balance how much XMS memory the drivers use RDISK must take no more XMS than its files may need UIDE UHDD can take most remaining XMS for its caches Some XMS memory must be saved for other programs needing it As an example on a 4 GB system RDISK might use 500 MB UIDE UHDD might use 3 GB and 500 MB is free for other programs These values can be adjusted so RDISK holds programs and "fast" data files while UIDE UHDD cache "ordinary" files Properly balanced use of XMS will give a VERY high speed DOS system Please be sure to set each hard disk"s geometry correctly in your BIOS Set it to "Auto" "LBA" or "LBA Assisted" but NOT to "None" "Normal" "CHS" "ECHS" "User Cylinders Heads Sectors" "Revised ECHS" or "Bit Shift" should run but are NOT preferred If a BIOS has a setting like "UltraDMA" or "UDMA Capable" for a disk enable it "Laptop" power saving items like a "drive spin down timeout" should run O K but must be TESTED before use All these drivers allow 7 seconds for a disk or CD DVD drive to spin up after being idle More DRASTIC power saving items like a "drive SHUTDOWN timeout" may require "extra" logic to restart the drive should be DISABLED or driver I O requests may time out Also be sure to use an 80 connector cable for any UltraDMA drive using "mode 3" ATA 44 44 MB sec or higher When cabling a single drive to an IDE channel note that you MUST use both "ends" of the cable NOT an "end" and the middle connector This prevents ERRORS since an unused cable end can pick up "noise" like a RADIO antenna Be sure to enable all CD DVD drive s through the BIOS set up routines A drive that is "disabled" may cause the BIOS to clear all its UltraDMA flags and force the drive into "PIO mode" zero which is terribly SLOW 6 Error Reporting XMGR and UIDE UHDD UDVD2 will return normal XMS and CD DVD error codes as needed They are listed in the "V3 0 XMS Specification" and in the Microsoft "MS DOS CD ROM Extensions 2 1" document Both are available from Microsoft or from other Internet sources UIDE and UHDD work as "BIOS drivers" and return whichever codes are set for diskettes and hard disks handled by the BIOS For their SATA and IDE hard disks UIDE UHDD can post the following error codes: Code 0Fh DMA error CCh Disk is FAULTED 20h Controller busy E0h Hard I O error AAh Disk not ready FFh XMS memory error Many DOS programs display only "Disk Error" messages with NO code thus disk errors may require running a diagnostic to get better information 7 Technical Notes In all of the following notes "UIDE" also applies to UHDD or UDVD2 as necessary The JEMMEX or JEMM386 drivers are now recommended for use with UIDE if using a DOS system that needs their extra upper memory DPMI VCPI logic etc Other EMM drivers are essentially "abandoned" some with never corrected ERRORS and they should NOT be used The "VirtualBox" emulator as of 15 Oct 2012 does not set a "change line available" bit in BIOS byte 0:48Fh for A: and B: diskettes UIDE will IGNORE diskette drives without a "change line" normally 1985 or older as they cannot declare "media changes" i e a NEW diskette was loaded Until "VirtualBox" gets corrected UIDE will NOT run A: or B: diskettes in such an environment UIDE"s R15 or R63 switches DOS "game" programs are for a real mode system using UMBPCI and XMGR Game players like real mode as it gives more speed If protected mode JEMM386 EMM386 is desired UIDE using a R switch must load prior to the "EMM" driver so the XMS reserved by UIDE is just beyond the HMA If using UMBPCI XMGR UIDE and then an EMM driver this works fine But FreeDOS users and others whose DOS systems permit only one XMS provider i e UMBPCI cannot be used must load XMGR HIMEMX first UIDE second into low memory upper memory isn"t yet enabled then JEMM386 EMM386 last Using JEMMEX with UIDE and a R switch is unrecommended JEMMEX must load first and takes some XMS itself which pushes the reserved XMS above its intended 16 64 MB area and a few DOS "games" programs may CRASH UIDE shall NOT include any huge AHCI logic and will run hard disks in "Legacy" "Compatibility" "Native IDE" mode when using AHCI controllers If a "new" AHCI BIOS has no such settings UIDE with a E switch should be able to call the BIOS and use its logic to handle AHCI disks NOTE that much "DOS driver" code is now being omitted in AHCI BIOS programs Thus UIDE should be TESTED before normal use with an AHCI mainboard Also note that CD DVD drives are not supported by an AHCI BIOS for file I O only for "boot" CDs On a system whose AHCI chips can be set for "Legacy" "Compatibility" "Native IDE" mode CD DVD drives should be run from AHCI ports using such modes On mainboards with no such settings UIDE can run CD DVD drives only on the parallel IDE port 80 pin cable or IDE capable "add on" cards from Promise etc that UIDE can "detect" using normal PCI bus logic UIDE handles only "Legacy" or "Native PCI" IDE controllers RAID only chipsets Via VT6420 etc "port multiplier" chips and ADMA chipsets are not currently supported AHCI is supported only through "Legacy" "Compatiblity" or "Native IDE" controller settings or by UIDE "calling the BIOS" as noted above To use UIDE a mainboard BIOS must set SATA and IDE controllers to some form of "IDE" mode not RAID ADMA AHCI for best speed If no "Legacy" "Compatibility" "Native IDE" BIOS setting for disk controllers is provided a Sabrent converter card or similar will let UIDE handle SATA hard disks or CD DVD drives from the parallel port IDE controller channel using full UltraDMA speeds Except if necessary for AHCI it is NOT RECOMMENDED for UIDE to run any DOS disk using only the BIOS Many BIOS programs have no DOS "Virtual DMA" logic If so when an EMM driver JEMM386 etc enables its "V86 protected mode" the BIOS can do only PIO mode transfers and LOSES much speed If needed get SATA to IDE adapters for SATA disks as above or get "Int 13h" disk drivers for SCSI or other disk models UIDE can then handle such disks at full DMA speeds XMGR loads in UMBPCI upper memory BEFORE that memory is declared to the DOS system Memory displays using UMBPCI may not list XMGR since its memory is not part of the DOS memory lists Such memory displays will begin with a block having a 00A7h offset or greater if using 80 or 128 XMS "Handles" The upper memory skipped by this offset contains XMGR The UMBPCI upper memory manager uses system "Shadow RAM" that CANNOT do DMA Newer BIOS programs may use UltraDMA to load programs into upper memory If this is UMBPCI "Shadow RAM" a CRASH will occur To stop this and handle new BIOS programs users should follow these two RULES for running UMBPCI together with XMGR and UIDE UHDD: A The loading "order" for V3 70+ UMBPCI and XMGR shown in section 5 above MUST be used This lets the XMGR "I O Catcher" intercept and process upper memory disk I O until UIDE UHDD loads and takes over disk UltraDMA Old UMBPCI versions or other UMBPCI loading schemes are NOT recommended B When CHS I O is done MS DOS V6 22 or older every disk MUST have valid CHS parameters Otherwise UIDE UHDD and the "I O Catcher" let the BIOS deal with CHS I O If BIOS UltraDMA is not disabled a similar "Shadow RAM" CRASH will occur Some "CD ROM boot" programs handle the CD DVD as a "fake" hard disk and provide incorrect EDD BIOS data for it In scanning for disks to use UIDE may display "EDD BIOS error Unit ignored " then go on searching for more UltraDMA disks Users who did NOT "boot" from CD DVD need to see which disk was passed over and why Users who DID "boot" from CD DVD where all SATA UltraDMA disks were found may IGNORE this message It is caused by an ERROR in the "CD ROM boot" program NOT by a problem with UIDE or its SATA UltraDMA disks Some BIOS programs do not "configure" a mainboard controller if no user drives are on it An unconfigured controller causes UIDE to display "BAD controller" then it goes on looking for others to use If this message is displayed users should verify that each SATA UltraDMA drive was made "active" thru the BIOS set up logic If so "BAD controller" says a chip was not set to both "Bus Master" and "I O Space" modes and the BIOS should be UPDATED ">XMGR RDISK and UIDE DOS Device Drivers 1 Description XMGR RDISK and UIDE are a group of DOS device drivers for a PC system with an 80386+ CPU and using MS DOS V5 0+ or equivalent XMGR is a DOS driver w [更多]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值