thread_context_impl.hh revision 8852:c744483edfcf
18706Sandreas.hansson@arm.com/* 27586SAli.Saidi@arm.com * Copyright (c) 2010-2011 ARM Limited 37586SAli.Saidi@arm.com * All rights reserved 47586SAli.Saidi@arm.com * 57586SAli.Saidi@arm.com * The license below extends only to copyright in the software and shall 67586SAli.Saidi@arm.com * not be construed as granting a license to any other intellectual 77586SAli.Saidi@arm.com * property including but not limited to intellectual property relating 87586SAli.Saidi@arm.com * to a hardware implementation of the functionality of the software 97586SAli.Saidi@arm.com * licensed hereunder. You may use the software subject to the license 107586SAli.Saidi@arm.com * terms below provided that you ensure that this notice is replicated 117586SAli.Saidi@arm.com * unmodified and in its entirety in all distributions of the software, 127586SAli.Saidi@arm.com * modified or unmodified, in source code or in binary form. 137905SBrad.Beckmann@amd.com * 145323Sgblack@eecs.umich.edu * Copyright (c) 2004-2006 The Regents of The University of Michigan 152934Sktlim@umich.edu * All rights reserved. 162934Sktlim@umich.edu * 172934Sktlim@umich.edu * Redistribution and use in source and binary forms, with or without 182934Sktlim@umich.edu * modification, are permitted provided that the following conditions are 192934Sktlim@umich.edu * met: redistributions of source code must retain the above copyright 202934Sktlim@umich.edu * notice, this list of conditions and the following disclaimer; 212934Sktlim@umich.edu * redistributions in binary form must reproduce the above copyright 222934Sktlim@umich.edu * notice, this list of conditions and the following disclaimer in the 232934Sktlim@umich.edu * documentation and/or other materials provided with the distribution; 242934Sktlim@umich.edu * neither the name of the copyright holders nor the names of its 252934Sktlim@umich.edu * contributors may be used to endorse or promote products derived from 262934Sktlim@umich.edu * this software without specific prior written permission. 272934Sktlim@umich.edu * 282934Sktlim@umich.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 292934Sktlim@umich.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 302934Sktlim@umich.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 312934Sktlim@umich.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 322934Sktlim@umich.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 332934Sktlim@umich.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 342934Sktlim@umich.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 352934Sktlim@umich.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 362934Sktlim@umich.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 372934Sktlim@umich.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 382934Sktlim@umich.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 392934Sktlim@umich.edu * 402934Sktlim@umich.edu * Authors: Kevin Lim 412934Sktlim@umich.edu * Korey Sewell 422934Sktlim@umich.edu */ 432995Ssaidi@eecs.umich.edu 448528SAli.Saidi@ARM.com#include "arch/kernel_stats.hh" 452934Sktlim@umich.edu#include "arch/registers.hh" 462934Sktlim@umich.edu#include "config/the_isa.hh" 472934Sktlim@umich.edu#include "config/use_checker.hh" 482934Sktlim@umich.edu#include "cpu/o3/thread_context.hh" 492934Sktlim@umich.edu#include "cpu/quiesce_event.hh" 502934Sktlim@umich.edu#include "debug/O3CPU.hh" 512934Sktlim@umich.edu 522934Sktlim@umich.edutemplate <class Impl> 536122SSteve.Reinhardt@amd.comFSTranslatingPortProxy& 546122SSteve.Reinhardt@amd.comO3ThreadContext<Impl>::getVirtProxy() 556122SSteve.Reinhardt@amd.com{ 566122SSteve.Reinhardt@amd.com return thread->getVirtProxy(); 576122SSteve.Reinhardt@amd.com} 584520Ssaidi@eecs.umich.edu 598713Sandreas.hansson@arm.comtemplate <class Impl> 604520Ssaidi@eecs.umich.eduvoid 614982Ssaidi@eecs.umich.eduO3ThreadContext<Impl>::dumpFuncProfile() 624520Ssaidi@eecs.umich.edu{ 634520Ssaidi@eecs.umich.edu thread->dumpFuncProfile(); 642934Sktlim@umich.edu} 652934Sktlim@umich.edu 663005Sstever@eecs.umich.edutemplate <class Impl> 673005Sstever@eecs.umich.eduvoid 683304Sstever@eecs.umich.eduO3ThreadContext<Impl>::takeOverFrom(ThreadContext *old_context) 692995Ssaidi@eecs.umich.edu{ 702934Sktlim@umich.edu // some things should already be set up 716122SSteve.Reinhardt@amd.com assert(getSystemPtr() == old_context->getSystemPtr()); 728713Sandreas.hansson@arm.com assert(getProcessPtr() == old_context->getProcessPtr()); 738713Sandreas.hansson@arm.com 748713Sandreas.hansson@arm.com // copy over functional state 758713Sandreas.hansson@arm.com setStatus(old_context->status()); 765266Sksewell@umich.edu copyArchRegs(old_context); 778713Sandreas.hansson@arm.com setContextId(old_context->contextId()); 788713Sandreas.hansson@arm.com setThreadId(old_context->threadId()); 792934Sktlim@umich.edu 802934Sktlim@umich.edu if (FullSystem) { 812934Sktlim@umich.edu EndQuiesceEvent *other_quiesce = old_context->getQuiesceEvent(); 822995Ssaidi@eecs.umich.edu if (other_quiesce) { 832934Sktlim@umich.edu // Point the quiesce event's TC at this TC so that it wakes up 842934Sktlim@umich.edu // the proper CPU. 852934Sktlim@umich.edu other_quiesce->tc = this; 862934Sktlim@umich.edu } 878714Sandreas.hansson@arm.com if (thread->quiesceEvent) { 888714Sandreas.hansson@arm.com thread->quiesceEvent->tc = this; 892934Sktlim@umich.edu } 908714Sandreas.hansson@arm.com 918714Sandreas.hansson@arm.com // Transfer kernel stats from one CPU to the other. 922995Ssaidi@eecs.umich.edu thread->kernelStats = old_context->getKernelStats(); 932934Sktlim@umich.edu cpu->lockFlag = false; 942934Sktlim@umich.edu } else { 952953Sktlim@umich.edu thread->funcExeInst = old_context->readFuncExeInst(); 965478Snate@binkert.org } 972934Sktlim@umich.edu 983449Shsul@eecs.umich.edu old_context->setStatus(ThreadContext::Halted); 992934Sktlim@umich.edu 1002934Sktlim@umich.edu thread->inSyscall = false; 1012934Sktlim@umich.edu thread->trapPending = false; 1028706Sandreas.hansson@arm.com} 1038706Sandreas.hansson@arm.com 1042934Sktlim@umich.edutemplate <class Impl> 1052934Sktlim@umich.eduvoid 1067014SBrad.Beckmann@amd.comO3ThreadContext<Impl>::activate(int delay) 1076765SBrad.Beckmann@amd.com{ 1086765SBrad.Beckmann@amd.com DPRINTF(O3CPU, "Calling activate on Thread Context %d\n", 1096765SBrad.Beckmann@amd.com threadId()); 1106765SBrad.Beckmann@amd.com 1116765SBrad.Beckmann@amd.com if (thread->status() == ThreadContext::Active) 1127014SBrad.Beckmann@amd.com return; 1137014SBrad.Beckmann@amd.com 1146765SBrad.Beckmann@amd.com thread->lastActivate = curTick(); 1156765SBrad.Beckmann@amd.com thread->setStatus(ThreadContext::Active); 1166765SBrad.Beckmann@amd.com 1176765SBrad.Beckmann@amd.com // status() == Suspended 1186765SBrad.Beckmann@amd.com cpu->activateContext(thread->threadId(), delay); 1196765SBrad.Beckmann@amd.com} 1206765SBrad.Beckmann@amd.com 1216893SBrad.Beckmann@amd.comtemplate <class Impl> 1226893SBrad.Beckmann@amd.comvoid 1236893SBrad.Beckmann@amd.comO3ThreadContext<Impl>::suspend(int delay) 1246893SBrad.Beckmann@amd.com{ 1256893SBrad.Beckmann@amd.com DPRINTF(O3CPU, "Calling suspend on Thread Context %d\n", 1266893SBrad.Beckmann@amd.com threadId()); 1277014SBrad.Beckmann@amd.com 1286893SBrad.Beckmann@amd.com if (thread->status() == ThreadContext::Suspended) 1296765SBrad.Beckmann@amd.com return; 1306765SBrad.Beckmann@amd.com 1316765SBrad.Beckmann@amd.com thread->lastActivate = curTick(); 1326765SBrad.Beckmann@amd.com thread->lastSuspend = curTick(); 1336765SBrad.Beckmann@amd.com 1346765SBrad.Beckmann@amd.com thread->setStatus(ThreadContext::Suspended); 1356765SBrad.Beckmann@amd.com cpu->suspendContext(thread->threadId()); 1368714Sandreas.hansson@arm.com} 1378714Sandreas.hansson@arm.com 1386765SBrad.Beckmann@amd.comtemplate <class Impl> 1398714Sandreas.hansson@arm.comvoid 1408714Sandreas.hansson@arm.comO3ThreadContext<Impl>::halt(int delay) 1416765SBrad.Beckmann@amd.com{ 1426893SBrad.Beckmann@amd.com DPRINTF(O3CPU, "Calling halt on Thread Context %d\n", 1437633SBrad.Beckmann@amd.com threadId()); 1447633SBrad.Beckmann@amd.com 1456893SBrad.Beckmann@amd.com if (thread->status() == ThreadContext::Halted) 1467633SBrad.Beckmann@amd.com return; 1476765SBrad.Beckmann@amd.com 1486765SBrad.Beckmann@amd.com thread->setStatus(ThreadContext::Halted); 1496765SBrad.Beckmann@amd.com cpu->haltContext(thread->threadId()); 1506765SBrad.Beckmann@amd.com} 1516765SBrad.Beckmann@amd.com 1526765SBrad.Beckmann@amd.comtemplate <class Impl> 1536765SBrad.Beckmann@amd.comvoid 1546765SBrad.Beckmann@amd.comO3ThreadContext<Impl>::regStats(const std::string &name) 1556765SBrad.Beckmann@amd.com{ 1566765SBrad.Beckmann@amd.com if (FullSystem) { 1576765SBrad.Beckmann@amd.com thread->kernelStats = new TheISA::Kernel::Statistics(cpu->system); 1586765SBrad.Beckmann@amd.com thread->kernelStats->regStats(name + ".kern"); 1596765SBrad.Beckmann@amd.com } 1603584Ssaidi@eecs.umich.edu} 1618713Sandreas.hansson@arm.com 1628713Sandreas.hansson@arm.comtemplate <class Impl> 1638713Sandreas.hansson@arm.comvoid 1648713Sandreas.hansson@arm.comO3ThreadContext<Impl>::serialize(std::ostream &os) 1654486Sbinkertn@umich.edu{ 1664486Sbinkertn@umich.edu if (FullSystem && thread->kernelStats) 1674486Sbinkertn@umich.edu thread->kernelStats->serialize(os); 1684486Sbinkertn@umich.edu} 1694486Sbinkertn@umich.edu 1704486Sbinkertn@umich.edutemplate <class Impl> 1714486Sbinkertn@umich.eduvoid 1723584Ssaidi@eecs.umich.eduO3ThreadContext<Impl>::unserialize(Checkpoint *cp, const std::string §ion) 1733584Ssaidi@eecs.umich.edu{ 1743584Ssaidi@eecs.umich.edu if (FullSystem && thread->kernelStats) 1753584Ssaidi@eecs.umich.edu thread->kernelStats->unserialize(cp, section); 1763584Ssaidi@eecs.umich.edu} 1773743Sgblack@eecs.umich.edu 1786122SSteve.Reinhardt@amd.comtemplate <class Impl> 1794972Ssaidi@eecs.umich.eduTick 1803743Sgblack@eecs.umich.eduO3ThreadContext<Impl>::readLastActivate() 1814104Ssaidi@eecs.umich.edu{ 1823743Sgblack@eecs.umich.edu return thread->lastActivate; 1833823Ssaidi@eecs.umich.edu} 1843814Ssaidi@eecs.umich.edu 1858713Sandreas.hansson@arm.comtemplate <class Impl> 1868713Sandreas.hansson@arm.comTick 1873584Ssaidi@eecs.umich.eduO3ThreadContext<Impl>::readLastSuspend() 1883814Ssaidi@eecs.umich.edu{ 1893584Ssaidi@eecs.umich.edu return thread->lastSuspend; 1903745Sgblack@eecs.umich.edu} 1913745Sgblack@eecs.umich.edu 1923745Sgblack@eecs.umich.edutemplate <class Impl> 1933584Ssaidi@eecs.umich.eduvoid 1943898Ssaidi@eecs.umich.eduO3ThreadContext<Impl>::profileClear() 1953898Ssaidi@eecs.umich.edu{ 1963898Ssaidi@eecs.umich.edu thread->profileClear(); 1978713Sandreas.hansson@arm.com} 1988713Sandreas.hansson@arm.com 1998713Sandreas.hansson@arm.comtemplate <class Impl> 2008713Sandreas.hansson@arm.comvoid 2018713Sandreas.hansson@arm.comO3ThreadContext<Impl>::profileSample() 2028713Sandreas.hansson@arm.com{ 2038713Sandreas.hansson@arm.com thread->profileSample(); 2048713Sandreas.hansson@arm.com} 2058713Sandreas.hansson@arm.com 2068713Sandreas.hansson@arm.comtemplate <class Impl> 2078713Sandreas.hansson@arm.comvoid 2088713Sandreas.hansson@arm.comO3ThreadContext<Impl>::copyArchRegs(ThreadContext *tc) 2098713Sandreas.hansson@arm.com{ 2108713Sandreas.hansson@arm.com // Prevent squashing 2118713Sandreas.hansson@arm.com thread->inSyscall = true; 2128713Sandreas.hansson@arm.com TheISA::copyRegs(tc, this); 2138713Sandreas.hansson@arm.com thread->inSyscall = false; 2148713Sandreas.hansson@arm.com 2158713Sandreas.hansson@arm.com if (!FullSystem) 2164103Ssaidi@eecs.umich.edu this->thread->funcExeInst = tc->readFuncExeInst(); 2174103Ssaidi@eecs.umich.edu} 2184103Ssaidi@eecs.umich.edu 2193745Sgblack@eecs.umich.edutemplate <class Impl> 2203745Sgblack@eecs.umich.eduvoid 2213745Sgblack@eecs.umich.eduO3ThreadContext<Impl>::clearArchRegs() 2223584Ssaidi@eecs.umich.edu{ 2238706Sandreas.hansson@arm.com cpu->isa[thread->threadId()].clear(); 2248706Sandreas.hansson@arm.com} 2253584Ssaidi@eecs.umich.edu 2263584Ssaidi@eecs.umich.edutemplate <class Impl> 2278061SAli.Saidi@ARM.comuint64_t 2288061SAli.Saidi@ARM.comO3ThreadContext<Impl>::readIntReg(int reg_idx) 2298061SAli.Saidi@ARM.com{ 2307586SAli.Saidi@arm.com reg_idx = cpu->isa[thread->threadId()].flattenIntIndex(reg_idx); 2317586SAli.Saidi@arm.com return cpu->readArchIntReg(reg_idx, thread->threadId()); 2327586SAli.Saidi@arm.com} 2337586SAli.Saidi@arm.com 2347586SAli.Saidi@arm.comtemplate <class Impl> 2357586SAli.Saidi@arm.comTheISA::FloatReg 2367586SAli.Saidi@arm.comO3ThreadContext<Impl>::readFloatReg(int reg_idx) 2377586SAli.Saidi@arm.com{ 2387586SAli.Saidi@arm.com reg_idx = cpu->isa[thread->threadId()].flattenFloatIndex(reg_idx); 2397586SAli.Saidi@arm.com return cpu->readArchFloatReg(reg_idx, thread->threadId()); 2407586SAli.Saidi@arm.com} 2417586SAli.Saidi@arm.com 2427586SAli.Saidi@arm.comtemplate <class Impl> 2437586SAli.Saidi@arm.comTheISA::FloatRegBits 2448713Sandreas.hansson@arm.comO3ThreadContext<Impl>::readFloatRegBits(int reg_idx) 2458713Sandreas.hansson@arm.com{ 2467586SAli.Saidi@arm.com reg_idx = cpu->isa[thread->threadId()].flattenFloatIndex(reg_idx); 2477586SAli.Saidi@arm.com return cpu->readArchFloatRegInt(reg_idx, thread->threadId()); 2487586SAli.Saidi@arm.com} 2497586SAli.Saidi@arm.com 2507586SAli.Saidi@arm.comtemplate <class Impl> 2517586SAli.Saidi@arm.comvoid 2527586SAli.Saidi@arm.comO3ThreadContext<Impl>::setIntReg(int reg_idx, uint64_t val) 2538525SAli.Saidi@ARM.com{ 2548525SAli.Saidi@ARM.com reg_idx = cpu->isa[thread->threadId()].flattenIntIndex(reg_idx); 2557586SAli.Saidi@arm.com cpu->setArchIntReg(reg_idx, val, thread->threadId()); 2567586SAli.Saidi@arm.com 2577586SAli.Saidi@arm.com // Squash if we're not already in a state update mode. 2587586SAli.Saidi@arm.com if (!thread->trapPending && !thread->inSyscall) { 2598528SAli.Saidi@ARM.com cpu->squashFromTC(thread->threadId()); 2608528SAli.Saidi@ARM.com } 2618528SAli.Saidi@ARM.com} 2628528SAli.Saidi@ARM.com 2638528SAli.Saidi@ARM.comtemplate <class Impl> 2648528SAli.Saidi@ARM.comvoid 2658528SAli.Saidi@ARM.comO3ThreadContext<Impl>::setFloatReg(int reg_idx, FloatReg val) 2668528SAli.Saidi@ARM.com{ 2678528SAli.Saidi@ARM.com reg_idx = cpu->isa[thread->threadId()].flattenFloatIndex(reg_idx); 2688061SAli.Saidi@ARM.com cpu->setArchFloatReg(reg_idx, val, thread->threadId()); 2698061SAli.Saidi@ARM.com 2708061SAli.Saidi@ARM.com if (!thread->trapPending && !thread->inSyscall) { 2718528SAli.Saidi@ARM.com cpu->squashFromTC(thread->threadId()); 2728212SAli.Saidi@ARM.com } 2738061SAli.Saidi@ARM.com} 2748528SAli.Saidi@ARM.com 2757586SAli.Saidi@arm.comtemplate <class Impl> 2768528SAli.Saidi@ARM.comvoid 2778528SAli.Saidi@ARM.comO3ThreadContext<Impl>::setFloatRegBits(int reg_idx, FloatRegBits val) 2788528SAli.Saidi@ARM.com{ 2798528SAli.Saidi@ARM.com reg_idx = cpu->isa[thread->threadId()].flattenFloatIndex(reg_idx); 2808528SAli.Saidi@ARM.com cpu->setArchFloatRegInt(reg_idx, val, thread->threadId()); 2818212SAli.Saidi@ARM.com 2828528SAli.Saidi@ARM.com // Squash if we're not already in a state update mode. 2838528SAli.Saidi@ARM.com if (!thread->trapPending && !thread->inSyscall) { 2848528SAli.Saidi@ARM.com cpu->squashFromTC(thread->threadId()); 2858528SAli.Saidi@ARM.com } 2868528SAli.Saidi@ARM.com} 2878528SAli.Saidi@ARM.com 2888528SAli.Saidi@ARM.comtemplate <class Impl> 2898528SAli.Saidi@ARM.comvoid 2908528SAli.Saidi@ARM.comO3ThreadContext<Impl>::pcState(const TheISA::PCState &val) 2918528SAli.Saidi@ARM.com{ 2928528SAli.Saidi@ARM.com cpu->pcState(val, thread->threadId()); 2938287SAli.Saidi@ARM.com 2948643Satgutier@umich.edu // Squash if we're not already in a state update mode. 2958595SAli.Saidi@ARM.com if (!thread->trapPending && !thread->inSyscall) { 2968212SAli.Saidi@ARM.com cpu->squashFromTC(thread->threadId()); 2977586SAli.Saidi@arm.com } 2988145SAli.Saidi@ARM.com} 2998713Sandreas.hansson@arm.com 3007586SAli.Saidi@arm.com#if USE_CHECKER 3017586SAli.Saidi@arm.comtemplate <class Impl> 3027586SAli.Saidi@arm.comvoid 3037949SAli.Saidi@ARM.comO3ThreadContext<Impl>::pcStateNoRecord(const TheISA::PCState &val) 3047586SAli.Saidi@arm.com{ 3058706Sandreas.hansson@arm.com cpu->pcState(val, thread->threadId()); 3068706Sandreas.hansson@arm.com 3077586SAli.Saidi@arm.com // Squash if we're not already in a state update mode. 3087586SAli.Saidi@arm.com if (!thread->trapPending && !thread->inSyscall) { 3097586SAli.Saidi@arm.com cpu->squashFromTC(thread->threadId()); 3105222Sksewell@umich.edu } 3115222Sksewell@umich.edu} 3125222Sksewell@umich.edu#endif 3135222Sksewell@umich.edu 3145222Sksewell@umich.edutemplate <class Impl> 3155222Sksewell@umich.eduint 3165222Sksewell@umich.eduO3ThreadContext<Impl>::flattenIntIndex(int reg) 3175222Sksewell@umich.edu{ 3185222Sksewell@umich.edu return cpu->isa[thread->threadId()].flattenIntIndex(reg); 3195222Sksewell@umich.edu} 3205222Sksewell@umich.edu 3215222Sksewell@umich.edutemplate <class Impl> 3226122SSteve.Reinhardt@amd.comint 3235222Sksewell@umich.eduO3ThreadContext<Impl>::flattenFloatIndex(int reg) 3245222Sksewell@umich.edu{ 3258713Sandreas.hansson@arm.com return cpu->isa[thread->threadId()].flattenFloatIndex(reg); 3268713Sandreas.hansson@arm.com} 3275222Sksewell@umich.edu 3285222Sksewell@umich.edutemplate <class Impl> 3295222Sksewell@umich.eduvoid 3305222Sksewell@umich.eduO3ThreadContext<Impl>::setMiscRegNoEffect(int misc_reg, const MiscReg &val) 3315222Sksewell@umich.edu{ 3325222Sksewell@umich.edu cpu->setMiscRegNoEffect(misc_reg, val, thread->threadId()); 3335222Sksewell@umich.edu 3345222Sksewell@umich.edu // Squash if we're not already in a state update mode. 3358714Sandreas.hansson@arm.com if (!thread->trapPending && !thread->inSyscall) { 3368714Sandreas.hansson@arm.com cpu->squashFromTC(thread->threadId()); 3375222Sksewell@umich.edu } 3388714Sandreas.hansson@arm.com} 3398714Sandreas.hansson@arm.com 3405222Sksewell@umich.edutemplate <class Impl> 3415222Sksewell@umich.eduvoid 3425222Sksewell@umich.eduO3ThreadContext<Impl>::setMiscReg(int misc_reg, const MiscReg &val) 3435222Sksewell@umich.edu{ 3445478Snate@binkert.org cpu->setMiscReg(misc_reg, val, thread->threadId()); 3455222Sksewell@umich.edu 3465222Sksewell@umich.edu // Squash if we're not already in a state update mode. 3475222Sksewell@umich.edu if (!thread->trapPending && !thread->inSyscall) { 3485222Sksewell@umich.edu cpu->squashFromTC(thread->threadId()); 3498706Sandreas.hansson@arm.com } 3508706Sandreas.hansson@arm.com} 3515222Sksewell@umich.edu 3525222Sksewell@umich.edu