📄 mtrr.c
字号:
case MTRR_IF_CYRIX_ARR: case MTRR_IF_CENTAUR_MCR: if ( mtrr_if == MTRR_IF_CENTAUR_MCR ) { /* * FIXME: Winchip2 supports uncached */ if (type != MTRR_TYPE_WRCOMB && (centaur_mcr_type == 0 || type != MTRR_TYPE_UNCACHABLE)) { printk (KERN_WARNING "mtrr: only write-combining%s supported\n", centaur_mcr_type?" and uncacheable are":" is"); return -EINVAL; } } else if (base + size < 0x100) { printk (KERN_WARNING "mtrr: cannot set region below 1 MiB (0x%lx000,0x%lx000)\n", base, size); return -EINVAL; } /* Check upper bits of base and last are equal and lower bits are 0 for base and 1 for last */ last = base + size - 1; for (lbase = base; !(lbase & 1) && (last & 1); lbase = lbase >> 1, last = last >> 1); if (lbase != last) { printk (KERN_WARNING "mtrr: base(0x%lx000) is not aligned on a size(0x%lx000) boundary\n", base, size); return -EINVAL; } break; default: return -EINVAL; } if (type >= MTRR_NUM_TYPES) { printk ("mtrr: type: %u illegal\n", type); return -EINVAL; } /* If the type is WC, check that this processor supports it */ if ( (type == MTRR_TYPE_WRCOMB) && !have_wrcomb () ) { printk (KERN_WARNING "mtrr: your processor doesn't support write-combining\n"); return -ENOSYS; } if ( base & size_or_mask || size & size_or_mask ) { printk ("mtrr: base or size exceeds the MTRR width\n"); return -EINVAL; } increment = increment ? 1 : 0; max = get_num_var_ranges (); /* Search for existing MTRR */ down(&main_lock); for (i = 0; i < max; ++i) { (*get_mtrr) (i, &lbase, &lsize, <ype); if (base >= lbase + lsize) continue; if ( (base < lbase) && (base + size <= lbase) ) continue; /* At this point we know there is some kind of overlap/enclosure */ if ( (base < lbase) || (base + size > lbase + lsize) ) { up(&main_lock); printk (KERN_WARNING "mtrr: 0x%lx000,0x%lx000 overlaps existing" " 0x%lx000,0x%lx000\n", base, size, lbase, lsize); return -EINVAL; } /* New region is enclosed by an existing region */ if (ltype != type) { if (type == MTRR_TYPE_UNCACHABLE) continue; up(&main_lock); printk ( "mtrr: type mismatch for %lx000,%lx000 old: %s new: %s\n", base, size, attrib_to_str (ltype), attrib_to_str (type) ); return -EINVAL; } if (increment) ++usage_table[i]; compute_ascii (); up(&main_lock); return i; } /* Search for an empty MTRR */ i = (*get_free_region) (base, size); if (i < 0) { up(&main_lock); printk ("mtrr: no more MTRRs available\n"); return i; } set_mtrr (i, base, size, type); usage_table[i] = 1; compute_ascii (); up(&main_lock); return i;} /* End Function mtrr_add_page *//** * mtrr_add - Add a memory type region * @base: Physical base address of region * @size: Physical size of region * @type: Type of MTRR desired * @increment: If this is true do usage counting on the region * * Memory type region registers control the caching on newer Intel and * non Intel processors. This function allows drivers to request an * MTRR is added. The details and hardware specifics of each processor's * implementation are hidden from the caller, but nevertheless the * caller should expect to need to provide a power of two size on an * equivalent power of two boundary. * * If the region cannot be added either because all regions are in use * or the CPU cannot support it a negative value is returned. On success * the register number for this entry is returned, but should be treated * as a cookie only. * * On a multiprocessor machine the changes are made to all processors. * This is required on x86 by the Intel processors. * * The available types are * * %MTRR_TYPE_UNCACHABLE - No caching * * %MTRR_TYPE_WRBACK - Write data back in bursts whenever * * %MTRR_TYPE_WRCOMB - Write data back soon but allow bursts * * %MTRR_TYPE_WRTHROUGH - Cache reads but not writes * * BUGS: Needs a quiet flag for the cases where drivers do not mind * failures and do not wish system log messages to be sent. */int mtrr_add(unsigned long base, unsigned long size, unsigned int type, char increment){/* [SUMMARY] Add an MTRR entry. <base> The starting (base) address of the region. <size> The size (in bytes) of the region. <type> The type of the new region. <increment> If true and the region already exists, the usage count will be incremented. [RETURNS] The MTRR register on success, else a negative number indicating the error code.*/ if ( (base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)) ) { printk ("mtrr: size and base must be multiples of 4 kiB\n"); printk ("mtrr: size: 0x%lx base: 0x%lx\n", size, base); return -EINVAL; } return mtrr_add_page(base >> PAGE_SHIFT, size >> PAGE_SHIFT, type, increment);} /* End Function mtrr_add *//** * mtrr_del_page - delete a memory type region * @reg: Register returned by mtrr_add * @base: Physical base address * @size: Size of region * * If register is supplied then base and size are ignored. This is * how drivers should call it. * * Releases an MTRR region. If the usage count drops to zero the * register is freed and the region returns to default state. * On success the register is returned, on failure a negative error * code. */ int mtrr_del_page (int reg, unsigned long base, unsigned long size)/* [SUMMARY] Delete MTRR/decrement usage count. <reg> The register. If this is less than 0 then <<base>> and <<size>> must be supplied. <base> The base address of the region. This is ignored if <<reg>> is >= 0. <size> The size of the region. This is ignored if <<reg>> is >= 0. [RETURNS] The register on success, else a negative number indicating the error code. [NOTE] This routine uses a spinlock.*/{ int i, max; mtrr_type ltype; unsigned long lbase, lsize; if ( mtrr_if == MTRR_IF_NONE ) return -ENXIO; max = get_num_var_ranges (); down (&main_lock); if (reg < 0) { /* Search for existing MTRR */ for (i = 0; i < max; ++i) { (*get_mtrr) (i, &lbase, &lsize, <ype); if (lbase == base && lsize == size) { reg = i; break; } } if (reg < 0) { up(&main_lock); printk ("mtrr: no MTRR for %lx000,%lx000 found\n", base, size); return -EINVAL; } } if (reg >= max) { up (&main_lock); printk ("mtrr: register: %d too big\n", reg); return -EINVAL; } if ( mtrr_if == MTRR_IF_CYRIX_ARR ) { if ( (reg == 3) && arr3_protected ) { up (&main_lock); printk ("mtrr: ARR3 cannot be changed\n"); return -EINVAL; } } (*get_mtrr) (reg, &lbase, &lsize, <ype); if (lsize < 1) { up (&main_lock); printk ("mtrr: MTRR %d not used\n", reg); return -EINVAL; } if (usage_table[reg] < 1) { up (&main_lock); printk ("mtrr: reg: %d has count=0\n", reg); return -EINVAL; } if (--usage_table[reg] < 1) set_mtrr (reg, 0, 0, 0); compute_ascii (); up (&main_lock); return reg;} /* End Function mtrr_del_page *//** * mtrr_del - delete a memory type region * @reg: Register returned by mtrr_add * @base: Physical base address * @size: Size of region * * If register is supplied then base and size are ignored. This is * how drivers should call it. * * Releases an MTRR region. If the usage count drops to zero the * register is freed and the region returns to default state. * On success the register is returned, on failure a negative error * code. */ int mtrr_del (int reg, unsigned long base, unsigned long size)/* [SUMMARY] Delete MTRR/decrement usage count. <reg> The register. If this is less than 0 then <<base>> and <<size>> must be supplied. <base> The base address of the region. This is ignored if <<reg>> is >= 0. <size> The size of the region. This is ignored if <<reg>> is >= 0. [RETURNS] The register on success, else a negative number indicating the error code.*/{ if ( (base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)) ) { printk ("mtrr: size and base must be multiples of 4 kiB\n"); printk ("mtrr: size: 0x%lx base: 0x%lx\n", size, base); return -EINVAL; } return mtrr_del_page(reg, base >> PAGE_SHIFT, size >> PAGE_SHIFT);}#ifdef USERSPACE_INTERFACEstatic int mtrr_file_add (unsigned long base, unsigned long size, unsigned int type, char increment, struct file *file, int page){ int reg, max; unsigned int *fcount = file->private_data; max = get_num_var_ranges (); if (fcount == NULL) { if ( ( fcount = kmalloc (max * sizeof *fcount, GFP_KERNEL) ) == NULL ) { printk ("mtrr: could not allocate\n"); return -ENOMEM; } memset (fcount, 0, max * sizeof *fcount); file->private_data = fcount; } if (!page) { if ( (base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)) ) { printk ("mtrr: size and base must be multiples of 4 kiB\n"); printk ("mtrr: size: 0x%lx base: 0x%lx\n", size, base); return -EINVAL; } base >>= PAGE_SHIFT; size >>= PAGE_SHIFT; } reg = mtrr_add_page (base, size, type, 1); if (reg >= 0) ++fcount[reg]; return reg;} /* End Function mtrr_file_add */static int mtrr_file_del (unsigned long base, unsigned long size, struct file *file, int page){ int reg; unsigned int *fcount = file->private_data; if (!page) { if ( (base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)) ) { printk ("mtrr: size and base must be multiples of 4 kiB\n"); printk ("mtrr: size: 0x%lx base: 0x%lx\n", size, base); return -EINVAL; } base >>= PAGE_SHIFT; size >>= PAGE_SHIFT; } reg = mtrr_del_page (-1, base, size); if (reg < 0) return reg; if (fcount == NULL) return reg; if (fcount[reg] < 1) return -EINVAL; --fcount[reg]; return reg;} /* End Function mtrr_file_del */static ssize_t mtrr_read (struct file *file, char *buf, size_t len, loff_t *ppos){ if (*ppos >= ascii_buf_bytes) return 0; if (*ppos + len > ascii_buf_bytes) len = ascii_buf_bytes - *ppos; if ( copy_to_user (buf, ascii_buffer + *ppos, len) ) return -EFAULT; *ppos += len; return len;} /* End Function mtrr_read */static ssize_t mtrr_write (struct file *file, const char *buf, size_t len, loff_t *ppos)/* Format of control line: "base=%Lx size=%Lx type=%s" OR: "disable=%d"*/{ int i, err; unsigned long reg; unsigned long long base, size; char *ptr; char line[LINE_SIZE]; if ( !suser () ) return -EPERM; /* Can't seek (pwrite) on this device */ if (ppos != &file->f_pos) return -ESPIPE; memset (line, 0, LINE_SIZE); if (len > LINE_SIZE) len = LINE_SIZE; if ( copy_from_user (line, buf, len - 1) ) return -EFAULT; ptr = line + strlen (line) - 1; if (*ptr == '\n') *ptr = '\0'; if ( !strncmp (line, "disable=", 8) ) { reg = simple_strtoul (line + 8, &ptr, 0); err = mtrr_del_page (reg, 0, 0); if (err < 0) return err; return len; } if ( strncmp (line, "base=", 5) ) { printk ("mtrr: no \"base=\" in line: \"%s\"\n", line); return -EINVAL; } base = simple_strtoull (line + 5, &ptr, 0); for (; isspace (*ptr); ++ptr); if ( strncmp (ptr, "size=", 5) ) { printk ("mtrr: no \"size=\" in line: \"%s\"\n", line); return -EINVAL; } size = simple_strtoull (ptr + 5, &ptr, 0); if ( (base & 0xfff) || (size & 0xfff) ) { printk ("mtrr: size and base must be multiples of 4 kiB\n"); printk ("mtrr: size: 0x%Lx base: 0x%Lx\n", size, base); return -EINVAL; } for (; isspace (*ptr); ++ptr); if ( strncmp (ptr, "type=", 5) ) { printk ("mtrr: no \"type=\" in line: \"%s\"\n", line); return -EINVAL; } ptr += 5; for (; isspace (*ptr); ++ptr); for (i = 0; i < MTRR_NUM_TYPES; ++i) { if ( strcmp (ptr, mtrr_strings[i]) ) continue; base >>= PAGE_SHIFT; size >>= PAGE_SHIFT; err = mtrr_add_page ((unsigned long)base, (unsigned long)size, i, 1); if (err < 0) return err; return len; } printk ("mtrr: illegal type: \"%s\"\n", ptr); return -EINVAL;} /* End Function mtrr_write */static int mtrr_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ int err;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -