📄 htab.c
字号:
case VMALLOC_REGION_ID: mm = &init_mm; vsid = get_kernel_vsid( ea ); break; case IO_UNMAPPED_REGION_ID: udbg_printf("EEH Error ea = 0x%lx\n", ea); PPCDBG_ENTER_DEBUGGER(); panic("EEH Error ea = 0x%lx\n", ea); break; case KERNEL_REGION_ID: /* As htab_initialize is now, we shouldn't ever get here since * we're bolting the entire 0xC0... region. */ udbg_printf("Little faulted on kernel address 0x%lx\n", ea); PPCDBG_ENTER_DEBUGGER(); panic("Little faulted on kernel address 0x%lx\n", ea); break; default: /* Not a valid range, send the problem up to do_page_fault */ return 1; break; } /* Search the Linux page table for a match with va */ va = ( vsid << 28 ) | ( ea & 0x0fffffff ); vpn = va >> PAGE_SHIFT; pgdir = mm->pgd; PPCDBG(PPCDBG_MM, "hash_page ea = 0x%16.16lx, va = 0x%16.16lx\n current = 0x%16.16lx, access = %lx\n", ea, va, current, access); if ( pgdir == NULL ) { return 1; } /* Lock the Linux page table to prevent mmap and kswapd * from modifying entries while we search and update */ spin_lock( &mm->page_table_lock ); ptep = find_linux_pte( pgdir, ea ); /* If no pte found, send the problem up to do_page_fault */ if ( ! ptep ) { spin_unlock( &mm->page_table_lock ); return 1; } /* Acquire the hash table lock to guarantee that the linux * pte we fetch will not change */ spin_lock( &hash_table_lock ); old_pte = *ptep; /* If the pte is not "present" (valid), send the problem * up to do_page_fault. */ if ( ! pte_present( old_pte ) ) { spin_unlock( &hash_table_lock ); spin_unlock( &mm->page_table_lock ); return 1; } /* At this point we have found a pte (which was present). * The spinlocks prevent this status from changing * The hash_table_lock prevents the _PAGE_HASHPTE status * from changing (RPN, DIRTY and ACCESSED too) * The page_table_lock prevents the pte from being * invalidated or modified *//* At this point, we have a pte (old_pte) which can be used to build or update * an HPTE. There are 5 cases: * * 1. There is a valid (present) pte with no associated HPTE (this is * the most common case) * 2. There is a valid (present) pte with an associated HPTE. The * current values of the pp bits in the HPTE prevent access because the * user doesn't have appropriate access rights. * 3. There is a valid (present) pte with an associated HPTE. The * current values of the pp bits in the HPTE prevent access because we are * doing software DIRTY bit management and the page is currently not DIRTY. * 4. This is a Kernel address (0xC---) for which there is no page directory. * There is an HPTE for this page, but the pp bits prevent access. * Since we always set up kernel pages with R/W access for the kernel * this case only comes about for users trying to access the kernel. * This case is always an error and is not dealt with further here. * 5. This is a Kernel address (0xC---) for which there is no page directory. * There is no HPTE for this page. * Check the user's access rights to the page. If access should be prevented * then send the problem up to do_page_fault. */ access |= _PAGE_PRESENT; if ( 0 == ( access & ~(pte_val(old_pte)) ) ) { /* * Check if pte might have an hpte, but we have * no slot information */ if ( pte_val(old_pte) & _PAGE_HPTENOIX ) { unsigned long slot; pte_val(old_pte) &= ~_PAGE_HPTEFLAGS; slot = ppc_md.hpte_find( vpn ); if ( slot != -1 ) { if ( slot < 0 ) { pte_val(old_pte) |= _PAGE_SECONDARY; slot = -slot; } pte_val(old_pte) |= ((slot << 12) & _PAGE_GROUP_IX) | _PAGE_HASHPTE; } } /* User has appropriate access rights. */ new_pte = old_pte; /* If the attempted access was a store */ if ( access & _PAGE_RW ) pte_val(new_pte) |= _PAGE_ACCESSED | _PAGE_DIRTY; else pte_val(new_pte) |= _PAGE_ACCESSED; /* Only cases 1, 3 and 5 still in play */ newpp = computeHptePP( pte_val(new_pte) ); /* Check if pte already has an hpte (case 3) */ if ( pte_val(old_pte) & _PAGE_HASHPTE ) { /* There MIGHT be an HPTE for this pte */ unsigned long hash, slot, secondary; /* Local copy of first doubleword of HPTE */ union { unsigned long d; Hpte_dword0 h; } hpte_dw0; hash = hpt_hash(vpn, 0); secondary = (pte_val(old_pte) & _PAGE_SECONDARY) >> 15; if ( secondary ) hash = ~hash; slot = (hash & htab_data.htab_hash_mask) * HPTES_PER_GROUP; slot += (pte_val(old_pte) & _PAGE_GROUP_IX) >> 12; /* If there is an HPTE for this page it is indexed by slot */ hpte_dw0.d = ppc_md.hpte_getword0( slot ); if ( (hpte_dw0.h.avpn == (vpn >> 11) ) && (hpte_dw0.h.v) && (hpte_dw0.h.h == secondary ) ){ /* HPTE matches */ ppc_md.hpte_updatepp( slot, newpp, va ); if ( !pte_same( old_pte, new_pte ) ) *ptep = new_pte; } else { /* HPTE is not for this pte */ pte_val(old_pte) &= ~_PAGE_HPTEFLAGS; } } if ( !( pte_val(old_pte) & _PAGE_HASHPTE ) ) { /* Cases 1 and 5 */ /* For these cases we need to create a new * HPTE and update the linux pte (for * case 1). For case 5 there is no linux pte. * * Find an available HPTE slot */ slot = ppc_md.hpte_selectslot( vpn ); /* If hpte_selectslot returns 0x8000000000000000 that means * that there was already an entry in the HPT even though * the linux PTE said there couldn't be. */ /* Debug code */ if ( slot == 0x8000000000000000 ) { unsigned long xold_pte = pte_val(old_pte); unsigned long xnew_pte = pte_val(new_pte); udbg_printf("hash_page: ptep = 0x%016lx\n", (unsigned long)ptep ); udbg_printf("hash_page: old_pte = 0x%016lx\n", xold_pte ); udbg_printf("hash_page: new_pte = 0x%016lx\n", xnew_pte ); udbg_printf("hash_page: ea = 0x%016lx\n", ea ); udbg_printf("hash_page: va = 0x%016lx\n", va ); udbg_printf("hash_page: access = 0x%016lx\n", access ); panic("hash_page: hpte already exists\n"); } hash_ind = 0; if ( slot < 0 ) { slot = -slot; hash_ind = 1; } /* Set the physical address */ prpn = pte_val(old_pte) >> PTE_SHIFT; if ( ptep ) { /* Update the linux pte with the HPTE slot */ pte_val(new_pte) &= ~_PAGE_HPTEFLAGS; pte_val(new_pte) |= hash_ind << 15; pte_val(new_pte) |= (slot<<12) & _PAGE_GROUP_IX; pte_val(new_pte) |= _PAGE_HASHPTE; /* No need to use ldarx/stdcx here because all * who might be updating the pte will hold the page_table_lock * or the hash_table_lock (we hold both) */ *ptep = new_pte; } /* copy appropriate flags from linux pte */ hpteflags = (pte_val(new_pte) & 0x1f8) | newpp; /* Create the HPTE */ ppc_md.hpte_create_valid( slot, vpn, prpn, hash_ind, ptep, hpteflags, 0 ); } /* Indicate success */ rc = 0; } spin_unlock( &hash_table_lock ); if (ptep) spin_unlock( &mm->page_table_lock ); return rc;}void flush_hash_page( unsigned long context, unsigned long ea, pte_t *ptep ){ unsigned long vsid, vpn, va, hash, secondary, slot, flags; /* Local copy of first doubleword of HPTE */ union { unsigned long d; Hpte_dword0 h; } hpte_dw0; pte_t pte; if ( (ea >= USER_START ) && ( ea <= USER_END ) ) vsid = get_vsid( context, ea ); else vsid = get_kernel_vsid( ea ); va = (vsid << 28) | (ea & 0x0fffffff); vpn = va >> PAGE_SHIFT; hash = hpt_hash(vpn, 0); spin_lock_irqsave( &hash_table_lock, flags); pte = __pte(pte_update(ptep, _PAGE_HPTEFLAGS, 0)); if ( pte_val(pte) & _PAGE_HASHPTE ) { secondary = (pte_val(pte) & _PAGE_SECONDARY) >> 15; if ( secondary ) hash = ~hash; slot = (hash & htab_data.htab_hash_mask) * HPTES_PER_GROUP; slot += (pte_val(pte) & _PAGE_GROUP_IX) >> 12; /* If there is an HPTE for this page it is indexed by slot */ hpte_dw0.d = ppc_md.hpte_getword0( slot ); if ( (hpte_dw0.h.avpn == (vpn >> 11) ) && (hpte_dw0.h.v) && (hpte_dw0.h.h == secondary ) ){ /* HPTE matches */ ppc_md.hpte_invalidate( slot ); } else { unsigned k; /* Temporarily lets check for the hpte in all possible slots */ for ( secondary = 0; secondary < 2; ++secondary ) { hash = hpt_hash(vpn, 0); if ( secondary ) hash = ~hash; slot = (hash & htab_data.htab_hash_mask) * HPTES_PER_GROUP; for ( k=0; k<8; ++k ) { hpte_dw0.d = ppc_md.hpte_getword0( slot+k ); if ( ( hpte_dw0.h.avpn == (vpn >> 11) ) && ( hpte_dw0.h.v ) && ( hpte_dw0.h.h == secondary ) ) { while (1) ; } } } } } spin_unlock_irqrestore( &hash_table_lock, flags );}int proc_dol2crvec(ctl_table *table, int write, struct file *filp, void *buffer, size_t *lenp){ int vleft, first=1, len, left, val;#define TMPBUFLEN 256 char buf[TMPBUFLEN], *p; static const char *sizestrings[4] = { "2MB", "256KB", "512KB", "1MB" }; static const char *clockstrings[8] = { "clock disabled", "+1 clock", "+1.5 clock", "reserved(3)", "+2 clock", "+2.5 clock", "+3 clock", "reserved(7)" }; static const char *typestrings[4] = { "flow-through burst SRAM", "reserved SRAM", "pipelined burst SRAM", "pipelined late-write SRAM" }; static const char *holdstrings[4] = { "0.5", "1.0", "(reserved2)", "(reserved3)" }; if ( ((_get_PVR() >> 16) != 8) && ((_get_PVR() >> 16) != 12)) return -EFAULT; if ( /*!table->maxlen ||*/ (filp->f_pos && !write)) { *lenp = 0; return 0; } vleft = table->maxlen / sizeof(int); left = *lenp; for (; left /*&& vleft--*/; first=0) { if (write) { while (left) { char c; if(get_user(c,(char *) buffer)) return -EFAULT; if (!isspace(c)) break; left--; ((char *) buffer)++; } if (!left) break; len = left; if (len > TMPBUFLEN-1) len = TMPBUFLEN-1; if(copy_from_user(buf, buffer, len)) return -EFAULT; buf[len] = 0; p = buf; if (*p < '0' || *p > '9') break; val = simple_strtoul(p, &p, 0); len = p-buf; if ((len < left) && *p && !isspace(*p)) break; buffer += len; left -= len;#if 0 /* DRENG need a def */ _set_L2CR(0); _set_L2CR(val); while ( _get_L2CR() & 0x1 ) /* wait for invalidate to finish */;#endif } else { p = buf; if (!first) *p++ = '\t';#if 0 /* DRENG need a def */ val = _get_L2CR();#endif p += sprintf(p, "0x%08x: ", val); p += sprintf(p, " %s", (val >> 31) & 1 ? "enabled" : "disabled"); p += sprintf(p, ", %sparity", (val>>30)&1 ? "" : "no "); p += sprintf(p, ", %s", sizestrings[(val >> 28) & 3]); p += sprintf(p, ", %s", clockstrings[(val >> 25) & 7]); p += sprintf(p, ", %s", typestrings[(val >> 23) & 2]); p += sprintf(p, "%s", (val>>22)&1 ? ", data only" : ""); p += sprintf(p, "%s", (val>>20)&1 ? ", ZZ enabled": ""); p += sprintf(p, ", %s", (val>>19)&1 ? "write-through" : "copy-back"); p += sprintf(p, "%s", (val>>18)&1 ? ", testing" : ""); p += sprintf(p, ", %sns hold",holdstrings[(val>>16)&3]); p += sprintf(p, "%s", (val>>15)&1 ? ", DLL slow" : ""); p += sprintf(p, "%s", (val>>14)&1 ? ", diff clock" :""); p += sprintf(p, "%s", (val>>13)&1 ? ", DLL bypass" :""); p += sprintf(p,"\n"); len = strlen(buf); if (len > left) len = left; if(copy_to_user(buffer, buf, len)) return -EFAULT; left -= len; buffer += len; break; } } if (!write && !first && left) { if(put_user('\n', (char *) buffer)) return -EFAULT; left--, buffer++; } if (write) { p = (char *) buffer; while (left) { char c; if(get_user(c, p++)) return -EFAULT; if (!isspace(c)) break; left--; } } if (write && first) return -EINVAL; *lenp -= left; filp->f_pos += *lenp; return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -