<commit_before>/*
 * Copyright (c) 2007 The Hewlett-Packard Development Company
 * All rights reserved.
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Copyright (c) 2003-2007 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "arch/x86/faults.hh"

#include "arch/x86/generated/decoder.hh"
#include "arch/x86/insts/static_inst.hh"
#include "arch/x86/mmu.hh"
#include "arch/x86/regs/misc.hh"
#include "base/loader/symtab.hh"
#include "base/trace.hh"
#include "cpu/thread_context.hh"
#include "debug/Faults.hh"
#include "sim/full_system.hh"
#include "sim/process.hh"

namespace gem5
{

namespace X86ISA
{

void
X86FaultBase::invoke(ThreadContext *tc, const StaticInstPtr &inst)
{
    if (!FullSystem) {
        FaultBase::invoke(tc, inst);
        return;
    }

    PCState pc = tc->pcState().as<PCState>();
    DPRINTF(Faults, "RIP %#x: vector %d: %s\n", pc.pc(), vector, describe());
    using namespace X86ISAInst::rom_labels;
    HandyM5Reg m5reg = tc->readMiscRegNoEffect(MISCREG_M5_REG);
    MicroPC entry;
    if (m5reg.mode == LongMode) {
        entry = isSoft() ? extern_label_longModeSoftInterrupt :
                           extern_label_longModeInterrupt;
    } else {
        entry = extern_label_legacyModeInterrupt;
    }
    tc->setIntReg(INTREG_MICRO(1), vector);
    tc->setIntReg(INTREG_MICRO(7), pc.pc());
    if (errorCode != (uint64_t)(-1)) {
        if (m5reg.mode == LongMode) {
            entry = extern_label_longModeInterruptWithError;
        } else {
            panic("Legacy mode interrupts with error codes "
                    "aren't implemented.");
        }
        // Software interrupts shouldn't have error codes. If one
        // does, there would need to be microcode to set it up.
        assert(!isSoft());
        tc->setIntReg(INTREG_MICRO(15), errorCode);
    }
    pc.upc(romMicroPC(entry));
    pc.nupc(romMicroPC(entry) + 1);
    tc->pcState(pc);
}

std::string
X86FaultBase::describe() const
{
    std::stringstream ss;
    ccprintf(ss, "%s", mnemonic());
    if (errorCode != (uint64_t)(-1))
        ccprintf(ss, "(%#x)", errorCode);

    return ss.str();
}

void
X86Trap::invoke(ThreadContext *tc, const StaticInstPtr &inst)
{
    // This is the same as a fault, but it happens -after- the
    // instruction.
    X86FaultBase::invoke(tc);
}

void
X86Abort::invoke(ThreadContext *tc, const StaticInstPtr &inst)
{
    panic("Abort exception!");
}

void
InvalidOpcode::invoke(ThreadContext *tc, const StaticInstPtr &inst)
{
    if (FullSystem) {
        X86Fault::invoke(tc, inst);
    } else {
        auto *xsi = static_cast<X86StaticInst *>(inst.get());
        panic("Unrecognized/invalid instruction executed:\n %s",
                xsi->machInst);
    }
}

void
PageFault::invoke(ThreadContext *tc, const StaticInstPtr &inst)
{
    if (FullSystem) {
        // Invalidate any matching TLB entries before handling the page fault.
        tc->getMMUPtr()->demapPage(addr, 0);
        HandyM5Reg m5reg = tc->readMiscRegNoEffect(MISCREG_M5_REG);
        X86FaultBase::invoke(tc);
        // If something bad happens while trying to enter the page fault
        // handler, I'm pretty sure that's a double fault and then all
        // bets are off. That means it should be safe to update this
        // state now.
        if (m5reg.mode == LongMode)
            tc->setMiscReg(MISCREG_CR2, addr);
        else
            tc->setMiscReg(MISCREG_CR2, (uint32_t)addr);
    } else if (!tc->getProcessPtr()->fixupFault(addr)) {
        PageFaultErrorCode code = errorCode;
        const char *modeStr = "";
        if (code.fetch)
            modeStr = "execute";
        else if (code.write)
            modeStr = "write";
        else
            modeStr = "read";

        // print information about what we are panic'ing on
        if (!inst) {
            panic("Tried to %s unmapped address %#x.", modeStr, addr);
        } else {
            panic("Tried to %s unmapped address %#x.\nPC: %#x, Instr: %s",
                  modeStr, addr, tc->pcState(),
                  inst->disassemble(tc->pcState().instAddr(),
                      &loader::debugSymbolTable));
        }
    }
}

std::string
PageFault::describe() const
{
    std::stringstream ss;
    ccprintf(ss, "%s at %#x", X86FaultBase::describe(), addr);
    return ss.str();
}

void
InitInterrupt::invoke(ThreadContext *tc, const StaticInstPtr &inst)
{
    DPRINTF(Faults, "Init interrupt.\n");
    // The otherwise unmodified integer registers should be set to 0.
    for (int index = 0; index < NUM_ARCH_INTREGS; index++) {
        tc->setIntReg(index, 0);
    }

    CR0 cr0 = tc->readMiscReg(MISCREG_CR0);
    CR0 newCR0 = 1 << 4;
    newCR0.cd = cr0.cd;
    newCR0.nw = cr0.nw;
    tc->setMiscReg(MISCREG_CR0, newCR0);
    tc->setMiscReg(MISCREG_CR2, 0);
    tc->setMiscReg(MISCREG_CR3, 0);
    tc->setMiscReg(MISCREG_CR4, 0);

    tc->setMiscReg(MISCREG_RFLAGS, 0x0000000000000002ULL);

    tc->setMiscReg(MISCREG_EFER, 0);

    SegAttr dataAttr = 0;
    dataAttr.dpl = 0;
    dataAttr.unusable = 0;
    dataAttr.defaultSize = 0;
    dataAttr.longMode = 0;
    dataAttr.avl = 0;
    dataAttr.granularity = 0;
    dataAttr.present = 1;
    dataAttr.type = 3;
    dataAttr.writable = 1;
    dataAttr.readable = 1;
    dataAttr.expandDown = 0;
    dataAttr.system = 1;

    for (int seg = 0; seg != NUM_SEGMENTREGS; seg++) {
        tc->setMiscReg(MISCREG_SEG_SEL(seg), 0);
        tc->setMiscReg(MISCREG_SEG_BASE(seg), 0);
        tc->setMiscReg(MISCREG_SEG_EFF_BASE(seg), 0);
        tc->setMiscReg(MISCREG_SEG_LIMIT(seg), 0xffff);
        tc->setMiscReg(MISCREG_SEG_ATTR(seg), dataAttr);
    }

    SegAttr codeAttr = 0;
    codeAttr.dpl = 0;
    codeAttr.unusable = 0;
    codeAttr.defaultSize = 0;
    codeAttr.longMode = 0;
    codeAttr.avl = 0;
    codeAttr.granularity = 0;
    codeAttr.present = 1;
    codeAttr.type = 10;
    codeAttr.writable = 0;
    codeAttr.readable = 1;
    codeAttr.expandDown = 0;
    codeAttr.system = 1;

    tc->setMiscReg(MISCREG_CS, 0xf000);
    tc->setMiscReg(MISCREG_CS_BASE,
            0x00000000ffff0000ULL);
    tc->setMiscReg(MISCREG_CS_EFF_BASE,
            0x00000000ffff0000ULL);
    // This has the base value pre-added.
    tc->setMiscReg(MISCREG_CS_LIMIT, 0xffffffff);
    tc->setMiscReg(MISCREG_CS_ATTR, codeAttr);

    PCState pc(0x000000000000fff0ULL + tc->readMiscReg(MISCREG_CS_BASE));
    tc->pcState(pc);

    tc->setMiscReg(MISCREG_TSG_BASE, 0);
    tc->setMiscReg(MISCREG_TSG_LIMIT, 0xffff);

    tc->setMiscReg(MISCREG_IDTR_BASE, 0);
    tc->setMiscReg(MISCREG_IDTR_LIMIT, 0xffff);

    SegAttr tslAttr = 0;
    tslAttr.unusable = 1;
    tslAttr.present = 0;
    tslAttr.type = 2; // LDT
    tc->setMiscReg(MISCREG_TSL, 0);
    tc->setMiscReg(MISCREG_TSL_BASE, 0);
    tc->setMiscReg(MISCREG_TSL_LIMIT, 0xffff);
    tc->setMiscReg(MISCREG_TSL_ATTR, tslAttr);

    SegAttr trAttr = 0;
    trAttr.unusable = 1;
    trAttr.present = 0;
    trAttr.type = 3; // Busy 16-bit TSS
    tc->setMiscReg(MISCREG_TR, 0);
    tc->setMiscReg(MISCREG_TR_BASE, 0);
    tc->setMiscReg(MISCREG_TR_LIMIT, 0xffff);
    tc->setMiscReg(MISCREG_TR_ATTR, trAttr);

    // This value should be the family/model/stepping of the processor.
    // (page 418). It should be consistent with the value from CPUID, but
    // the actual value probably doesn't matter much.
    tc->setIntReg(INTREG_RDX, 0);

    tc->setMiscReg(MISCREG_DR0, 0);
    tc->setMiscReg(MISCREG_DR1, 0);
    tc->setMiscReg(MISCREG_DR2, 0);
    tc->setMiscReg(MISCREG_DR3, 0);

    tc->setMiscReg(MISCREG_DR6, 0x00000000ffff0ff0ULL);
    tc->setMiscReg(MISCREG_DR7, 0x0000000000000400ULL);

    tc->setMiscReg(MISCREG_MXCSR, 0x1f80);

    // Flag all elements on the x87 stack as empty.
    tc->setMiscReg(MISCREG_FTW, 0xFFFF);

    // Update the handy M5 Reg.
    tc->setMiscReg(MISCREG_M5_REG, 0);
    MicroPC entry = X86ISAInst::rom_labels::extern_label_initIntHalt;
    pc.upc(romMicroPC(entry));
    pc.nupc(romMicroPC(entry) + 1);
    tc->pcState(pc);
}

void
StartupInterrupt::invoke(ThreadContext *tc, const StaticInstPtr &inst)
{
    DPRINTF(Faults, "Startup interrupt with vector %#x.\n", vector);
    HandyM5Reg m5Reg = tc->readMiscReg(MISCREG_M5_REG);
    if (m5Reg.mode != LegacyMode || m5Reg.submode != RealMode) {
        panic("Startup IPI recived outside of real mode. "
                "Don't know what to do. %d, %d", m5Reg.mode, m5Reg.submode);
    }

    tc->setMiscReg(MISCREG_CS, vector << 8);
    tc->setMiscReg(MISCREG_CS_BASE, vector << 12);
    tc->setMiscReg(MISCREG_CS_EFF_BASE, vector << 12);
    // This has the base value pre-added.
    tc->setMiscReg(MISCREG_CS_LIMIT, 0xffff);

    tc->pcState(tc->readMiscReg(MISCREG_CS_BASE));
}

} // namespace X86ISA
} // namespace gem5
<commit_msg>arch-x86: Fix some settings installed by the init interrupt.<commit_after>/*
 * Copyright (c) 2007 The Hewlett-Packard Development Company
 * All rights reserved.
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Copyright (c) 2003-2007 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "arch/x86/faults.hh"

#include "arch/x86/generated/decoder.hh"
#include "arch/x86/insts/static_inst.hh"
#include "arch/x86/mmu.hh"
#include "arch/x86/regs/misc.hh"
#include "base/loader/symtab.hh"
#include "base/trace.hh"
#include "cpu/thread_context.hh"
#include "debug/Faults.hh"
#include "sim/full_system.hh"
#include "sim/process.hh"

namespace gem5
{

namespace X86ISA
{

void
X86FaultBase::invoke(ThreadContext *tc, const StaticInstPtr &inst)
{
    if (!FullSystem) {
        FaultBase::invoke(tc, inst);
        return;
    }

    PCState pc = tc->pcState().as<PCState>();
    DPRINTF(Faults, "RIP %#x: vector %d: %s\n", pc.pc(), vector, describe());
    using namespace X86ISAInst::rom_labels;
    HandyM5Reg m5reg = tc->readMiscRegNoEffect(MISCREG_M5_REG);
    MicroPC entry;
    if (m5reg.mode == LongMode) {
        entry = isSoft() ? extern_label_longModeSoftInterrupt :
                           extern_label_longModeInterrupt;
    } else {
        entry = extern_label_legacyModeInterrupt;
    }
    tc->setIntReg(INTREG_MICRO(1), vector);
    tc->setIntReg(INTREG_MICRO(7), pc.pc());
    if (errorCode != (uint64_t)(-1)) {
        if (m5reg.mode == LongMode) {
            entry = extern_label_longModeInterruptWithError;
        } else {
            panic("Legacy mode interrupts with error codes "
                    "aren't implemented.");
        }
        // Software interrupts shouldn't have error codes. If one
        // does, there would need to be microcode to set it up.
        assert(!isSoft());
        tc->setIntReg(INTREG_MICRO(15), errorCode);
    }
    pc.upc(romMicroPC(entry));
    pc.nupc(romMicroPC(entry) + 1);
    tc->pcState(pc);
}

std::string
X86FaultBase::describe() const
{
    std::stringstream ss;
    ccprintf(ss, "%s", mnemonic());
    if (errorCode != (uint64_t)(-1))
        ccprintf(ss, "(%#x)", errorCode);

    return ss.str();
}

void
X86Trap::invoke(ThreadContext *tc, const StaticInstPtr &inst)
{
    // This is the same as a fault, but it happens -after- the
    // instruction.
    X86FaultBase::invoke(tc);
}

void
X86Abort::invoke(ThreadContext *tc, const StaticInstPtr &inst)
{
    panic("Abort exception!");
}

void
InvalidOpcode::invoke(ThreadContext *tc, const StaticInstPtr &inst)
{
    if (FullSystem) {
        X86Fault::invoke(tc, inst);
    } else {
        auto *xsi = static_cast<X86StaticInst *>(inst.get());
        panic("Unrecognized/invalid instruction executed:\n %s",
                xsi->machInst);
    }
}

void
PageFault::invoke(ThreadContext *tc, const StaticInstPtr &inst)
{
    if (FullSystem) {
        // Invalidate any matching TLB entries before handling the page fault.
        tc->getMMUPtr()->demapPage(addr, 0);
        HandyM5Reg m5reg = tc->readMiscRegNoEffect(MISCREG_M5_REG);
        X86FaultBase::invoke(tc);
        // If something bad happens while trying to enter the page fault
        // handler, I'm pretty sure that's a double fault and then all
        // bets are off. That means it should be safe to update this
        // state now.
        if (m5reg.mode == LongMode)
            tc->setMiscReg(MISCREG_CR2, addr);
        else
            tc->setMiscReg(MISCREG_CR2, (uint32_t)addr);
    } else if (!tc->getProcessPtr()->fixupFault(addr)) {
        PageFaultErrorCode code = errorCode;
        const char *modeStr = "";
        if (code.fetch)
            modeStr = "execute";
        else if (code.write)
            modeStr = "write";
        else
            modeStr = "read";

        // print information about what we are panic'ing on
        if (!inst) {
            panic("Tried to %s unmapped address %#x.", modeStr, addr);
        } else {
            panic("Tried to %s unmapped address %#x.\nPC: %#x, Instr: %s",
                  modeStr, addr, tc->pcState(),
                  inst->disassemble(tc->pcState().instAddr(),
                      &loader::debugSymbolTable));
        }
    }
}

std::string
PageFault::describe() const
{
    std::stringstream ss;
    ccprintf(ss, "%s at %#x", X86FaultBase::describe(), addr);
    return ss.str();
}

void
InitInterrupt::invoke(ThreadContext *tc, const StaticInstPtr &inst)
{
    DPRINTF(Faults, "Init interrupt.\n");
    // The otherwise unmodified integer registers should be set to 0.
    for (int index = 0; index < NUM_ARCH_INTREGS; index++) {
        tc->setIntReg(index, 0);
    }

    CR0 cr0 = tc->readMiscReg(MISCREG_CR0);
    CR0 newCR0 = 1 << 4;
    newCR0.cd = cr0.cd;
    newCR0.nw = cr0.nw;
    tc->setMiscReg(MISCREG_CR0, newCR0);
    tc->setMiscReg(MISCREG_CR2, 0);
    tc->setMiscReg(MISCREG_CR3, 0);
    tc->setMiscReg(MISCREG_CR4, 0);

    tc->setMiscReg(MISCREG_RFLAGS, 0x0000000000000002ULL);

    tc->setMiscReg(MISCREG_EFER, 0);

    SegAttr dataAttr = 0;
    dataAttr.dpl = 0;
    dataAttr.unusable = 0;
    dataAttr.defaultSize = 0;
    dataAttr.longMode = 0;
    dataAttr.avl = 0;
    dataAttr.granularity = 0;
    dataAttr.present = 1;
    dataAttr.type = 3;
    dataAttr.writable = 1;
    dataAttr.readable = 1;
    dataAttr.expandDown = 0;
    dataAttr.system = 1;

    for (int seg = 0; seg != NUM_SEGMENTREGS; seg++) {
        tc->setMiscReg(MISCREG_SEG_SEL(seg), 0);
        tc->setMiscReg(MISCREG_SEG_BASE(seg), 0);
        tc->setMiscReg(MISCREG_SEG_EFF_BASE(seg), 0);
        tc->setMiscReg(MISCREG_SEG_LIMIT(seg), 0xffff);
        tc->setMiscReg(MISCREG_SEG_ATTR(seg), dataAttr);
    }

    SegAttr codeAttr = 0;
    codeAttr.dpl = 0;
    codeAttr.unusable = 0;
    codeAttr.defaultSize = 0;
    codeAttr.longMode = 0;
    codeAttr.avl = 0;
    codeAttr.granularity = 0;
    codeAttr.present = 1;
    codeAttr.type = 10;
    codeAttr.writable = 0;
    codeAttr.readable = 1;
    codeAttr.expandDown = 0;
    codeAttr.system = 1;

    tc->setMiscReg(MISCREG_CS, 0xf000);
    tc->setMiscReg(MISCREG_CS_BASE,
            0x00000000ffff0000ULL);
    tc->setMiscReg(MISCREG_CS_EFF_BASE,
            0x00000000ffff0000ULL);
    // This has the base value pre-added.
    tc->setMiscReg(MISCREG_CS_LIMIT, 0xffffffff);
    tc->setMiscReg(MISCREG_CS_ATTR, codeAttr);

    PCState pc(0x000000000000fff0ULL + tc->readMiscReg(MISCREG_CS_BASE));
    tc->pcState(pc);

    tc->setMiscReg(MISCREG_TSG_BASE, 0);
    tc->setMiscReg(MISCREG_TSG_LIMIT, 0xffff);

    tc->setMiscReg(MISCREG_IDTR_BASE, 0);
    tc->setMiscReg(MISCREG_IDTR_LIMIT, 0xffff);

    SegAttr tslAttr = 0;
    tslAttr.unusable = 1;
    tslAttr.present = 1;
    tslAttr.type = 2; // LDT
    tc->setMiscReg(MISCREG_TSL, 0);
    tc->setMiscReg(MISCREG_TSL_BASE, 0);
    tc->setMiscReg(MISCREG_TSL_LIMIT, 0xffff);
    tc->setMiscReg(MISCREG_TSL_ATTR, tslAttr);

    SegAttr trAttr = 0;
    trAttr.unusable = 0;
    trAttr.present = 1;
    trAttr.type = 3; // Busy 16-bit TSS
    tc->setMiscReg(MISCREG_TR, 0);
    tc->setMiscReg(MISCREG_TR_BASE, 0);
    tc->setMiscReg(MISCREG_TR_LIMIT, 0xffff);
    tc->setMiscReg(MISCREG_TR_ATTR, trAttr);

    // This value should be the family/model/stepping of the processor.
    // (page 418). It should be consistent with the value from CPUID, but
    // the actual value probably doesn't matter much.
    tc->setIntReg(INTREG_RDX, 0);

    tc->setMiscReg(MISCREG_DR0, 0);
    tc->setMiscReg(MISCREG_DR1, 0);
    tc->setMiscReg(MISCREG_DR2, 0);
    tc->setMiscReg(MISCREG_DR3, 0);

    tc->setMiscReg(MISCREG_DR6, 0x00000000ffff0ff0ULL);
    tc->setMiscReg(MISCREG_DR7, 0x0000000000000400ULL);

    tc->setMiscReg(MISCREG_MXCSR, 0x1f80);

    // Flag all elements on the x87 stack as empty.
    tc->setMiscReg(MISCREG_FTW, 0xFFFF);

    // Update the handy M5 Reg.
    tc->setMiscReg(MISCREG_M5_REG, 0);
    MicroPC entry = X86ISAInst::rom_labels::extern_label_initIntHalt;
    pc.upc(romMicroPC(entry));
    pc.nupc(romMicroPC(entry) + 1);
    tc->pcState(pc);
}

void
StartupInterrupt::invoke(ThreadContext *tc, const StaticInstPtr &inst)
{
    DPRINTF(Faults, "Startup interrupt with vector %#x.\n", vector);
    HandyM5Reg m5Reg = tc->readMiscReg(MISCREG_M5_REG);
    if (m5Reg.mode != LegacyMode || m5Reg.submode != RealMode) {
        panic("Startup IPI recived outside of real mode. "
                "Don't know what to do. %d, %d", m5Reg.mode, m5Reg.submode);
    }

    tc->setMiscReg(MISCREG_CS, vector << 8);
    tc->setMiscReg(MISCREG_CS_BASE, vector << 12);
    tc->setMiscReg(MISCREG_CS_EFF_BASE, vector << 12);
    // This has the base value pre-added.
    tc->setMiscReg(MISCREG_CS_LIMIT, 0xffff);

    tc->pcState(tc->readMiscReg(MISCREG_CS_BASE));
}

} // namespace X86ISA
} // namespace gem5
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2007 The Hewlett-Packard Development Company
 * All rights reserved.
 *
 * Redistribution and use of this software in source and binary forms,
 * with or without modification, are permitted provided that the
 * following conditions are met:
 *
 * The software must be used only for Non-Commercial Use which means any
 * use which is NOT directed to receiving any direct monetary
 * compensation for, or commercial advantage from such use.  Illustrative
 * examples of non-commercial use are academic research, personal study,
 * teaching, education and corporate research & development.
 * Illustrative examples of commercial use are distributing products for
 * commercial advantage and providing services using the software for
 * commercial advantage.
 *
 * If you wish to use this software or functionality therein that may be
 * covered by patents for commercial use, please contact:
 *     Director of Intellectual Property Licensing
 *     Office of Strategy and Technology
 *     Hewlett-Packard Company
 *     1501 Page Mill Road
 *     Palo Alto, California  94304
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.  Redistributions
 * in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or
 * other materials provided with the distribution.  Neither the name of
 * the COPYRIGHT HOLDER(s), HEWLETT-PACKARD COMPANY, nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.  No right of
 * sublicense is granted herewith.  Derivatives of the software and
 * output created using the software may be prepared, but only for
 * Non-Commercial Uses.  Derivatives of the software may be shared with
 * others provided: (i) the others agree to abide by the list of
 * conditions herein which includes the Non-Commercial Use restrictions;
 * and (ii) such Derivatives of the software include the above copyright
 * notice to acknowledge the contribution from this software where
 * applicable, this list of conditions and the disclaimer below.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Gabe Black
 */

#include "arch/x86/bios/smbios.hh"
#include "arch/x86/bios/intelmp.hh"
#include "arch/x86/miscregs.hh"
#include "arch/x86/system.hh"
#include "arch/vtophys.hh"
#include "base/intmath.hh"
#include "base/loader/object_file.hh"
#include "base/loader/symtab.hh"
#include "base/remote_gdb.hh"
#include "base/trace.hh"
#include "cpu/thread_context.hh"
#include "mem/physical.hh"
#include "params/X86System.hh"
#include "sim/byteswap.hh"

using namespace LittleEndianGuest;
using namespace X86ISA;

X86System::X86System(Params *p) :
    System(p), smbiosTable(p->smbios_table),
    mpFloatingPointer(p->intel_mp_pointer),
    mpConfigTable(p->intel_mp_table),
    rsdp(p->acpi_description_table_pointer)
{}

void
X86System::startup()
{
    System::startup();
    // This is the boot strap processor (BSP). Initialize it to look like
    // the boot loader has just turned control over to the 64 bit OS. We
    // won't actually set up real mode or legacy protected mode descriptor
    // tables because we aren't executing any code that would require
    // them. We do, however toggle the control bits in the correct order
    // while allowing consistency checks and the underlying mechansims
    // just to be safe.

    const int NumPDTs = 4;

    const Addr PageMapLevel4 = 0x70000;
    const Addr PageDirPtrTable = 0x71000;
    const Addr PageDirTable[NumPDTs] =
        {0x72000, 0x73000, 0x74000, 0x75000};
    const Addr GDTBase = 0x76000;

    const int PML4Bits = 9;
    const int PDPTBits = 9;
    const int PDTBits = 9;

    // Get a port to write the page tables and descriptor tables.
    FunctionalPort * physPort = threadContexts[0]->getPhysPort();

    /*
     * Set up the gdt.
     */
    // Place holder at selector 0
    uint64_t nullDescriptor = 0;
    physPort->writeBlob(GDTBase, (uint8_t *)(&nullDescriptor), 8);

    //64 bit code segment
    SegDescriptor csDesc = 0;
    csDesc.type.c = 0; // Not conforming
    csDesc.dpl = 0; // Privelege level 0
    csDesc.p = 1; // Present
    csDesc.l = 1; // 64 bit
    csDesc.d = 0; // default operand size
    //Because we're dealing with a pointer and I don't think it's
    //guaranteed that there isn't anything in a nonvirtual class between
    //it's beginning in memory and it's actual data, we'll use an
    //intermediary.
    uint64_t csDescVal = csDesc;
    physPort->writeBlob(GDTBase, (uint8_t *)(&csDescVal), 8);

    threadContexts[0]->setMiscReg(MISCREG_TSG_BASE, GDTBase);
    threadContexts[0]->setMiscReg(MISCREG_TSG_LIMIT, 0xF);

    /*
     * Identity map the first 4GB of memory. In order to map this region
     * of memory in long mode, there needs to be one actual page map level
     * 4 entry which points to one page directory pointer table which
     * points to 4 different page directory tables which are full of two
     * megabyte pages. All of the other entries in valid tables are set
     * to indicate that they don't pertain to anything valid and will
     * cause a fault if used.
     */

    // Put valid values in all of the various table entries which indicate
    // that those entries don't point to further tables or pages. Then
    // set the values of those entries which are needed.

    // Page Map Level 4

    // read/write, user, not present
    uint64_t pml4e = X86ISA::htog(0x6);
    for (int offset = 0; offset < (1 << PML4Bits) * 8; offset += 8) {
        physPort->writeBlob(PageMapLevel4 + offset, (uint8_t *)(&pml4e), 8);
    }
    // Point to the only PDPT
    pml4e = X86ISA::htog(0x7 | PageDirPtrTable);
    physPort->writeBlob(PageMapLevel4, (uint8_t *)(&pml4e), 8);

    // Page Directory Pointer Table

    // read/write, user, not present
    uint64_t pdpe = X86ISA::htog(0x6);
    for (int offset = 0; offset < (1 << PDPTBits) * 8; offset += 8) {
        physPort->writeBlob(PageDirPtrTable + offset,
                (uint8_t *)(&pdpe), 8);
    }
    // Point to the PDTs
    for (int table = 0; table < NumPDTs; table++) {
        pdpe = X86ISA::htog(0x7 | PageDirTable[table]);
        physPort->writeBlob(PageDirPtrTable + table * 8,
                (uint8_t *)(&pdpe), 8);
    }

    // Page Directory Tables

    Addr base = 0;
    const Addr pageSize = 2 << 20;
    for (int table = 0; table < NumPDTs; table++) {
        for (int offset = 0; offset < (1 << PDTBits) * 8; offset += 8) {
            // read/write, user, present, 4MB
            uint64_t pdte = X86ISA::htog(0x87 | base);
            physPort->writeBlob(PageDirTable[table] + offset,
                    (uint8_t *)(&pdte), 8);
            base += pageSize;
        }
    }

    /*
     * Transition from real mode all the way up to Long mode
     */
    CR0 cr0 = threadContexts[0]->readMiscRegNoEffect(MISCREG_CR0);
    //Turn off paging.
    cr0.pg = 0;
    threadContexts[0]->setMiscReg(MISCREG_CR0, cr0);
    //Turn on protected mode.
    cr0.pe = 1;
    threadContexts[0]->setMiscReg(MISCREG_CR0, cr0);

    CR4 cr4 = threadContexts[0]->readMiscRegNoEffect(MISCREG_CR4);
    //Turn on pae.
    cr4.pae = 1;
    threadContexts[0]->setMiscReg(MISCREG_CR4, cr4);

    //Point to the page tables.
    threadContexts[0]->setMiscReg(MISCREG_CR3, PageMapLevel4);

    Efer efer = threadContexts[0]->readMiscRegNoEffect(MISCREG_EFER);
    //Enable long mode.
    efer.lme = 1;
    threadContexts[0]->setMiscReg(MISCREG_EFER, efer);

    //Activate long mode.
    cr0.pg = 1;
    threadContexts[0]->setMiscReg(MISCREG_CR0, cr0);

    /*
     * Far jump into 64 bit mode.
     */
    // Set the selector
    threadContexts[0]->setMiscReg(MISCREG_CS, 1);
    // Manually set up the segment attributes. In the future when there's
    // other existing functionality to do this, that could be used
    // instead.
    SegAttr csAttr = 0;
    csAttr.writable = 0;
    csAttr.readable = 1;
    csAttr.expandDown = 0;
    csAttr.dpl = 0;
    csAttr.defaultSize = 0;
    csAttr.longMode = 1;
    threadContexts[0]->setMiscReg(MISCREG_CS_ATTR, csAttr);

    threadContexts[0]->setPC(threadContexts[0]->getSystemPtr()->kernelEntry);
    threadContexts[0]->setNextPC(threadContexts[0]->readPC());

    // We should now be in long mode. Yay!

    Addr ebdaPos = 0xF0000;
    Addr fixed, table;

    //Write out the SMBios/DMI table
    writeOutSMBiosTable(ebdaPos, fixed, table);
    ebdaPos += (fixed + table);
    ebdaPos = roundUp(ebdaPos, 16);

    //Write out the Intel MP Specification configuration table
    writeOutMPTable(ebdaPos, fixed, table);
    ebdaPos += (fixed + table);
}

void
X86System::writeOutSMBiosTable(Addr header,
        Addr &headerSize, Addr &structSize, Addr table)
{
    // Get a port to write the table and header to memory.
    FunctionalPort * physPort = threadContexts[0]->getPhysPort();

    // If the table location isn't specified, just put it after the header.
    // The header size as of the 2.5 SMBios specification is 0x1F bytes
    if (!table)
        table = header + 0x1F;
    smbiosTable->setTableAddr(table);

    smbiosTable->writeOut(physPort, header, headerSize, structSize);

    // Do some bounds checking to make sure we at least didn't step on
    // ourselves.
    assert(header > table || header + headerSize <= table);
    assert(table > header || table + structSize <= header);
}

void
X86System::writeOutMPTable(Addr fp,
        Addr &fpSize, Addr &tableSize, Addr table)
{
    // Get a port to write the table and header to memory.
    FunctionalPort * physPort = threadContexts[0]->getPhysPort();

    // If the table location isn't specified and it exists, just put
    // it after the floating pointer. The fp size as of the 1.4 Intel MP
    // specification is 0x10 bytes.
    if (mpConfigTable) {
        if (!table)
            table = fp + 0x10;
        mpFloatingPointer->setTableAddr(table);
    }

    fpSize = mpFloatingPointer->writeOut(physPort, fp);
    if (mpConfigTable)
        tableSize = mpConfigTable->writeOut(physPort, table);
    else
        tableSize = 0;

    // Do some bounds checking to make sure we at least didn't step on
    // ourselves and the fp structure was the size we thought it was.
    assert(fp > table || fp + fpSize <= table);
    assert(table > fp || table + tableSize <= fp);
    assert(fpSize == 0x10);
}

