📄 mtrr.c
字号:
set_mtrr_prepare (&ctxt); /* Notify master that I've flushed and disabled my cache */ atomic_dec (&undone_count); while (wait_barrier_execute) barrier (); /* The master has cleared me to execute */ set_mtrr_up (data->smp_reg, data->smp_base, data->smp_size, data->smp_type, FALSE); /* Notify master CPU that I've executed the function */ atomic_dec (&undone_count); /* Wait for master to clear me to enable cache and return */ while (wait_barrier_cache_enable) barrier (); set_mtrr_done (&ctxt);}static void set_mtrr_smp (unsigned int reg, u64 base, u32 size, mtrr_type type){ struct set_mtrr_data data; struct set_mtrr_context ctxt; data.smp_reg = reg; data.smp_base = base; data.smp_size = size; data.smp_type = type; wait_barrier_execute = TRUE; wait_barrier_cache_enable = TRUE; atomic_set (&undone_count, smp_num_cpus - 1); /* Start the ball rolling on other CPUs */ if (smp_call_function (ipi_handler, &data, 1, 0) != 0) panic ("mtrr: timed out waiting for other CPUs\n"); /* Flush and disable the local CPU's cache */ set_mtrr_prepare (&ctxt); /* Wait for all other CPUs to flush and disable their caches */ while (atomic_read (&undone_count) > 0) barrier (); /* Set up for completion wait and then release other CPUs to change MTRRs */ atomic_set (&undone_count, smp_num_cpus - 1); wait_barrier_execute = FALSE; set_mtrr_up (reg, base, size, type, FALSE); /* Now wait for other CPUs to complete the function */ while (atomic_read (&undone_count) > 0) barrier (); /* Now all CPUs should have finished the function. Release the barrier to allow them to re-enable their caches and return from their interrupt, then enable the local cache and return */ wait_barrier_cache_enable = FALSE; set_mtrr_done (&ctxt);}/* Some BIOS's are fucked and don't set all MTRRs the same! */static void __init mtrr_state_warn (u32 mask){ if (!mask) return; if (mask & MTRR_CHANGE_MASK_FIXED) printk (KERN_INFO "mtrr: your CPUs had inconsistent fixed MTRR settings\n"); if (mask & MTRR_CHANGE_MASK_VARIABLE) printk (KERN_INFO "mtrr: your CPUs had inconsistent variable MTRR settings\n"); if (mask & MTRR_CHANGE_MASK_DEFTYPE) printk (KERN_INFO "mtrr: your CPUs had inconsistent MTRRdefType settings\n"); printk (KERN_INFO "mtrr: probably your BIOS does not setup all CPUs\n");}#endif /* CONFIG_SMP */static inline char * attrib_to_str (int x){ return (x <= 6) ? mtrr_strings[x] : "?";}static void __init init_table (void){ int i, max; max = get_num_var_ranges (); if ((usage_table = kmalloc (max * sizeof *usage_table, GFP_KERNEL))==NULL) { printk ("mtrr: could not allocate\n"); return; } for (i = 0; i < max; i++) usage_table[i] = 1;#ifdef USERSPACE_INTERFACE if ((ascii_buffer = kmalloc (max * LINE_SIZE, GFP_KERNEL)) == NULL) { printk ("mtrr: could not allocate\n"); return; } ascii_buf_bytes = 0; compute_ascii ();#endif}/* * Get a free MTRR. * returns the index of the region on success, else -1 on error.*/static int get_free_region(void){ int i, max; mtrr_type ltype; u64 lbase; u32 lsize; max = get_num_var_ranges (); for (i = 0; i < max; ++i) { get_mtrr (i, &lbase, &lsize, <ype); if (lsize == 0) return i; } return -ENOSPC;}/** * mtrr_add_page - Add a memory type region * @base: Physical base address of region in pages (4 KB) * @size: Physical size of region in pages (4 KB) * @type: Type of MTRR desired * @increment: If this is true do usage counting on the region * Returns The MTRR register on success, else a negative number * indicating the error code. * * Memory type region registers control the caching on newer * processors. This function allows drivers to request an MTRR is added. * 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. * * 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_page (u64 base, u32 size, unsigned int type, char increment){ int i, max; mtrr_type ltype; u64 lbase, last; u32 lsize; if (base + size < 0x100) { printk (KERN_WARNING "mtrr: cannot set region below 1 MiB (0x%Lx000,0x%x000)\n", base, size); return -EINVAL; }#if 0 && defined(__x86_64__) && defined(CONFIG_AGP) { agp_kern_info info; if (type != MTRR_TYPE_UNCACHABLE && agp_copy_info(&info) >= 0 && base<<PAGE_SHIFT >= info.aper_base && (base<<PAGE_SHIFT)+(size<<PAGE_SHIFT) >= info.aper_base+info.aper_size*1024*1024) printk(KERN_INFO "%s[%d] setting conflicting mtrr into agp aperture\n",current->comm,current->pid); }#endif /* 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%x000) boundary\n", base, size); 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>>PAGE_SHIFT)) { printk (KERN_WARNING "mtrr: base(%Lx) exceeds the MTRR width(%Lx)\n", base, (size_or_mask>>PAGE_SHIFT)); return -EINVAL; } if (size & (size_or_mask>>PAGE_SHIFT)) { printk (KERN_WARNING "mtrr: size exceeds the MTRR width\n"); return -EINVAL; } increment = increment ? 1 : 0; max = get_num_var_ranges (); /* Search for existing MTRR */ down (&mtrr_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 (&mtrr_lock); printk (KERN_WARNING "mtrr: 0x%Lx000,0x%x000 overlaps existing" " 0x%Lx000,0x%x000\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 (&mtrr_lock); printk ("mtrr: type mismatch for %Lx000,%x000 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 (&mtrr_lock); return i; } /* Search for an empty MTRR */ i = get_free_region(); if (i < 0) { up (&mtrr_lock); printk ("mtrr: no more MTRRs available\n"); return i; } set_mtrr (i, base, size, type); usage_table[i] = 1; compute_ascii (); up (&mtrr_lock); return i;}/** * 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 * Return the MTRR register on success, else a negative numbe * indicating the error code. * * Memory type region registers control the caching on newer processors. * This function allows drivers to request an MTRR is added. * 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 (u64 base, u32 size, unsigned int type, char increment){ 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%x base: 0x%Lx\n", size, base); return -EINVAL; } return mtrr_add_page (base >> PAGE_SHIFT, size >> PAGE_SHIFT, type, increment);}/** * 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, u64 base, u32 size){ int i, max; mtrr_type ltype; u64 lbase; u32 lsize; max = get_num_var_ranges (); down (&mtrr_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 (&mtrr_lock); printk ("mtrr: no MTRR for %Lx000,%x000 found\n", base, size); return -EINVAL; } } if (reg >= max) { up (&mtrr_lock); printk ("mtrr: register: %d too big\n", reg); return -EINVAL; } get_mtrr (reg, &lbase, &lsize, <ype); if (lsize < 1) { up (&mtrr_lock); printk ("mtrr: MTRR %d not used\n", reg); return -EINVAL; } if (usage_table[reg] < 1) { up (&mtrr_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 (&mtrr_lock); return reg;}/** * 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, u64 base, u32 size){ 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%x 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 (u64 base, u32 size, unsigned int type, 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 (KERN_INFO "mtrr: size and base must be multiples of 4 kiB\n"); printk (KERN_INFO "mtrr: size: 0x%x base: 0x%Lx\n", size, base); return -EINVAL; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -