duke@435: /* twisti@2103: * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. duke@435: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@435: * duke@435: * This code is free software; you can redistribute it and/or modify it duke@435: * under the terms of the GNU General Public License version 2 only, as duke@435: * published by the Free Software Foundation. duke@435: * duke@435: * This code is distributed in the hope that it will be useful, but WITHOUT duke@435: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@435: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@435: * version 2 for more details (a copy is included in the LICENSE file that duke@435: * accompanied this code). duke@435: * duke@435: * You should have received a copy of the GNU General Public License version duke@435: * 2 along with this work; if not, write to the Free Software Foundation, duke@435: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@435: * trims@1907: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA trims@1907: * or visit www.oracle.com if you need additional information or have any trims@1907: * questions. duke@435: * duke@435: */ duke@435: stefank@2314: #include "precompiled.hpp" stefank@2314: #include "memory/resourceArea.hpp" stefank@2314: #include "runtime/icache.hpp" duke@435: duke@435: // The flush stub function address duke@435: AbstractICache::flush_icache_stub_t AbstractICache::_flush_icache_stub = NULL; duke@435: duke@435: void AbstractICache::initialize() { duke@435: // Making this stub must be FIRST use of assembler duke@435: ResourceMark rm; duke@435: duke@435: BufferBlob* b = BufferBlob::create("flush_icache_stub", ICache::stub_size); twisti@2103: CodeBuffer c(b); duke@435: duke@435: ICacheStubGenerator g(&c); duke@435: g.generate_icache_flush(&_flush_icache_stub); duke@435: duke@435: // The first use of flush_icache_stub must apply it to itself. duke@435: // The StubCodeMark destructor in generate_icache_flush will duke@435: // call Assembler::flush, which in turn will call invalidate_range, duke@435: // which will in turn call the flush stub. Thus we don't need an duke@435: // explicit call to invalidate_range here. This assumption is duke@435: // checked in invalidate_range. duke@435: } duke@435: duke@435: void AbstractICache::call_flush_stub(address start, int lines) { duke@435: // The business with the magic number is just a little security. duke@435: // We cannot call the flush stub when generating the flush stub duke@435: // because it isn't there yet. So, the stub also returns its third duke@435: // parameter. This is a cheap check that the stub was really executed. duke@435: static int magic = 0xbaadbabe; duke@435: duke@435: int auto_magic = magic; // Make a local copy to avoid race condition duke@435: int r = (*_flush_icache_stub)(start, lines, auto_magic); duke@435: guarantee(r == auto_magic, "flush stub routine did not execute"); duke@435: ++magic; duke@435: } duke@435: duke@435: void AbstractICache::invalidate_word(address addr) { duke@435: // Because this is called for instruction patching on the fly, long after duke@435: // bootstrapping, we execute the stub directly. Account for a 4-byte word duke@435: // spanning two cache lines by computing a start line address by rounding duke@435: // addr down to a line_size boundary, and an end line address by adding duke@435: // the word size - 1 and rounding the result down to a line_size boundary. duke@435: // If we just added word size, we'd mistakenly flush the next cache line duke@435: // if the word to be flushed started in the last 4 bytes of the line. duke@435: // Doing that would segv if the next line weren't mapped. duke@435: duke@435: const int word_size_in_bytes = 4; // Always, regardless of platform duke@435: duke@435: intptr_t start_line = ((intptr_t)addr + 0) & ~(ICache::line_size - 1); duke@435: intptr_t end_line = ((intptr_t)addr + word_size_in_bytes - 1) duke@435: & ~(ICache::line_size - 1); duke@435: (*_flush_icache_stub)((address)start_line, start_line == end_line ? 1 : 2, 0); duke@435: } duke@435: duke@435: void AbstractICache::invalidate_range(address start, int nbytes) { duke@435: static bool firstTime = true; duke@435: if (firstTime) { duke@435: guarantee(start == CAST_FROM_FN_PTR(address, _flush_icache_stub), duke@435: "first flush should be for flush stub"); duke@435: firstTime = false; duke@435: return; duke@435: } duke@435: if (nbytes == 0) { duke@435: return; duke@435: } duke@435: // Align start address to an icache line boundary and transform duke@435: // nbytes to an icache line count. duke@435: const uint line_offset = mask_address_bits(start, ICache::line_size-1); duke@435: if (line_offset != 0) { duke@435: start -= line_offset; duke@435: nbytes += line_offset; duke@435: } duke@435: call_flush_stub(start, round_to(nbytes, ICache::line_size) >> duke@435: ICache::log2_line_size); duke@435: } duke@435: duke@435: // For init.cpp duke@435: void icache_init() { duke@435: ICache::initialize(); duke@435: }