X86System::~X86System()
{
    delete smbiosTable;
}

void
X86System::serialize(std::ostream &os)
{
    System::serialize(os);
}

void
X86System::unserialize(Checkpoint *cp, const std::string &section)
{
    System::unserialize(cp,section);
}

X86System *
X86SystemParams::create()
{
    return new X86System(this);
}
<commit_msg>X86: Really set up the GDT and various hidden/visible segment registers.<commit_after>/*
 * Copyright (c) 2007 The Hewlett-Packard Development Company
 * All rights reserved.
 *
 * Redistribution and use of this software in source and binary forms,
 * with or without modification, are permitted provided that the
 * following conditions are met:
 *
 * The software must be used only for Non-Commercial Use which means any
 * use which is NOT directed to receiving any direct monetary
 * compensation for, or commercial advantage from such use.  Illustrative
 * examples of non-commercial use are academic research, personal study,
 * teaching, education and corporate research & development.
 * Illustrative examples of commercial use are distributing products for
 * commercial advantage and providing services using the software for
 * commercial advantage.
 *
 * If you wish to use this software or functionality therein that may be
 * covered by patents for commercial use, please contact:
 *     Director of Intellectual Property Licensing
 *     Office of Strategy and Technology
 *     Hewlett-Packard Company
 *     1501 Page Mill Road
 *     Palo Alto, California  94304
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.  Redistributions
 * in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or
 * other materials provided with the distribution.  Neither the name of
 * the COPYRIGHT HOLDER(s), HEWLETT-PACKARD COMPANY, nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.  No right of
 * sublicense is granted herewith.  Derivatives of the software and
 * output created using the software may be prepared, but only for
 * Non-Commercial Uses.  Derivatives of the software may be shared with
 * others provided: (i) the others agree to abide by the list of
 * conditions herein which includes the Non-Commercial Use restrictions;
 * and (ii) such Derivatives of the software include the above copyright
 * notice to acknowledge the contribution from this software where
 * applicable, this list of conditions and the disclaimer below.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Gabe Black
 */

#include "arch/x86/bios/smbios.hh"
#include "arch/x86/bios/intelmp.hh"
#include "arch/x86/miscregs.hh"
#include "arch/x86/system.hh"
#include "arch/vtophys.hh"
#include "base/intmath.hh"
#include "base/loader/object_file.hh"
#include "base/loader/symtab.hh"
#include "base/remote_gdb.hh"
#include "base/trace.hh"
#include "cpu/thread_context.hh"
#include "mem/physical.hh"
#include "params/X86System.hh"
#include "sim/byteswap.hh"

using namespace LittleEndianGuest;
using namespace X86ISA;

X86System::X86System(Params *p) :
    System(p), smbiosTable(p->smbios_table),
    mpFloatingPointer(p->intel_mp_pointer),
    mpConfigTable(p->intel_mp_table),
    rsdp(p->acpi_description_table_pointer)
{}

static void
installSegDesc(ThreadContext *tc, SegmentRegIndex seg,
        SegDescriptor desc, bool longmode)
{
    uint64_t base = desc.baseLow + (desc.baseHigh << 24);
    bool honorBase = !longmode || seg == SEGMENT_REG_FS ||
                                  seg == SEGMENT_REG_GS ||
                                  seg == SEGMENT_REG_TSL ||
                                  seg == SYS_SEGMENT_REG_TR;
    uint64_t limit = desc.limitLow | (desc.limitHigh << 16);

    SegAttr attr = 0;
    if (desc.s) {
        if (desc.type.codeOrData) {
            // Code segment
            attr.readable = desc.type.r;
        } else {
            // Data segment
            attr.readable = 1;
            attr.writable = desc.type.w;
            attr.expandDown = desc.type.e;
        }
    } else {
        attr.writable = 1;
        attr.readable = 1;
        attr.expandDown = 0;
    }
    attr.longMode = desc.l;
    attr.dpl = desc.dpl;
    attr.defaultSize = desc.d;

    tc->setMiscReg(MISCREG_SEG_BASE(seg), base);
    tc->setMiscReg(MISCREG_SEG_EFF_BASE(seg), honorBase ? base : 0);
    tc->setMiscReg(MISCREG_SEG_LIMIT(seg), limit);
    tc->setMiscReg(MISCREG_SEG_ATTR(seg), (MiscReg)attr);
}

void
X86System::startup()
{
    System::startup();
    ThreadContext *tc = threadContexts[0];
    // This is the boot strap processor (BSP). Initialize it to look like
    // the boot loader has just turned control over to the 64 bit OS. We
    // won't actually set up real mode or legacy protected mode descriptor
    // tables because we aren't executing any code that would require
    // them. We do, however toggle the control bits in the correct order
    // while allowing consistency checks and the underlying mechansims
    // just to be safe.

    const int NumPDTs = 4;

    const Addr PageMapLevel4 = 0x70000;
    const Addr PageDirPtrTable = 0x71000;
    const Addr PageDirTable[NumPDTs] =
        {0x72000, 0x73000, 0x74000, 0x75000};
    const Addr GDTBase = 0x76000;

    const int PML4Bits = 9;
    const int PDPTBits = 9;
    const int PDTBits = 9;

    // Get a port to write the page tables and descriptor tables.
    FunctionalPort * physPort = tc->getPhysPort();

    /*
     * Set up the gdt.
     */
    uint8_t numGDTEntries = 0;
    // Place holder at selector 0
    uint64_t nullDescriptor = 0;
    physPort->writeBlob(GDTBase + numGDTEntries * 8,
            (uint8_t *)(&nullDescriptor), 8);
    numGDTEntries++;

    //64 bit code segment
    SegDescriptor csDesc = 0;
    csDesc.type.codeOrData = 1;
    csDesc.type.c = 0; // Not conforming
    csDesc.type.r = 1; // Readable
    csDesc.dpl = 0; // Privelege level 0
    csDesc.p = 1; // Present
    csDesc.l = 1; // 64 bit
    csDesc.d = 0; // default operand size
    csDesc.g = 1; // Page granularity
    csDesc.s = 1; // Not a system segment
    csDesc.limitHigh = 0xF;
    csDesc.limitLow = 0xFF;
    //Because we're dealing with a pointer and I don't think it's
    //guaranteed that there isn't anything in a nonvirtual class between
    //it's beginning in memory and it's actual data, we'll use an
    //intermediary.
    uint64_t csDescVal = csDesc;
    physPort->writeBlob(GDTBase + numGDTEntries * 8,
            (uint8_t *)(&csDescVal), 8);

    numGDTEntries++;

    SegSelector cs = 0;
    cs.si = numGDTEntries - 1;

    tc->setMiscReg(MISCREG_CS, (MiscReg)cs);

    //32 bit data segment
    SegDescriptor dsDesc = 0;
    dsDesc.type.codeOrData = 0;
    dsDesc.type.e = 0; // Not expand down
    dsDesc.type.w = 1; // Writable
    dsDesc.dpl = 0; // Privelege level 0
    dsDesc.p = 1; // Present
    dsDesc.d = 1; // default operand size
    dsDesc.g = 1; // Page granularity
    dsDesc.s = 1; // Not a system segment
    dsDesc.limitHigh = 0xF;
    dsDesc.limitLow = 0xFF;
    uint64_t dsDescVal = dsDesc;
    physPort->writeBlob(GDTBase + numGDTEntries * 8,
            (uint8_t *)(&dsDescVal), 8);

    numGDTEntries++;

    SegSelector ds;
    ds.si = numGDTEntries - 1;

    tc->setMiscReg(MISCREG_DS, (MiscReg)ds);
    tc->setMiscReg(MISCREG_ES, (MiscReg)ds);
    tc->setMiscReg(MISCREG_FS, (MiscReg)ds);
    tc->setMiscReg(MISCREG_GS, (MiscReg)ds);
    tc->setMiscReg(MISCREG_SS, (MiscReg)ds);

    tc->setMiscReg(MISCREG_TSL, 0);
    tc->setMiscReg(MISCREG_TSG_BASE, GDTBase);
    tc->setMiscReg(MISCREG_TSG_LIMIT, 8 * numGDTEntries - 1);

    SegDescriptor tssDesc = 0;
    tssDesc.type = 0xB;
    tssDesc.dpl = 0; // Privelege level 0
    tssDesc.p = 1; // Present
    tssDesc.d = 1; // default operand size
    tssDesc.g = 1; // Page granularity
    tssDesc.s = 1; // Not a system segment
    tssDesc.limitHigh = 0xF;
    tssDesc.limitLow = 0xFF;
    uint64_t tssDescVal = tssDesc;
    physPort->writeBlob(GDTBase + numGDTEntries * 8,
            (uint8_t *)(&tssDescVal), 8);

    numGDTEntries++;

    SegSelector tss = 0;
    tss.si = numGDTEntries - 1;

    tc->setMiscReg(MISCREG_TR, (MiscReg)tss);
    installSegDesc(tc, SYS_SEGMENT_REG_TR, tssDesc, true);

    /*
     * Identity map the first 4GB of memory. In order to map this region
     * of memory in long mode, there needs to be one actual page map level
     * 4 entry which points to one page directory pointer table which
     * points to 4 different page directory tables which are full of two
     * megabyte pages. All of the other entries in valid tables are set
     * to indicate that they don't pertain to anything valid and will
     * cause a fault if used.
     */

    // Put valid values in all of the various table entries which indicate
    // that those entries don't point to further tables or pages. Then
    // set the values of those entries which are needed.

    // Page Map Level 4

    // read/write, user, not present
    uint64_t pml4e = X86ISA::htog(0x6);
    for (int offset = 0; offset < (1 << PML4Bits) * 8; offset += 8) {
        physPort->writeBlob(PageMapLevel4 + offset, (uint8_t *)(&pml4e), 8);
    }
    // Point to the only PDPT
    pml4e = X86ISA::htog(0x7 | PageDirPtrTable);
    physPort->writeBlob(PageMapLevel4, (uint8_t *)(&pml4e), 8);

    // Page Directory Pointer Table

    // read/write, user, not present
    uint64_t pdpe = X86ISA::htog(0x6);
    for (int offset = 0; offset < (1 << PDPTBits) * 8; offset += 8) {
        physPort->writeBlob(PageDirPtrTable + offset,
                (uint8_t *)(&pdpe), 8);
    }
    // Point to the PDTs
    for (int table = 0; table < NumPDTs; table++) {
        pdpe = X86ISA::htog(0x7 | PageDirTable[table]);
        physPort->writeBlob(PageDirPtrTable + table * 8,
                (uint8_t *)(&pdpe), 8);
    }

    // Page Directory Tables

    Addr base = 0;
    const Addr pageSize = 2 << 20;
    for (int table = 0; table < NumPDTs; table++) {
        for (int offset = 0; offset < (1 << PDTBits) * 8; offset += 8) {
            // read/write, user, present, 4MB
            uint64_t pdte = X86ISA::htog(0x87 | base);
            physPort->writeBlob(PageDirTable[table] + offset,
                    (uint8_t *)(&pdte), 8);
            base += pageSize;
        }
    }

    /*
     * Transition from real mode all the way up to Long mode
     */
    CR0 cr0 = tc->readMiscRegNoEffect(MISCREG_CR0);
    //Turn off paging.
    cr0.pg = 0;
    tc->setMiscReg(MISCREG_CR0, cr0);
    //Turn on protected mode.
    cr0.pe = 1;
    tc->setMiscReg(MISCREG_CR0, cr0);

    CR4 cr4 = tc->readMiscRegNoEffect(MISCREG_CR4);
    //Turn on pae.
    cr4.pae = 1;
    tc->setMiscReg(MISCREG_CR4, cr4);

    //Point to the page tables.
    tc->setMiscReg(MISCREG_CR3, PageMapLevel4);

    Efer efer = tc->readMiscRegNoEffect(MISCREG_EFER);
    //Enable long mode.
    efer.lme = 1;
    tc->setMiscReg(MISCREG_EFER, efer);

    //Start using longmode segments.
    installSegDesc(tc, SEGMENT_REG_CS, csDesc, true);
    installSegDesc(tc, SEGMENT_REG_DS, dsDesc, true);
    installSegDesc(tc, SEGMENT_REG_ES, dsDesc, true);
    installSegDesc(tc, SEGMENT_REG_FS, dsDesc, true);
    installSegDesc(tc, SEGMENT_REG_GS, dsDesc, true);
    installSegDesc(tc, SEGMENT_REG_SS, dsDesc, true);

    //Activate long mode.
    cr0.pg = 1;
    tc->setMiscReg(MISCREG_CR0, cr0);

    tc->setPC(tc->getSystemPtr()->kernelEntry);
    tc->setNextPC(tc->readPC());

    // We should now be in long mode. Yay!

    Addr ebdaPos = 0xF0000;
    Addr fixed, table;

    //Write out the SMBios/DMI table
    writeOutSMBiosTable(ebdaPos, fixed, table);
    ebdaPos += (fixed + table);
    ebdaPos = roundUp(ebdaPos, 16);

    //Write out the Intel MP Specification configuration table
    writeOutMPTable(ebdaPos, fixed, table);
    ebdaPos += (fixed + table);
}

void
X86System::writeOutSMBiosTable(Addr header,
        Addr &headerSize, Addr &structSize, Addr table)
{
    // Get a port to write the table and header to memory.
    FunctionalPort * physPort = threadContexts[0]->getPhysPort();

    // If the table location isn't specified, just put it after the header.
    // The header size as of the 2.5 SMBios specification is 0x1F bytes
    if (!table)
        table = header + 0x1F;
    smbiosTable->setTableAddr(table);

    smbiosTable->writeOut(physPort, header, headerSize, structSize);

    // Do some bounds checking to make sure we at least didn't step on
    // ourselves.
    assert(header > table || header + headerSize <= table);
    assert(table > header || table + structSize <= header);
}

void
X86System::writeOutMPTable(Addr fp,
        Addr &fpSize, Addr &tableSize, Addr table)
{
    // Get a port to write the table and header to memory.
    FunctionalPort * physPort = threadContexts[0]->getPhysPort();

    // If the table location isn't specified and it exists, just put
    // it after the floating pointer. The fp size as of the 1.4 Intel MP
    // specification is 0x10 bytes.
    if (mpConfigTable) {
        if (!table)
            table = fp + 0x10;
        mpFloatingPointer->setTableAddr(table);
    }

    fpSize = mpFloatingPointer->writeOut(physPort, fp);
    if (mpConfigTable)
        tableSize = mpConfigTable->writeOut(physPort, table);
    else
        tableSize = 0;

    // Do some bounds checking to make sure we at least didn't step on
    // ourselves and the fp structure was the size we thought it was.
    assert(fp > table || fp + fpSize <= table);
    assert(table > fp || table + tableSize <= fp);
    assert(fpSize == 0x10);
}

X86System::~X86System()
{
    delete smbiosTable;
}

void
X86System::serialize(std::ostream &os)
{
    System::serialize(os);
}

void
X86System::unserialize(Checkpoint *cp, const std::string &section)
{
    System::unserialize(cp,section);
}

X86System *
X86SystemParams::create()
{
    return new X86System(this);
}
<|endoftext|>


<commit_before>/**
 * @file
 * @brief Implementation of module to read weighting fields
 * @copyright Copyright (c) 2017 CERN and the Allpix Squared authors.
 * This software is distributed under the terms of the MIT License, copied
 * verbatim in the file "LICENSE.md".
 * In applying this license, CERN does not waive the privileges and immunities
 * granted to it by virtue of its status as an
 * Intergovernmental Organization or submit itself to any jurisdiction.
 */

#include "WeightingFieldReaderModule.hpp"

#include <fstream>
#include <limits>
#include <memory>
#include <new>
#include <stdexcept>
#include <string>
#include <utility>

#include <Math/RotationZ.h>
#include <Math/Vector3D.h>
#include <TH2F.h>

#include "core/config/exceptions.h"
#include "core/geometry/DetectorModel.hpp"
#include "core/utils/log.h"
#include "core/utils/unit.h"

using namespace allpix;

WeightingFieldReaderModule::WeightingFieldReaderModule(Configuration& config, Messenger*, std::shared_ptr<Detector> detector)
    : Module(config, detector), detector_(std::move(detector)) {}

void WeightingFieldReaderModule::init() {

    auto field_model = config_.get<std::string>("model");

    // Calculate thickness domain
    auto model = detector_->getModel();
    auto sensor_max_z = model->getSensorCenter().z() + model->getSensorSize().z() / 2.0;
    auto thickness_domain = std::make_pair(sensor_max_z - model->getSensorSize().z(), sensor_max_z);

    // Calculate the field depending on the configuration
    if(field_model == "init") {
        auto field_data = read_init_field(thickness_domain);
        detector_->setWeightingFieldGrid(
            std::get<0>(field_data), std::get<1>(field_data), std::get<2>(field_data), thickness_domain);
    } else if(field_model == "pad") {
        LOG(TRACE) << "Adding weighting field from pad in plane condenser";

        // Get pixel implant size from the detector model:
        auto implant = model->getImplantSize();
        FieldFunction<ROOT::Math::XYZVector> function = get_pad_field_function(implant, thickness_domain);
        detector_->setWeightingFieldFunction(function, thickness_domain, FieldType::CUSTOM);
    } else {
        throw InvalidValueError(config_, "model", "model should be 'init' or `pad`");
    }

    // Produce histograms if needed
    if(config_.get<bool>("output_plots", false)) {
        create_output_plots();
    }
}

/**
 * Implement weighting field from doi:10.1016/j.nima.2014.08.44 and return it as a lookup function.
 */
FieldFunction<ROOT::Math::XYZVector>
WeightingFieldReaderModule::get_pad_field_function(const ROOT::Math::XYVector& implant,
                                                   std::pair<double, double> thickness_domain) {

    LOG(TRACE) << "Calculating function for the plane condenser weighting field field." << std::endl;

    return [implant, thickness_domain](const ROOT::Math::XYZPoint& pos) {
        // Calculate values of the "g" function for all three components
        auto g = [implant](double x, double y, double u) {
            // Calculate terms derived from the arctan functions in the potential for all components
            auto fraction = [](double a, double b, double c) {
                double denominator = std::sqrt(a * a + b * b + c * c);
                // Three different components required since the derivatives have different solutions
                return ROOT::Math::XYZVector(b * c / ((a * a + c * c) * denominator),
                                             a * c / ((b * b + c * c) * denominator),
                                             a * b * (a * a + b * b + 2 * c * c) /
                                                 ((a * a + c * c) * (b * b + c * c) * denominator));
            };

            // Shift the x and y coordinates by plus/minus half the implant size:
            double x1 = x - implant.x() / 2;
            double x2 = x + implant.x() / 2;
            double y1 = y - implant.y() / 2;
            double y2 = y + implant.y() / 2;

            // Sum the four components of the "g" function and return:
            return (fraction(x1, y1, u) + fraction(x2, y2, u) - fraction(x1, y2, u) - fraction(x2, y1, u));
        };

        // Transform into coordinate system with sensor between d/2 < z < -d/2:
        auto d = thickness_domain.second - thickness_domain.first;
        auto local_z = -pos.z() + thickness_domain.second;

        // Calculate the series expansion
        ROOT::Math::XYZVector sum;
        for(int n = 1; n <= 100; n++) {
            sum += g(pos.x(), pos.y(), 2 * n * d - local_z) +
                   ROOT::Math::RotationZ(M_PI) * g(pos.x(), pos.y(), 2 * n * d + local_z);
        }

        return (1 / (2 * M_PI) * (ROOT::Math::RotationZ(M_PI) * g(pos.x(), pos.y(), local_z) + sum));
    };
}

void WeightingFieldReaderModule::create_output_plots() {
    LOG(TRACE) << "Creating output plots";

    auto steps = config_.get<size_t>("output_plots_steps", 500);
    auto position = config_.get<ROOT::Math::XYPoint>("output_plots_position", ROOT::Math::XYPoint(0, 0));

    auto model = detector_->getModel();

    double min = model->getSensorCenter().z() - model->getSensorSize().z() / 2.0;
    double max = model->getSensorCenter().z() + model->getSensorSize().z() / 2.0;

    // Create 1D histograms
    auto histogramX =
        new TH1F("field1d_x", "E_{w}/V_{w} (x-component);x (mm);field strength (1/cm)", static_cast<int>(steps), min, max);
    auto histogramY =
        new TH1F("field1d_y", "E_{w}/V_{w} (y-component);y (mm);field strength (1/cm)", static_cast<int>(steps), min, max);
    auto histogramZ =
        new TH1F("field1d_z", "E_{w}/V_{w} (z-component);z (mm);field strength (1/cm)", static_cast<int>(steps), min, max);

    // Get the weighting field at every index
    for(size_t j = 0; j < steps; ++j) {
        double z = min + ((static_cast<double>(j) + 0.5) / static_cast<double>(steps)) * (max - min);

        // Get field strength from detector
        auto field = detector_->getWeightingField(ROOT::Math::XYZPoint(position.x(), position.y(), z), 0, 0);
        auto field_x_strength = Units::convert(field.x(), "1/cm");
        auto field_y_strength = Units::convert(field.y(), "1/cm");
        auto field_z_strength = Units::convert(field.z(), "1/cm");

        // Fill the histograms
        histogramX->Fill(z, static_cast<double>(field_x_strength));
        histogramY->Fill(z, static_cast<double>(field_y_strength));
        histogramZ->Fill(z, static_cast<double>(field_z_strength));
    }

    // Create 2D histogram
    auto histogram = new TH2F("field_z",
                              "E_{w}/V_{w} (z-component);x (mm); z (mm); field strength (1/cm)",
                              static_cast<int>(steps),
                              -1.5 * model->getPixelSize().x(),
                              1.5 * model->getPixelSize().x(),
                              static_cast<int>(steps),
                              min,
                              max);
    // histogram->SetMinimum(0);

    // Get the weighting field at every index
    for(size_t j = 0; j < steps; ++j) {
        LOG_PROGRESS(INFO, "plotting") << "Plotting progress " << 100 * j * steps / (steps * steps) << "%";
        for(size_t k = 0; k < steps; ++k) {
            double z = min + ((static_cast<double>(j) + 0.5) / static_cast<double>(steps)) * (max - min);
            double x = -0.5 * model->getPixelSize().x() +
                       ((static_cast<double>(k) + 0.5) / static_cast<double>(steps)) * 3 * model->getPixelSize().x();

            // LOG(DEBUG) << "Requesting field ad local coords (" << Units::display(x, {"cm", "mm", "um"}) << "," << 0;
            // Get field strength from detector
            auto field = detector_->getWeightingField(ROOT::Math::XYZPoint(x, 0, z), 1, 0);
            auto field_z_strength = Units::convert(field.z(), "1/cm");

            // Fill the histograms, shift x-axis by one pixel so the reference electrode in centered.
            histogram->Fill(x - model->getPixelSize().x(), z, static_cast<double>(field_z_strength));
        }
    }

    // Write the histogram to module file
    histogramX->Write();
    histogramY->Write();
    histogramZ->Write();
    histogram->Write();
}

/**
 * The field read from the INIT format are shared between module instantiations
 * using the static WeightingFieldReaderModule::get_by_file_name method.
 */
FieldParser<double, 3> WeightingFieldReaderModule::field_parser_("");
FieldData<double> WeightingFieldReaderModule::read_init_field(std::pair<double, double> thickness_domain) {
    using namespace ROOT::Math;

    try {
        LOG(TRACE) << "Fetching weighting field from init file";

        // Get field from file
        auto field_data = field_parser_.get_by_file_name(config_.getPath("file_name", true));

        // Check if electric field matches chip
        check_detector_match(std::get<2>(field_data), thickness_domain);

        LOG(INFO) << "Set weighting field with " << std::get<1>(field_data).at(0) << "x" << std::get<1>(field_data).at(1)
                  << "x" << std::get<1>(field_data).at(2) << " cells";

        // Return the field data
        return field_data;
    } catch(std::invalid_argument& e) {
        throw InvalidValueError(config_, "file_name", e.what());
    } catch(std::runtime_error& e) {
        throw InvalidValueError(config_, "file_name", e.what());
    } catch(std::bad_alloc& e) {
        throw InvalidValueError(config_, "file_name", "file too large");
    }
}

/**
 * @brief Check if the detector matches the file header
 */
void WeightingFieldReaderModule::check_detector_match(std::array<double, 3> dimensions,
                                                      std::pair<double, double> thickness_domain) {
    auto xpixsz = dimensions[0];
    auto ypixsz = dimensions[1];
    auto thickness = dimensions[2];

    auto model = detector_->getModel();
    // Do a several checks with the detector model
    if(model != nullptr) {
        // Check field dimension in z versus the requested thickness domain:
        auto eff_thickness = thickness_domain.second - thickness_domain.first;
        if(std::fabs(thickness - eff_thickness) > std::numeric_limits<double>::epsilon()) {
            LOG(WARNING) << "Thickness of weighting field is " << Units::display(thickness, "um")
                         << " but the depleted region is " << Units::display(eff_thickness, "um");
        }

        // Check that the total field size is n*pitch:
        if(std::fmod(xpixsz, model->getPixelSize().x()) > std::numeric_limits<double>::epsilon() ||
           std::fmod(ypixsz, model->getPixelSize().y()) > std::numeric_limits<double>::epsilon()) {
            LOG(WARNING) << "Field size is (" << Units::display(xpixsz, {"um", "mm"}) << ","
                         << Units::display(ypixsz, {"um", "mm"}) << ") but expecting a multiple of the pixel pitch ("
                         << Units::display(model->getPixelSize().x(), {"um", "mm"}) << ", "
                         << Units::display(model->getPixelSize().y(), {"um", "mm"}) << ")";
        }
    }
}
<commit_msg>WeightingfieldReader: calculate field scales from size and pixel pitch<commit_after>/**
 * @file
 * @brief Implementation of module to read weighting fields
 * @copyright Copyright (c) 2017 CERN and the Allpix Squared authors.
 * This software is distributed under the terms of the MIT License, copied
 * verbatim in the file "LICENSE.md".
 * In applying this license, CERN does not waive the privileges and immunities
 * granted to it by virtue of its status as an
 * Intergovernmental Organization or submit itself to any jurisdiction.
 */

#include "WeightingFieldReaderModule.hpp"

#include <fstream>
#include <limits>
#include <memory>
#include <new>
#include <stdexcept>
#include <string>
#include <utility>

#include <Math/RotationZ.h>
#include <Math/Vector3D.h>
#include <TH2F.h>

#include "core/config/exceptions.h"
#include "core/geometry/DetectorModel.hpp"
#include "core/utils/log.h"
#include "core/utils/unit.h"

using namespace allpix;

WeightingFieldReaderModule::WeightingFieldReaderModule(Configuration& config, Messenger*, std::shared_ptr<Detector> detector)
    : Module(config, detector), detector_(std::move(detector)) {}

void WeightingFieldReaderModule::init() {

    auto field_model = config_.get<std::string>("model");

    // Calculate thickness domain
    auto model = detector_->getModel();
    auto sensor_max_z = model->getSensorCenter().z() + model->getSensorSize().z() / 2.0;
    auto thickness_domain = std::make_pair(sensor_max_z - model->getSensorSize().z(), sensor_max_z);

    // Calculate the field depending on the configuration
    if(field_model == "init") {
        auto field_data = read_init_field(thickness_domain);

        // Calculate field scale from field size and pixel pitch:
        auto pixel_size = model->getPixelSize();
        std::array<double, 2> field_scale{
            {std::get<2>(field_data)[0] / pixel_size.x(), std::get<2>(field_data)[1] / pixel_size.y()}};

        detector_->setWeightingFieldGrid(
            std::get<0>(field_data), std::get<1>(field_data), field_scale, std::array<double, 2>{0, 0}, thickness_domain);
    } else if(field_model == "pad") {
        LOG(TRACE) << "Adding weighting field from pad in plane condenser";

        // Get pixel implant size from the detector model:
        auto implant = model->getImplantSize();
        FieldFunction<ROOT::Math::XYZVector> function = get_pad_field_function(implant, thickness_domain);
        detector_->setWeightingFieldFunction(function, thickness_domain, FieldType::CUSTOM);
    } else {
        throw InvalidValueError(config_, "model", "model should be 'init' or `pad`");
    }

    // Produce histograms if needed
    if(config_.get<bool>("output_plots", false)) {
        create_output_plots();
    }
}

/**
 * Implement weighting field from doi:10.1016/j.nima.2014.08.44 and return it as a lookup function.
 */
FieldFunction<ROOT::Math::XYZVector>
WeightingFieldReaderModule::get_pad_field_function(const ROOT::Math::XYVector& implant,
                                                   std::pair<double, double> thickness_domain) {

    LOG(TRACE) << "Calculating function for the plane condenser weighting field." << std::endl;

    return [implant, thickness_domain](const ROOT::Math::XYZPoint& pos) {
        // Calculate values of the "g" function for all three components
        auto g = [implant](double x, double y, double u) {
            // Calculate terms derived from the arctan functions in the potential for all components
            auto fraction = [](double a, double b, double c) {
                double denominator = std::sqrt(a * a + b * b + c * c);
                // Three different components required since the derivatives have different solutions
                return ROOT::Math::XYZVector(b * c / ((a * a + c * c) * denominator),
                                             a * c / ((b * b + c * c) * denominator),
                                             a * b * (a * a + b * b + 2 * c * c) /
                                                 ((a * a + c * c) * (b * b + c * c) * denominator));
            };

            // Shift the x and y coordinates by plus/minus half the implant size:
            double x1 = x - implant.x() / 2;
            double x2 = x + implant.x() / 2;
            double y1 = y - implant.y() / 2;
            double y2 = y + implant.y() / 2;

            // Sum the four components of the "g" function and return:
            return (fraction(x1, y1, u) + fraction(x2, y2, u) - fraction(x1, y2, u) - fraction(x2, y1, u));
        };

        // Transform into coordinate system with sensor between d/2 < z < -d/2:
        auto d = thickness_domain.second - thickness_domain.first;
        auto local_z = -pos.z() + thickness_domain.second;

        // Calculate the series expansion
        ROOT::Math::XYZVector sum;
        for(int n = 1; n <= 100; n++) {
            sum += g(pos.x(), pos.y(), 2 * n * d - local_z) +
                   ROOT::Math::RotationZ(M_PI) * g(pos.x(), pos.y(), 2 * n * d + local_z);
        }

        return (1 / (2 * M_PI) * (ROOT::Math::RotationZ(M_PI) * g(pos.x(), pos.y(), local_z) + sum));
    };
}

void WeightingFieldReaderModule::create_output_plots() {
    LOG(TRACE) << "Creating output plots";

    auto steps = config_.get<size_t>("output_plots_steps", 500);
    auto position = config_.get<ROOT::Math::XYPoint>("output_plots_position", ROOT::Math::XYPoint(0, 0));

    auto model = detector_->getModel();

    double min = model->getSensorCenter().z() - model->getSensorSize().z() / 2.0;
    double max = model->getSensorCenter().z() + model->getSensorSize().z() / 2.0;

    // Create 1D histograms
    auto histogramX =
        new TH1F("field1d_x", "E_{w}/V_{w} (x-component);x (mm);field strength (1/cm)", static_cast<int>(steps), min, max);
    auto histogramY =
        new TH1F("field1d_y", "E_{w}/V_{w} (y-component);y (mm);field strength (1/cm)", static_cast<int>(steps), min, max);
    auto histogramZ =
        new TH1F("field1d_z", "E_{w}/V_{w} (z-component);z (mm);field strength (1/cm)", static_cast<int>(steps), min, max);

    // Get the weighting field at every index
    for(size_t j = 0; j < steps; ++j) {
        double z = min + ((static_cast<double>(j) + 0.5) / static_cast<double>(steps)) * (max - min);

        // Get field strength from detector
        auto field = detector_->getWeightingField(ROOT::Math::XYZPoint(position.x(), position.y(), z), 0, 0);
        auto field_x_strength = Units::convert(field.x(), "1/cm");
        auto field_y_strength = Units::convert(field.y(), "1/cm");
        auto field_z_strength = Units::convert(field.z(), "1/cm");

        // Fill the histograms
        histogramX->Fill(z, static_cast<double>(field_x_strength));
        histogramY->Fill(z, static_cast<double>(field_y_strength));
        histogramZ->Fill(z, static_cast<double>(field_z_strength));
    }

    // Create 2D histogram
    auto histogram = new TH2F("field_z",
                              "E_{w}/V_{w} (z-component);x (mm); z (mm); field strength (1/cm)",
                              static_cast<int>(steps),
                              -1.5 * model->getPixelSize().x(),
                              1.5 * model->getPixelSize().x(),
                              static_cast<int>(steps),
                              min,
                              max);
    // histogram->SetMinimum(0);

    // Get the weighting field at every index
    for(size_t j = 0; j < steps; ++j) {
        LOG_PROGRESS(INFO, "plotting") << "Plotting progress " << 100 * j * steps / (steps * steps) << "%";
        for(size_t k = 0; k < steps; ++k) {
            double z = min + ((static_cast<double>(j) + 0.5) / static_cast<double>(steps)) * (max - min);
            double x = -0.5 * model->getPixelSize().x() +
                       ((static_cast<double>(k) + 0.5) / static_cast<double>(steps)) * 3 * model->getPixelSize().x();

            // LOG(DEBUG) << "Requesting field ad local coords (" << Units::display(x, {"cm", "mm", "um"}) << "," << 0;
            // Get field strength from detector
            auto field = detector_->getWeightingField(ROOT::Math::XYZPoint(x, 0, z), 1, 0);
            auto field_z_strength = Units::convert(field.z(), "1/cm");

            // Fill the histograms, shift x-axis by one pixel so the reference electrode in centered.
            histogram->Fill(x - model->getPixelSize().x(), z, static_cast<double>(field_z_strength));
        }
    }

    // Write the histogram to module file
    histogramX->Write();
    histogramY->Write();
    histogramZ->Write();
    histogram->Write();
}

/**
 * The field read from the INIT format are shared between module instantiations
 * using the static WeightingFieldReaderModule::get_by_file_name method.
 */
FieldParser<double, 3> WeightingFieldReaderModule::field_parser_("");
FieldData<double> WeightingFieldReaderModule::read_init_field(std::pair<double, double> thickness_domain) {
    using namespace ROOT::Math;

    try {
        LOG(TRACE) << "Fetching weighting field from init file";

        // Get field from file
        auto field_data = field_parser_.get_by_file_name(config_.getPath("file_name", true));

        // Check if electric field matches chip
        check_detector_match(std::get<2>(field_data), thickness_domain);

        LOG(INFO) << "Set weighting field with " << std::get<1>(field_data).at(0) << "x" << std::get<1>(field_data).at(1)
                  << "x" << std::get<1>(field_data).at(2) << " cells";

        // Return the field data
        return field_data;
    } catch(std::invalid_argument& e) {
        throw InvalidValueError(config_, "file_name", e.what());
    } catch(std::runtime_error& e) {
        throw InvalidValueError(config_, "file_name", e.what());
    } catch(std::bad_alloc& e) {
        throw InvalidValueError(config_, "file_name", "file too large");
    }
}

/**
 * @brief Check if the detector matches the file header
 */
void WeightingFieldReaderModule::check_detector_match(std::array<double, 3> dimensions,
                                                      std::pair<double, double> thickness_domain) {
    auto xpixsz = dimensions[0];
    auto ypixsz = dimensions[1];
    auto thickness = dimensions[2];

    auto model = detector_->getModel();
    // Do a several checks with the detector model
    if(model != nullptr) {
        // Check field dimension in z versus the requested thickness domain:
        auto eff_thickness = thickness_domain.second - thickness_domain.first;
        if(std::fabs(thickness - eff_thickness) > std::numeric_limits<double>::epsilon()) {
            LOG(WARNING) << "Thickness of weighting field is " << Units::display(thickness, "um")
                         << " but the depleted region is " << Units::display(eff_thickness, "um");
        }

        // Check that the total field size is n*pitch:
        if(std::fmod(xpixsz, model->getPixelSize().x()) > std::numeric_limits<double>::epsilon() ||
           std::fmod(ypixsz, model->getPixelSize().y()) > std::numeric_limits<double>::epsilon()) {
            LOG(WARNING) << "Field size is (" << Units::display(xpixsz, {"um", "mm"}) << ","
                         << Units::display(ypixsz, {"um", "mm"}) << ") but expecting a multiple of the pixel pitch ("
                         << Units::display(model->getPixelSize().x(), {"um", "mm"}) << ", "
                         << Units::display(model->getPixelSize().y(), {"um", "mm"}) << ")";
        }
    }
}
<|endoftext|>


<commit_before>/*
Resembla: Word-based Japanese similar sentence search library
https://github.com/tuem/resembla

Copyright 2017 Takashi Uemura

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#ifndef RESEMBLA_BASIC_RESEMBLA_HPP
#define RESEMBLA_BASIC_RESEMBLA_HPP

#include <string>
#include <fstream>
#include <vector>
#include <unordered_map>
#include <memory>
#include <stdexcept>
#include <mutex>

#include <simstring/simstring.h>
#include <json.hpp>

#include "resembla_interface.hpp"
#include "reranker.hpp"

#include "measure/weighted_sequence_serializer.hpp"

namespace resembla {

// Resembla with a fixed pair of preprocessor and score function
template<
    typename Preprocessor,
    typename ScoreFunction
>
class BasicResembla: public ResemblaInterface
{
public:
    BasicResembla(const std::string& db_path, const std::string& inverse_path,
            const int simstring_measure, const double simstring_threshold, const size_t max_reranking_num,
            std::shared_ptr<Preprocessor> preprocess, std::shared_ptr<ScoreFunction> score_func,
            bool preprocess_corpus = true, size_t preprocessed_data_col = 3):
        simstring_measure(simstring_measure), simstring_threshold(simstring_threshold), max_reranking_num(max_reranking_num),
        reranker(), preprocess(preprocess), score_func(score_func), preprocess_corpus(preprocess_corpus)
    {
        db.open(db_path);
        std::basic_ifstream<string_type::value_type> ifs(inverse_path);
        if(ifs.fail()){
            throw std::runtime_error("input file is not available: " + inverse_path);
        }
        while(ifs.good()){
            string_type line;
            std::getline(ifs, line);
            if(ifs.eof() || line.length() == 0){
                break;
            }

            auto columns = split(line, column_delimiter<string_type::value_type>());
            if(columns.size() < 2){
                throw std::runtime_error("too few columns, corpus=" + inverse_path + ", line=" + cast_string<std::string>(line));
            }
            const auto& indexed = columns[0];
            auto original = columns[1];

            const auto& i = inverse.find(indexed);
            if(i == std::end(inverse)){
                inverse[indexed] = {original};
            }
            else{
                inverse.at(indexed).push_back(original);
            }

            if(preprocess_corpus){
                if(preprocessed_data_col > 0 && preprocessed_data_col - 1 < columns.size() && !columns[preprocessed_data_col - 1].empty()){
                    nlohmann::json j = nlohmann::json::parse(cast_string<std::string>(columns[preprocessed_data_col - 1]));
                    typename Preprocessor::output_type preprocessed = j;
                    preprocessed_corpus[columns[1]] = std::make_pair(columns[1], preprocessed);
                }
                else{
                    preprocessed_corpus[columns[1]] = std::make_pair(columns[1], (*preprocess)(original, true));
                }
            }
        }
    }

    std::vector<output_type> find(const string_type& query, double threshold = 0.0, size_t max_response = 0) const
    {
        // search from N-gram index
        std::vector<string_type> simstring_result;
        {
            std::lock_guard<std::mutex> lock(mutex_simstring);
            string_type search_query = preprocess->index(query);
            db.retrieve(search_query, simstring_measure, simstring_threshold, std::back_inserter(simstring_result));
            if(simstring_result.empty()){
                return {};
            }
        }

        std::vector<string_type> candidate_texts;
        for(const auto& i: simstring_result){
            if(i.empty()){
                continue;
            }
            const auto& j = inverse.at(i);
            std::copy(std::begin(j), std::end(j), std::back_inserter(candidate_texts));
            if(candidate_texts.size() == max_reranking_num){
                break;
            }
        }
        return eval(query, candidate_texts, threshold, max_response);
    }

    std::vector<output_type> eval(const string_type& query, const std::vector<string_type>& targets,
            double threshold = 0.0, size_t max_response = 0) const
    {
        // load preprocessed data if preprocessing is enabled. otherwise, process corpus texts on demand
        std::vector<WorkData> candidates;
        for(const auto& t: targets){
            if(preprocess_corpus){
                const auto i = preprocessed_corpus.find(t);
                if(i != std::end(preprocessed_corpus)){
                    candidates.push_back(i->second);
                    continue;
                }
            }
            auto tabpos = t.find(column_delimiter<string_type::value_type>());
            candidates.push_back(std::make_pair(
                tabpos != string_type::npos ? t.substr(0, tabpos) : t,
                (*preprocess)(t, true)));
        }

        // execute reranking
        WorkData input_data = std::make_pair(query, (*preprocess)(query, false));
        std::vector<output_type> response;
        for(const auto& r: reranker.rerank(input_data, std::begin(candidates), std::end(candidates), *score_func, threshold, max_response)){
            response.push_back({r.first, score_func->name, r.second});
        }
        return response;
    }

protected:
    using WorkData = std::pair<string_type, typename Preprocessor::output_type>;

    mutable simstring::reader db;
    std::unordered_map<string_type, std::vector<string_type>> inverse;

    const int simstring_measure;
    const double simstring_threshold;
    const size_t max_reranking_num;
    const Reranker<string_type> reranker;

    const std::shared_ptr<Preprocessor> preprocess;
    const std::shared_ptr<ScoreFunction> score_func;

    const bool preprocess_corpus;
    std::unordered_map<string_type, WorkData> preprocessed_corpus;

    mutable std::mutex mutex_simstring;
};

}
#endif
<commit_msg>add const<commit_after>/*
Resembla: Word-based Japanese similar sentence search library
https://github.com/tuem/resembla

Copyright 2017 Takashi Uemura

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#ifndef RESEMBLA_BASIC_RESEMBLA_HPP
#define RESEMBLA_BASIC_RESEMBLA_HPP

#include <string>
#include <fstream>
#include <vector>
#include <unordered_map>
#include <memory>
#include <stdexcept>
#include <mutex>

#include <simstring/simstring.h>
#include <json.hpp>

#include "resembla_interface.hpp"
#include "reranker.hpp"

#include "measure/weighted_sequence_serializer.hpp"

namespace resembla {

// Resembla with a fixed pair of preprocessor and score function
template<
    typename Preprocessor,
    typename ScoreFunction
>
class BasicResembla: public ResemblaInterface
{
public:
    BasicResembla(const std::string& db_path, const std::string& inverse_path,
            const int simstring_measure, const double simstring_threshold, const size_t max_reranking_num,
            std::shared_ptr<Preprocessor> preprocess, std::shared_ptr<ScoreFunction> score_func,
            bool preprocess_corpus = true, size_t preprocessed_data_col = 3):
        simstring_measure(simstring_measure), simstring_threshold(simstring_threshold), max_reranking_num(max_reranking_num),
        reranker(), preprocess(preprocess), score_func(score_func), preprocess_corpus(preprocess_corpus)
    {
        db.open(db_path);
        std::basic_ifstream<string_type::value_type> ifs(inverse_path);
        if(ifs.fail()){
            throw std::runtime_error("input file is not available: " + inverse_path);
        }
        while(ifs.good()){
            string_type line;
            std::getline(ifs, line);
            if(ifs.eof() || line.length() == 0){
                break;
            }

            auto columns = split(line, column_delimiter<string_type::value_type>());
            if(columns.size() < 2){
                throw std::runtime_error("too few columns, corpus=" + inverse_path + ", line=" + cast_string<std::string>(line));
            }
            const auto& indexed = columns[0];
            const auto& original = columns[1];

            const auto& i = inverse.find(indexed);
            if(i == std::end(inverse)){
                inverse[indexed] = {original};
            }
            else{
                inverse.at(indexed).push_back(original);
            }

            if(preprocess_corpus){
                if(preprocessed_data_col > 0 && preprocessed_data_col - 1 < columns.size() && !columns[preprocessed_data_col - 1].empty()){
                    nlohmann::json j = nlohmann::json::parse(cast_string<std::string>(columns[preprocessed_data_col - 1]));
                    typename Preprocessor::output_type preprocessed = j;
                    preprocessed_corpus[columns[1]] = std::make_pair(columns[1], preprocessed);
                }
                else{
                    preprocessed_corpus[columns[1]] = std::make_pair(columns[1], (*preprocess)(original, true));
                }
            }
        }
    }

    std::vector<output_type> find(const string_type& query, double threshold = 0.0, size_t max_response = 0) const
    {
        // search from N-gram index
        std::vector<string_type> simstring_result;
        {
            std::lock_guard<std::mutex> lock(mutex_simstring);
            string_type search_query = preprocess->index(query);
            db.retrieve(search_query, simstring_measure, simstring_threshold, std::back_inserter(simstring_result));
            if(simstring_result.empty()){
                return {};
            }
        }

        std::vector<string_type> candidate_texts;
        for(const auto& i: simstring_result){
            if(i.empty()){
                continue;
            }
            const auto& j = inverse.at(i);
            std::copy(std::begin(j), std::end(j), std::back_inserter(candidate_texts));
            if(candidate_texts.size() == max_reranking_num){
                break;
            }
        }
        return eval(query, candidate_texts, threshold, max_response);
    }

    std::vector<output_type> eval(const string_type& query, const std::vector<string_type>& targets,
            double threshold = 0.0, size_t max_response = 0) const
    {
        // load preprocessed data if preprocessing is enabled. otherwise, process corpus texts on demand
        std::vector<WorkData> candidates;
        for(const auto& t: targets){
            if(preprocess_corpus){
                const auto i = preprocessed_corpus.find(t);
                if(i != std::end(preprocessed_corpus)){
                    candidates.push_back(i->second);
                    continue;
                }
            }
            auto tabpos = t.find(column_delimiter<string_type::value_type>());
            candidates.push_back(std::make_pair(
                tabpos != string_type::npos ? t.substr(0, tabpos) : t,
                (*preprocess)(t, true)));
        }

        // execute reranking
        WorkData input_data = std::make_pair(query, (*preprocess)(query, false));
        std::vector<output_type> response;
        for(const auto& r: reranker.rerank(input_data, std::begin(candidates), std::end(candidates), *score_func, threshold, max_response)){
            response.push_back({r.first, score_func->name, r.second});
        }
        return response;
    }

protected:
    using WorkData = std::pair<string_type, typename Preprocessor::output_type>;

    mutable simstring::reader db;
    std::unordered_map<string_type, std::vector<string_type>> inverse;

    const int simstring_measure;
    const double simstring_threshold;
    const size_t max_reranking_num;
    const Reranker<string_type> reranker;

    const std::shared_ptr<Preprocessor> preprocess;
    const std::shared_ptr<ScoreFunction> score_func;

    const bool preprocess_corpus;
    std::unordered_map<string_type, WorkData> preprocessed_corpus;

    mutable std::mutex mutex_simstring;
};

}
#endif
<|endoftext|>