1 Star 0 Fork 38

xisme/qemu-kvm_src-anolis

forked from src-anolis-os/qemu-kvm 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
0003-Add-loongarch-machine.patch 188.99 KB
一键复制 编辑 原始数据 按行查看 历史
Xianglai Li 提交于 2024-08-29 19:00 +08:00 . fix compile error for loongarch
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752
From 3cb18285115f8dd7f9135c65b39c93cb7eb3580c Mon Sep 17 00:00:00 2001
From: lixianglai <lixianglai@loongson.cn>
Date: Fri, 19 Aug 2022 23:39:00 -0400
Subject: [PATCH 03/28] Add loongarch machine.
Change-Id: I7e31f874c676b740269945d5e19c6bc836de6a99
Signed-off-by: lixianglai <lixianglai@loongson.cn>
---
hw/loongarch/Kconfig | 17 +
hw/loongarch/acpi-build.c | 783 ++++++++++++
hw/loongarch/acpi-build.h | 16 +
hw/loongarch/apic.c | 675 +++++++++++
hw/loongarch/ioapic.c | 422 +++++++
hw/loongarch/iocsr.c | 219 ++++
hw/loongarch/ipi.c | 267 +++++
hw/loongarch/larch_3a.c | 2026 ++++++++++++++++++++++++++++++++
hw/loongarch/larch_hotplug.c | 355 ++++++
hw/loongarch/larch_int.c | 91 ++
hw/loongarch/ls7a_nb.c | 352 ++++++
hw/loongarch/meson.build | 15 +
include/hw/loongarch/bios.h | 5 +
include/hw/loongarch/cpudevs.h | 53 +
include/hw/loongarch/larch.h | 163 +++
include/hw/loongarch/ls7a.h | 152 +++
16 files changed, 5611 insertions(+)
create mode 100644 hw/loongarch/Kconfig
create mode 100644 hw/loongarch/acpi-build.c
create mode 100644 hw/loongarch/acpi-build.h
create mode 100644 hw/loongarch/apic.c
create mode 100644 hw/loongarch/ioapic.c
create mode 100644 hw/loongarch/iocsr.c
create mode 100644 hw/loongarch/ipi.c
create mode 100644 hw/loongarch/larch_3a.c
create mode 100644 hw/loongarch/larch_hotplug.c
create mode 100644 hw/loongarch/larch_int.c
create mode 100644 hw/loongarch/ls7a_nb.c
create mode 100644 hw/loongarch/meson.build
create mode 100644 include/hw/loongarch/bios.h
create mode 100644 include/hw/loongarch/cpudevs.h
create mode 100644 include/hw/loongarch/larch.h
create mode 100644 include/hw/loongarch/ls7a.h
diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig
new file mode 100644
index 000000000..3fe2677fd
--- /dev/null
+++ b/hw/loongarch/Kconfig
@@ -0,0 +1,17 @@
+config LS7A_APIC
+ bool
+
+config LS7A_RTC
+ bool
+
+config LOONGSON3A
+ bool
+
+config MEM_HOTPLUG
+ bool
+
+config ACPI_LOONGARCH
+ bool
+
+config E1000E_PCI
+ bool
diff --git a/hw/loongarch/acpi-build.c b/hw/loongarch/acpi-build.c
new file mode 100644
index 000000000..6ba637be5
--- /dev/null
+++ b/hw/loongarch/acpi-build.c
@@ -0,0 +1,783 @@
+/* Support for generating ACPI tables and passing them to Guests
+ *
+ * Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net>
+ * Copyright (C) 2006 Fabrice Bellard
+ * Copyright (C) 2013 Red Hat Inc
+ *
+ * Author: Michael S. Tsirkin <mst@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qapi/qmp/qnum.h"
+#include "acpi-build.h"
+#include "qemu-common.h"
+#include "qemu/bitmap.h"
+#include "qemu/error-report.h"
+#include "hw/pci/pci.h"
+#include "hw/boards.h"
+#include "hw/core/cpu.h"
+#include "target/loongarch64/cpu.h"
+#include "hw/misc/pvpanic.h"
+#include "hw/timer/hpet.h"
+#include "hw/acpi/acpi-defs.h"
+#include "hw/acpi/acpi.h"
+#include "hw/acpi/cpu.h"
+#include "hw/nvram/fw_cfg.h"
+#include "hw/acpi/bios-linker-loader.h"
+#include "hw/loader.h"
+#include "hw/isa/isa.h"
+#include "hw/block/fdc.h"
+#include "hw/acpi/memory_hotplug.h"
+#include "sysemu/tpm.h"
+#include "hw/acpi/tpm.h"
+#include "hw/acpi/vmgenid.h"
+#include "sysemu/tpm_backend.h"
+#include "hw/rtc/mc146818rtc_regs.h"
+#include "sysemu/numa.h"
+#include "sysemu/runstate.h"
+#include "sysemu/reset.h"
+#include "migration/vmstate.h"
+#include "hw/mem/memory-device.h"
+#include "hw/acpi/utils.h"
+#include "hw/acpi/pci.h"
+/* Supported chipsets: */
+#include "hw/acpi/aml-build.h"
+#include "hw/loongarch/larch.h"
+#include "hw/loongarch/ls7a.h"
+
+#include "hw/acpi/ipmi.h"
+#include "hw/acpi/ls7a.h"
+
+/* These are used to size the ACPI tables for -M pc-i440fx-1.7 and
+ * -M pc-i440fx-2.0. Even if the actual amount of AML generated grows
+ * a little bit, there should be plenty of free space since the DSDT
+ * shrunk by ~1.5k between QEMU 2.0 and QEMU 2.1.
+ */
+#define ACPI_BUILD_ALIGN_SIZE 0x1000
+
+#define ACPI_BUILD_TABLE_SIZE 0x20000
+
+/* #define DEBUG_ACPI_BUILD */
+#ifdef DEBUG_ACPI_BUILD
+#define ACPI_BUILD_DPRINTF(fmt, ...) \
+ do {printf("ACPI_BUILD: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define ACPI_BUILD_DPRINTF(fmt, ...)
+#endif
+
+/* Default IOAPIC ID */
+#define ACPI_BUILD_IOAPIC_ID 0x0
+
+/* PCI fw r3.0 MCFG table. */
+/* Subtable */
+
+typedef struct AcpiMiscInfo {
+ bool is_piix4;
+ bool has_hpet;
+ TPMVersion tpm_version;
+ const unsigned char *dsdt_code;
+ unsigned dsdt_size;
+ uint16_t pvpanic_port;
+ uint16_t applesmc_io_base;
+} AcpiMiscInfo;
+
+typedef struct AcpiBuildPciBusHotplugState {
+ GArray *device_table;
+ GArray *notify_table;
+ struct AcpiBuildPciBusHotplugState *parent;
+ bool pcihp_bridge_en;
+} AcpiBuildPciBusHotplugState;
+
+static void init_common_fadt_data(AcpiFadtData *data)
+{
+ AmlAddressSpace as = AML_AS_SYSTEM_MEMORY;
+ uint64_t base = LS7A_ACPI_REG_BASE;
+ AcpiFadtData fadt = {
+ .rev = 3,
+ .flags =
+ (1 << ACPI_FADT_F_WBINVD) |
+ (1 << ACPI_FADT_F_PROC_C1) |
+ (1 << ACPI_FADT_F_SLP_BUTTON) |
+ (1 << ACPI_FADT_F_TMR_VAL_EXT) |
+ (1 << ACPI_FADT_F_RESET_REG_SUP) ,
+ .plvl2_lat = 0xfff /* C2 state not supported */,
+ .plvl3_lat = 0xfff /* C3 state not supported */,
+ .smi_cmd = 0x00,
+ .sci_int = ACPI_SCI_IRQ,
+ .acpi_enable_cmd = 0x00,
+ .acpi_disable_cmd = 0x00,
+ .pm1a_evt = { .space_id = as, .bit_width = 8 * 8,
+ .address = base + LS7A_PM_EVT_BLK },
+ .pm1a_cnt = { .space_id = as, .bit_width = 4 * 8,
+ .address = base + LS7A_PM_CNT_BLK },
+ .pm_tmr = { .space_id = as, .bit_width = 4 * 8,
+ .address = base + LS7A_PM_TMR_BLK },
+ .gpe0_blk = { .space_id = as, .bit_width = 8 * 8,
+ .address = base + LS7A_GPE0_STS_REG},
+ .reset_reg = { .space_id = as, .bit_width = 4 * 8,
+ .address = base + LS7A_GPE0_RESET_REG},
+ .reset_val = 0x1,
+ };
+ *data = fadt;
+}
+
+static void acpi_align_size(GArray *blob, unsigned align)
+{
+ /* Align size to multiple of given size. This reduces the chance
+ * we need to change size in the future (breaking cross version migration).
+ */
+ g_array_set_size(blob, ROUND_UP(acpi_data_len(blob), align));
+}
+
+/* FACS */
+static void
+build_facs(GArray *table_data)
+{
+ const char *sig = "FACS";
+ const uint8_t reserved[40] = {};
+
+ g_array_append_vals(table_data, sig, 4); /* Signature */
+ build_append_int_noprefix(table_data, 64, 4); /* Length */
+ build_append_int_noprefix(table_data, 0, 4); /* Hardware Signature */
+ build_append_int_noprefix(table_data, 0, 4); /* Firmware Waking Vector */
+ build_append_int_noprefix(table_data, 0, 4); /* Global Lock */
+ build_append_int_noprefix(table_data, 0, 4); /* Flags */
+ g_array_append_vals(table_data, reserved, 40); /* Reserved */
+}
+
+void ls7a_madt_cpu_entry(AcpiDeviceIf *adev, int uid,
+ const CPUArchIdList *apic_ids, GArray *entry, bool force_enabled)
+{
+ uint32_t apic_id = apic_ids->cpus[uid].arch_id;
+ /* Flags – Local APIC Flags */
+ uint32_t flags = apic_ids->cpus[uid].cpu != NULL || force_enabled ?
+ 1 /* Enabled */ : 0;
+
+ /* Rev 1.0b, Table 5-13 Processor Local APIC Structure */
+ build_append_int_noprefix(entry, 0, 1); /* Type */
+ build_append_int_noprefix(entry, 8, 1); /* Length */
+ build_append_int_noprefix(entry, uid, 1); /* ACPI Processor ID */
+ build_append_int_noprefix(entry, apic_id, 1); /* APIC ID */
+ build_append_int_noprefix(entry, flags, 4); /* Flags */
+}
+static void build_ioapic(GArray *entry, uint8_t id, uint32_t addr, uint32_t irq)
+{
+ /* Rev 1.0b, 5.2.8.2 IO APIC */
+ build_append_int_noprefix(entry, 1, 1); /* Type */
+ build_append_int_noprefix(entry, 12, 1); /* Length */
+ build_append_int_noprefix(entry, id, 1); /* IO APIC ID */
+ build_append_int_noprefix(entry, 0, 1); /* Reserved */
+ build_append_int_noprefix(entry, addr, 4); /* IO APIC Address */
+ build_append_int_noprefix(entry, irq, 4); /* System Vector Base */
+}
+
+static void
+build_madt(GArray *table_data, BIOSLinker *linker, LoongarchMachineState *lsms)
+{
+ LoongarchMachineClass *lsmc = LoongarchMACHINE_GET_CLASS(lsms);
+ MachineClass *mc = MACHINE_GET_CLASS(lsms);
+ const CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(MACHINE(lsms));
+ AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(lsms->acpi_dev);
+ AcpiDeviceIf *adev = ACPI_DEVICE_IF(lsms->acpi_dev);
+ int i;
+ AcpiTable table = { .sig = "APIC", .rev = 1, .oem_id = lsms->oem_id,
+ .oem_table_id = lsms->oem_table_id};
+
+ acpi_table_begin(&table, table_data);
+
+ /* Local APIC Address */
+ build_append_int_noprefix(table_data, 0, 4);
+ build_append_int_noprefix(table_data, 1 /* PCAT_COMPAT */, 4); /* Flags */
+
+
+ for (i = 0; i < apic_ids->len; i++) {
+ adevc->madt_cpu(adev, i, apic_ids, table_data, false);
+ }
+
+
+ build_ioapic(table_data, ACPI_BUILD_IOAPIC_ID, lsmc->ls7a_ioapic_reg_base, LOONGARCH_PCH_IRQ_BASE);
+
+ /* Rev 1.0b, 5.2.8.3.3 Local APIC NMI */
+ build_append_int_noprefix(table_data, 3, 1); /* Type */
+ build_append_int_noprefix(table_data, 6, 1); /* Length */
+ /* ACPI Processor ID */
+ build_append_int_noprefix(table_data, 0xFF /* all processors */, 1);
+ build_append_int_noprefix(table_data, 0, 2); /* Flags */
+ /* Local APIC INTI# */
+ build_append_int_noprefix(table_data, 1 /* ACPI_LINT1 */, 1);
+
+ /* Rev 1.0b, 5.2.8.3.3 Local APIC NMI */
+ build_append_int_noprefix(table_data, 4, 1); /* Type */
+ build_append_int_noprefix(table_data, 6, 1); /* Length */
+ /* ACPI Processor ID */
+ build_append_int_noprefix(table_data, 0xFF /* all processors */, 1);
+ build_append_int_noprefix(table_data, 0, 2); /* Flags */
+ /* Local APIC INTI# */
+ build_append_int_noprefix(table_data, 1 /* ACPI_LINT1 */, 1);
+
+
+ acpi_table_end(linker, &table);
+}
+
+static void
+build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine)
+{
+ uint64_t i, mem_len, mem_base;
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
+ LoongarchMachineState *lsms = LoongarchMACHINE(machine);
+ const CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(machine);
+ int nb_numa_nodes = machine->numa_state->num_nodes;
+ NodeInfo *numa_info = machine->numa_state->nodes;
+ AcpiTable table = { .sig = "SRAT", .rev = 1, .oem_id = lsms->oem_id,
+ .oem_table_id = lsms->oem_table_id};
+
+ acpi_table_begin(&table, table_data);
+ build_append_int_noprefix(table_data, 1, 4); /* Reserved */
+ build_append_int_noprefix(table_data, 0, 8); /* Reserved */
+
+ for (i = 0; i < apic_ids->len; ++i) {
+ /* 5.2.15.1 Processor Local APIC/SAPIC Affinity Structure */
+ build_append_int_noprefix(table_data, 0, 1); /* Type */
+ build_append_int_noprefix(table_data, 16, 1); /* Length */
+ /* Proximity Domain [7:0] */
+ build_append_int_noprefix(table_data, apic_ids->cpus[i].props.node_id, 1);
+ build_append_int_noprefix(table_data, apic_ids->cpus[i].arch_id, 1); /* APIC ID */
+ /* Flags, Table 5-36 */
+ build_append_int_noprefix(table_data, 1, 4);
+ build_append_int_noprefix(table_data, 0, 1); /* Local SAPIC EID */
+ /* Proximity Domain [31:8] */
+ build_append_int_noprefix(table_data, 0, 3);
+ build_append_int_noprefix(table_data, 0, 4); /* Reserved */
+ }
+
+ /* node0 */
+ mem_base = (uint64_t)0;
+ mem_len = 0x10000000;
+ build_srat_memory(table_data, mem_base, mem_len,
+ 0, MEM_AFFINITY_ENABLED);
+ mem_base = 0x90000000;
+ if (!nb_numa_nodes) {
+ mem_len = machine->ram_size - 0x10000000;
+ } else {
+ mem_len = numa_info[0].node_mem - 0x10000000;
+ }
+
+ build_srat_memory(table_data, mem_base, mem_len,
+ 0, MEM_AFFINITY_ENABLED);
+ mem_base += mem_len;
+
+ /* node1 ~ nodemax */
+ for (i = 1; i < nb_numa_nodes; ++i) {
+ mem_len = numa_info[i].node_mem;
+ build_srat_memory(table_data, mem_base, mem_len,
+ i, MEM_AFFINITY_ENABLED);
+ mem_base += mem_len;
+ }
+
+ if (lsms->hotplug_memory_size) {
+ build_srat_memory(table_data, machine->device_memory->base,
+ lsms->hotplug_memory_size, 0,
+ MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED);
+ }
+
+ acpi_table_end(linker, &table);
+
+}
+
+typedef
+struct AcpiBuildState {
+ /* Copy of table in RAM (for patching). */
+ MemoryRegion *table_mr;
+ /* Is table patched? */
+ uint8_t patched;
+ void *rsdp;
+ MemoryRegion *rsdp_mr;
+ MemoryRegion *linker_mr;
+} AcpiBuildState;
+
+static void build_ls7a_pci0_int(Aml *table)
+{
+ Aml *sb_scope = aml_scope("_SB");
+ Aml *pci0_scope = aml_scope("PCI0");
+ Aml *prt_pkg = aml_varpackage(128);
+ int slot, pin;
+
+ for (slot = 0; slot < PCI_SLOT_MAX; slot++) {
+ for (pin = 0; pin < PCI_NUM_PINS; pin++) {
+ Aml *pkg = aml_package(4);
+ aml_append(pkg, aml_int((slot << 16) | 0xFFFF));
+ aml_append(pkg, aml_int(pin));
+ aml_append(pkg, aml_int(0));
+ aml_append(pkg, aml_int(LOONGARCH_PCH_IRQ_BASE + 16 + (slot * 4 + pin) % 16));
+ aml_append(prt_pkg, pkg);
+ }
+ }
+ aml_append(pci0_scope,
+ aml_name_decl("_PRT", prt_pkg));
+
+ aml_append(sb_scope, pci0_scope);
+
+ aml_append(table, sb_scope);
+}
+
+static void build_dbg_aml(Aml *table)
+{
+ Aml *field;
+ Aml *method;
+ Aml *while_ctx;
+ Aml *scope = aml_scope("\\");
+ Aml *buf = aml_local(0);
+ Aml *len = aml_local(1);
+ Aml *idx = aml_local(2);
+
+ aml_append(scope,
+ aml_operation_region("DBG", AML_SYSTEM_IO, aml_int(0x0402), 0x01));
+ field = aml_field("DBG", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE);
+ aml_append(field, aml_named_field("DBGB", 8));
+ aml_append(scope, field);
+
+ method = aml_method("DBUG", 1, AML_NOTSERIALIZED);
+
+ aml_append(method, aml_to_hexstring(aml_arg(0), buf));
+ aml_append(method, aml_to_buffer(buf, buf));
+ aml_append(method, aml_subtract(aml_sizeof(buf), aml_int(1), len));
+ aml_append(method, aml_store(aml_int(0), idx));
+
+ while_ctx = aml_while(aml_lless(idx, len));
+ aml_append(while_ctx,
+ aml_store(aml_derefof(aml_index(buf, idx)), aml_name("DBGB")));
+ aml_append(while_ctx, aml_increment(idx));
+ aml_append(method, while_ctx);
+
+ aml_append(method, aml_store(aml_int(0x0A), aml_name("DBGB")));
+ aml_append(scope, method);
+
+ aml_append(table, scope);
+}
+
+static Aml *build_ls7a_osc_method(void)
+{
+ Aml *if_ctx;
+ Aml *if_ctx2;
+ Aml *else_ctx;
+ Aml *method;
+ Aml *a_cwd1 = aml_name("CDW1");
+ Aml *a_ctrl = aml_local(0);
+
+ method = aml_method("_OSC", 4, AML_NOTSERIALIZED);
+ aml_append(method, aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1"));
+
+ if_ctx = aml_if(aml_equal(
+ aml_arg(0), aml_touuid("33DB4D5B-1FF7-401C-9657-7441C03DD766")));
+ aml_append(if_ctx, aml_create_dword_field(aml_arg(3), aml_int(4), "CDW2"));
+ aml_append(if_ctx, aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3"));
+
+ aml_append(if_ctx, aml_store(aml_name("CDW3"), a_ctrl));
+
+ /*
+ * Always allow native PME, AER (no dependencies)
+ * Allow SHPC (PCI bridges can have SHPC controller)
+ */
+ aml_append(if_ctx, aml_and(a_ctrl, aml_int(0x1F), a_ctrl));
+
+ if_ctx2 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(1))));
+ /* Unknown revision */
+ aml_append(if_ctx2, aml_or(a_cwd1, aml_int(0x08), a_cwd1));
+ aml_append(if_ctx, if_ctx2);
+
+ if_ctx2 = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), a_ctrl)));
+ /* Capabilities bits were masked */
+ aml_append(if_ctx2, aml_or(a_cwd1, aml_int(0x10), a_cwd1));
+ aml_append(if_ctx, if_ctx2);
+
+ /* Update DWORD3 in the buffer */
+ aml_append(if_ctx, aml_store(a_ctrl, aml_name("CDW3")));
+ aml_append(method, if_ctx);
+
+ else_ctx = aml_else();
+ /* Unrecognized UUID */
+ aml_append(else_ctx, aml_or(a_cwd1, aml_int(4), a_cwd1));
+ aml_append(method, else_ctx);
+
+ aml_append(method, aml_return(aml_arg(3)));
+ return method;
+}
+
+static void build_ls7a_rtc_device_aml(Aml *table)
+{
+ Aml *dev;
+ Aml *crs;
+ uint32_t rtc_irq = LS7A_RTC_IRQ;
+
+ Aml *scope = aml_scope("_SB");
+ dev = aml_device("RTC");
+ aml_append(dev, aml_name_decl("_HID", aml_string("LOON0001")));
+ crs = aml_resource_template();
+ aml_append(crs,
+ aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
+ AML_NON_CACHEABLE, AML_READ_WRITE,
+ 0, LS7A_RTC_REG_BASE,
+ LS7A_RTC_REG_BASE + LS7A_RTC_LEN - 1, 0, LS7A_RTC_LEN));
+ aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
+ AML_EXCLUSIVE, &rtc_irq, 1));
+
+ aml_append(dev, aml_name_decl("_CRS", crs));
+ aml_append(scope, dev);
+ aml_append(table, scope);
+}
+
+static void build_ls7a_uart_device_aml(Aml *table)
+{
+ Aml *dev;
+ Aml *crs;
+ Aml *pkg0, *pkg1, *pkg2;
+ uint32_t uart_irq = LS7A_UART_IRQ;
+
+ Aml *scope = aml_scope("_SB");
+ dev = aml_device("COMA");
+ aml_append(dev, aml_name_decl("_HID", aml_string("PNP0501")));
+ aml_append(dev, aml_name_decl("_UID", aml_int(0)));
+ aml_append(dev, aml_name_decl("_CCA", aml_int(1)));
+ crs = aml_resource_template();
+ aml_append(crs,
+ aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
+ AML_NON_CACHEABLE, AML_READ_WRITE,
+ 0, LS7A_UART_BASE, LS7A_UART_BASE + LS7A_UART_LEN - 1, 0, 0x8));
+ aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH,
+ AML_EXCLUSIVE, &uart_irq, 1));
+ aml_append(dev, aml_name_decl("_CRS", crs));
+ pkg0 = aml_package(0x2);
+ aml_append(pkg0, aml_int(0x01F78A40));
+ aml_append(pkg0, aml_string("clock-frenquency"));
+ pkg1 = aml_package(0x1);
+ aml_append(pkg1, pkg0);
+ pkg2 = aml_package(0x2);
+ aml_append(pkg2, aml_touuid("DAFFD814-6EBA-4D8C-8A91-BC9BBF4AA301"));
+ aml_append(pkg2, pkg1);
+
+ aml_append(dev, aml_name_decl("_DSD", pkg2));
+
+ aml_append(scope, dev);
+ aml_append(table, scope);
+}
+
+static void
+build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine)
+{
+ Aml *dsdt, *sb_scope, *scope, *dev, *crs, *pkg;
+ LoongarchMachineState *lsms = LoongarchMACHINE(machine);
+ uint32_t nr_mem = machine->ram_slots;
+ uint64_t base = LS7A_ACPI_REG_BASE;
+ int root_bus_limit = PCIE_MMCFG_BUS(LS_PCIECFG_SIZE - 1);
+ AcpiTable table = { .sig = "DSDT", .rev = 1, .oem_id = lsms->oem_id,
+ .oem_table_id = lsms->oem_table_id};
+
+ acpi_table_begin(&table, table_data);
+ dsdt = init_aml_allocator();
+
+ build_dbg_aml(dsdt);
+
+ sb_scope = aml_scope("_SB");
+ dev = aml_device("PCI0");
+ aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A08")));
+ aml_append(dev, aml_name_decl("_CID", aml_eisaid("PNP0A03")));
+ aml_append(dev, aml_name_decl("_ADR", aml_int(0)));
+ aml_append(dev, aml_name_decl("_BBN", aml_int(0)));
+ aml_append(dev, aml_name_decl("_UID", aml_int(1)));
+ aml_append(dev, build_ls7a_osc_method());
+ aml_append(sb_scope, dev);
+ aml_append(dsdt, sb_scope);
+
+ build_ls7a_pci0_int(dsdt);
+ build_ls7a_rtc_device_aml(dsdt);
+ build_ls7a_uart_device_aml(dsdt);
+
+ if (lsms->acpi_dev) {
+ CPUHotplugFeatures opts = {
+ .acpi_1_compatible = true, .has_legacy_cphp = false
+ };
+ build_cpus_aml(dsdt, machine, opts, CPU_HOTPLUG_BASE,
+ "\\_SB.PCI0", "\\_GPE._E02");
+
+ build_memory_hotplug_aml(dsdt, nr_mem, "\\_SB.PCI0",
+ "\\_GPE._E03", AML_SYSTEM_MEMORY,
+ MEMORY_HOTPLUG_BASE);
+ }
+
+ scope = aml_scope("_GPE");
+ {
+ aml_append(scope, aml_name_decl("_HID", aml_string("ACPI0006")));
+ }
+ aml_append(dsdt, scope);
+
+ scope = aml_scope("\\_SB.PCI0");
+ /* build PCI0._CRS */
+ crs = aml_resource_template();
+ aml_append(crs,
+ aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE,
+ 0x0000, 0x0, root_bus_limit,
+ 0x0000, root_bus_limit + 1));
+ aml_append(crs,
+ aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED,
+ AML_POS_DECODE, AML_ENTIRE_RANGE,
+ 0x0000, 0x4000, 0xFFFF, 0x0000, 0xC000));
+ aml_append(crs,
+ aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
+ AML_CACHEABLE, AML_READ_WRITE,
+ 0, 0x40000000, 0x7FFFFFFF, 0, 0x40000000));
+ aml_append(scope, aml_name_decl("_CRS", crs));
+
+ /* reserve GPE0 block resources */
+ dev = aml_device("GPE0");
+ aml_append(dev, aml_name_decl("_HID", aml_string("PNP0A06")));
+ aml_append(dev, aml_name_decl("_UID", aml_string("GPE0 resources")));
+ /* device present, functioning, decoding, not shown in UI */
+ aml_append(dev, aml_name_decl("_STA", aml_int(0xB)));
+ crs = aml_resource_template();
+ aml_append(crs,
+ aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED,
+ AML_CACHEABLE, AML_READ_WRITE,
+ 0, base + LS7A_GPE0_STS_REG,
+ base + LS7A_GPE0_STS_REG + 0x3, 0, 0x4));
+ aml_append(dev, aml_name_decl("_CRS", crs));
+ aml_append(scope, dev);
+ aml_append(dsdt, scope);
+
+ scope = aml_scope("\\");
+ pkg = aml_package(4);
+ aml_append(pkg, aml_int(7)); /* PM1a_CNT.SLP_TYP */
+ aml_append(pkg, aml_int(7)); /* PM1b_CNT.SLP_TYP not impl. */
+ aml_append(pkg, aml_int(0)); /* reserved */
+ aml_append(pkg, aml_int(0)); /* reserved */
+ aml_append(scope, aml_name_decl("_S5", pkg));
+ aml_append(dsdt, scope);
+
+ /* copy AML table into ACPI tables blob and patch header there */
+ g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len);
+ acpi_table_end(linker, &table);
+ free_aml_allocator();
+}
+
+
+static
+void acpi_build(AcpiBuildTables *tables, MachineState *machine)
+{
+ LoongarchMachineState *lsms = LoongarchMACHINE(machine);
+ GArray *table_offsets;
+ AcpiFadtData fadt_data;
+ unsigned facs, rsdt, fadt, dsdt;
+ uint8_t *u;
+ size_t aml_len = 0;
+ GArray *tables_blob = tables->table_data;
+
+ init_common_fadt_data(&fadt_data);
+
+ table_offsets = g_array_new(false, true /* clear */,
+ sizeof(uint32_t));
+ ACPI_BUILD_DPRINTF("init ACPI tables\n");
+
+ bios_linker_loader_alloc(tables->linker,
+ ACPI_BUILD_TABLE_FILE, tables_blob,
+ 64 /* Ensure FACS is aligned */,
+ false /* high memory */);
+
+ /*
+ * FACS is pointed to by FADT.
+ * We place it first since it's the only table that has alignment
+ * requirements.
+ */
+ facs = tables_blob->len;
+ build_facs(tables_blob);
+
+ /* DSDT is pointed to by FADT */
+ dsdt = tables_blob->len;
+ build_dsdt(tables_blob, tables->linker, MACHINE(qdev_get_machine()));
+
+ /* Count the size of the DSDT and SSDT, we will need it for legacy
+ * sizing of ACPI tables.
+ */
+ aml_len += tables_blob->len - dsdt;
+
+ /* ACPI tables pointed to by RSDT */
+ fadt = tables_blob->len;
+ acpi_add_table(table_offsets, tables_blob);
+ fadt_data.facs_tbl_offset = &facs;
+ fadt_data.dsdt_tbl_offset = &dsdt;
+ fadt_data.xdsdt_tbl_offset = &dsdt;
+ build_fadt(tables_blob, tables->linker, &fadt_data,
+ "LOONGS", "TP-R00");
+ aml_len += tables_blob->len - fadt;
+
+ acpi_add_table(table_offsets, tables_blob);
+ build_madt(tables_blob, tables->linker, lsms);
+
+ acpi_add_table(table_offsets, tables_blob);
+ build_srat(tables_blob, tables->linker, machine);
+ if (machine->numa_state->have_numa_distance) {
+ acpi_add_table(table_offsets, tables_blob);
+ build_slit(tables_blob, tables->linker, machine, lsms->oem_id,
+ lsms->oem_table_id);
+ }
+
+ /* Build mcfg */
+ acpi_add_table(table_offsets, tables_blob);
+ {
+ AcpiMcfgInfo mcfg = {
+ .base = LS_PCIECFG_BASE,
+ .size = LS_PCIECFG_SIZE,
+ };
+ build_mcfg(tables_blob, tables->linker, &mcfg, lsms->oem_id,
+ lsms->oem_table_id);
+ }
+
+ /* Add tables supplied by user (if any) */
+ for (u = acpi_table_first(); u; u = acpi_table_next(u)) {
+ unsigned len = acpi_table_len(u);
+
+ acpi_add_table(table_offsets, tables_blob);
+ g_array_append_vals(tables_blob, u, len);
+ }
+
+ /* RSDT is pointed to by RSDP */
+ rsdt = tables_blob->len;
+ build_rsdt(tables_blob, tables->linker, table_offsets,
+ "LOONGS", "TP-R00");
+
+ /* RSDP is in FSEG memory, so allocate it separately */
+ {
+ AcpiRsdpData rsdp_data = {
+ .revision = 0,
+ .oem_id = lsms->oem_id,
+ .xsdt_tbl_offset = NULL,
+ .rsdt_tbl_offset = &rsdt,
+ };
+ build_rsdp(tables->rsdp, tables->linker, &rsdp_data);
+ }
+ acpi_align_size(tables->linker->cmd_blob, ACPI_BUILD_ALIGN_SIZE);
+
+ /* Cleanup memory that's no longer used. */
+ g_array_free(table_offsets, true);
+}
+
+static void acpi_ram_update(MemoryRegion *mr, GArray *data)
+{
+ uint32_t size = acpi_data_len(data);
+
+ /* Make sure RAM size is correct -
+ in case it got changed e.g. by migration */
+ memory_region_ram_resize(mr, size, &error_abort);
+
+ memcpy(memory_region_get_ram_ptr(mr), data->data, size);
+ memory_region_set_dirty(mr, 0, size);
+}
+
+static void acpi_build_update(void *build_opaque)
+{
+ AcpiBuildState *build_state = build_opaque;
+ AcpiBuildTables tables;
+
+ /* No state to update or already patched? Nothing to do. */
+ if (!build_state || build_state->patched) {
+ return;
+ }
+ build_state->patched = 1;
+
+ acpi_build_tables_init(&tables);
+
+ acpi_build(&tables, MACHINE(qdev_get_machine()));
+
+ acpi_ram_update(build_state->table_mr, tables.table_data);
+
+ if (build_state->rsdp) {
+ memcpy(build_state->rsdp, tables.rsdp->data,
+ acpi_data_len(tables.rsdp));
+ } else {
+ acpi_ram_update(build_state->rsdp_mr, tables.rsdp);
+ }
+
+ acpi_ram_update(build_state->linker_mr, tables.linker->cmd_blob);
+ acpi_build_tables_cleanup(&tables, true);
+}
+
+static void acpi_build_reset(void *build_opaque)
+{
+ AcpiBuildState *build_state = build_opaque;
+ build_state->patched = 0;
+}
+
+static const VMStateDescription vmstate_acpi_build = {
+ .name = "acpi_build",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8(patched, AcpiBuildState),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
+void loongarch_acpi_setup(void)
+{
+ LoongarchMachineState *lsms = LoongarchMACHINE(qdev_get_machine());
+ AcpiBuildTables tables;
+ AcpiBuildState *build_state;
+
+ if (!lsms->fw_cfg) {
+ ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n");
+ return;
+ }
+
+ if (!lsms->acpi_build_enabled) {
+ ACPI_BUILD_DPRINTF("ACPI build disabled. Bailing out.\n");
+ return;
+ }
+
+ if (!loongarch_is_acpi_enabled(lsms)) {
+ ACPI_BUILD_DPRINTF("ACPI disabled. Bailing out.\n");
+ return;
+ }
+
+ build_state = g_malloc0(sizeof *build_state);
+
+ acpi_build_tables_init(&tables);
+ acpi_build(&tables, MACHINE(lsms));
+
+ /* Now expose it all to Guest */
+ build_state->table_mr = acpi_add_rom_blob(acpi_build_update, build_state,
+ tables.table_data,
+ ACPI_BUILD_TABLE_FILE);
+ assert(build_state->table_mr != NULL);
+
+ build_state->linker_mr =
+ acpi_add_rom_blob(acpi_build_update, build_state, tables.linker->cmd_blob,
+ "etc/table-loader");
+
+ fw_cfg_add_file(lsms->fw_cfg, ACPI_BUILD_TPMLOG_FILE,
+ tables.tcpalog->data, acpi_data_len(tables.tcpalog));
+
+ build_state->rsdp = NULL;
+ build_state->rsdp_mr = acpi_add_rom_blob(acpi_build_update, build_state, tables.rsdp,
+ ACPI_BUILD_RSDP_FILE);
+
+ qemu_register_reset(acpi_build_reset, build_state);
+ acpi_build_reset(build_state);
+ vmstate_register(NULL, 0, &vmstate_acpi_build, build_state);
+
+ /* Cleanup tables but don't free the memory: we track it
+ * in build_state.
+ */
+ acpi_build_tables_cleanup(&tables, false);
+}
diff --git a/hw/loongarch/acpi-build.h b/hw/loongarch/acpi-build.h
new file mode 100644
index 000000000..a914268bb
--- /dev/null
+++ b/hw/loongarch/acpi-build.h
@@ -0,0 +1,16 @@
+
+#ifndef HW_LARCH_ACPI_BUILD_H
+#define HW_LARCH_ACPI_BUILD_H
+
+#define EFI_ACPI_OEM_ID "LARCH"
+#define EFI_ACPI_OEM_TABLE_ID "LARCH" /* OEM table id 8 bytes long */
+#define EFI_ACPI_OEM_REVISION 0x00000002
+#define EFI_ACPI_CREATOR_ID "LINUX"
+#define EFI_ACPI_CREATOR_REVISION 0x01000013
+
+#define ACPI_COMPATIBLE_1_0 0
+#define ACPI_COMPATIBLE_2_0 1
+
+void loongarch_acpi_setup(void);
+
+#endif
diff --git a/hw/loongarch/apic.c b/hw/loongarch/apic.c
new file mode 100644
index 000000000..d6ba2a2ce
--- /dev/null
+++ b/hw/loongarch/apic.c
@@ -0,0 +1,675 @@
+/*
+ * Loongarch 3A5000 interrupt controller emulation
+ *
+ * Copyright (C) 2020 Lu Zeng <zenglu@loongson.cn>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/boards.h"
+#include "hw/irq.h"
+#include "hw/loongarch/cpudevs.h"
+#include "hw/sysbus.h"
+#include "qemu/host-utils.h"
+#include "qemu/error-report.h"
+#include "sysemu/kvm.h"
+#include "hw/hw.h"
+#include "hw/irq.h"
+#include "target/loongarch64/cpu.h"
+#include "exec/address-spaces.h"
+#include "hw/loongarch/larch.h"
+#include "migration/vmstate.h"
+
+#define DEBUG_APIC 0
+
+#define DPRINTF(fmt, ...) \
+do { \
+ if (DEBUG_APIC) { \
+ fprintf(stderr, "APIC: " fmt , ## __VA_ARGS__); \
+ } \
+} while (0)
+
+#define APIC_OFFSET 0x400
+#define APIC_BASE (0x1f010000ULL)
+#define EXTIOI_NODETYPE_START (0x4a0 - APIC_OFFSET)
+#define EXTIOI_NODETYPE_END (0x4c0 - APIC_OFFSET)
+#define EXTIOI_IPMAP_START (0x4c0 - APIC_OFFSET)
+#define EXTIOI_IPMAP_END (0x4c8 - APIC_OFFSET)
+#define EXTIOI_ENABLE_START (0x600 - APIC_OFFSET)
+#define EXTIOI_ENABLE_END (0x620 - APIC_OFFSET)
+#define EXTIOI_BOUNCE_START (0x680 - APIC_OFFSET)
+#define EXTIOI_BOUNCE_END (0x6a0 - APIC_OFFSET)
+#define EXTIOI_ISR_START (0x700 - APIC_OFFSET)
+#define EXTIOI_ISR_END (0x720 - APIC_OFFSET)
+#define EXTIOI_COREMAP_START (0xC00 - APIC_OFFSET)
+#define EXTIOI_COREMAP_END (0xD00 - APIC_OFFSET)
+#define EXTIOI_COREISR_START (0x10000)
+#define EXTIOI_COREISR_END (EXTIOI_COREISR_START + 0x10000)
+
+static int ext_irq_pre_save(void *opaque)
+{
+#ifdef CONFIG_KVM
+ apicState *apic = opaque;
+ struct loongarch_kvm_irqchip *chip;
+ struct kvm_loongarch_ls3a_extirq_state *kstate;
+ int ret, length, i, vcpuid;
+#endif
+ if (!kvm_irqchip_in_kernel()) {
+ return 0;
+ }
+#ifdef CONFIG_KVM
+ length = sizeof(struct loongarch_kvm_irqchip) +
+ sizeof(struct kvm_loongarch_ls3a_extirq_state);
+ chip = g_malloc0(length);
+ memset(chip, 0, length);
+ chip->chip_id = KVM_IRQCHIP_LS3A_EXTIRQ;
+ chip->len = length;
+
+ ret = kvm_vm_ioctl(kvm_state, KVM_GET_IRQCHIP, chip);
+ if (ret < 0) {
+ fprintf(stderr, "KVM_GET_IRQCHIP failed: %s\n", strerror(ret));
+ abort();
+ }
+
+ kstate = (struct kvm_loongarch_ls3a_extirq_state *)chip->data;
+ for (i = 0; i < EXTIOI_IRQS_BITMAP_SIZE; i++) {
+ apic->ext_en[i] = kstate->ext_en_r.reg_u8[i];
+ apic->ext_bounce[i] = kstate->bounce_r.reg_u8[i];
+ apic->ext_isr[i] = kstate->ext_isr_r.reg_u8[i];
+ for (vcpuid = 0; vcpuid < MAX_CORES; vcpuid++) {
+ apic->ext_coreisr[vcpuid][i] = kstate->ext_core_isr_r.reg_u8[vcpuid][i];
+ }
+ }
+ for (i = 0; i < EXTIOI_IRQS_IPMAP_SIZE; i++) {
+ apic->ext_ipmap[i] = kstate->ip_map_r.reg_u8[i];
+ }
+ for (i = 0; i < EXTIOI_IRQS; i++) {
+ apic->ext_coremap[i] = kstate->core_map_r.reg_u8[i];;
+ }
+ for (i = 0; i < 16; i++) {
+ apic->ext_nodetype[i] = kstate->node_type_r.reg_u16[i];
+ }
+ g_free(chip);
+#endif
+ return 0;
+}
+
+static int ext_irq_post_load(void *opaque, int version)
+{
+#ifdef CONFIG_KVM
+ apicState *apic = opaque;
+ struct loongarch_kvm_irqchip *chip;
+ struct kvm_loongarch_ls3a_extirq_state *kstate;
+ int ret, length, i, vcpuid;
+#endif
+ if (!kvm_irqchip_in_kernel()) {
+ return 0;
+ }
+#ifdef CONFIG_KVM
+ length = sizeof(struct loongarch_kvm_irqchip) +
+ sizeof(struct kvm_loongarch_ls3a_extirq_state);
+ chip = g_malloc0(length);
+
+ chip->chip_id = KVM_IRQCHIP_LS3A_EXTIRQ;
+ chip->len = length;
+
+ kstate = (struct kvm_loongarch_ls3a_extirq_state *)chip->data;
+ for (i = 0; i < EXTIOI_IRQS_BITMAP_SIZE; i++) {
+ kstate->ext_en_r.reg_u8[i] = apic->ext_en[i];
+ kstate->bounce_r.reg_u8[i] = apic->ext_bounce[i];
+ kstate->ext_isr_r.reg_u8[i] = apic->ext_isr[i];
+ for (vcpuid = 0; vcpuid < MAX_CORES; vcpuid++) {
+ kstate->ext_core_isr_r.reg_u8[vcpuid][i] = apic->ext_coreisr[vcpuid][i];
+ }
+ }
+ for (i = 0; i < EXTIOI_IRQS_IPMAP_SIZE; i++) {
+ kstate->ip_map_r.reg_u8[i] = apic->ext_ipmap[i];
+ }
+ for (i = 0; i < EXTIOI_IRQS; i++) {
+ kstate->core_map_r.reg_u8[i] = apic->ext_coremap[i];
+ }
+ for (i = 0; i < 16; i++) {
+ kstate->node_type_r.reg_u16[i] = apic->ext_nodetype[i];
+ }
+
+ ret = kvm_vm_ioctl(kvm_state, KVM_SET_IRQCHIP, chip);
+ if (ret < 0) {
+ fprintf(stderr, "KVM_SET_IRQCHIP failed: %s\n", strerror(ret));
+ abort();
+ }
+ g_free(chip);
+#endif
+ return 0;
+}
+typedef struct nodeApicState {
+ unsigned long addr;
+ int nodeid;
+ apicState *apic;
+} nodeApicState;
+
+static void ioapic_update_irq(void *opaque, int irq, int level)
+{
+ apicState *s = opaque;
+ uint8_t ipnum, cpu, cpu_ipnum;
+ unsigned long found1, found2;
+ uint8_t reg_count, reg_bit;
+
+ reg_count = irq / 32;
+ reg_bit = irq % 32;
+
+ ipnum = s->ext_sw_ipmap[irq];
+ cpu = s->ext_sw_coremap[irq];
+ cpu_ipnum = cpu * LS3A_INTC_IP + ipnum;
+ if (level == 1) {
+ if (test_bit(reg_bit, ((void *)s->ext_en + 0x4 * reg_count)) == false) {
+ return;
+ }
+
+ if (test_bit(reg_bit, ((void *)s->ext_isr + 0x4 * reg_count)) == false) {
+ return;
+ }
+ bitmap_set(((void *)s->ext_coreisr[cpu] + 0x4 * reg_count), reg_bit, 1);
+ found1 = find_next_bit(((void *)s->ext_ipisr[cpu_ipnum] + 0x4 * reg_count),
+ EXTIOI_IRQS, 0);
+ bitmap_set(((void *)s->ext_ipisr[cpu_ipnum] + 0x4 * reg_count), reg_bit, 1);
+ if (found1 >= EXTIOI_IRQS) {
+ qemu_set_irq(s->parent_irq[cpu][ipnum], level);
+ }
+ } else {
+ bitmap_clear(((void *)s->ext_isr + 0x4 * reg_count), reg_bit, 1);
+ bitmap_clear(((void *)s->ext_coreisr[cpu] + 0x4 * reg_count), reg_bit, 1);
+ found1 = find_next_bit(((void *)s->ext_ipisr[cpu_ipnum] + 0x4 * reg_count),
+ EXTIOI_IRQS, 0);
+ found1 += reg_count * 32;
+ bitmap_clear(((void *)s->ext_ipisr[cpu_ipnum] + 0x4 * reg_count), reg_bit, 1);
+ found2 = find_next_bit(((void *)s->ext_ipisr[cpu_ipnum] + 0x4 * reg_count),
+ EXTIOI_IRQS, 0);
+ if ((found1 < EXTIOI_IRQS) && (found2 >= EXTIOI_IRQS)) {
+ qemu_set_irq(s->parent_irq[cpu][ipnum], level);
+ }
+ }
+}
+
+static void ioapic_setirq(void *opaque, int irq, int level)
+{
+ apicState *s = opaque;
+ uint8_t reg_count, reg_bit;
+
+ reg_count = irq / 32;
+ reg_bit = irq % 32;
+
+ if (level) {
+ bitmap_set(((void *)s->ext_isr + 0x4 * reg_count), reg_bit, 1);
+ } else {
+ bitmap_clear(((void *)s->ext_isr + 0x4 * reg_count), reg_bit, 1);
+ }
+
+ ioapic_update_irq(s, irq, level);
+}
+
+static uint32_t apic_readb(void *opaque, hwaddr addr)
+{
+ nodeApicState *node;
+ apicState *state;
+ unsigned long off;
+ uint8_t ret;
+ int cpu;
+
+ node = (nodeApicState *)opaque;
+ state = node->apic;
+ off = addr & 0xfffff;
+ ret = 0;
+ if ((off >= EXTIOI_ENABLE_START) && (off < EXTIOI_ENABLE_END)) {
+ off -= EXTIOI_ENABLE_START;
+ ret = *(uint8_t *)((void *)state->ext_en + off);
+ } else if ((off >= EXTIOI_BOUNCE_START) && (off < EXTIOI_BOUNCE_END)) {
+ off -= EXTIOI_BOUNCE_START;
+ ret = *(uint8_t *)((void *)state->ext_bounce + off);
+ } else if ((off >= EXTIOI_ISR_START) && (off < EXTIOI_ISR_END)) {
+ off -= EXTIOI_ISR_START;
+ ret = *(uint8_t *)((void *)state->ext_isr + off);
+ } else if ((off >= EXTIOI_COREISR_START) && (off < EXTIOI_COREISR_END)) {
+ off -= EXTIOI_COREISR_START;
+ cpu = (off >> 8) & 0xff;
+ ret = *(uint8_t *)((void *)state->ext_coreisr[cpu] + (off & 0x1f));
+ } else if ((off >= EXTIOI_IPMAP_START) && (off < EXTIOI_IPMAP_END)) {
+ off -= EXTIOI_IPMAP_START;
+ ret = *(uint8_t *)((void *)state->ext_ipmap + off);
+ } else if ((off >= EXTIOI_COREMAP_START) && (off < EXTIOI_COREMAP_END)) {
+ off -= EXTIOI_COREMAP_START;
+ ret = *(uint8_t *)((void *)state->ext_coremap + off);
+ } else if ((off >= EXTIOI_NODETYPE_START) && (off < EXTIOI_NODETYPE_END)) {
+ off -= EXTIOI_NODETYPE_START;
+ ret = *(uint8_t *)((void *)state->ext_nodetype + off);
+ }
+
+ DPRINTF("readb reg 0x" TARGET_FMT_plx " = %x\n", node->addr + addr, ret);
+ return ret;
+}
+
+static uint32_t apic_readw(void *opaque, hwaddr addr)
+{
+ nodeApicState *node;
+ apicState *state;
+ unsigned long off;
+ uint16_t ret;
+ int cpu;
+
+ node = (nodeApicState *)opaque;
+ state = node->apic;
+ off = addr & 0xfffff;
+ ret = 0;
+ if ((off >= EXTIOI_ENABLE_START) && (off < EXTIOI_ENABLE_END)) {
+ off -= EXTIOI_ENABLE_START;
+ ret = *(uint16_t *)((void *)state->ext_en + off);
+ } else if ((off >= EXTIOI_BOUNCE_START) && (off < EXTIOI_BOUNCE_END)) {
+ off -= EXTIOI_BOUNCE_START;
+ ret = *(uint16_t *)((void *)state->ext_bounce + off);
+ } else if ((off >= EXTIOI_ISR_START) && (off < EXTIOI_ISR_END)) {
+ off -= EXTIOI_ISR_START;
+ ret = *(uint16_t *)((void *)state->ext_isr + off);
+ } else if ((off >= EXTIOI_COREISR_START) && (off < EXTIOI_COREISR_END)) {
+ off -= EXTIOI_COREISR_START;
+ cpu = (off >> 8) & 0xff;
+ ret = *(uint16_t *)((void *)state->ext_coreisr[cpu] + (off & 0x1f));
+ } else if ((off >= EXTIOI_IPMAP_START) && (off < EXTIOI_IPMAP_END)) {
+ off -= EXTIOI_IPMAP_START;
+ ret = *(uint16_t *)((void *)state->ext_ipmap + off);
+ } else if ((off >= EXTIOI_COREMAP_START) && (off < EXTIOI_COREMAP_END)) {
+ off -= EXTIOI_COREMAP_START;
+ ret = *(uint16_t *)((void *)state->ext_coremap + off);
+ } else if ((off >= EXTIOI_NODETYPE_START) && (off < EXTIOI_NODETYPE_END)) {
+ off -= EXTIOI_NODETYPE_START;
+ ret = *(uint16_t *)((void *)state->ext_nodetype + off);
+ }
+
+ DPRINTF("readw reg 0x" TARGET_FMT_plx " = %x\n", node->addr + addr, ret);
+ return ret;
+}
+
+static uint32_t apic_readl(void *opaque, hwaddr addr)
+{
+ nodeApicState *node;
+ apicState *state;
+ unsigned long off;
+ uint32_t ret;
+ int cpu;
+
+ node = (nodeApicState *)opaque;
+ state = node->apic;
+ off = addr & 0xfffff;
+ ret = 0;
+ if ((off >= EXTIOI_ENABLE_START) && (off < EXTIOI_ENABLE_END)) {
+ off -= EXTIOI_ENABLE_START;
+ ret = *(uint32_t *)((void *)state->ext_en + off);
+ } else if ((off >= EXTIOI_BOUNCE_START) && (off < EXTIOI_BOUNCE_END)) {
+ off -= EXTIOI_BOUNCE_START;
+ ret = *(uint32_t *)((void *)state->ext_bounce + off);
+ } else if ((off >= EXTIOI_ISR_START) && (off < EXTIOI_ISR_END)) {
+ off -= EXTIOI_ISR_START;
+ ret = *(uint32_t *)((void *)state->ext_isr + off);
+ } else if ((off >= EXTIOI_COREISR_START) && (off < EXTIOI_COREISR_END)) {
+ off -= EXTIOI_COREISR_START;
+ cpu = (off >> 8) & 0xff;
+ ret = *(uint32_t *)((void *)state->ext_coreisr[cpu] + (off & 0x1f));
+ } else if ((off >= EXTIOI_IPMAP_START) && (off < EXTIOI_IPMAP_END)) {
+ off -= EXTIOI_IPMAP_START;
+ ret = *(uint32_t *)((void *)state->ext_ipmap + off);
+ } else if ((off >= EXTIOI_COREMAP_START) && (off < EXTIOI_COREMAP_END)) {
+ off -= EXTIOI_COREMAP_START;
+ ret = *(uint32_t *)((void *)state->ext_coremap + off);
+ } else if ((off >= EXTIOI_NODETYPE_START) && (off < EXTIOI_NODETYPE_END)) {
+ off -= EXTIOI_NODETYPE_START;
+ ret = *(uint32_t *)((void *)state->ext_nodetype + off);
+ }
+
+ DPRINTF("readl reg 0x" TARGET_FMT_plx " = %x\n", node->addr + addr, ret);
+ return ret;
+
+}
+
+static void apic_writeb(void *opaque, hwaddr addr, uint32_t val)
+{
+ nodeApicState *node;
+ apicState *state;
+ unsigned long off;
+ uint8_t old;
+ int cpu, i, ipnum, level, mask;
+
+ node = (nodeApicState *)opaque;
+ state = node->apic;
+ off = addr & 0xfffff;
+ if ((off >= EXTIOI_ENABLE_START) && (off < EXTIOI_ENABLE_END)) {
+ off -= EXTIOI_ENABLE_START;
+ old = *(uint8_t *)((void *)state->ext_en + off);
+ if (old != val) {
+ *(uint8_t *)((void *)state->ext_en + off) = val;
+ old = old ^ val;
+ mask = 0x1;
+ for (i = 0; i < 8; i++) {
+ if (old & mask) {
+ level = !!(val & (0x1 << i));
+ ioapic_update_irq(state, i + off * 8, level);
+ }
+ mask = mask << 1;
+ }
+ }
+ } else if ((off >= EXTIOI_BOUNCE_START) && (off < EXTIOI_BOUNCE_END)) {
+ off -= EXTIOI_BOUNCE_START;
+ *(uint8_t *)((void *)state->ext_bounce + off) = val;
+ } else if ((off >= EXTIOI_ISR_START) && (off < EXTIOI_ISR_END)) {
+ off -= EXTIOI_ISR_START;
+ old = *(uint8_t *)((void *)state->ext_isr + off);
+ *(uint8_t *)((void *)state->ext_isr + off) = old & ~val;
+ mask = 0x1;
+ for (i = 0; i < 8; i++) {
+ if ((old & mask) && (val & mask)) {
+ ioapic_update_irq(state, i + off * 8, 0);
+ }
+ mask = mask << 1;
+ }
+ } else if ((off >= EXTIOI_COREISR_START) && (off < EXTIOI_COREISR_END)) {
+ off -= EXTIOI_COREISR_START;
+ cpu = (off >> 8) & 0xff;
+ off = off & 0x1f;
+ old = *(uint8_t *)((void *)state->ext_coreisr[cpu] + off);
+ *(uint8_t *)((void *)state->ext_coreisr[cpu] + off) = old & ~val;
+ mask = 0x1;
+ for (i = 0; i < 8; i++) {
+ if ((old & mask) && (val & mask)) {
+ ioapic_update_irq(state, i + off * 8, 0);
+ }
+ mask = mask << 1;
+ }
+ } else if ((off >= EXTIOI_IPMAP_START) && (off < EXTIOI_IPMAP_END)) {
+ off -= EXTIOI_IPMAP_START;
+ val = val & 0xf;
+ *(uint8_t *)((void *)state->ext_ipmap + off) = val;
+ ipnum = 0;
+ for (i = 0; i < 4; i++) {
+ if (val & (0x1 << i)) {
+ ipnum = i;
+ break;
+ }
+ }
+ if (val) {
+ for (i = 0; i < 32; i++) {
+ cpu = off * 32 + i;
+ state->ext_sw_ipmap[cpu] = ipnum;
+ }
+ }
+ } else if ((off >= EXTIOI_COREMAP_START) && (off < EXTIOI_COREMAP_END)) {
+ off -= EXTIOI_COREMAP_START;
+ val = val & 0xff;
+ *(uint8_t *)((void *)state->ext_coremap + off) = val;
+ state->ext_sw_coremap[off] = val;
+ } else if ((off >= EXTIOI_NODETYPE_START) && (off < EXTIOI_NODETYPE_END)) {
+ off -= EXTIOI_NODETYPE_START;
+ *(uint8_t *)((void *)state->ext_nodetype + off) = val;
+ }
+
+ DPRINTF("writeb reg 0x" TARGET_FMT_plx " = %x\n", node->addr + addr, val);
+}
+
+static void apic_writew(void *opaque, hwaddr addr, uint32_t val)
+{
+ nodeApicState *node;
+ apicState *state;
+ unsigned long off;
+ uint16_t old;
+ int cpu, i, level, mask;
+
+ node = (nodeApicState *)opaque;
+ state = node->apic;
+ off = addr & 0xfffff;
+ if ((off >= EXTIOI_ENABLE_START) && (off < EXTIOI_ENABLE_END)) {
+ off -= EXTIOI_ENABLE_START;
+ old = *(uint16_t *)((void *)state->ext_en + off);
+ if (old != val) {
+ *(uint16_t *)((void *)state->ext_en + off) = val;
+ old = old ^ val;
+ mask = 0x1;
+ for (i = 0; i < 16; i++) {
+ if (old & mask) {
+ level = !!(val & (0x1 << i));
+ ioapic_update_irq(state, i + off * 8, level);
+ }
+ mask = mask << 1;
+ }
+ }
+ } else if ((off >= EXTIOI_BOUNCE_START) && (off < EXTIOI_BOUNCE_END)) {
+ off -= EXTIOI_BOUNCE_START;
+ *(uint16_t *)((void *)state->ext_bounce + off) = val;
+ } else if ((off >= EXTIOI_ISR_START) && (off < EXTIOI_ISR_END)) {
+ off -= EXTIOI_ISR_START;
+ old = *(uint16_t *)((void *)state->ext_isr + off);
+ *(uint16_t *)((void *)state->ext_isr + off) = old & ~val;
+ mask = 0x1;
+ for (i = 0; i < 16; i++) {
+ if ((old & mask) && (val & mask)) {
+ ioapic_update_irq(state, i + off * 8, 0);
+ }
+ mask = mask << 1;
+ }
+ } else if ((off >= EXTIOI_COREISR_START) && (off < EXTIOI_COREISR_END)) {
+ off -= EXTIOI_COREISR_START;
+ cpu = (off >> 8) & 0xff;
+ off = off & 0x1f;
+ old = *(uint16_t *)((void *)state->ext_coreisr[cpu] + off);
+ *(uint16_t *)((void *)state->ext_coreisr[cpu] + off) = old & ~val;
+ mask = 0x1;
+ for (i = 0; i < 16; i++) {
+ if ((old & mask) && (val & mask)) {
+ ioapic_update_irq(state, i + off * 8, 0);
+ }
+ mask = mask << 1;
+ }
+ } else if ((off >= EXTIOI_IPMAP_START) && (off < EXTIOI_IPMAP_END)) {
+ apic_writeb(opaque, addr, val & 0xff);
+ apic_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+
+ } else if ((off >= EXTIOI_COREMAP_START) && (off < EXTIOI_COREMAP_END)) {
+ apic_writeb(opaque, addr, val & 0xff);
+ apic_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+
+ } else if ((off >= EXTIOI_NODETYPE_START) && (off < EXTIOI_NODETYPE_END)) {
+ off -= EXTIOI_NODETYPE_START;
+ *(uint16_t *)((void *)state->ext_nodetype + off) = val;
+ }
+
+ DPRINTF("writew reg 0x" TARGET_FMT_plx " = %x\n", node->addr + addr, val);
+}
+
+static void apic_writel(void *opaque, hwaddr addr, uint32_t val)
+{
+ nodeApicState *node;
+ apicState *state;
+ unsigned long off;
+ uint32_t old;
+ int cpu, i, level, mask;
+
+ node = (nodeApicState *)opaque;
+ state = node->apic;
+ off = addr & 0xfffff;
+ if ((off >= EXTIOI_ENABLE_START) && (off < EXTIOI_ENABLE_END)) {
+ off -= EXTIOI_ENABLE_START;
+ old = *(uint32_t *)((void *)state->ext_en + off);
+ if (old != val) {
+ *(uint32_t *)((void *)state->ext_en + off) = val;
+ old = old ^ val;
+ mask = 0x1;
+ for (i = 0; i < 32; i++) {
+ if (old & mask) {
+ level = !!(val & (0x1 << i));
+ ioapic_update_irq(state, i + off * 8, level);
+ }
+ mask = mask << 1;
+ }
+ }
+ } else if ((off >= EXTIOI_BOUNCE_START) && (off < EXTIOI_BOUNCE_END)) {
+ off -= EXTIOI_BOUNCE_START;
+ *(uint32_t *)((void *)state->ext_bounce + off) = val;
+ } else if ((off >= EXTIOI_ISR_START) && (off < EXTIOI_ISR_END)) {
+ off -= EXTIOI_ISR_START;
+ old = *(uint32_t *)((void *)state->ext_isr + off);
+ *(uint32_t *)((void *)state->ext_isr + off) = old & ~val;
+ mask = 0x1;
+ for (i = 0; i < 32; i++) {
+ if ((old & mask) && (val & mask)) {
+ ioapic_update_irq(state, i + off * 8, 0);
+ }
+ mask = mask << 1;
+ }
+ } else if ((off >= EXTIOI_COREISR_START) && (off < EXTIOI_COREISR_END)) {
+ off -= EXTIOI_COREISR_START;
+ cpu = (off >> 8) & 0xff;
+ off = off & 0x1f;
+ old = *(uint32_t *)((void *)state->ext_coreisr[cpu] + off);
+ *(uint32_t *)((void *)state->ext_coreisr[cpu] + off) = old & ~val;
+ mask = 0x1;
+ for (i = 0; i < 32; i++) {
+ if ((old & mask) && (val & mask)) {
+ ioapic_update_irq(state, i + off * 8, 0);
+ }
+ mask = mask << 1;
+ }
+ } else if ((off >= EXTIOI_IPMAP_START) && (off < EXTIOI_IPMAP_END)) {
+ apic_writeb(opaque, addr, val & 0xff);
+ apic_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+ apic_writeb(opaque, addr + 2, (val >> 16) & 0xff);
+ apic_writeb(opaque, addr + 3, (val >> 24) & 0xff);
+
+ } else if ((off >= EXTIOI_COREMAP_START) && (off < EXTIOI_COREMAP_END)) {
+ apic_writeb(opaque, addr, val & 0xff);
+ apic_writeb(opaque, addr + 1, (val >> 8) & 0xff);
+ apic_writeb(opaque, addr + 2, (val >> 16) & 0xff);
+ apic_writeb(opaque, addr + 3, (val >> 24) & 0xff);
+
+ } else if ((off >= EXTIOI_NODETYPE_START) && (off < EXTIOI_NODETYPE_END)) {
+ off -= EXTIOI_NODETYPE_START;
+ *(uint32_t *)((void *)state->ext_nodetype + off) = val;
+ }
+
+ DPRINTF("writel reg 0x" TARGET_FMT_plx " = %x\n", node->addr + addr, val);
+}
+
+static uint64_t apic_readfn(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ switch (size) {
+ case 1:
+ return apic_readb(opaque, addr);
+ case 2:
+ return apic_readw(opaque, addr);
+ case 4:
+ return apic_readl(opaque, addr);
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void apic_writefn(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ switch (size) {
+ case 1:
+ apic_writeb(opaque, addr, value);
+ break;
+ case 2:
+ apic_writew(opaque, addr, value);
+ break;
+ case 4:
+ apic_writel(opaque, addr, value);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static const VMStateDescription vmstate_apic = {
+ .name = "apic",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .pre_save = ext_irq_pre_save,
+ .post_load = ext_irq_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8_ARRAY(ext_en, apicState, EXTIOI_IRQS_BITMAP_SIZE),
+ VMSTATE_UINT8_ARRAY(ext_bounce, apicState, EXTIOI_IRQS_BITMAP_SIZE),
+ VMSTATE_UINT8_ARRAY(ext_isr, apicState, EXTIOI_IRQS_BITMAP_SIZE),
+ VMSTATE_UINT8_2DARRAY(ext_coreisr, apicState, MAX_CORES,
+ EXTIOI_IRQS_BITMAP_SIZE),
+ VMSTATE_UINT8_ARRAY(ext_ipmap, apicState, EXTIOI_IRQS_IPMAP_SIZE),
+ VMSTATE_UINT8_ARRAY(ext_coremap, apicState, EXTIOI_IRQS),
+ VMSTATE_UINT16_ARRAY(ext_nodetype, apicState, 16),
+ VMSTATE_UINT64(ext_control, apicState),
+ VMSTATE_UINT8_ARRAY(ext_sw_ipmap, apicState, EXTIOI_IRQS),
+ VMSTATE_UINT8_ARRAY(ext_sw_coremap, apicState, EXTIOI_IRQS),
+ VMSTATE_UINT8_2DARRAY(ext_ipisr, apicState, MAX_CORES * LS3A_INTC_IP,
+ EXTIOI_IRQS_BITMAP_SIZE),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const MemoryRegionOps apic_ops = {
+ .read = apic_readfn,
+ .write = apic_writefn,
+ .impl.min_access_size = 1,
+ .impl.max_access_size = 4,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+int cpu_init_apic(LoongarchMachineState *ms, CPULOONGARCHState *env, int cpu)
+{
+ apicState *apic;
+ nodeApicState *node;
+ MemoryRegion *iomem;
+ unsigned long base;
+ int pin;
+ char str[32];
+
+ if (ms->apic == NULL) {
+ apic = g_malloc0(sizeof(apicState));
+ vmstate_register(NULL, 0, &vmstate_apic, apic);
+ apic->irq = qemu_allocate_irqs(ioapic_setirq, apic, EXTIOI_IRQS);
+
+ for (pin = 0; pin < LS3A_INTC_IP; pin++) {
+ /* cpu_pin[9:2] <= intc_pin[7:0] */
+ apic->parent_irq[cpu][pin] = env->irq[pin + 2];
+ }
+ ms->apic = apic;
+
+ if (cpu == 0) {
+ base = APIC_BASE;
+ node = g_malloc0(sizeof(nodeApicState));
+ node->apic = ms->apic;
+ node->addr = base;
+
+ iomem = g_new(MemoryRegion, 1);
+ sprintf(str, "apic%d", cpu);
+ /* extioi addr 0x1f010000~0x1f02ffff */
+ memory_region_init_io(iomem, NULL, &apic_ops, node, str, 0x20000);
+ memory_region_add_subregion(get_system_memory(), base, iomem);
+ }
+
+ } else {
+ if (cpu != 0) {
+ for (pin = 0; pin < LS3A_INTC_IP; pin++) {
+ ms->apic->parent_irq[cpu][pin] = env->irq[pin + 2];
+ }
+ }
+ }
+ return 0;
+}
+
diff --git a/hw/loongarch/ioapic.c b/hw/loongarch/ioapic.c
new file mode 100644
index 000000000..3de0ed88d
--- /dev/null
+++ b/hw/loongarch/ioapic.c
@@ -0,0 +1,422 @@
+/*
+ * LS7A1000 Northbridge IOAPIC support
+ *
+ * Copyright (c) 2019 Loongarch Technology
+ * Authors:
+ * Zhu Chen <zhuchen@loongson.cn>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/sysbus.h"
+#include "hw/irq.h"
+#include "qemu/log.h"
+#include "sysemu/kvm.h"
+#include "linux/kvm.h"
+#include "migration/vmstate.h"
+
+#define DEBUG_LS7A_APIC 0
+
+#define DPRINTF(fmt, ...) \
+do { \
+ if (DEBUG_LS7A_APIC) { \
+ fprintf(stderr, "IOAPIC: " fmt , ## __VA_ARGS__); \
+ } \
+} while (0)
+
+#define TYPE_LS7A_APIC "ioapic"
+#define LS7A_APIC(obj) OBJECT_CHECK(LS7AApicState, (obj), TYPE_LS7A_APIC)
+
+#define LS7A_IOAPIC_ROUTE_ENTRY_OFFSET 0x100
+#define LS7A_IOAPIC_INT_ID_OFFSET 0x00
+#define LS7A_INT_ID_VAL 0x7000000UL
+#define LS7A_INT_ID_VER 0x1f0001UL
+#define LS7A_IOAPIC_INT_MASK_OFFSET 0x20
+#define LS7A_IOAPIC_INT_EDGE_OFFSET 0x60
+#define LS7A_IOAPIC_INT_CLEAR_OFFSET 0x80
+#define LS7A_IOAPIC_INT_STATUS_OFFSET 0x3a0
+#define LS7A_IOAPIC_INT_POL_OFFSET 0x3e0
+#define LS7A_IOAPIC_HTMSI_EN_OFFSET 0x40
+#define LS7A_IOAPIC_HTMSI_VEC_OFFSET 0x200
+#define LS7A_AUTO_CTRL0_OFFSET 0xc0
+#define LS7A_AUTO_CTRL1_OFFSET 0xe0
+
+typedef struct LS7AApicState {
+ SysBusDevice parent_obj;
+ qemu_irq parent_irq[257];
+ uint64_t int_id;
+ uint64_t int_mask; /*0x020 interrupt mask register*/
+ uint64_t htmsi_en;/*0x040 1=msi*/
+ uint64_t intedge; /*0x060 edge=1 level =0*/
+ uint64_t intclr; /*0x080 for clean edge int,set 1 clean,set 0 is noused*/
+ uint64_t auto_crtl0; /*0x0c0*/
+ uint64_t auto_crtl1; /*0x0e0*/
+ uint8_t route_entry[64]; /*0x100 - 0x140*/
+ uint8_t htmsi_vector[64]; /*0x200 - 0x240*/
+ uint64_t intisr_chip0; /*0x300*/
+ uint64_t intisr_chip1;/*0x320*/
+ uint64_t last_intirr; /* edge detection */
+ uint64_t intirr; /* 0x380 interrupt request register */
+ uint64_t intisr; /* 0x3a0 interrupt service register */
+ uint64_t int_polarity; /*0x3e0 interrupt level polarity
+ selection register 0 for high level tirgger*/
+ MemoryRegion iomem;
+} LS7AApicState;
+
+static void update_irq(LS7AApicState *s)
+{
+ int i;
+ if ((s->intirr & (~s->int_mask)) & (~s->htmsi_en)) {
+ DPRINTF("7a update irqline up\n");
+ s->intisr = (s->intirr & (~s->int_mask) & (~s->htmsi_en));
+ qemu_set_irq(s->parent_irq[256], 1);
+ } else {
+ DPRINTF("7a update irqline down\n");
+ s->intisr &= (~s->htmsi_en);
+ qemu_set_irq(s->parent_irq[256], 0);
+ }
+ if (s->htmsi_en) {
+ for (i = 0; i < 64; i++) {
+ if ((((~s->intisr) & s->intirr) & s->htmsi_en) & (1ULL << i)) {
+ s->intisr |= 1ULL << i;
+ qemu_set_irq(s->parent_irq[s->htmsi_vector[i]], 1);
+ } else if (((~(s->intisr | s->intirr)) & s->htmsi_en) &
+ (1ULL << i)) {
+ qemu_set_irq(s->parent_irq[s->htmsi_vector[i]], 0);
+ }
+ }
+ }
+}
+
+static void irq_handler(void *opaque, int irq, int level)
+{
+ LS7AApicState *s = opaque;
+
+ assert(irq < 64);
+ uint64_t mask = 1ULL << irq;
+ DPRINTF("------ %s irq %d %d\n", __func__, irq, level);
+
+ if (s->intedge & mask) {
+ /* edge triggered */
+ /*TODO*/
+ } else {
+ /* level triggered */
+ if (level) {
+ s->intirr |= mask;
+ } else {
+ s->intirr &= ~mask;
+ }
+
+ }
+ update_irq(s);
+
+}
+
+static uint64_t ls7a_apic_reg_read(void *opaque, hwaddr addr, unsigned size)
+{
+ LS7AApicState *a = opaque;
+ uint64_t val = 0;
+ uint64_t offset;
+ int64_t offset_tmp;
+ offset = addr & 0xfff;
+ if (8 == size) {
+ switch (offset) {
+ case LS7A_IOAPIC_INT_ID_OFFSET:
+ val = LS7A_INT_ID_VER;
+ val = (val << 32) + LS7A_INT_ID_VAL;
+ break;
+ case LS7A_IOAPIC_INT_MASK_OFFSET:
+ val = a->int_mask;
+ break;
+ case LS7A_IOAPIC_INT_STATUS_OFFSET:
+ val = a->intisr & (~a->int_mask);
+ break;
+ case LS7A_IOAPIC_INT_EDGE_OFFSET:
+ val = a->intedge;
+ break;
+ case LS7A_IOAPIC_INT_POL_OFFSET:
+ val = a->int_polarity;
+ break;
+ case LS7A_IOAPIC_HTMSI_EN_OFFSET:
+ val = a->htmsi_en;
+ break;
+ case LS7A_AUTO_CTRL0_OFFSET:
+ case LS7A_AUTO_CTRL1_OFFSET:
+ break;
+ default:
+ break;
+ }
+ } else if (1 == size) {
+ if (offset >= LS7A_IOAPIC_HTMSI_VEC_OFFSET) {
+ offset_tmp = offset - LS7A_IOAPIC_HTMSI_VEC_OFFSET;
+ if (offset_tmp >= 0 && offset_tmp < 64) {
+ val = a->htmsi_vector[offset_tmp];
+ }
+ } else if (offset >= LS7A_IOAPIC_ROUTE_ENTRY_OFFSET) {
+ offset_tmp = offset - LS7A_IOAPIC_ROUTE_ENTRY_OFFSET;
+ if (offset_tmp >= 0 && offset_tmp < 64) {
+ val = a->route_entry[offset_tmp];
+ DPRINTF("addr %lx val %lx\n", addr, val);
+ }
+ }
+ }
+ DPRINTF(TARGET_FMT_plx" val %lx\n", addr, val);
+ return val;
+}
+
+static void ls7a_apic_reg_write(void *opaque, hwaddr addr, uint64_t data, unsigned size)
+{
+ LS7AApicState *a = opaque;
+ int64_t offset_tmp;
+ uint64_t offset;
+ offset = addr & 0xfff;
+ DPRINTF(TARGET_FMT_plx" size %d val %lx\n", addr, size, data);
+ if (8 == size) {
+ switch (offset) {
+ case LS7A_IOAPIC_INT_MASK_OFFSET:
+ a->int_mask = data;
+ update_irq(a);
+ break;
+ case LS7A_IOAPIC_INT_STATUS_OFFSET:
+ a->intisr = data;
+ break;
+ case LS7A_IOAPIC_INT_EDGE_OFFSET:
+ a->intedge = data;
+ break;
+ case LS7A_IOAPIC_INT_CLEAR_OFFSET:
+ a->intisr &= (~data);
+ update_irq(a);
+ break;
+ case LS7A_IOAPIC_INT_POL_OFFSET:
+ a->int_polarity = data;
+ break;
+ case LS7A_IOAPIC_HTMSI_EN_OFFSET:
+ a->htmsi_en = data;
+ break;
+ case LS7A_AUTO_CTRL0_OFFSET:
+ case LS7A_AUTO_CTRL1_OFFSET:
+ break;
+ default:
+ break;
+ }
+ } else if (1 == size) {
+ if (offset >= LS7A_IOAPIC_HTMSI_VEC_OFFSET) {
+ offset_tmp = offset - LS7A_IOAPIC_HTMSI_VEC_OFFSET;
+ if (offset_tmp >= 0 && offset_tmp < 64) {
+ a->htmsi_vector[offset_tmp] = (uint8_t)(data & 0xff);
+ }
+ } else if (offset >= LS7A_IOAPIC_ROUTE_ENTRY_OFFSET) {
+ offset_tmp = offset - LS7A_IOAPIC_ROUTE_ENTRY_OFFSET;
+ if (offset_tmp >= 0 && offset_tmp < 64) {
+ a->route_entry[offset_tmp] = (uint8_t)(data & 0xff);
+ }
+ }
+ }
+}
+
+static const MemoryRegionOps ls7a_apic_ops = {
+ .read = ls7a_apic_reg_read,
+ .write = ls7a_apic_reg_write,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 8,
+ },
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 8,
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static int kvm_ls7a_pre_save(void *opaque)
+{
+#ifdef CONFIG_KVM
+ LS7AApicState *s = opaque;
+ struct loongarch_kvm_irqchip *chip;
+ struct ls7a_ioapic_state *state;
+ int ret, i, length;
+
+ if (!kvm_irqchip_in_kernel()) {
+ return 0;
+ }
+
+ length = sizeof(struct loongarch_kvm_irqchip) + sizeof(struct ls7a_ioapic_state);
+ chip = g_malloc0(length);
+ memset(chip, 0, length);
+ chip->chip_id = KVM_IRQCHIP_LS7A_IOAPIC;
+ chip->len = length;
+ ret = kvm_vm_ioctl(kvm_state, KVM_GET_IRQCHIP, chip);
+ if (ret < 0) {
+ fprintf(stderr, "KVM_GET_IRQCHIP failed: %s\n", strerror(ret));
+ abort();
+ }
+ state = (struct ls7a_ioapic_state *)chip->data;
+ s->int_id = state->int_id;
+ s->int_mask = state->int_mask;
+ s->htmsi_en = state->htmsi_en;
+ s->intedge = state->intedge;
+ s->intclr = state->intclr;
+ s->auto_crtl0 = state->auto_crtl0;
+ s->auto_crtl1 = state->auto_crtl1;
+ for (i = 0; i < 64; i++) {
+ s->route_entry[i] = state->route_entry[i];
+ s->htmsi_vector[i] = state->htmsi_vector[i];
+ }
+ s->intisr_chip0 = state->intisr_chip0;
+ s->intisr_chip1 = state->intisr_chip1;
+ s->intirr = state->intirr;
+ s->intisr = state->intisr;
+ s->int_polarity = state->int_polarity;
+ g_free(chip);
+#endif
+ return 0;
+}
+
+static int kvm_ls7a_post_load(void *opaque, int version)
+{
+#ifdef CONFIG_KVM
+ LS7AApicState *s = opaque;
+ struct loongarch_kvm_irqchip *chip;
+ struct ls7a_ioapic_state *state;
+ int ret, i, length;
+
+ if (!kvm_irqchip_in_kernel()) {
+ return 0;
+ }
+ length = sizeof(struct loongarch_kvm_irqchip) + sizeof(struct ls7a_ioapic_state);
+ chip = g_malloc0(length);
+ memset(chip, 0, length);
+ chip->chip_id = KVM_IRQCHIP_LS7A_IOAPIC;
+ chip->len = length;
+
+ state = (struct ls7a_ioapic_state *)chip->data;
+ state->int_id = s->int_id;
+ state->int_mask = s->int_mask;
+ state->htmsi_en = s->htmsi_en;
+ state->intedge = s->intedge;
+ state->intclr = s->intclr;
+ state->auto_crtl0 = s->auto_crtl0;
+ state->auto_crtl1 = s->auto_crtl1;
+ for (i = 0; i < 64; i++) {
+ state->route_entry[i] = s->route_entry[i];
+ state->htmsi_vector[i] = s->htmsi_vector[i];
+ }
+ state->intisr_chip0 = s->intisr_chip0;
+ state->intisr_chip1 = s->intisr_chip1;
+ state->last_intirr = 0;
+ state->intirr = s->intirr;
+ state->intisr = s->intisr;
+ state->int_polarity = s->int_polarity;
+
+ ret = kvm_vm_ioctl(kvm_state, KVM_SET_IRQCHIP, chip);
+ if (ret < 0) {
+ fprintf(stderr, "KVM_GET_IRQCHIP failed: %s\n", strerror(ret));
+ abort();
+ }
+ g_free(chip);
+#endif
+ return 0;
+}
+
+static void ls7a_apic_reset(DeviceState *d)
+{
+ LS7AApicState *s = LS7A_APIC(d);
+ int i;
+
+ s->int_id = 0x001f000107000000;
+ s->int_mask = 0xffffffffffffffff;
+ s->htmsi_en = 0x0;
+ s->intedge = 0x0;
+ s->intclr = 0x0;
+ s->auto_crtl0 = 0x0;
+ s->auto_crtl1 = 0x0;
+ for (i = 0; i < 64; i++) {
+ s->route_entry[i] = 0x1;
+ s->htmsi_vector[i] = 0x0;
+ }
+ s->intisr_chip0 = 0x0;
+ s->intisr_chip1 = 0x0;
+ s->intirr = 0x0;
+ s->intisr = 0x0;
+ s->int_polarity = 0x0;
+ kvm_ls7a_post_load(s, 0);
+}
+
+static void ls7a_apic_init(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+ LS7AApicState *s = LS7A_APIC(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ int tmp;
+ memory_region_init_io(&s->iomem, obj, &ls7a_apic_ops, s, TYPE_LS7A_APIC, 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+ for (tmp = 0; tmp < 257; tmp++) {
+ sysbus_init_irq(sbd, &s->parent_irq[tmp]);
+ }
+ qdev_init_gpio_in(dev, irq_handler, 64);
+}
+
+static const VMStateDescription vmstate_ls7a_apic = {
+ .name = TYPE_LS7A_APIC,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .pre_save = kvm_ls7a_pre_save,
+ .post_load = kvm_ls7a_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(int_mask, LS7AApicState),
+ VMSTATE_UINT64(htmsi_en, LS7AApicState),
+ VMSTATE_UINT64(intedge, LS7AApicState),
+ VMSTATE_UINT64(intclr, LS7AApicState),
+ VMSTATE_UINT64(auto_crtl0, LS7AApicState),
+ VMSTATE_UINT64(auto_crtl1, LS7AApicState),
+ VMSTATE_UINT8_ARRAY(route_entry, LS7AApicState, 64),
+ VMSTATE_UINT8_ARRAY(htmsi_vector, LS7AApicState, 64),
+ VMSTATE_UINT64(intisr_chip0, LS7AApicState),
+ VMSTATE_UINT64(intisr_chip1, LS7AApicState),
+ VMSTATE_UINT64(last_intirr, LS7AApicState),
+ VMSTATE_UINT64(intirr, LS7AApicState),
+ VMSTATE_UINT64(intisr, LS7AApicState),
+ VMSTATE_UINT64(int_polarity, LS7AApicState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void ls7a_apic_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = ls7a_apic_reset;
+ dc->vmsd = &vmstate_ls7a_apic;
+}
+
+static const TypeInfo ls7a_apic_info = {
+ .name = TYPE_LS7A_APIC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(LS7AApicState),
+ .instance_init = ls7a_apic_init,
+ .class_init = ls7a_apic_class_init,
+};
+
+static void ls7a_apic_register_types(void)
+{
+ type_register_static(&ls7a_apic_info);
+}
+
+type_init(ls7a_apic_register_types)
diff --git a/hw/loongarch/iocsr.c b/hw/loongarch/iocsr.c
new file mode 100644
index 000000000..14521c2d5
--- /dev/null
+++ b/hw/loongarch/iocsr.c
@@ -0,0 +1,219 @@
+/*
+ * LOONGARCH IOCSR support
+ *
+ * Copyright (c) 2021 Loongarch Technology
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/sysbus.h"
+#include "qemu/log.h"
+#include "sysemu/kvm.h"
+#include "linux/kvm.h"
+#include "migration/vmstate.h"
+#include "hw/boards.h"
+#include "hw/loongarch/larch.h"
+
+#define BIT_ULL(nr) (1ULL << (nr))
+#define LOONGARCH_IOCSR_FEATURES 0x8
+#define IOCSRF_TEMP BIT_ULL(0)
+#define IOCSRF_NODECNT BIT_ULL(1)
+#define IOCSRF_MSI BIT_ULL(2)
+#define IOCSRF_EXTIOI BIT_ULL(3)
+#define IOCSRF_CSRIPI BIT_ULL(4)
+#define IOCSRF_FREQCSR BIT_ULL(5)
+#define IOCSRF_FREQSCALE BIT_ULL(6)
+#define IOCSRF_DVFSV1 BIT_ULL(7)
+#define IOCSRF_GMOD BIT_ULL(9)
+#define IOCSRF_VM BIT_ULL(11)
+#define LOONGARCH_IOCSR_VENDOR 0x10
+#define LOONGARCH_IOCSR_CPUNAME 0x20
+#define LOONGARCH_IOCSR_NODECNT 0x408
+#define LOONGARCH_IOCSR_MISC_FUNC 0x420
+#define IOCSR_MISC_FUNC_TIMER_RESET BIT_ULL(21)
+#define IOCSR_MISC_FUNC_EXT_IOI_EN BIT_ULL(48)
+
+enum {
+ IOCSR_FEATURES,
+ IOCSR_VENDOR,
+ IOCSR_CPUNAME,
+ IOCSR_NODECNT,
+ IOCSR_MISC_FUNC,
+ IOCSR_MAX
+};
+
+static uint32_t iocsr_array[IOCSR_MAX] = {
+ [IOCSR_FEATURES] = LOONGARCH_IOCSR_FEATURES,
+ [IOCSR_VENDOR] = LOONGARCH_IOCSR_VENDOR,
+ [IOCSR_CPUNAME] = LOONGARCH_IOCSR_CPUNAME,
+ [IOCSR_NODECNT] = LOONGARCH_IOCSR_NODECNT,
+ [IOCSR_MISC_FUNC] = LOONGARCH_IOCSR_MISC_FUNC,
+};
+
+
+#define TYPE_IOCSR "iocsr"
+#define IOCSR(obj) OBJECT_CHECK(IOCSRState, (obj), TYPE_IOCSR)
+
+typedef struct IOCSRState {
+ SysBusDevice parent_obj;
+ uint64_t iocsr_val[IOCSR_MAX];
+} IOCSRState;
+
+IOCSRState iocsr_init = {
+ .iocsr_val = {
+ IOCSRF_NODECNT | IOCSRF_MSI | IOCSRF_EXTIOI
+ | IOCSRF_CSRIPI | IOCSRF_GMOD | IOCSRF_VM,
+ 0x6e6f73676e6f6f4c, /* Loongson */
+ 0x303030354133, /*3A5000*/
+ 0x4,
+ 0x0,
+ }
+};
+
+static int kvm_iocsr_pre_save(void *opaque)
+{
+#ifdef CONFIG_KVM
+ IOCSRState *s = opaque;
+ struct kvm_iocsr_entry entry;
+ int i = 0;
+ for (i = 0; i < IOCSR_MAX; i++) {
+ entry.addr = iocsr_array[i];
+ kvm_vm_ioctl(kvm_state, KVM_LOONGARCH_GET_IOCSR, &entry);
+ s->iocsr_val[i] = entry.data;
+ }
+#endif
+ return 0;
+}
+
+static int kvm_iocsr_post_load(void *opaque, int version)
+{
+#ifdef CONFIG_KVM
+ IOCSRState *s = opaque;
+ struct kvm_iocsr_entry entry;
+ int i = 0;
+
+ if (!kvm_enabled()) {
+ return 0;
+ }
+
+ for (i = 0; i < IOCSR_MAX; i++) {
+ entry.addr = iocsr_array[i];
+ entry.data = s->iocsr_val[i];
+ kvm_vm_ioctl(kvm_state, KVM_LOONGARCH_SET_IOCSR, &entry);
+ }
+#endif
+ return 0;
+}
+
+static void iocsr_reset(DeviceState *d)
+{
+ IOCSRState *s = IOCSR(d);
+ int i;
+
+ for (i = 0; i < IOCSR_MAX; i++) {
+ s->iocsr_val[i] = iocsr_init.iocsr_val[i];
+ }
+ kvm_iocsr_post_load(s, 0);
+}
+static void init_vendor_cpuname(uint64_t *vendor,
+ uint64_t *cpu_name, char *cpuname)
+{
+ int i = 0, len = 0;
+ char *index = NULL, *index_end = NULL;
+ char *vendor_c = (char *)vendor;
+ char *cpu_name_c = (char *)cpu_name;
+
+ index = strstr(cpuname, "-");
+ len = strlen(cpuname);
+ if ((index == NULL) || (len <= 0)) {
+ return ;
+ }
+
+ *vendor = 0;
+ *cpu_name = 0;
+ index_end = cpuname + len;
+
+ while (((cpuname + i) < index) && (i < sizeof(uint64_t))) {
+ vendor_c[i] = cpuname[i];
+ i++;
+ }
+
+ index += 1;
+ i = 0;
+
+ while (((index + i) < index_end) && (i < sizeof(uint64_t))) {
+ cpu_name_c[i] = index[i];
+ i++;
+ }
+
+ return ;
+}
+
+static void iocsr_instance_init(Object *obj)
+{
+ IOCSRState *s = IOCSR(obj);
+ int i;
+ LoongarchMachineState *lsms = LoongarchMACHINE(qdev_get_machine());
+ LoongarchMachineClass *lsmc = LoongarchMACHINE_GET_CLASS(lsms);
+
+ init_vendor_cpuname((uint64_t *)&iocsr_init.iocsr_val[IOCSR_VENDOR],
+ (uint64_t *)&iocsr_init.iocsr_val[IOCSR_CPUNAME],
+ lsmc->cpu_name);
+
+ for (i = 0; i < IOCSR_MAX; i++) {
+ s->iocsr_val[i] = iocsr_init.iocsr_val[i];
+ }
+}
+
+static const VMStateDescription vmstate_iocsr = {
+ .name = TYPE_IOCSR,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .pre_save = kvm_iocsr_pre_save,
+ .post_load = kvm_iocsr_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64_ARRAY(iocsr_val, IOCSRState, IOCSR_MAX),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void iocsr_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = iocsr_reset;
+ dc->vmsd = &vmstate_iocsr;
+
+}
+
+static const TypeInfo iocsr_info = {
+ .name = TYPE_IOCSR,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(IOCSRState),
+ .instance_init = iocsr_instance_init,
+ .class_init = iocsr_class_init,
+};
+
+static void iocsr_register_types(void)
+{
+ type_register_static(&iocsr_info);
+}
+
+type_init(iocsr_register_types)
diff --git a/hw/loongarch/ipi.c b/hw/loongarch/ipi.c
new file mode 100644
index 000000000..ade182abc
--- /dev/null
+++ b/hw/loongarch/ipi.c
@@ -0,0 +1,267 @@
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "qapi/error.h"
+#include "hw/hw.h"
+#include "hw/irq.h"
+#include "hw/loongarch/cpudevs.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/cpus.h"
+#include "sysemu/kvm.h"
+#include "hw/core/cpu.h"
+#include "qemu/log.h"
+#include "hw/loongarch/bios.h"
+#include "elf.h"
+#include "linux/kvm.h"
+#include "hw/loongarch/larch.h"
+#include "hw/loongarch/ls7a.h"
+#include "migration/vmstate.h"
+
+static int gipi_pre_save(void *opaque)
+{
+#ifdef CONFIG_KVM
+ gipiState *state = opaque;
+ struct loongarch_gipiState *kstate;
+ struct loongarch_kvm_irqchip *chip;
+ int ret, i, j, length;
+#endif
+
+ if (!kvm_irqchip_in_kernel()) {
+ return 0;
+ }
+
+#ifdef CONFIG_KVM
+ length = sizeof(struct loongarch_kvm_irqchip) + sizeof(struct loongarch_gipiState);
+ chip = g_malloc0(length);
+ memset(chip, 0, length);
+ chip->chip_id = KVM_IRQCHIP_LS3A_GIPI;
+ chip->len = length;
+ ret = kvm_vm_ioctl(kvm_state, KVM_GET_IRQCHIP, chip);
+ if (ret < 0) {
+ fprintf(stderr, "KVM_GET_IRQCHIP failed: %s\n", strerror(ret));
+ abort();
+ }
+
+ kstate = (struct loongarch_gipiState *)chip->data;
+
+ for (i = 0; i < MAX_GIPI_CORE_NUM; i++) {
+ state->core[i].status = kstate->core[i].status;
+ state->core[i].en = kstate->core[i].en;
+ state->core[i].set = kstate->core[i].set;
+ state->core[i].clear = kstate->core[i].clear;
+ for (j = 0; j < MAX_GIPI_MBX_NUM; j++) {
+ state->core[i].buf[j] = kstate->core[i].buf[j];
+ }
+ }
+ g_free(chip);
+#endif
+
+ return 0;
+}
+
+static int gipi_post_load(void *opaque, int version)
+{
+#ifdef CONFIG_KVM
+ gipiState *state = opaque;
+ struct loongarch_gipiState *kstate;
+ struct loongarch_kvm_irqchip *chip;
+ int ret, i, j, length;
+#endif
+
+ if (!kvm_irqchip_in_kernel()) {
+ return 0;
+ }
+
+#ifdef CONFIG_KVM
+ length = sizeof(struct loongarch_kvm_irqchip) + sizeof(struct loongarch_gipiState);
+ chip = g_malloc0(length);
+ memset(chip, 0, length);
+ chip->chip_id = KVM_IRQCHIP_LS3A_GIPI;
+ chip->len = length;
+ kstate = (struct loongarch_gipiState *)chip->data;
+
+ for (i = 0; i < MAX_GIPI_CORE_NUM; i++) {
+ kstate->core[i].status = state->core[i].status;
+ kstate->core[i].en = state->core[i].en;
+ kstate->core[i].set = state->core[i].set;
+ kstate->core[i].clear = state->core[i].clear;
+ for (j = 0; j < MAX_GIPI_MBX_NUM; j++) {
+ kstate->core[i].buf[j] = state->core[i].buf[j];
+ }
+ }
+
+ ret = kvm_vm_ioctl(kvm_state, KVM_SET_IRQCHIP, chip);
+ if (ret < 0) {
+ fprintf(stderr, "KVM_GET_IRQCHIP failed: %s\n", strerror(ret));
+ abort();
+ }
+ g_free(chip);
+#endif
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_gipi_core = {
+ .name = "gipi-single",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(status, gipi_core),
+ VMSTATE_UINT32(en, gipi_core),
+ VMSTATE_UINT32(set, gipi_core),
+ VMSTATE_UINT32(clear, gipi_core),
+ VMSTATE_UINT64_ARRAY(buf, gipi_core, MAX_GIPI_MBX_NUM),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_gipi = {
+ .name = "gipi",
+ .pre_save = gipi_pre_save,
+ .post_load = gipi_post_load,
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_STRUCT_ARRAY(core, gipiState, MAX_GIPI_CORE_NUM, 0,
+ vmstate_gipi_core, gipi_core),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+
+static void gipi_writel(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+ gipi_core *s = opaque;
+ gipi_core *ss;
+ void *pbuf;
+ uint32_t cpu, action_data, mailaddr;
+ LoongarchMachineState *ms = LoongarchMACHINE(qdev_get_machine());
+
+ if ((size != 4) && (size != 8)) {
+ hw_error("size not 4 and not 8");
+ }
+ addr &= 0xff;
+ switch (addr) {
+ case CORE0_STATUS_OFF:
+ hw_error("CORE0_STATUS_OFF Can't be write\n");
+ break;
+ case CORE0_EN_OFF:
+ s->en = val;
+ break;
+ case CORE0_IPI_SEND:
+ cpu = (val >> 16) & 0x3ff;
+ action_data = 1UL << (val & 0x1f);
+ ss = &ms->gipi->core[cpu];
+ ss->status |= action_data;
+ if (ss->status != 0) {
+ qemu_irq_raise(ss->irq);
+ }
+ break;
+ case CORE0_MAIL_SEND:
+ cpu = (val >> 16) & 0x3ff;
+ mailaddr = (val >> 2) & 0x7;
+ ss = &ms->gipi->core[cpu];
+ pbuf = (void *)ss->buf + mailaddr * 4;
+ *(unsigned int *)pbuf = (val >> 32);
+ break;
+ case CORE0_SET_OFF:
+ hw_error("CORE0_SET_OFF Can't be write\n");
+ break;
+ case CORE0_CLEAR_OFF:
+ s->status ^= val;
+ if (s->status == 0) {
+ qemu_irq_lower(s->irq);
+ }
+ break;
+ case 0x20 ... 0x3c:
+ pbuf = (void *)s->buf + (addr - 0x20);
+ if (size == 1) {
+ *(unsigned char *)pbuf = (unsigned char)val;
+ } else if (size == 2) {
+ *(unsigned short *)pbuf = (unsigned short)val;
+ } else if (size == 4) {
+ *(unsigned int *)pbuf = (unsigned int)val;
+ } else if (size == 8) {
+ *(unsigned long *)pbuf = (unsigned long)val;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static uint64_t gipi_readl(void *opaque, hwaddr addr, unsigned size)
+{
+ gipi_core *s = opaque;
+ uint64_t ret = 0;
+ void *pbuf;
+
+ addr &= 0xff;
+ if ((size != 4) && (size != 8)) {
+ hw_error("size not 4 and not 8 size:%d\n", size);
+ }
+ switch (addr) {
+ case CORE0_STATUS_OFF:
+ ret = s->status;
+ break;
+ case CORE0_EN_OFF:
+ ret = s->en;
+ break;
+ case CORE0_SET_OFF:
+ ret = 0;
+ break;
+ case CORE0_CLEAR_OFF:
+ ret = 0;
+ break;
+ case 0x20 ... 0x3c:
+ pbuf = (void *)s->buf + (addr - 0x20);
+ if (size == 1) {
+ ret = *(unsigned char *)pbuf;
+ } else if (size == 2) {
+ ret = *(unsigned short *)pbuf;
+ } else if (size == 4) {
+ ret = *(unsigned int *)pbuf;
+ } else if (size == 8) {
+ ret = *(unsigned long *)pbuf;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static const MemoryRegionOps gipi_ops = {
+ .read = gipi_readl,
+ .write = gipi_writel,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 8,
+ },
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 8,
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+int cpu_init_ipi(LoongarchMachineState *ms, qemu_irq parent, int cpu)
+{
+ hwaddr addr;
+ MemoryRegion *region;
+ char str[32];
+
+ if (ms->gipi == NULL) {
+ ms->gipi = g_malloc0(sizeof(gipiState));
+ vmstate_register(NULL, 0, &vmstate_gipi, ms->gipi);
+ }
+
+ ms->gipi->core[cpu].irq = parent;
+
+ addr = SMP_GIPI_MAILBOX | (cpu << 8);
+ region = g_new(MemoryRegion, 1);
+ sprintf(str, "gipi%d", cpu);
+ memory_region_init_io(region, NULL, &gipi_ops, &ms->gipi->core[cpu], str, 0x100);
+ memory_region_add_subregion(get_system_memory(), addr, region);
+ return 0;
+}
diff --git a/hw/loongarch/larch_3a.c b/hw/loongarch/larch_3a.c
new file mode 100644
index 000000000..3db269274
--- /dev/null
+++ b/hw/loongarch/larch_3a.c
@@ -0,0 +1,2026 @@
+/*
+ * QEMU loongarch 3a develop board emulation
+ *
+ * Copyright (C) 2013-2014 qiaochong <qiaochong@loongson.cn>
+ * Copyright (C) 2016-2017 zhangshuangshuang <zhangshuangshuang@loongson.cn>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "qemu/units.h"
+#include "qapi/error.h"
+#include "qemu/datadir.h"
+#include "hw/hw.h"
+#include "hw/loongarch/cpudevs.h"
+#include "hw/i386/pc.h"
+#include "hw/char/serial.h"
+#include "hw/isa/isa.h"
+#include "hw/qdev-core.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/runstate.h"
+#include "sysemu/reset.h"
+#include "migration/vmstate.h"
+#include "sysemu/cpus.h"
+#include "hw/boards.h"
+#include "qemu/log.h"
+#include "hw/loongarch/bios.h"
+#include "hw/loader.h"
+#include "elf.h"
+#include "exec/address-spaces.h"
+#include "hw/ide.h"
+#include "hw/pci/pci_host.h"
+#include "hw/pci/msi.h"
+#include "linux/kvm.h"
+#include "sysemu/kvm.h"
+#include "sysemu/numa.h"
+#include "hw/rtc/mc146818rtc.h"
+#include "hw/irq.h"
+#include "net/net.h"
+#include "hw/timer/i8254.h"
+#include "hw/loongarch/larch.h"
+#include "hw/loongarch/ls7a.h"
+#include "hw/nvram/fw_cfg.h"
+#include "hw/firmware/smbios.h"
+#include "acpi-build.h"
+#include <sys/wait.h>
+#include <sys/resource.h>
+#include "sysemu/block-backend.h"
+#include "hw/block/flash.h"
+#include "sysemu/device_tree.h"
+#include "qapi/visitor.h"
+#include "qapi/qapi-visit-common.h"
+
+#include <libfdt.h>
+
+#define DMA64_SUPPORTED 0x2
+#define MAX_IDE_BUS 2
+
+#define BOOTPARAM_PHYADDR 0x0ff00000ULL
+#define BOOTPARAM_ADDR (0x9000000000000000ULL + BOOTPARAM_PHYADDR)
+#define SMBIOS_PHYSICAL_ADDRESS 0x0fe00000
+#define SMBIOS_SIZE_LIMIT 0x200000
+#define RESERVED_SIZE_LIMIT 0x1100000
+#define COMMAND_LINE_SIZE 4096
+#define FW_CONF_ADDR 0x0fff0000
+
+#define PHYS_TO_VIRT(x) ((x) | 0x9000000000000000ULL)
+
+#define TARGET_REALPAGE_MASK (TARGET_PAGE_MASK << 2)
+
+#ifdef CONFIG_KVM
+#define LS_ISA_IO_SIZE 0x02000000
+#define LS_ISA_MEM_SIZE 0x40000000
+#else
+#define LS_ISA_IO_SIZE 0x00010000
+#define LS_ISA_MEM_SIZE 0x01000000
+#endif
+
+#ifdef CONFIG_KVM
+#define align(x) (((x) + 63) & ~63)
+#else
+#define align(x) (((x) + 15) & ~15)
+#endif
+
+#define DEBUG_LOONGARCH3A 0
+#define FLASH_SECTOR_SIZE 4096
+
+#define DPRINTF(fmt, ...) \
+ do { if (DEBUG_LOONGARCH3A) { fprintf(stderr, fmt, ## __VA_ARGS__); } } while (0)
+
+#define DEFINE_LS3A5K_MACHINE(suffix, name, optionfn) \
+ static void ls3a5k_init_##suffix(MachineState *machine) \
+ { \
+ ls3a5k_init(machine); \
+ } \
+ DEFINE_LOONGARCH_MACHINE(suffix, name, ls3a5k_init_##suffix, optionfn)
+
+struct efi_memory_map_loongarch {
+ uint16_t vers; /* version of efi_memory_map */
+ uint32_t nr_map; /* number of memory_maps */
+ uint32_t mem_freq; /* memory frequence */
+ struct mem_map {
+ uint32_t node_id; /* node_id which memory attached to */
+ uint32_t mem_type; /* system memory, pci memory, pci io, etc. */
+ uint64_t mem_start; /* memory map start address */
+ uint32_t mem_size; /* each memory_map size, not the total size */
+ } map[128];
+} __attribute__((packed));
+
+enum loongarch_cpu_type {
+ Loongson3 = 0x1,
+ Loongson3_comp = 0x2
+};
+
+struct GlobalProperty loongarch_compat[] = {
+ {
+ .driver = "rtl8139",
+ .property = "romfile",
+ .value = "",
+ },{
+ .driver = "e1000",
+ .property = "romfile",
+ .value = "",
+ },{
+ .driver = "virtio-net-pci",
+ .property = "romfile",
+ .value = "",
+ },{
+ .driver = "qxl-vga",
+ .property = "romfile",
+ .value = "",
+ },{
+ .driver = "VGA",
+ .property = "romfile",
+ .value = "",
+ },{
+ .driver = "cirrus-vga",
+ .property = "romfile",
+ .value = "",
+ },{
+ .driver = "virtio-vga",
+ .property = "romfile",
+ .value = "",
+ },{
+ .driver = "vmware-svga",
+ .property = "romfile",
+ .value = "",
+ },
+};
+const size_t loongarch_compat_len = G_N_ELEMENTS(loongarch_compat);
+
+/*
+ * Capability and feature descriptor structure for LOONGARCH CPU
+ */
+struct efi_cpuinfo_loongarch {
+ uint16_t vers; /* version of efi_cpuinfo_loongarch */
+ uint32_t processor_id; /* PRID, e.g. 6305, 6306 */
+ enum loongarch_cpu_type cputype; /* 3A, 3B, etc. */
+ uint32_t total_node; /* num of total numa nodes */
+ uint16_t cpu_startup_core_id; /* Core id */
+ uint16_t reserved_cores_mask;
+ uint32_t cpu_clock_freq; /* cpu_clock */
+ uint32_t nr_cpus;
+} __attribute__((packed));
+
+#define MAX_UARTS 64
+struct uart_device {
+ uint32_t iotype; /* see include/linux/serial_core.h */
+ uint32_t uartclk;
+ uint32_t int_offset;
+ uint64_t uart_base;
+} __attribute__((packed));
+
+#define MAX_SENSORS 64
+#define SENSOR_TEMPER 0x00000001
+#define SENSOR_VOLTAGE 0x00000002
+#define SENSOR_FAN 0x00000004
+struct sensor_device {
+ char name[32]; /* a formal name */
+ char label[64]; /* a flexible description */
+ uint32_t type; /* SENSOR_* */
+ uint32_t id; /* instance id of a sensor-class */
+ uint32_t fan_policy; /* see arch/loongarch/include/
+ asm/mach-loongarch/loongarch_hwmon.h */
+ uint32_t fan_percent;/* only for constant speed policy */
+ uint64_t base_addr; /* base address of device registers */
+} __attribute__((packed));
+
+struct system_loongarch {
+ uint16_t vers; /* version of system_loongarch */
+ uint32_t ccnuma_smp; /* 0: no numa; 1: has numa */
+ uint32_t sing_double_channel;/* 1:single; 2:double */
+ uint32_t nr_uarts;
+ struct uart_device uarts[MAX_UARTS];
+ uint32_t nr_sensors;
+ struct sensor_device sensors[MAX_SENSORS];
+ char has_ec;
+ char ec_name[32];
+ uint64_t ec_base_addr;
+ char has_tcm;
+ char tcm_name[32];
+ uint64_t tcm_base_addr;
+ uint64_t workarounds; /* see workarounds.h */
+} __attribute__((packed));
+
+struct irq_source_routing_table {
+ uint16_t vers;
+ uint16_t size;
+ uint16_t rtr_bus;
+ uint16_t rtr_devfn;
+ uint32_t vendor;
+ uint32_t device;
+ uint32_t PIC_type; /* conform use HT or PCI to route to CPU-PIC */
+ uint64_t ht_int_bit; /* 3A: 1<<24; 3B: 1<<16 */
+ uint64_t ht_enable; /* irqs used in this PIC */
+ uint32_t node_id; /* node id: 0x0-0; 0x1-1; 0x10-2; 0x11-3 */
+ uint64_t pci_mem_start_addr;
+ uint64_t pci_mem_end_addr;
+ uint64_t pci_io_start_addr;
+ uint64_t pci_io_end_addr;
+ uint64_t pci_config_addr;
+ uint32_t dma_mask_bits;
+ uint16_t dma_noncoherent;
+} __attribute__((packed));
+
+struct interface_info {
+ uint16_t vers; /* version of the specificition */
+ uint16_t size;
+ uint8_t flag;
+ char description[64];
+} __attribute__((packed));
+
+#define MAX_RESOURCE_NUMBER 128
+struct resource_loongarch {
+ uint64_t start; /* resource start address */
+ uint64_t end; /* resource end address */
+ char name[64];
+ uint32_t flags;
+};
+
+struct archdev_data {}; /* arch specific additions */
+
+struct board_devices {
+ char name[64]; /* hold the device name */
+ uint32_t num_resources; /* number of device_resource */
+ /* for each device's resource */
+ struct resource_loongarch resource[MAX_RESOURCE_NUMBER];
+ /* arch specific additions */
+ struct archdev_data archdata;
+};
+
+struct loongarch_special_attribute {
+ uint16_t vers; /* version of this special */
+ char special_name[64]; /* special_atribute_name */
+ uint32_t loongarch_special_type; /* type of special device */
+ /* for each device's resource */
+ struct resource_loongarch resource[MAX_RESOURCE_NUMBER];
+};
+
+struct loongarch_params {
+ uint64_t memory_offset; /* efi_memory_map_loongarch struct offset */
+ uint64_t cpu_offset; /* efi_cpuinfo_loongarch struct offset */
+ uint64_t system_offset; /* system_loongarch struct offset */
+ uint64_t irq_offset; /* irq_source_routing_table struct offset */
+ uint64_t interface_offset; /* interface_info struct offset */
+ uint64_t special_offset; /* loongarch_special_attribute struct offset */
+ uint64_t boarddev_table_offset; /* board_devices offset */
+};
+
+struct smbios_tables {
+ uint16_t vers; /* version of smbios */
+ uint64_t vga_bios; /* vga_bios address */
+ struct loongarch_params lp;
+};
+
+struct efi_reset_system_t {
+ uint64_t ResetCold;
+ uint64_t ResetWarm;
+ uint64_t ResetType;
+ uint64_t Shutdown;
+ uint64_t DoSuspend; /* NULL if not support */
+};
+
+struct efi_loongarch {
+ uint64_t mps; /* MPS table */
+ uint64_t acpi; /* ACPI table (IA64 ext 0.71) */
+ uint64_t acpi20; /* ACPI table (ACPI 2.0) */
+ struct smbios_tables smbios; /* SM BIOS table */
+ uint64_t sal_systab; /* SAL system table */
+ uint64_t boot_info; /* boot info table */
+};
+
+struct boot_params {
+ struct efi_loongarch efi;
+ struct efi_reset_system_t reset_system;
+};
+
+static struct _loaderparams {
+ unsigned long ram_size;
+ const char *kernel_filename;
+ const char *kernel_cmdline;
+ const char *initrd_filename;
+ unsigned long a0, a1, a2;
+} loaderparams;
+
+static struct _firmware_config {
+ unsigned long ram_size;
+ unsigned int mem_freq;
+ unsigned int cpu_nr;
+ unsigned int cpu_clock_freq;
+} fw_config;
+
+struct la_memmap_entry {
+ uint64_t address;
+ uint64_t length;
+ uint32_t type;
+ uint32_t reserved;
+} ;
+
+static void *boot_params_buf;
+static void *boot_params_p;
+static struct la_memmap_entry *la_memmap_table;
+static unsigned la_memmap_entries;
+
+CPULOONGARCHState *cpu_states[LOONGARCH_MAX_VCPUS];
+
+struct kvm_cpucfg ls3a5k_cpucfgs = {
+ .cpucfg[LOONGARCH_CPUCFG0] = CPUCFG0_3A5000_PRID,
+ .cpucfg[LOONGARCH_CPUCFG1] = CPUCFG1_ISGR64 | CPUCFG1_PAGING |
+ CPUCFG1_IOCSR | CPUCFG1_PABITS | CPUCFG1_VABITS | CPUCFG1_UAL |
+ CPUCFG1_RI | CPUCFG1_XI | CPUCFG1_RPLV | CPUCFG1_HUGEPG |
+ CPUCFG1_IOCSRBRD,
+ .cpucfg[LOONGARCH_CPUCFG2] = CPUCFG2_FP | CPUCFG2_FPSP | CPUCFG2_FPDP |
+ CPUCFG2_FPVERS | CPUCFG2_LSX | CPUCFG2_LASX | CPUCFG2_COMPLEX |
+ CPUCFG2_CRYPTO | CPUCFG2_LLFTP | CPUCFG2_LLFTPREV | CPUCFG2_LSPW |
+ CPUCFG2_LAM,
+ .cpucfg[LOONGARCH_CPUCFG3] = CPUCFG3_CCDMA | CPUCFG3_SFB | CPUCFG3_UCACC |
+ CPUCFG3_LLEXC | CPUCFG3_SCDLY | CPUCFG3_LLDBAR | CPUCFG3_ITLBT |
+ CPUCFG3_ICACHET | CPUCFG3_SPW_LVL | CPUCFG3_SPW_HG_HF | CPUCFG3_RVA |
+ CPUCFG3_RVAMAX,
+ .cpucfg[LOONGARCH_CPUCFG4] = CCFREQ_100M,
+ .cpucfg[LOONGARCH_CPUCFG5] = CPUCFG5_CCMUL | CPUCFG5_CCDIV,
+ .cpucfg[LOONGARCH_CPUCFG6] = CPUCFG6_PMP | CPUCFG6_PAMVER | CPUCFG6_PMNUM |
+ CPUCFG6_PMBITS | CPUCFG6_UPM,
+ .cpucfg[LOONGARCH_CPUCFG16] = CPUCFG16_L1_IUPRE | CPUCFG16_L1_DPRE |
+ CPUCFG16_L2_IUPRE | CPUCFG16_L2_IUUNIFY | CPUCFG16_L2_IUPRIV |
+ CPUCFG16_L3_IUPRE | CPUCFG16_L3_IUUNIFY | CPUCFG16_L3_IUINCL,
+ .cpucfg[LOONGARCH_CPUCFG17] = CPUCFG17_L1I_WAYS_M | CPUCFG17_L1I_SETS_M |
+ CPUCFG17_L1I_SIZE_M,
+ .cpucfg[LOONGARCH_CPUCFG18] = CPUCFG18_L1D_WAYS_M | CPUCFG18_L1D_SETS_M |
+ CPUCFG18_L1D_SIZE_M,
+ .cpucfg[LOONGARCH_CPUCFG19] = CPUCFG19_L2_WAYS_M | CPUCFG19_L2_SETS_M |
+ CPUCFG19_L2_SIZE_M,
+ .cpucfg[LOONGARCH_CPUCFG20] = CPUCFG20_L3_WAYS_M | CPUCFG20_L3_SETS_M |
+ CPUCFG20_L3_SIZE_M,
+};
+
+bool loongarch_is_acpi_enabled(LoongarchMachineState *vms)
+{
+ if (vms->acpi == ON_OFF_AUTO_OFF) {
+ return false;
+ }
+ return true;
+}
+static void loongarch_get_acpi(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ LoongarchMachineState *lsms = LoongarchMACHINE(obj);
+ OnOffAuto acpi = lsms->acpi;
+
+ visit_type_OnOffAuto(v, name, &acpi, errp);
+}
+
+static void loongarch_set_acpi(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ LoongarchMachineState *lsms = LoongarchMACHINE(obj);
+
+ visit_type_OnOffAuto(v, name, &lsms->acpi, errp);
+}
+int la_memmap_add_entry(uint64_t address, uint64_t length, uint32_t type)
+{
+ int i;
+
+ for (i = 0; i < la_memmap_entries; i++) {
+ if (la_memmap_table[i].address == address) {
+ fprintf(stderr, "%s address:0x%lx length:0x%lx already exists\n",
+ __func__, address, length);
+ return 0;
+ }
+ }
+
+ la_memmap_table = g_renew(struct la_memmap_entry, la_memmap_table,
+ la_memmap_entries + 1);
+ la_memmap_table[la_memmap_entries].address = cpu_to_le64(address);
+ la_memmap_table[la_memmap_entries].length = cpu_to_le64(length);
+ la_memmap_table[la_memmap_entries].type = cpu_to_le32(type);
+ la_memmap_entries++;
+
+ return la_memmap_entries;
+}
+
+static ram_addr_t get_hotplug_membase(ram_addr_t ram_size)
+{
+ ram_addr_t sstart;
+
+ if (ram_size <= 0x10000000) {
+ sstart = 0x90000000;
+ } else {
+ sstart = 0x90000000 + ROUND_UP((ram_size - 0x10000000),
+ LOONGARCH_HOTPLUG_MEM_ALIGN);
+ }
+ return sstart;
+}
+
+static struct efi_memory_map_loongarch *init_memory_map(void *g_map)
+{
+ struct efi_memory_map_loongarch *emap = g_map;
+
+ emap->nr_map = 4;
+ emap->mem_freq = 266000000;
+
+ emap->map[0].node_id = 0;
+ emap->map[0].mem_type = 1;
+ emap->map[0].mem_start = 0x0;
+#ifdef CONFIG_KVM
+ emap->map[0].mem_size = (loaderparams.ram_size > 0x10000000
+ ? 256 : (loaderparams.ram_size >> 20)) - 18;
+#else
+ emap->map[0].mem_size = atoi(getenv("memsize"));
+#endif
+
+ emap->map[1].node_id = 0;
+ emap->map[1].mem_type = 2;
+ emap->map[1].mem_start = 0x90000000;
+#ifdef CONFIG_KVM
+ emap->map[1].mem_size = (loaderparams.ram_size > 0x10000000
+ ? (loaderparams.ram_size >> 20) - 256 : 0);
+#else
+ emap->map[1].mem_size = atoi(getenv("highmemsize"));
+#endif
+
+ /* support for smbios */
+ emap->map[2].node_id = 0;
+ emap->map[2].mem_type = 10;
+ emap->map[2].mem_start = SMBIOS_PHYSICAL_ADDRESS;
+ emap->map[2].mem_size = SMBIOS_SIZE_LIMIT >> 20;
+
+ emap->map[3].node_id = 0;
+ emap->map[3].mem_type = 3;
+ emap->map[3].mem_start = 0xee00000;
+ emap->map[3].mem_size = RESERVED_SIZE_LIMIT >> 20;
+
+ return emap;
+}
+
+static uint64_t get_host_cpu_freq(void)
+{
+ int fd = 0;
+ char buf[1024];
+ uint64_t freq = 0, size = 0;
+ char *buf_p;
+
+ fd = open("/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq", O_RDONLY);
+ if (fd == -1) {
+ fprintf(stderr, "/sys/devices/system/cpu/cpu0/cpufreq/ \
+ cpuinfo_max_freq not exist!\n");
+ fprintf(stderr, "Trying /proc/cpuinfo...\n");
+ } else {
+ size = read(fd, buf, 16);
+ if (size == -1) {
+ fprintf(stderr, "read err...\n");
+ }
+ close(fd);
+ freq = (uint64_t)atoi(buf);
+ return freq * 1000;
+ }
+
+ fd = open("/proc/cpuinfo", O_RDONLY);
+ if (fd == -1) {
+ fprintf(stderr, "Failed to open /proc/cpuinfo!\n");
+ return 0;
+ }
+
+ size = read(fd, buf, 1024);
+ if (size == -1) {
+ fprintf(stderr, "read err...\n");
+ }
+ close(fd);
+
+ buf_p = strstr(buf, "MHz");
+ if (buf_p) {
+ while (*buf_p != ':') {
+ buf_p++;
+ }
+ buf_p += 2;
+ } else {
+ buf_p = strstr(buf, "name");
+ while (*buf_p != '@') {
+ buf_p++;
+ }
+ buf_p += 2;
+ }
+
+ memcpy(buf, buf_p, 12);
+ buf_p = buf;
+ while ((*buf_p >= '0') && (*buf_p <= '9')) {
+ buf_p++;
+ }
+ *buf_p = '\0';
+
+ freq = (uint64_t)atoi(buf);
+ return freq * 1000 * 1000;
+}
+
+static char *get_host_cpu_model_name(void)
+{
+ int fd = 0;
+ int size = 0;
+ static char buf[1024];
+ char *buf_p;
+
+ fd = open("/proc/cpuinfo", O_RDONLY);
+ if (fd == -1) {
+ fprintf(stderr, "Failed to open /proc/cpuinfo!\n");
+ return 0;
+ }
+
+ size = read(fd, buf, 1024);
+ if (size == -1) {
+ fprintf(stderr, "read err...\n");
+ }
+ close(fd);
+ buf_p = strstr(buf, "name");
+
+ while (*buf_p != ':') {
+ buf_p++;
+ }
+ buf_p = buf_p + 2;
+ memcpy(buf, buf_p, 40);
+ buf_p = buf;
+ while (*buf_p != '\n') {
+ buf_p++;
+ }
+
+ *(buf_p) = '\0';
+
+ return buf;
+}
+
+static void fw_conf_init(unsigned long ramsize)
+{
+ MachineState *ms = MACHINE(qdev_get_machine());
+ int smp_cpus = ms->smp.cpus;
+ fw_config.ram_size = ramsize;
+ fw_config.mem_freq = 266000000;
+ fw_config.cpu_nr = smp_cpus ;
+ fw_config.cpu_clock_freq = get_host_cpu_freq();
+}
+
+static struct efi_cpuinfo_loongarch *init_cpu_info(void *g_cpuinfo_loongarch)
+{
+ struct efi_cpuinfo_loongarch *c = g_cpuinfo_loongarch;
+ MachineState *ms = MACHINE(qdev_get_machine());
+ int smp_cpus = ms->smp.cpus;
+ LoongarchMachineState *lsms = LoongarchMACHINE(qdev_get_machine());
+ LoongarchMachineClass *lsmc = LoongarchMACHINE_GET_CLASS(lsms);
+
+ if (strstr(lsmc->cpu_name, "5000")) {
+ c->processor_id = 0x14c010;
+ }
+ c->cputype = Loongson3_comp;
+ c->cpu_clock_freq = get_host_cpu_freq();
+ if (!c->cpu_clock_freq) {
+ c->cpu_clock_freq = 200000000;
+ }
+ c->total_node = 1;
+ c->nr_cpus = smp_cpus;
+ c->cpu_startup_core_id = 0;
+ c->reserved_cores_mask = 0xffff & (0xffff << smp_cpus);
+
+ return c;
+}
+
+static struct system_loongarch *init_system_loongarch(void *g_sysitem)
+{
+ struct system_loongarch *s = g_sysitem;
+
+ s->ccnuma_smp = 1;
+ s->ccnuma_smp = 0;
+ s->sing_double_channel = 1;
+
+ return s;
+}
+
+enum loongarch_irq_source_enum {
+ HT, I8259, UNKNOWN
+};
+
+static struct irq_source_routing_table *init_irq_source(void *g_irq_source)
+{
+ struct irq_source_routing_table *irq_info = g_irq_source;
+ LoongarchMachineState *lsms = LoongarchMACHINE(qdev_get_machine());
+ LoongarchMachineClass *lsmc = LoongarchMACHINE_GET_CLASS(lsms);
+
+ irq_info->PIC_type = HT;
+ irq_info->ht_int_bit = 1 << 24;
+ irq_info->ht_enable = 0x0000d17b;
+ irq_info->node_id = 0;
+
+ irq_info->pci_mem_start_addr = LS_ISA_MEM_BASE;
+ irq_info->pci_mem_end_addr = irq_info->pci_mem_start_addr + LS_ISA_MEM_SIZE - 1;
+
+ if (strstr(lsmc->cpu_name, "5000")) {
+ irq_info->pci_io_start_addr = LS3A5K_ISA_IO_BASE;
+ }
+ irq_info->dma_noncoherent = 1;
+ return irq_info;
+}
+
+static struct interface_info *init_interface_info(void *g_interface)
+{
+ struct interface_info *inter = g_interface;
+ int flashsize = 0x80000;
+
+ inter->vers = 0x0001;
+ inter->size = flashsize / 0x400;
+ inter->flag = 1;
+
+ strcpy(inter->description, "PMON_Version_v2.1");
+
+ return inter;
+}
+
+static struct board_devices *board_devices_info(void *g_board)
+{
+ struct board_devices *bd = g_board;
+ LoongarchMachineState *lsms = LoongarchMACHINE(qdev_get_machine());
+ LoongarchMachineClass *lsmc = LoongarchMACHINE_GET_CLASS(lsms);
+
+ if (!strcmp(lsmc->bridge_name, "ls7a")) {
+ strcpy(bd->name, "Loongarch-3A-7A-1w-V1.03-demo");
+ }
+ bd->num_resources = 10;
+
+ return bd;
+}
+
+static struct loongarch_special_attribute *init_special_info(void *g_special)
+{
+ struct loongarch_special_attribute *special = g_special;
+ char update[11] = "2013-01-01";
+ int VRAM_SIZE = 0x20000;
+
+ strcpy(special->special_name, update);
+ special->resource[0].flags = 0;
+ special->resource[0].start = 0;
+ special->resource[0].end = VRAM_SIZE;
+ strcpy(special->resource[0].name, "SPMODULE");
+ special->resource[0].flags |= DMA64_SUPPORTED;
+
+ return special;
+}
+
+static void init_loongarch_params(struct loongarch_params *lp)
+{
+ void *p = boot_params_p;
+
+ lp->memory_offset = (unsigned long long)init_memory_map(p)
+ - (unsigned long long)lp;
+ p += align(sizeof(struct efi_memory_map_loongarch));
+
+ lp->cpu_offset = (unsigned long long)init_cpu_info(p)
+ - (unsigned long long)lp;
+ p += align(sizeof(struct efi_cpuinfo_loongarch));
+
+ lp->system_offset = (unsigned long long)init_system_loongarch(p)
+ - (unsigned long long)lp;
+ p += align(sizeof(struct system_loongarch));
+
+ lp->irq_offset = (unsigned long long)init_irq_source(p)
+ - (unsigned long long)lp;
+ p += align(sizeof(struct irq_source_routing_table));
+
+ lp->interface_offset = (unsigned long long)init_interface_info(p)
+ - (unsigned long long)lp;
+ p += align(sizeof(struct interface_info));
+
+ lp->boarddev_table_offset = (unsigned long long)board_devices_info(p)
+ - (unsigned long long)lp;
+ p += align(sizeof(struct board_devices));
+
+ lp->special_offset = (unsigned long long)init_special_info(p)
+ - (unsigned long long)lp;
+ p += align(sizeof(struct loongarch_special_attribute));
+
+ boot_params_p = p;
+}
+
+static void init_smbios(struct smbios_tables *smbios)
+{
+ smbios->vers = 1;
+ smbios->vga_bios = 1;
+ init_loongarch_params(&(smbios->lp));
+}
+
+static void init_efi(struct efi_loongarch *efi)
+{
+ init_smbios(&(efi->smbios));
+}
+
+static int init_boot_param(struct boot_params *bp)
+{
+ init_efi(&(bp->efi));
+
+ return 0;
+}
+
+static unsigned int ls3a5k_aui_boot_code[] = {
+ 0x0380200d, /* ori $r13,$r0,0x8 */
+ 0x0400002d, /* csrwr $r13,0x0 */
+ 0x0401000e, /* csrrd $r14,0x40 */
+ 0x0343fdce, /* andi $r14,$r14,0xff */
+ 0x143fc02c, /* lu12i.w $r12,261889(0x1fe01) */
+ 0x1600000c, /* lu32i.d $r12,0 */
+ 0x0320018c, /* lu52i.d $r12,$r12,-1792(0x800) */
+ 0x03400dcf, /* andi $r15,$r14,0x3 */
+ 0x004121ef, /* slli.d $r15,$r15,0x8 */
+ 0x00153d8c, /* or $r12,$r12,$r15 */
+ 0x034031d0, /* andi $r16,$r14,0xc */
+ 0x0041aa10, /* slli.d $r16,$r16,0x2a */
+ 0x0015418c, /* or $r12,$r12,$r16 */
+ 0x28808184, /* ld.w $r4,$r12,32(0x20) */
+ 0x43fffc9f, /* beqz $r4,0 -4 <waitforinit+0x4> */
+ 0x28c08184, /* ld.d $r4,$r12,32(0x20) */
+ 0x28c0a183, /* ld.d $r3,$r12,40(0x28) */
+ 0x28c0c182, /* ld.d $r2,$r12,48(0x30) */
+ 0x28c0e185, /* ld.d $r5,$r12,56(0x38) */
+ 0x4c000080, /* jirl $r0,$r4,0 */
+};
+
+static int set_bootparam_uefi(ram_addr_t initrd_offset, long initrd_size)
+{
+ long params_size;
+ char memenv[32];
+ char highmemenv[32];
+ void *params_buf;
+ unsigned long *parg_env;
+ int ret = 0;
+
+ /* Allocate params_buf for command line. */
+ params_size = 0x100000;
+ params_buf = g_malloc0(params_size);
+
+ /*
+ * Layout of params_buf looks like this:
+ * argv[0], argv[1], 0, env[0], env[1], ...env[i], 0,
+ * argv[0]'s data, argv[1]'s data, env[0]'data, ..., env[i]'s data, 0
+ */
+ parg_env = (void *)params_buf;
+
+ ret = (3 + 1) * sizeof(target_ulong);
+ *parg_env++ = (BOOTPARAM_ADDR + ret);
+ ret += (1 + snprintf(params_buf + ret, COMMAND_LINE_SIZE - ret, "g"));
+
+ /* argv1 */
+ *parg_env++ = BOOTPARAM_ADDR + ret;
+ if (initrd_size > 0)
+ ret += (1 + snprintf(params_buf + ret, COMMAND_LINE_SIZE - ret,
+ "rd_start=0x%llx rd_size=%li %s",
+ PHYS_TO_VIRT((uint32_t)initrd_offset),
+ initrd_size, loaderparams.kernel_cmdline));
+ else
+ ret += (1 + snprintf(params_buf + ret, COMMAND_LINE_SIZE - ret, "%s",
+ loaderparams.kernel_cmdline));
+
+ /* argv2 */
+ *parg_env++ = 0;
+
+ /* env */
+ sprintf(memenv, "%lu", loaderparams.ram_size > 0x10000000
+ ? 256 : (loaderparams.ram_size >> 20));
+ sprintf(highmemenv, "%lu", loaderparams.ram_size > 0x10000000
+ ? (loaderparams.ram_size >> 20) - 256 : 0);
+
+ setenv("memsize", memenv, 1);
+ setenv("highmemsize", highmemenv, 1);
+
+ ret = ((ret + 32) & ~31);
+
+ boot_params_buf = (void *)(params_buf + ret);
+ boot_params_p = boot_params_buf + align(sizeof(struct boot_params));
+ init_boot_param(boot_params_buf);
+ rom_add_blob_fixed("params", params_buf, params_size,
+ BOOTPARAM_PHYADDR);
+ loaderparams.a0 = 2;
+ loaderparams.a1 = BOOTPARAM_ADDR;
+ loaderparams.a2 = BOOTPARAM_ADDR + ret;
+
+ return 0;
+}
+
+static uint64_t cpu_loongarch_virt_to_phys(void *opaque, uint64_t addr)
+{
+ return addr & 0x1fffffffll;
+}
+
+static void fw_cfg_add_kernel_info(FWCfgState *fw_cfg,
+ uint64_t highram_size,
+ uint64_t phyAddr_initrd)
+{
+ int64_t entry, kernel_low, kernel_high;
+ long initrd_size = 0;
+ uint64_t initrd_offset = 0;
+ void *cmdline_buf;
+ int ret = 0;
+
+ ret = load_elf(loaderparams.kernel_filename, NULL, cpu_loongarch_virt_to_phys, NULL,
+ (uint64_t *)&entry, (uint64_t *)&kernel_low,
+ (uint64_t *)&kernel_high, NULL, 0, EM_LOONGARCH, 1, 0);
+
+ if(0 > ret) {
+ error_report("kernel image load error");
+ exit(1);
+ }
+
+ fw_cfg_add_i64(fw_cfg, FW_CFG_KERNEL_ENTRY, entry);
+
+ if (loaderparams.initrd_filename) {
+ initrd_size = get_image_size(loaderparams.initrd_filename);
+ if (0 < initrd_size) {
+ if (initrd_size > highram_size) {
+ error_report("initrd size is too big, should below %ld MB",
+ highram_size / S_1MiB);
+ /*prevent write io memory address space*/
+ exit(1);
+ }
+ initrd_offset = (phyAddr_initrd - initrd_size) & TARGET_REALPAGE_MASK;
+ initrd_size = load_image_targphys(loaderparams.initrd_filename,
+ initrd_offset,
+ loaderparams.ram_size - initrd_offset);
+ fw_cfg_add_i64(fw_cfg, FW_CFG_INITRD_ADDR, initrd_offset);
+ fw_cfg_add_i64(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size);
+ } else {
+ error_report("initrd image size is error");
+ }
+ }
+
+ cmdline_buf = g_malloc0(COMMAND_LINE_SIZE);
+ if (initrd_size > 0)
+ ret = (1 + snprintf(cmdline_buf, COMMAND_LINE_SIZE,
+ "rd_start=0x%llx rd_size=%li %s",
+ PHYS_TO_VIRT(initrd_offset),
+ initrd_size, loaderparams.kernel_cmdline));
+ else
+ ret = (1 + snprintf(cmdline_buf, COMMAND_LINE_SIZE, "%s",
+ loaderparams.kernel_cmdline));
+
+ fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, ret);
+ fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, (const char *)cmdline_buf);
+
+ return ;
+}
+
+static int64_t load_kernel(void)
+{
+ int64_t entry, kernel_low, kernel_high;
+ long initrd_size = 0;
+ ram_addr_t initrd_offset = 0;
+
+ load_elf(loaderparams.kernel_filename, NULL, cpu_loongarch_virt_to_phys, NULL,
+ (uint64_t *)&entry, (uint64_t *)&kernel_low,
+ (uint64_t *)&kernel_high, NULL, 0, EM_LOONGARCH, 1, 0);
+
+ if (loaderparams.initrd_filename) {
+ initrd_size = get_image_size(loaderparams.initrd_filename);
+
+ if (initrd_size > 0) {
+ initrd_offset = (kernel_high * 4 + ~TARGET_REALPAGE_MASK)
+ & TARGET_REALPAGE_MASK;
+ initrd_size = load_image_targphys(loaderparams.initrd_filename,
+ initrd_offset,
+ loaderparams.ram_size - initrd_offset);
+ }
+ }
+ set_bootparam_uefi(initrd_offset, initrd_size);
+
+ return entry;
+}
+
+static void main_cpu_reset(void *opaque)
+{
+ ResetData *s = (ResetData *)opaque;
+ CPULOONGARCHState *env = &s->cpu->env;
+
+ cpu_reset(CPU(s->cpu));
+ env->active_tc.PC = s->vector;
+ env->active_tc.gpr[4] = loaderparams.a0;
+ env->active_tc.gpr[5] = loaderparams.a1;
+ env->active_tc.gpr[6] = loaderparams.a2;
+}
+
+void slave_cpu_reset(void *opaque)
+{
+ ResetData *s = (ResetData *)opaque;
+
+ cpu_reset(CPU(s->cpu));
+}
+
+
+/* KVM_IRQ_LINE irq field index values */
+#define KVM_LOONGARCH_IRQ_TYPE_SHIFT 24
+#define KVM_LOONGARCH_IRQ_TYPE_MASK 0xff
+#define KVM_LOONGARCH_IRQ_VCPU_SHIFT 16
+#define KVM_LOONGARCH_IRQ_VCPU_MASK 0xff
+#define KVM_LOONGARCH_IRQ_NUM_SHIFT 0
+#define KVM_LOONGARCH_IRQ_NUM_MASK 0xffff
+
+/* irq_type field */
+#define KVM_LOONGARCH_IRQ_TYPE_CPU_IP 0
+#define KVM_LOONGARCH_IRQ_TYPE_CPU_IO 1
+#define KVM_LOONGARCH_IRQ_TYPE_HT 2
+#define KVM_LOONGARCH_IRQ_TYPE_MSI 3
+#define KVM_LOONGARCH_IRQ_TYPE_IOAPIC 4
+
+static void legacy_set_irq(void *opaque, int irq, int level)
+{
+ qemu_irq *pic = opaque;
+
+ qemu_set_irq(pic[irq], level);
+}
+
+typedef struct ls3a_intctlstate {
+ uint8_t nodecounter_reg[0x100];
+ uint8_t pm_reg[0x100];
+ uint8_t msi_reg[0x8];
+ CPULOONGARCHState **env;
+ DeviceState *apicdev;
+ qemu_irq *ioapic_irq;
+#ifdef CONFIG_KVM
+ struct loongarch_kvm_irqchip chip;
+#endif
+} ls3a_intctlstate;
+
+typedef struct ls3a_func_args {
+ ls3a_intctlstate *state;
+ uint64_t base;
+ uint32_t mask;
+ uint8_t *mem;
+} ls3a_func_args;
+
+static uint64_t ls3a_msi_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+ return 0;
+}
+
+static void ls3a_msi_mem_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ struct kvm_msi msi;
+ apicState *apic;
+
+ apic = (apicState *)opaque;
+ msi.address_lo = 0;
+ msi.address_hi = 0;
+ msi.data = val & 0xff;
+ msi.flags = 0;
+ memset(msi.pad, 0, sizeof(msi.pad));
+
+ if (kvm_irqchip_in_kernel()) {
+ kvm_vm_ioctl(kvm_state, KVM_SIGNAL_MSI, &msi);
+ } else {
+ qemu_set_irq(apic->irq[msi.data], 1);
+ }
+}
+
+
+static const MemoryRegionOps ls3a_msi_ops = {
+ .read = ls3a_msi_mem_read,
+ .write = ls3a_msi_mem_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+
+static const VMStateDescription vmstate_ls3a_msi = {
+ .name = "ls3a-msi",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8_ARRAY(msi_reg, ls3a_intctlstate, 0x8),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void ioapic_handler(void *opaque, int irq, int level)
+{
+ apicState *apic;
+ int kvm_irq;
+
+ apic = (apicState *)opaque;
+
+ if (kvm_irqchip_in_kernel()) {
+ kvm_irq = (KVM_LOONGARCH_IRQ_TYPE_IOAPIC << KVM_LOONGARCH_IRQ_TYPE_SHIFT)
+ | (0 << KVM_LOONGARCH_IRQ_VCPU_SHIFT) | irq;
+ kvm_set_irq(kvm_state, kvm_irq, !!level);
+ } else {
+ qemu_set_irq(apic->irq[irq], level);
+ }
+}
+
+static void *ls3a_intctl_init(MachineState *machine, CPULOONGARCHState *env[])
+{
+ qemu_irq *irqhandler;
+ ls3a_intctlstate *s;
+ LoongarchMachineState *lsms = LoongarchMACHINE(machine);
+ LoongarchMachineClass *mc = LoongarchMACHINE_GET_CLASS(lsms);
+ DeviceState *dev;
+ SysBusDevice *busdev;
+ MemoryRegion *address_space_mem = get_system_memory();
+ MemoryRegion *iomem = NULL;
+ int i;
+
+ s = g_malloc0(sizeof(ls3a_intctlstate));
+
+ if (!s) {
+ return NULL;
+ }
+
+ /*Add MSI mmio memory*/
+ iomem = g_new(MemoryRegion, 1);
+ memory_region_init_io(iomem, NULL, &ls3a_msi_ops, lsms->apic,
+ "ls3a_msi", 0x8);
+ memory_region_add_subregion(address_space_mem,
+ MSI_ADDR_LOW, iomem);
+ vmstate_register(NULL, 0, &vmstate_ls3a_msi, s);
+
+ s->env = env;
+
+ if (!strcmp(mc->bridge_name, "ls7a")) {
+ if (lsms->apic_xrupt_override) {
+ DPRINTF("irqchip in kernel %d\n", kvm_irqchip_in_kernel());
+#ifdef CONFIG_KVM
+ if (kvm_has_gsi_routing()) {
+ for (i = 0; i < 32; ++i) {
+ kvm_irqchip_add_irq_route(kvm_state, i, 0, i);
+ }
+ kvm_gsi_routing_allowed = true;
+ }
+ kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled();
+#endif
+ }
+
+ irqhandler = qemu_allocate_irqs(ioapic_handler, lsms->apic, 64);
+ dev = qdev_new("ioapic");
+ busdev = SYS_BUS_DEVICE(dev);
+ sysbus_realize_and_unref(busdev, &error_fatal);
+ sysbus_mmio_map(busdev, 0, mc->ls7a_ioapic_reg_base);
+ s->ioapic_irq = irqhandler;
+ s->apicdev = dev;
+ return s->ioapic_irq;
+ }
+ return NULL;
+}
+
+/* Network support */
+static void network_init(PCIBus *pci_bus)
+{
+ int i;
+
+ for (i = 0; i < nb_nics; i++) {
+ NICInfo *nd = &nd_table[i];
+
+ if (!nd->model) {
+ nd->model = g_strdup("virtio-net-pci");
+ }
+
+ pci_nic_init_nofail(nd, pci_bus, nd->model, NULL);
+ }
+}
+
+void loongarch_cpu_destroy(MachineState *machine, LOONGARCHCPU *cpu)
+{
+ LoongarchMachineState *lsms = LoongarchMACHINE(machine);
+ unsigned int id;
+ int smp_cpus = machine->smp.cpus;
+ id = cpu->id;
+ qemu_unregister_reset(slave_cpu_reset, lsms->reset_info[id]);
+ g_free(lsms->reset_info[id]);
+ lsms->reset_info[id] = NULL;
+
+ smp_cpus -= 1;
+ if (lsms->fw_cfg) {
+ fw_cfg_modify_i16(lsms->fw_cfg, FW_CFG_NB_CPUS,
+ (uint16_t)smp_cpus);
+ }
+
+ qemu_del_vm_change_state_handler(cpu->cpuStateEntry);
+}
+
+LOONGARCHCPU *loongarch_cpu_create(MachineState *machine,
+ LOONGARCHCPU *cpu, Error **errp)
+{
+ CPULOONGARCHState *env;
+ unsigned int id;
+ LoongarchMachineState *lsms = LoongarchMACHINE(machine);
+ int smp_cpus = machine->smp.cpus;
+ id = cpu->id;
+ env = &cpu->env;
+ cpu_states[id] = env;
+ env->CSR_TMID |= id;
+
+ lsms = LoongarchMACHINE(machine);
+ lsms->reset_info[id] = g_malloc0(sizeof(ResetData));
+ lsms->reset_info[id]->cpu = cpu;
+ lsms->reset_info[id]->vector = env->active_tc.PC;
+ qemu_register_reset(slave_cpu_reset, lsms->reset_info[id]);
+
+ /* Init CPU internal devices */
+ cpu_init_irq(cpu);
+ cpu_loongarch_clock_init(cpu);
+
+ smp_cpus += 1;
+ if (lsms->fw_cfg) {
+ fw_cfg_modify_i16(lsms->fw_cfg, FW_CFG_NB_CPUS, (uint16_t)smp_cpus);
+ }
+ cpu_init_ipi(lsms, env->irq[12], id);
+ cpu_init_apic(lsms, env, id);
+
+ return cpu;
+}
+
+static void fw_cfg_boot_set(void *opaque, const char *boot_device,
+ Error **errp)
+{
+ fw_cfg_modify_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
+}
+
+static FWCfgState *loongarch_fw_cfg_init(ram_addr_t ram_size,
+ LoongarchMachineState *lsms)
+{
+ FWCfgState *fw_cfg;
+ uint64_t *numa_fw_cfg;
+ int i;
+ const CPUArchIdList *cpus;
+ MachineClass *mc = MACHINE_GET_CLASS(lsms);
+ MachineState *ms = MACHINE(OBJECT(lsms));
+ int max_cpus = ms->smp.max_cpus;
+ int smp_cpus = ms->smp.cpus;
+ int nb_numa_nodes = ms->numa_state->num_nodes;
+ NodeInfo *numa_info = ms->numa_state->nodes;
+
+ fw_cfg = fw_cfg_init_mem_wide(FW_CFG_ADDR + 8, FW_CFG_ADDR, 8, 0, NULL);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus);
+ fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)smp_cpus);
+
+ /* allocate memory for the NUMA channel: one (64bit) word for the number
+ * of nodes, one word for each VCPU->node and one word for each node to
+ * hold the amount of memory.
+ */
+ numa_fw_cfg = g_new0(uint64_t, 1 + max_cpus + nb_numa_nodes);
+ numa_fw_cfg[0] = cpu_to_le64(nb_numa_nodes);
+ cpus = mc->possible_cpu_arch_ids(MACHINE(lsms));
+ for (i = 0; i < cpus->len; i++) {
+ unsigned int apic_id = cpus->cpus[i].arch_id;
+ assert(apic_id < max_cpus);
+ numa_fw_cfg[apic_id + 1] = cpu_to_le64(cpus->cpus[i].props.node_id);
+ }
+ for (i = 0; i < nb_numa_nodes; i++) {
+ numa_fw_cfg[max_cpus + 1 + i] =
+ cpu_to_le64(numa_info[i].node_mem);
+ }
+ fw_cfg_add_bytes(fw_cfg, FW_CFG_NUMA, numa_fw_cfg,
+ (1 + max_cpus + nb_numa_nodes) *
+ sizeof(*numa_fw_cfg));
+
+ qemu_register_boot_set(fw_cfg_boot_set, fw_cfg);
+ return fw_cfg;
+}
+
+static void loongarch_build_smbios(LoongarchMachineState *lsms)
+{
+ LoongarchMachineClass *lsmc = LoongarchMACHINE_GET_CLASS(lsms);
+ MachineState *ms = MACHINE(OBJECT(lsms));
+ uint8_t *smbios_tables, *smbios_anchor;
+ size_t smbios_tables_len, smbios_anchor_len;
+ const char *product = "QEMU Virtual Machine";
+ ms->smp.cores = 4;
+
+ if (!lsms->fw_cfg) {
+ return;
+ }
+
+ if (kvm_enabled()) {
+ if (strstr(lsmc->cpu_name, "5000")) {
+ product = "KVM";
+ }
+ } else {
+ product = "Loongarch-3A5K-7A1000-TCG";
+ }
+
+ host_cpufreq = get_host_cpu_freq();
+
+ smbios_set_defaults("Loongson", product, lsmc->cpu_name, false,
+ true, NULL, NULL, SMBIOS_ENTRY_POINT_30);
+
+ smbios_get_tables(ms, NULL, 0, &smbios_tables, &smbios_tables_len,
+ &smbios_anchor, &smbios_anchor_len, &error_fatal);
+
+ if (smbios_anchor) {
+ fw_cfg_add_file(lsms->fw_cfg, "etc/smbios/smbios-tables",
+ smbios_tables, smbios_tables_len);
+ fw_cfg_add_file(lsms->fw_cfg, "etc/smbios/smbios-anchor",
+ smbios_anchor, smbios_anchor_len);
+ }
+}
+
+static
+void loongarch_machine_done(Notifier *notifier, void *data)
+{
+ LoongarchMachineState *lsms = container_of(notifier,
+ LoongarchMachineState, machine_done);
+ loongarch_acpi_setup();
+ loongarch_build_smbios(lsms);
+}
+
+#ifdef CONFIG_TCG
+#define FEATURE_REG 0x1fe00008
+#define VENDOR_REG 0x1fe00010
+#define CPUNAME_REG 0x1fe00020
+#define OTHER_FUNC_REG 0x1fe00420
+#define _str(x) #x
+#define str(x) _str(x)
+#define SIMPLE_OPS(ADDR, SIZE) \
+({\
+ MemoryRegion *iomem = g_new(MemoryRegion, 1);\
+ memory_region_init_io(iomem, NULL, &loongarch_qemu_ops, \
+ (void *)ADDR, str(ADDR) , SIZE);\
+ memory_region_add_subregion_overlap(address_space_mem, ADDR, iomem, 1);\
+})
+
+static int reg180;
+
+static void loongarch_qemu_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ addr = ((hwaddr)(long)opaque) + addr;
+ addr = addr & 0xffffffff;
+ switch (addr) {
+ case 0x1fe00180:
+ reg180 = val;
+ break;
+ }
+}
+
+static uint64_t loongarch_qemu_read(void *opaque, hwaddr addr, unsigned size)
+{
+ uint64_t feature = 0UL;
+ addr = ((hwaddr)(long)opaque) + addr;
+ addr = addr & 0xffffffff;
+ switch (addr) {
+ case 0x1fe00180:
+ return reg180;
+ case 0x1001041c:
+ return 0xa800;
+ case FEATURE_REG:
+ feature |= 1UL << 2 | 1UL << 3 | 1UL << 4 | 1UL << 11;
+ return feature ;
+ case VENDOR_REG:
+ return *(uint64_t *)"Loongson-3A5000";
+ case CPUNAME_REG:
+ return *(uint64_t *)"3A5000";
+ case 0x10013ffc:
+ return 0x80;
+ }
+ return 0;
+}
+
+static const MemoryRegionOps loongarch_qemu_ops = {
+ .read = loongarch_qemu_read,
+ .write = loongarch_qemu_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 8,
+ },
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 8,
+ },
+};
+#endif
+
+
+static void loongarch_system_flash_cleanup_unused(LoongarchMachineState *lsms)
+{
+ char *prop_name;
+ int i;
+ Object *dev_obj;
+
+ for (i = 0; i < ARRAY_SIZE(lsms->flash); i++) {
+ dev_obj = OBJECT(lsms->flash[i]);
+ if (!object_property_get_bool(dev_obj, "realized", &error_abort)) {
+ prop_name = g_strdup_printf("pflash%d", i);
+ object_property_del(OBJECT(lsms), prop_name);
+ g_free(prop_name);
+ object_unparent(dev_obj);
+ lsms->flash[i] = NULL;
+ }
+ }
+}
+
+
+static bool loongarch_system_flash_init( LoongarchMachineState *lsms)
+{
+ int i = 0;
+ int64_t size = 0;
+ PFlashCFI01 *pflash = NULL;
+ BlockBackend *pflash_blk;
+
+ for(i = 0; i < ARRAY_SIZE(lsms->flash); i++) {
+ pflash_blk = NULL;
+ pflash = NULL;
+
+ pflash = lsms->flash[i];
+ pflash_cfi01_legacy_drive(pflash,
+ drive_get(IF_PFLASH, 0, i));
+
+ pflash_blk = pflash_cfi01_get_blk(pflash);
+ /*The pflash0 must be exist, or not support boot by pflash*/
+ if(pflash_blk == NULL) {
+ if(i == 0) {
+ return false;
+ } else {
+ break;
+ }
+ }
+
+ size = blk_getlength(pflash_blk);
+ if (size == 0 || size % FLASH_SECTOR_SIZE != 0) {
+ error_report("system firmware block device %s has invalid size "
+ "%" PRId64,
+ blk_name(pflash_blk), size);
+ error_report("its size must be a non-zero multiple of 0x%x",
+ FLASH_SECTOR_SIZE);
+ exit(1);
+ }
+ qdev_prop_set_uint32(DEVICE(pflash), "num-blocks",
+ size / FLASH_SECTOR_SIZE);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(pflash), &error_fatal);
+ if(i == 0) {
+ sysbus_mmio_map(SYS_BUS_DEVICE(pflash), 0, LS_BIOS_BASE);
+ } else {
+ sysbus_mmio_map_overlap(SYS_BUS_DEVICE(pflash), 0, LS_BIOS_VAR_BASE, 1);
+ }
+ }
+
+ return true;
+}
+static void ls3a5k_bios_init(LoongarchMachineState *lsms,
+ ram_addr_t ram_size,
+ uint64_t highram_size,
+ uint64_t phyAddr_initrd,
+ const char *kernel_filename,
+ const char *kernel_cmdline,
+ const char *initrd_filename)
+{
+ MemoryRegion *bios;
+ bool fw_cfg_used = false;
+ LoongarchMachineClass *lsmc = LoongarchMACHINE_GET_CLASS(lsms);
+ char *filename;
+ int bios_size;
+ const char *bios_name;
+
+ bios_name = MACHINE(lsms)->firmware;
+ if (kernel_filename) {
+ loaderparams.ram_size = ram_size;
+ loaderparams.kernel_filename = kernel_filename;
+ loaderparams.kernel_cmdline = kernel_cmdline;
+ loaderparams.initrd_filename = initrd_filename;
+ }
+
+ if(loongarch_system_flash_init(lsms)) {
+ fw_cfg_used = true;
+ } else {
+ bios = g_new(MemoryRegion, 1);
+ memory_region_init_ram(bios, NULL, "loongarch.bios", LS_BIOS_SIZE, &error_fatal);
+ memory_region_set_readonly(bios, true);
+ memory_region_add_subregion(get_system_memory(), LS_BIOS_BASE, bios);
+
+ /* BIOS load */
+ if (bios_name) {
+ if (access(bios_name, R_OK) == 0) {
+ load_image_targphys(bios_name, LS_BIOS_BASE, LS_BIOS_SIZE);
+ } else {
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ load_image_targphys(filename, LS_BIOS_BASE, LS_BIOS_SIZE);
+ g_free(filename);
+ }
+ fw_cfg_used = true;
+ } else {
+ if (strstr(lsmc->cpu_name, "5000")) {
+ bios_size = sizeof(ls3a5k_aui_boot_code);
+ rom_add_blob_fixed("bios", ls3a5k_aui_boot_code, bios_size, LS_BIOS_BASE);
+ }
+
+ if (kernel_filename) {
+ lsms->reset_info[0]->vector = load_kernel();
+ } else {
+ error_report("Please specify at lease one of -bios and -kernel");
+ exit(1);
+ }
+ }
+ }
+
+ loongarch_system_flash_cleanup_unused(lsms);
+
+ if (fw_cfg_used) {
+ lsms->fw_cfg = loongarch_fw_cfg_init(ram_size, lsms);
+ rom_set_fw(lsms->fw_cfg);
+ fw_conf_init(ram_size);
+ rom_add_blob_fixed("fw_conf", (void *)&fw_config,
+ sizeof(fw_config), FW_CONF_ADDR);
+
+ if (kernel_filename) {
+ fw_cfg_add_kernel_info(lsms->fw_cfg, highram_size, phyAddr_initrd);
+ }
+ }
+
+ if (lsms->fw_cfg != NULL) {
+ fw_cfg_add_file(lsms->fw_cfg, "etc/memmap",
+ la_memmap_table,
+ sizeof(struct la_memmap_entry) * (la_memmap_entries));
+ }
+
+ return ;
+}
+static void create_fdt(LoongarchMachineState *lsms)
+{
+ lsms->fdt = create_device_tree(&lsms->fdt_size);
+ if (!lsms->fdt) {
+ error_report("create_device_tree() failed");
+ exit(1);
+ }
+
+ /* Header */
+ qemu_fdt_setprop_string(lsms->fdt, "/", "compatible",
+ "linux,dummy-loongson3");
+ qemu_fdt_setprop_cell(lsms->fdt, "/", "#address-cells", 0x2);
+ qemu_fdt_setprop_cell(lsms->fdt, "/", "#size-cells", 0x2);
+}
+
+static void fdt_add_cpu_nodes(const LoongarchMachineState *lsms)
+{
+ int num;
+ const MachineState *ms = MACHINE(lsms);
+ int smp_cpus = ms->smp.cpus;
+
+ qemu_fdt_add_subnode(lsms->fdt, "/cpus");
+ qemu_fdt_setprop_cell(lsms->fdt, "/cpus", "#address-cells", 0x1);
+ qemu_fdt_setprop_cell(lsms->fdt, "/cpus", "#size-cells", 0x0);
+
+ /* cpu nodes */
+ for (num = smp_cpus - 1; num >= 0; num--) {
+ char *nodename = g_strdup_printf("/cpus/cpu@%d", num);
+ LOONGARCHCPU *cpu = LOONGARCH_CPU(qemu_get_cpu(num));
+
+ qemu_fdt_add_subnode(lsms->fdt, nodename);
+ qemu_fdt_setprop_string(lsms->fdt, nodename, "device_type", "cpu");
+ qemu_fdt_setprop_string(lsms->fdt, nodename, "compatible",
+ cpu->dtb_compatible);
+ qemu_fdt_setprop_cell(lsms->fdt, nodename, "reg", num);
+ qemu_fdt_setprop_cell(lsms->fdt, nodename, "phandle",
+ qemu_fdt_alloc_phandle(lsms->fdt));
+ g_free(nodename);
+ }
+
+ /*cpu map */
+ qemu_fdt_add_subnode(lsms->fdt, "/cpus/cpu-map");
+
+ for (num = smp_cpus - 1; num >= 0; num--) {
+ char *cpu_path = g_strdup_printf("/cpus/cpu@%d", num);
+ char *map_path;
+
+ if (ms->smp.threads > 1) {
+ map_path = g_strdup_printf(
+ "/cpus/cpu-map/socket%d/core%d/thread%d",
+ num / (ms->smp.cores * ms->smp.threads),
+ (num / ms->smp.threads) % ms->smp.cores,
+ num % ms->smp.threads);
+ } else {
+ map_path = g_strdup_printf(
+ "/cpus/cpu-map/socket%d/core%d",
+ num / ms->smp.cores,
+ num % ms->smp.cores);
+ }
+ qemu_fdt_add_path(lsms->fdt, map_path);
+ qemu_fdt_setprop_phandle(lsms->fdt, map_path, "cpu", cpu_path);
+
+ g_free(map_path);
+ g_free(cpu_path);
+ }
+}
+
+static void fdt_add_fw_cfg_node(const LoongarchMachineState *lsms)
+{
+ char *nodename;
+ hwaddr base = FW_CFG_ADDR;
+
+ nodename = g_strdup_printf("/fw_cfg@%" PRIx64, base);
+ qemu_fdt_add_subnode(lsms->fdt, nodename);
+ qemu_fdt_setprop_string(lsms->fdt, nodename,
+ "compatible", "qemu,fw-cfg-mmio");
+ qemu_fdt_setprop_sized_cells(lsms->fdt, nodename, "reg",
+ 2, base, 2, 0x8);
+ qemu_fdt_setprop(lsms->fdt, nodename, "dma-coherent", NULL, 0);
+ g_free(nodename);
+}
+
+static void fdt_add_pcie_node(const LoongarchMachineState *lsms)
+{
+ char *nodename;
+ hwaddr base_mmio = LS_ISA_MEM_BASE;
+ hwaddr size_mmio = LS_ISA_MEM_SIZE;
+ hwaddr base_pio = LS3A5K_ISA_IO_BASE;
+ hwaddr size_pio = LS_ISA_IO_SIZE;
+ hwaddr base_pcie = LS_PCIECFG_BASE;
+ hwaddr size_pcie = LS_PCIECFG_SIZE;
+ hwaddr base = base_pcie;
+
+ nodename = g_strdup_printf("/pcie@%" PRIx64, base);
+ qemu_fdt_add_subnode(lsms->fdt, nodename);
+ qemu_fdt_setprop_string(lsms->fdt, nodename,
+ "compatible", "pci-host-ecam-generic");
+ qemu_fdt_setprop_string(lsms->fdt, nodename, "device_type", "pci");
+ qemu_fdt_setprop_cell(lsms->fdt, nodename, "#address-cells", 3);
+ qemu_fdt_setprop_cell(lsms->fdt, nodename, "#size-cells", 2);
+ qemu_fdt_setprop_cell(lsms->fdt, nodename, "linux,pci-domain", 0);
+ qemu_fdt_setprop_cells(lsms->fdt, nodename, "bus-range", 0,
+ PCIE_MMCFG_BUS(LS_PCIECFG_SIZE - 1));
+ qemu_fdt_setprop(lsms->fdt, nodename, "dma-coherent", NULL, 0);
+ qemu_fdt_setprop_sized_cells(lsms->fdt, nodename, "reg",
+ 2, base_pcie, 2, size_pcie);
+ qemu_fdt_setprop_sized_cells(lsms->fdt, nodename, "ranges",
+ 1, FDT_PCI_RANGE_IOPORT, 2, 0,
+ 2, base_pio, 2, size_pio,
+ 1, FDT_PCI_RANGE_MMIO, 2, base_mmio,
+ 2, base_mmio, 2, size_mmio);
+ g_free(nodename);
+ qemu_fdt_dumpdtb(lsms->fdt, lsms->fdt_size);
+}
+
+static void ls3a5k_init(MachineState *args)
+{
+ int i;
+ const char *cpu_model = args->cpu_type;
+ const char *kernel_filename = args->kernel_filename;
+ const char *kernel_cmdline = args->kernel_cmdline;
+ const char *initrd_filename = args->initrd_filename;
+
+ ram_addr_t ram_size = args->ram_size;
+ MemoryRegion *address_space_mem = get_system_memory();
+ ram_addr_t offset = 0;
+ MemoryRegion *isa_io = g_new(MemoryRegion, 1);
+ MemoryRegion *isa_mem = g_new(MemoryRegion, 1);
+ MachineState *machine = args;
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
+ LoongarchMachineState *lsms = LoongarchMACHINE(machine);
+ LoongarchMachineClass *lsmc = LoongarchMACHINE_GET_CLASS(lsms);
+ int smp_cpus = machine->smp.cpus;
+ int nb_numa_nodes = machine->numa_state->num_nodes;
+ NodeInfo *numa_info = machine->numa_state->nodes;
+ LOONGARCHCPU *cpu;
+ CPULOONGARCHState *env;
+ qemu_irq *ls7a_apic = NULL;
+ qemu_irq *pirq = NULL;
+ PCIBus *pci_bus = NULL;
+ char *ramName = NULL;
+ uint64_t lowram_size = 0, highram_size = 0, phyAddr = 0,
+ memmap_size = 0, highram_end_addr = 0;
+
+ CPUArchIdList *possible_cpus;
+ if (strstr(lsmc->cpu_name, "5000")) {
+ if (strcmp(cpu_model, LOONGARCH_CPU_TYPE_NAME("Loongson-3A5000")) &&
+ strcmp(cpu_model, LOONGARCH_CPU_TYPE_NAME("host"))) {
+ error_report("machine type %s does not match cpu type %s",
+ lsmc->cpu_name, cpu_model);
+ exit(1);
+ }
+ if (kvm_enabled()) {
+ kvm_vm_ioctl(kvm_state, KVM_LARCH_SET_CPUCFG, ls3a5k_cpucfgs);
+ }
+ }
+
+ create_fdt(lsms);
+
+ DPRINTF("isa 0x%lx\n", lsmc->isa_io_base);
+ DPRINTF("ht1lo 0x%lx\n", lsmc->ht1lo_pcicfg_base);
+ DPRINTF("cpu_name %s bridge_name %s\n",
+ lsmc->cpu_name, lsmc->bridge_name);
+
+ /* init CPUs */
+ mc->possible_cpu_arch_ids(machine);
+ possible_cpus = machine->possible_cpus;
+
+ for (i = 0; i < smp_cpus; i++) {
+ Object *obj = NULL;
+ Error *local_err = NULL;
+
+ obj = object_new(possible_cpus->cpus[i].type);
+
+ object_property_set_uint(obj, "id", possible_cpus->cpus[i].arch_id,
+ &local_err);
+ object_property_set_bool(obj, "realized", true, &local_err);
+
+ object_unref(obj);
+ error_propagate(&error_fatal, local_err);
+
+ cpu = LOONGARCH_CPU(CPU(obj));
+ if (cpu == NULL) {
+ fprintf(stderr, "Unable to find CPU definition\n");
+ exit(1);
+ }
+
+ env = &cpu->env;
+ cpu_states[i] = env;
+ env->CSR_TMID |= i;
+
+ lsms->reset_info[i] = g_malloc0(sizeof(ResetData));
+ lsms->reset_info[i]->cpu = cpu;
+ lsms->reset_info[i]->vector = env->active_tc.PC;
+ if (i == 0) {
+ qemu_register_reset(main_cpu_reset, lsms->reset_info[i]);
+ } else {
+ qemu_register_reset(slave_cpu_reset, lsms->reset_info[i]);
+ }
+
+ /* Init CPU internal devices */
+ cpu_init_irq(cpu);
+ cpu_loongarch_clock_init(cpu);
+ cpu_init_ipi(lsms, env->irq[12], i);
+ cpu_init_apic(lsms, env, i);
+ }
+
+ lsms->hotpluged_cpu_num = 0;
+ fdt_add_cpu_nodes(lsms);
+ env = cpu_states[0];
+
+ /* node0 mem*/
+ phyAddr = (uint64_t)0;
+ MemoryRegion *lowmem = g_new(MemoryRegion, 1);
+ ramName = g_strdup_printf("loongarch_ls3a.node%d.lowram", 0);
+
+ lowram_size = MIN(ram_size, 256 * 0x100000);
+ memory_region_init_alias(lowmem, NULL, ramName, machine->ram, 0, lowram_size);
+ memory_region_add_subregion(address_space_mem, phyAddr, lowmem);
+
+ offset += lowram_size;
+ if (nb_numa_nodes > 0) {
+ highram_size = numa_info[0].node_mem - S_256MiB;
+ if (numa_info[0].node_mem > S_1GiB) {
+ memmap_size = numa_info[0].node_mem - S_1GiB;
+ la_memmap_add_entry(0xc0000000ULL, memmap_size, SYSTEM_RAM);
+ }
+ } else {
+ highram_size = ram_size - S_256MiB;
+ if (ram_size > S_1GiB) {
+ memmap_size = ram_size - S_1GiB;
+ la_memmap_add_entry(0xc0000000ULL, memmap_size, SYSTEM_RAM);
+ }
+ }
+
+ phyAddr = (uint64_t)0x90000000;
+ MemoryRegion *highmem = g_new(MemoryRegion, 1);
+ ramName = g_strdup_printf("loongarch_ls3a.node%d.highram", 0);
+ memory_region_init_alias(highmem, NULL, ramName,
+ machine->ram, offset, highram_size);
+ memory_region_add_subregion(address_space_mem,
+ phyAddr, highmem);
+ offset += highram_size;
+ phyAddr += highram_size;
+
+ /* initrd address use high mem from high to low */
+ highram_end_addr = phyAddr;
+ /* node1~ nodemax */
+ for (i = 1; i < nb_numa_nodes; i++) {
+ MemoryRegion *nodemem = g_new(MemoryRegion, 1);
+ ramName = g_strdup_printf("loongarch_ls3a.node%d.ram", i);
+ memory_region_init_alias(nodemem, NULL, ramName,
+ machine->ram, offset, numa_info[i].node_mem);
+ memory_region_add_subregion(address_space_mem,
+ phyAddr, nodemem);
+ la_memmap_add_entry(phyAddr, numa_info[i].node_mem, SYSTEM_RAM);
+ offset += numa_info[i].node_mem;
+ phyAddr += numa_info[i].node_mem;
+ }
+
+ fdt_add_fw_cfg_node(lsms);
+ ls3a5k_bios_init(lsms, ram_size, highram_size, highram_end_addr,
+ kernel_filename, kernel_cmdline, initrd_filename);
+
+ lsms->machine_done.notify = loongarch_machine_done;
+ qemu_add_machine_init_done_notifier(&lsms->machine_done);
+ /*vmstate_register_ram_global(bios);*/
+
+ /* initialize hotplug memory address space */
+ lsms->hotplug_memory_size = 0;
+
+ /* always allocate the device memory information */
+ machine->device_memory = g_malloc0(sizeof(*machine->device_memory));
+ if (machine->ram_size < machine->maxram_size) {
+ int max_memslots;
+
+ lsms->hotplug_memory_size = machine->maxram_size - machine->ram_size;
+ /*
+ * Limit the number of hotpluggable memory slots to half the number
+ * slots that KVM supports, leaving the other half for PCI and other
+ * devices. However ensure that number of slots doesn't drop below 32.
+ */
+ max_memslots = LOONGARCH_MAX_RAM_SLOTS;
+ if (kvm_enabled()) {
+ max_memslots = kvm_get_max_memslots() / 2 ;
+ }
+
+ if (machine->ram_slots == 0)
+ machine->ram_slots = lsms->hotplug_memory_size /
+ LOONGARCH_HOTPLUG_MEM_ALIGN;
+
+ if (machine->ram_slots > max_memslots) {
+ error_report("Specified number of memory slots %"
+ PRIu64" exceeds max supported %d",
+ machine->ram_slots, max_memslots);
+ exit(1);
+ }
+
+ lsms->ram_slots = machine->ram_slots;
+
+ machine->device_memory->base = get_hotplug_membase(machine->ram_size);
+ memory_region_init(&machine->device_memory->mr, OBJECT(lsms),
+ "device-memory", lsms->hotplug_memory_size);
+ memory_region_add_subregion(get_system_memory(),
+ machine->device_memory->base,
+ &machine->device_memory->mr);
+ }
+
+ memory_region_init_alias(isa_io, NULL, "isa-io",
+ get_system_io(), 0, LS_ISA_IO_SIZE);
+ memory_region_init(isa_mem, NULL, "isa-mem", LS_ISA_MEM_SIZE);
+ memory_region_add_subregion(get_system_memory(), lsmc->isa_io_base, isa_io);
+ memory_region_add_subregion(get_system_memory(), LS_ISA_MEM_BASE, isa_mem);
+
+ if (!strcmp(lsmc->bridge_name, "ls7a")) {
+ /*Initialize the 7A IO interrupt subsystem*/
+ DeviceState *ls7a_dev;
+ lsms->apic_xrupt_override = kvm_irqchip_in_kernel();
+ ls7a_apic = ls3a_intctl_init(machine, cpu_states);
+ if (!ls7a_apic) {
+ perror("Init 7A APIC failed\n");
+ exit(1);
+ }
+ pci_bus = ls7a_init(machine, ls7a_apic, &ls7a_dev);
+
+ object_property_add_link(OBJECT(machine),
+ LOONGARCH_MACHINE_ACPI_DEVICE_PROP,
+ TYPE_HOTPLUG_HANDLER,
+ (Object **)&lsms->acpi_dev,
+ object_property_allow_set_link,
+ OBJ_PROP_LINK_STRONG);
+ object_property_set_link(OBJECT(machine), LOONGARCH_MACHINE_ACPI_DEVICE_PROP,
+ OBJECT(ls7a_dev), &error_abort);
+
+#ifdef CONFIG_KVM
+ if (kvm_enabled()) {
+ kvm_direct_msi_allowed = (kvm_check_extension(kvm_state,
+ KVM_CAP_SIGNAL_MSI) > 0);
+ } else {
+ kvm_direct_msi_allowed = 0;
+ }
+ msi_nonbroken = kvm_direct_msi_allowed;
+#else
+ msi_nonbroken = true;
+#endif
+ sysbus_create_simple("ls7a_rtc", LS7A_RTC_REG_BASE,
+ ls7a_apic[LS7A_RTC_IRQ - LOONGARCH_PCH_IRQ_BASE]);
+ }
+
+ /*Initialize the CPU serial device*/
+
+ if (serial_hd(0)) {
+ pirq = qemu_allocate_irqs(legacy_set_irq, ls7a_apic +
+ (LS7A_UART_IRQ - LOONGARCH_PCH_IRQ_BASE), 1);
+ serial_mm_init(address_space_mem, LS7A_UART_BASE, 0, pirq[0],
+ 115200, serial_hd(0), DEVICE_NATIVE_ENDIAN);
+ }
+
+ /*network card*/
+ network_init(pci_bus);
+ /* VGA setup. Don't bother loading the bios. */
+ pci_vga_init(pci_bus);
+
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(qdev_new("iocsr")), &error_fatal);
+
+#ifdef CONFIG_TCG
+ int nb_nodes = (smp_cpus - 1) / 4;
+ for (i = 0; i <= nb_nodes; i++) {
+ uint64_t off = (uint64_t)i << 44;
+ SIMPLE_OPS(((hwaddr)0x1fe00180 | off), 0x8);
+ SIMPLE_OPS(((hwaddr)0x1fe0019c | off), 0x8);
+ SIMPLE_OPS(((hwaddr)0x1fe001d0 | off), 0x8);
+ SIMPLE_OPS(((hwaddr)FEATURE_REG | off), 0x8);
+ SIMPLE_OPS(((hwaddr)VENDOR_REG | off), 0x8);
+ SIMPLE_OPS(((hwaddr)CPUNAME_REG | off), 0x8);
+ SIMPLE_OPS(((hwaddr)OTHER_FUNC_REG | off), 0x8);
+ }
+
+ SIMPLE_OPS(0x1001041c, 0x4);
+ SIMPLE_OPS(0x10002000, 0x14);
+ SIMPLE_OPS(0x10013ffc, 0x4);
+#endif
+
+ fdt_add_pcie_node(lsms);
+
+ /* load fdt */
+ MemoryRegion *fdt_rom = g_new(MemoryRegion, 1);
+ memory_region_init_rom(fdt_rom, NULL, "fdt", LS_FDT_SIZE, &error_fatal);
+ memory_region_add_subregion(get_system_memory(), LS_FDT_BASE, fdt_rom);
+ rom_add_blob_fixed("fdt", lsms->fdt, lsms->fdt_size, LS_FDT_BASE);
+}
+
+static const CPUArchIdList *loongarch_possible_cpu_arch_ids(MachineState *ms)
+{
+ int i;
+ int max_cpus = ms->smp.max_cpus;
+
+ if (ms->possible_cpus) {
+ /*
+ * make sure that max_cpus hasn't changed since the first use, i.e.
+ * -smp hasn't been parsed after it
+ */
+ assert(ms->possible_cpus->len == max_cpus);
+ return ms->possible_cpus;
+ }
+
+ ms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) +
+ sizeof(CPUArchId) * max_cpus);
+ ms->possible_cpus->len = max_cpus;
+ for (i = 0; i < ms->possible_cpus->len; i++) {
+ ms->possible_cpus->cpus[i].type = ms->cpu_type;
+ ms->possible_cpus->cpus[i].vcpus_count = 1;
+ ms->possible_cpus->cpus[i].props.has_core_id = true;
+ ms->possible_cpus->cpus[i].props.core_id = i;
+ ms->possible_cpus->cpus[i].arch_id = i;
+ }
+ return ms->possible_cpus;
+
+}
+
+static PFlashCFI01 *loongarch_pflash_create(LoongarchMachineState *lsms,
+ const char *name,
+ const char *alias_prop_name)
+{
+ DeviceState *dev = qdev_new(TYPE_PFLASH_CFI01);
+
+ qdev_prop_set_uint64(dev, "sector-length", FLASH_SECTOR_SIZE);
+ qdev_prop_set_uint8(dev, "width", 1);
+ qdev_prop_set_string(dev, "name", name);
+ object_property_add_child(OBJECT(lsms), name, OBJECT(dev));
+ object_property_add_alias(OBJECT(lsms), alias_prop_name,
+ OBJECT(dev), "drive");
+ return PFLASH_CFI01(dev);
+}
+
+
+static void loongarch_system_flash_create(LoongarchMachineState *lsms)
+{
+ lsms->flash[0] = loongarch_pflash_create(lsms, "system.flash0",
+ "pflash0");
+ lsms->flash[1] = loongarch_pflash_create(lsms, "system.flash1",
+ "pflash1");
+}
+
+static void loongarch_machine_initfn(Object *obj)
+{
+ LoongarchMachineState *lsms = LoongarchMACHINE(obj);
+ LoongarchMachineClass *lsmc = LoongarchMACHINE_GET_CLASS(lsms);
+ lsms->acpi_build_enabled = lsmc->has_acpi_build;
+ loongarch_system_flash_create(lsms);
+ lsms->oem_id = g_strndup(EFI_ACPI_OEM_ID, 6);
+ lsms->oem_table_id = g_strndup(EFI_ACPI_OEM_TABLE_ID, 6);
+}
+
+static void ls3a5k_ls7a_machine_options(MachineClass *m)
+{
+ char *cpu_name = get_host_cpu_model_name();
+ LoongarchMachineClass *lsmc = LoongarchMACHINE_CLASS(m);
+ m->desc = "Loongarch3a5k LS7A1000 machine";
+ m->max_cpus = LOONGARCH_MAX_VCPUS;
+ m->alias = "loongson7a";
+ m->is_default = 1;
+ lsmc->isa_io_base = LS3A5K_ISA_IO_BASE;
+ lsmc->ht1lo_pcicfg_base = LS3A5K_HT1LO_PCICFG_BASE;
+ lsmc->pciecfg_base = LS_PCIECFG_BASE;
+ lsmc->ls7a_ioapic_reg_base = LS3A5K_LS7A_IOAPIC_REG_BASE;
+ lsmc->node_shift = 44;
+ strncpy(lsmc->cpu_name, cpu_name, sizeof(lsmc->cpu_name) - 1);
+ lsmc->cpu_name[sizeof(lsmc->cpu_name) - 1] = 0;
+ strncpy(lsmc->bridge_name, "ls7a", sizeof(lsmc->bridge_name) - 1);
+ lsmc->bridge_name[sizeof(lsmc->bridge_name) - 1] = 0;
+ compat_props_add(m->compat_props, loongarch_compat, loongarch_compat_len);
+}
+
+static void ls3a_board_reset(MachineState *ms)
+{
+ qemu_devices_reset();
+#ifdef CONFIG_KVM
+ struct loongarch_kvm_irqchip *chip;
+ int length;
+
+ if (!kvm_enabled()) {
+ return;
+ }
+ length = sizeof(struct loongarch_kvm_irqchip) +
+ sizeof(struct loongarch_gipiState);
+ chip = g_malloc0(length);
+ memset(chip, 0, length);
+ chip->chip_id = KVM_IRQCHIP_LS3A_GIPI;
+ chip->len = length;
+ kvm_vm_ioctl(kvm_state, KVM_SET_IRQCHIP, chip);
+
+ length = sizeof(struct loongarch_kvm_irqchip) + sizeof(struct ls7a_ioapic_state);
+ chip = g_realloc(chip, length);
+ memset(chip, 0, length);
+ chip->chip_id = KVM_IRQCHIP_LS7A_IOAPIC;
+ chip->len = length;
+ kvm_vm_ioctl(kvm_state, KVM_SET_IRQCHIP, chip);
+
+ g_free(chip);
+#endif
+}
+
+static CpuInstanceProperties ls3a_cpu_index_to_props(MachineState *ms, unsigned cpu_index)
+{
+ MachineClass *mc = MACHINE_GET_CLASS(ms);
+ const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms);
+
+ assert(cpu_index < possible_cpus->len);
+ return possible_cpus->cpus[cpu_index].props;
+}
+
+static int64_t ls3a_get_default_cpu_node_id(const MachineState *ms, int idx)
+{
+ int nb_numa_nodes = ms->numa_state->num_nodes;
+ int smp_cores = ms->smp.cores;
+ return idx / smp_cores % nb_numa_nodes;
+}
+
+static void loongarch_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+ HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc);
+ LoongarchMachineClass *lsmc = LoongarchMACHINE_CLASS(oc);
+
+ lsmc->get_hotplug_handler = mc->get_hotplug_handler;
+ lsmc->has_acpi_build = true;
+ mc->get_hotplug_handler = loongarch_get_hotpug_handler;
+ mc->has_hotpluggable_cpus = true;
+ mc->cpu_index_to_instance_props = ls3a_cpu_index_to_props;
+ mc->possible_cpu_arch_ids = loongarch_possible_cpu_arch_ids;
+ mc->get_default_cpu_node_id = ls3a_get_default_cpu_node_id;
+ mc->default_ram_size = 1 * GiB;
+ mc->default_cpu_type = LOONGARCH_CPU_TYPE_NAME("Loongson-3A5000");
+ mc->default_ram_id = "loongarch_ls3a.ram";
+
+ mc->reset = ls3a_board_reset;
+ mc->max_cpus = LOONGARCH_MAX_VCPUS;
+ hc->pre_plug = loongarch_machine_device_pre_plug;
+ hc->plug = loongarch_machine_device_plug;
+ hc->unplug = longson_machine_device_unplug;
+ hc->unplug_request = loongarch_machine_device_unplug_request;
+
+ object_class_property_add(oc, "acpi", "OnOffAuto",
+ loongarch_get_acpi, loongarch_set_acpi,
+ NULL, NULL);
+ object_class_property_set_description(oc, "acpi",
+ "Enable ACPI");
+}
+
+static const TypeInfo loongarch_info = {
+ .name = TYPE_LOONGARCH_MACHINE,
+ .parent = TYPE_MACHINE,
+ .abstract = true,
+ .instance_size = sizeof(LoongarchMachineState),
+ .instance_init = loongarch_machine_initfn,
+ .class_size = sizeof(LoongarchMachineClass),
+ .class_init = loongarch_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_HOTPLUG_HANDLER },
+ { }
+ },
+
+};
+
+static void loongarch_machine_register_types(void)
+{
+ type_register_static(&loongarch_info);
+}
+
+type_init(loongarch_machine_register_types)
+
+DEFINE_LS3A5K_MACHINE(loongson7a_v1_0, "loongson7a_v1.0",
+ ls3a5k_ls7a_machine_options);
diff --git a/hw/loongarch/larch_hotplug.c b/hw/loongarch/larch_hotplug.c
new file mode 100644
index 000000000..7bce95712
--- /dev/null
+++ b/hw/loongarch/larch_hotplug.c
@@ -0,0 +1,355 @@
+/*
+ * Hotplug emulation on Loongarch system.
+ *
+ * Copyright (c) 2018 Loongarch Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "qemu/queue.h"
+#include "qemu/units.h"
+#include "qemu/cutils.h"
+#include "qemu/bcd.h"
+#include "hw/hotplug.h"
+#include "hw/loongarch/cpudevs.h"
+#include "hw/mem/memory-device.h"
+#include "sysemu/numa.h"
+#include "sysemu/cpus.h"
+#include "hw/loongarch/larch.h"
+#include "hw/cpu/core.h"
+#include "hw/nvram/fw_cfg.h"
+
+/* find cpu slot in machine->possible_cpus by core_id */
+static CPUArchId *loongarch_find_cpu_slot(MachineState *ms, uint32_t id,
+ int *idx)
+{
+ int index = id;
+
+ if (index >= ms->possible_cpus->len) {
+ return NULL;
+ }
+ if (idx) {
+ *idx = index;
+ }
+ return &ms->possible_cpus->cpus[index];
+}
+
+static void loongarch_memory_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ Error *local_err = NULL;
+ LoongarchMachineState *lsms = LoongarchMACHINE(hotplug_dev);
+ HotplugHandlerClass *hhc;
+ uint64_t size;
+
+ size = memory_device_get_region_size(MEMORY_DEVICE(dev), &error_abort);
+ if (size % LOONGARCH_HOTPLUG_MEM_ALIGN) {
+ error_setg(&local_err, "Hotplugged memory size must be a multiple of "
+ "%lld MB", LOONGARCH_HOTPLUG_MEM_ALIGN / MiB);
+ goto out;
+ }
+
+ pc_dimm_plug(PC_DIMM(dev), MACHINE(lsms));
+
+ hhc = HOTPLUG_HANDLER_GET_CLASS(lsms->acpi_dev);
+ hhc->plug(HOTPLUG_HANDLER(lsms->acpi_dev), dev, &error_abort);
+out:
+ error_propagate(errp, local_err);
+}
+
+static void loongarch_memory_unplug_request(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ Error *local_err = NULL;
+ HotplugHandlerClass *hhc;
+ LoongarchMachineState *lsms = LoongarchMACHINE(hotplug_dev);
+
+ if (!lsms->acpi_dev || !loongarch_is_acpi_enabled(lsms)) {
+ error_setg(&local_err,
+ "memory hotplug is not enabled: missing acpi device or acpi disabled");
+ goto out;
+ }
+ hhc = HOTPLUG_HANDLER_GET_CLASS(lsms->acpi_dev);
+ hhc->unplug_request(HOTPLUG_HANDLER(lsms->acpi_dev), dev, &local_err);
+
+out:
+ error_propagate(errp, local_err);
+}
+
+static void loongarch_cpu_unplug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ CPUArchId *found_cpu;
+ HotplugHandlerClass *hhc;
+ Error *local_err = NULL;
+ LOONGARCHCPU *cpu = LOONGARCH_CPU(dev);
+ MachineState *machine = MACHINE(OBJECT(hotplug_dev));
+ LoongarchMachineState *lsms = LoongarchMACHINE(machine);
+
+ hhc = HOTPLUG_HANDLER_GET_CLASS(lsms->acpi_dev);
+ hhc->unplug(HOTPLUG_HANDLER(lsms->acpi_dev), dev, &local_err);
+
+ if (local_err) {
+ goto out;
+ }
+
+ loongarch_cpu_destroy(machine, cpu);
+
+ found_cpu = loongarch_find_cpu_slot(MACHINE(lsms), cpu->id, NULL);
+ found_cpu->cpu = NULL;
+ object_unparent(OBJECT(dev));
+ lsms->hotpluged_cpu_num -= 1;
+out:
+ error_propagate(errp, local_err);
+}
+
+static void loongarch_memory_unplug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ Error *local_err = NULL;
+ HotplugHandlerClass *hhc;
+ LoongarchMachineState *lsms = LoongarchMACHINE(hotplug_dev);
+
+ hhc = HOTPLUG_HANDLER_GET_CLASS(lsms->acpi_dev);
+ hhc->unplug(HOTPLUG_HANDLER(lsms->acpi_dev), dev, &local_err);
+
+ if (local_err) {
+ goto out;
+ }
+
+ pc_dimm_unplug(PC_DIMM(dev), MACHINE(hotplug_dev));
+ object_unparent(OBJECT(dev));
+
+out:
+ error_propagate(errp, local_err);
+}
+
+static void loongarch_cpu_pre_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ MachineState *ms = MACHINE(OBJECT(hotplug_dev));
+ MachineClass *mc = MACHINE_GET_CLASS(hotplug_dev);
+ LoongarchMachineState *lsms = LoongarchMACHINE(ms);
+ LOONGARCHCPU *cpu = LOONGARCH_CPU(dev);
+ CPUArchId *cpu_slot;
+ Error *local_err = NULL;
+ int index;
+ int free_index = lsms->hotpluged_cpu_num + ms->smp.cpus;
+ int max_cpus = ms->smp.max_cpus;
+
+ if (dev->hotplugged && !mc->has_hotpluggable_cpus) {
+ error_setg(&local_err, "CPU hotplug not supported for this machine");
+ goto out;
+ }
+
+ if (!object_dynamic_cast(OBJECT(cpu), ms->cpu_type)) {
+ error_setg(errp, "Invalid CPU type, expected cpu type: '%s'",
+ ms->cpu_type);
+ return;
+ }
+
+ /* if ID is not set, set it based on core properties */
+ if (cpu->id == UNASSIGNED_CPU_ID) {
+ if ((cpu->core_id) > (max_cpus - 1)) {
+ error_setg(errp, "Invalid CPU core-id: %u must be in range 0:%u",
+ cpu->core_id, max_cpus - 1);
+ return;
+ }
+
+ if (free_index > (max_cpus - 1)) {
+ error_setg(errp, "The maximum number of CPUs cannot exceed %u.",
+ max_cpus);
+ return;
+ }
+
+ if (cpu->core_id != free_index) {
+ error_setg(errp, "Invalid CPU core-id: %u must be :%u",
+ cpu->core_id, free_index);
+ return;
+ }
+
+ cpu->id = cpu->core_id;
+ }
+
+ cpu_slot = loongarch_find_cpu_slot(MACHINE(hotplug_dev), cpu->id, &index);
+ if (!cpu_slot) {
+ error_setg(&local_err, "core id %d out of range", cpu->id);
+ goto out;
+ }
+
+ if (cpu_slot->cpu) {
+ error_setg(&local_err, "core %d already populated", cpu->id);
+ goto out;
+ }
+
+ numa_cpu_pre_plug(cpu_slot, dev, &local_err);
+
+ return ;
+out:
+ error_propagate(errp, local_err);
+}
+
+static void loongarch_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
+ Error **errp)
+{
+ MachineState *machine = MACHINE(OBJECT(hotplug_dev));
+ LoongarchMachineState *lsms = LoongarchMACHINE(machine);
+ PCDIMMDevice *dimm = PC_DIMM(dev);
+ Error *local_err = NULL;
+ uint64_t size;
+
+ if (!lsms->acpi_dev || !loongarch_is_acpi_enabled(lsms)) {
+ error_setg(errp,
+ "memory hotplug is not enabled: missing acpi device or acpi disabled");
+ return;
+ }
+
+ size = memory_device_get_region_size(MEMORY_DEVICE(dimm), &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ if (size % LOONGARCH_HOTPLUG_MEM_ALIGN) {
+ error_setg(errp, "Hotplugged memory size must be a multiple of "
+ "%lld MB", LOONGARCH_HOTPLUG_MEM_ALIGN / MiB);
+ return;
+ }
+
+ pc_dimm_pre_plug(dimm, MACHINE(hotplug_dev), NULL, errp);
+}
+
+static void loongarch_cpu_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ CPUArchId *found_cpu;
+ HotplugHandlerClass *hhc;
+ Error *local_err = NULL;
+ MachineState *machine = MACHINE(OBJECT(hotplug_dev));
+ LoongarchMachineState *lsms = LoongarchMACHINE(machine);
+ LOONGARCHCPU *cpu = LOONGARCH_CPU(dev);
+
+ if (lsms->acpi_dev) {
+ loongarch_cpu_create(machine, cpu, errp);
+ hhc = HOTPLUG_HANDLER_GET_CLASS(lsms->acpi_dev);
+ hhc->plug(HOTPLUG_HANDLER(lsms->acpi_dev), dev, &local_err);
+ if (local_err) {
+ goto out;
+ }
+ }
+
+ found_cpu = loongarch_find_cpu_slot(MACHINE(lsms), cpu->id, NULL);
+ found_cpu->cpu = OBJECT(dev);
+ lsms->hotpluged_cpu_num += 1;
+out:
+ error_propagate(errp, local_err);
+}
+
+static void loongarch_cpu_unplug_request(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ MachineState *machine = MACHINE(OBJECT(hotplug_dev));
+ LoongarchMachineState *lsms = LoongarchMACHINE(machine);
+ LOONGARCHCPU *cpu = LOONGARCH_CPU(dev);
+ Error *local_err = NULL;
+ HotplugHandlerClass *hhc;
+ int idx = -1;
+
+ if (!lsms->acpi_dev) {
+ error_setg(&local_err, "CPU hot unplug not supported without ACPI");
+ goto out;
+ }
+
+ loongarch_find_cpu_slot(MACHINE(lsms), cpu->id, &idx);
+ assert(idx != -1);
+ if (idx == 0) {
+ error_setg(&local_err, "Boot CPU is unpluggable");
+ goto out;
+ }
+
+ hhc = HOTPLUG_HANDLER_GET_CLASS(lsms->acpi_dev);
+ hhc->unplug_request(HOTPLUG_HANDLER(lsms->acpi_dev), dev, &local_err);
+
+ if (local_err) {
+ goto out;
+ }
+
+ out:
+ error_propagate(errp, local_err);
+}
+
+void longson_machine_device_unplug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
+
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ loongarch_memory_unplug(hotplug_dev, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) {
+ if (!mc->has_hotpluggable_cpus) {
+ error_setg(errp, "CPU hot unplug not supported on this machine");
+ return;
+ }
+ loongarch_cpu_unplug(hotplug_dev, dev, errp);
+ } else {
+ error_setg(errp, "acpi: device unplug for not supported device"
+ " type: %s", object_get_typename(OBJECT(dev)));
+ }
+
+ return;
+}
+
+void loongarch_machine_device_unplug_request(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ loongarch_memory_unplug_request(hotplug_dev, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) {
+ loongarch_cpu_unplug_request(hotplug_dev, dev, errp);
+ }
+}
+
+HotplugHandler *loongarch_get_hotpug_handler(MachineState *machine,
+ DeviceState *dev)
+{
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) ||
+ object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) {
+ return HOTPLUG_HANDLER(machine);
+ }
+ return NULL;
+}
+
+void loongarch_machine_device_pre_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ loongarch_memory_pre_plug(hotplug_dev, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) {
+ loongarch_cpu_pre_plug(hotplug_dev, dev, errp);
+ }
+}
+
+void loongarch_machine_device_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ loongarch_memory_plug(hotplug_dev, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_LOONGARCH_CPU)) {
+ loongarch_cpu_plug(hotplug_dev, dev, errp);
+ }
+}
+
diff --git a/hw/loongarch/larch_int.c b/hw/loongarch/larch_int.c
new file mode 100644
index 000000000..ca073a19c
--- /dev/null
+++ b/hw/loongarch/larch_int.c
@@ -0,0 +1,91 @@
+/*
+ * QEMU LOONGARCH interrupt support
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/main-loop.h"
+#include "hw/hw.h"
+#include "hw/irq.h"
+#include "hw/loongarch/cpudevs.h"
+#include "cpu.h"
+#include "sysemu/kvm.h"
+#include "kvm_larch.h"
+#ifdef CONFIG_KVM
+#include <linux/kvm.h>
+#endif
+
+static void cpu_irq_request(void *opaque, int irq, int level)
+{
+ LOONGARCHCPU *cpu = opaque;
+ CPULOONGARCHState *env = &cpu->env;
+ CPUState *cs = CPU(cpu);
+ bool locked = false;
+
+ if (irq < 0 || irq > 13) {
+ return;
+ }
+
+ /* Make sure locking works even if BQL is already held by the caller */
+ if (!qemu_mutex_iothread_locked()) {
+ locked = true;
+ qemu_mutex_lock_iothread();
+ }
+
+ if (level) {
+ env->CSR_ESTAT |= 1 << irq;
+ } else {
+ env->CSR_ESTAT &= ~(1 << irq);
+ }
+
+ if (kvm_enabled()) {
+ if (irq == 2) {
+ kvm_loongarch_set_interrupt(cpu, irq, level);
+ } else if (irq == 3) {
+ kvm_loongarch_set_interrupt(cpu, irq, level);
+ } else if (irq == 12) {
+ kvm_loongarch_set_ipi_interrupt(cpu, irq, level);
+ }
+ }
+
+ if (env->CSR_ESTAT & CSR_ESTAT_IPMASK) {
+ cpu_interrupt(cs, CPU_INTERRUPT_HARD);
+ } else {
+ cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
+ }
+
+ if (locked) {
+ qemu_mutex_unlock_iothread();
+ }
+}
+
+void cpu_init_irq(LOONGARCHCPU *cpu)
+{
+ CPULOONGARCHState *env = &cpu->env;
+ qemu_irq *qi;
+ int i;
+
+ qi = qemu_allocate_irqs(cpu_irq_request, loongarch_env_get_cpu(env), N_IRQS);
+ for (i = 0; i < N_IRQS; i++) {
+ env->irq[i] = qi[i];
+ }
+}
+
+
diff --git a/hw/loongarch/ls7a_nb.c b/hw/loongarch/ls7a_nb.c
new file mode 100644
index 000000000..5a500fbd5
--- /dev/null
+++ b/hw/loongarch/ls7a_nb.c
@@ -0,0 +1,352 @@
+/*
+ * Loongarch 7A1000 north bridge support
+ *
+ * Copyright (c) 2019 Loongarch Technology
+ * Authors:
+ * Zhu Chen <zhuchen@loongson.cn>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+
+#include "hw/hw.h"
+#include "hw/irq.h"
+#include "hw/sysbus.h"
+#include "hw/pci/pci.h"
+#include "hw/i386/pc.h"
+#include "hw/pci/pci_host.h"
+#include "hw/pci/pcie_host.h"
+#include "sysemu/sysemu.h"
+#include "exec/address-spaces.h"
+#include "qapi/error.h"
+#include "hw/loongarch/cpudevs.h"
+#include "hw/acpi/ls7a.h"
+#include "hw/i386/pc.h"
+#include "hw/isa/isa.h"
+#include "hw/boards.h"
+#include "qemu/log.h"
+#include "hw/loongarch/bios.h"
+#include "hw/loader.h"
+#include "elf.h"
+#include "exec/address-spaces.h"
+#include "exec/memory.h"
+#include "hw/pci/pci_bridge.h"
+#include "hw/pci/pci_bus.h"
+#include "linux/kvm.h"
+#include "sysemu/kvm.h"
+#include "sysemu/runstate.h"
+#include "sysemu/reset.h"
+#include "migration/vmstate.h"
+#include "hw/loongarch/larch.h"
+#include "hw/loongarch/ls7a.h"
+
+#undef DEBUG_LS7A
+
+#ifdef DEBUG_LS7A
+#define DPRINTF(fmt, ...) fprintf(stderr, "%s: " fmt, __func__, ##__VA_ARGS__)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
+static void ls7a_reset(void *opaque)
+{
+ uint64_t wmask;
+ wmask = ~(-1);
+
+ PCIDevice *dev = opaque;
+ pci_set_word(dev->config + PCI_VENDOR_ID, 0x0014);
+ pci_set_word(dev->wmask + PCI_VENDOR_ID, wmask & 0xffff);
+ pci_set_word(dev->cmask + PCI_VENDOR_ID, 0xffff);
+ pci_set_word(dev->config + PCI_DEVICE_ID, 0x7a00);
+ pci_set_word(dev->wmask + PCI_DEVICE_ID, wmask & 0xffff);
+ pci_set_word(dev->cmask + PCI_DEVICE_ID, 0xffff);
+ pci_set_word(dev->config + 0x4, 0x0000);
+ pci_set_word(dev->config + PCI_STATUS, 0x0010);
+ pci_set_word(dev->wmask + PCI_STATUS, wmask & 0xffff);
+ pci_set_word(dev->cmask + PCI_STATUS, 0xffff);
+ pci_set_byte(dev->config + PCI_REVISION_ID, 0x0);
+ pci_set_byte(dev->wmask + PCI_REVISION_ID, wmask & 0xff);
+ pci_set_byte(dev->cmask + PCI_REVISION_ID, 0xff);
+ pci_set_byte(dev->config + 0x9, 0x00);
+ pci_set_byte(dev->wmask + 0x9, wmask & 0xff);
+ pci_set_byte(dev->cmask + 0x9, 0xff);
+ pci_set_byte(dev->config + 0xa, 0x00);
+ pci_set_byte(dev->wmask + 0xa, wmask & 0xff);
+ pci_set_byte(dev->cmask + 0xa, 0xff);
+ pci_set_byte(dev->config + 0xb, 0x06);
+ pci_set_byte(dev->wmask + 0xb, wmask & 0xff);
+ pci_set_byte(dev->cmask + 0xb, 0xff);
+ pci_set_byte(dev->config + 0xc, 0x00);
+ pci_set_byte(dev->wmask + 0xc, wmask & 0xff);
+ pci_set_byte(dev->cmask + 0xc, 0xff);
+ pci_set_byte(dev->config + 0xe, 0x80);
+ pci_set_byte(dev->wmask + 0xe, wmask & 0xff);
+ pci_set_byte(dev->cmask + 0xe, 0xff);
+}
+
+static const VMStateDescription vmstate_ls7a_pcie = {
+ .name = "LS7A_PCIE",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(dev, LS7APCIState),
+ VMSTATE_STRUCT(pm, LS7APCIState, 0, vmstate_ls7a_pm, LS7APCIPMRegs),
+ VMSTATE_UINT32_ARRAY(regs, LS7APCIState, LS7A_REGS),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static PCIINTxRoute ls7a_route_intx_pin_to_irq(void *opaque, int pin)
+{
+ PCIINTxRoute route;
+
+ route.irq = pin;
+ route.mode = PCI_INTX_ENABLED;
+ return route;
+}
+
+static int pci_ls7a_map_irq(PCIDevice *d, int irq_num)
+{
+ int irq;
+
+ irq = 16 + ((PCI_SLOT(d->devfn) * 4 + irq_num) & 0xf);
+ return irq;
+}
+
+static void pci_ls7a_set_irq(void *opaque, int irq_num, int level)
+{
+ qemu_irq *pic = opaque;
+ DPRINTF("------ %s irq %d %d\n", __func__, irq_num, level);
+ qemu_set_irq(pic[irq_num], level);
+}
+
+/*
+static int ls7a_pciehost_initfn(SysBusDevice *dev)
+{
+ return 0;
+}*/
+
+static void ls7a_pcie_realize(PCIDevice *dev, Error **errp)
+{
+ LS7APCIState *s = PCIE_LS7A(dev);
+ /* Ls7a North Bridge, built on FPGA, VENDOR_ID/DEVICE_ID are "undefined" */
+ pci_config_set_prog_interface(dev->config, 0x00);
+
+ /* set the default value of north bridge pci config */
+ qemu_register_reset(ls7a_reset, s);
+}
+
+static void pci_ls7a_config_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ hwaddr tmp_addr;
+ tmp_addr = addr & 0xffffff;
+
+ pci_data_write(opaque, tmp_addr, val, size);
+}
+
+static uint64_t pci_ls7a_config_read(void *opaque,
+ hwaddr addr, unsigned size)
+{
+ uint64_t val;
+ hwaddr tmp_addr;
+
+ tmp_addr = addr & 0xffffff;
+ val = pci_data_read(opaque, tmp_addr, size);
+
+ if (addr & 0x3c) {
+ DPRINTF(TARGET_FMT_plx" val %lx\n", addr, val);
+ }
+ return val;
+}
+
+static const MemoryRegionOps pci_ls7a_config_ops = {
+ .read = pci_ls7a_config_read,
+ .write = pci_ls7a_config_write,
+ /* Set to access 64bits data, because default to 32bits*/
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+ /* Set to access 64bits data, because default to 32bits*/
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ },
+ .endianness = DEVICE_NATIVE_ENDIAN,
+
+};
+
+static AddressSpace *ls7a_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn)
+{
+ return &address_space_memory;
+}
+
+static PCIBus *pci_ls7a_init(MachineState *machine, DeviceState *dev,
+ qemu_irq *pic)
+{
+ LoongarchMachineState *lsms = LoongarchMACHINE(machine);
+ LoongarchMachineClass *lsmc = LoongarchMACHINE_GET_CLASS(lsms);
+ PCIExpressHost *e;
+ SysBusDevice *sysbus;
+ MemoryRegion *iomem = g_new(MemoryRegion, 1);
+ PCIHostState *phb;
+
+ e = PCIE_HOST_BRIDGE(dev);
+ sysbus = SYS_BUS_DEVICE(e);
+ phb = PCI_HOST_BRIDGE(e);
+ phb->bus = pci_register_root_bus(dev, "pcie.0", pci_ls7a_set_irq,
+ pci_ls7a_map_irq, pic,
+ get_system_memory(), get_system_io(),
+ (1 << 3), 128, TYPE_PCIE_BUS);
+ pcie_host_mmcfg_update(e, true, lsmc->pciecfg_base, LS_PCIECFG_SIZE);
+ DPRINTF("------ %d\n", __LINE__);
+
+ pci_bus_set_route_irq_fn(phb->bus, ls7a_route_intx_pin_to_irq);
+ memory_region_init_io(iomem, NULL, &pci_ls7a_config_ops, phb->bus,
+ "ls7a_pci_conf", HT1LO_PCICFG_SIZE);
+ sysbus_init_mmio(sysbus, iomem);
+ sysbus_mmio_map(sysbus, 0, lsmc->ht1lo_pcicfg_base);
+
+ return phb->bus;
+}
+
+PCIBus *ls7a_init(MachineState *machine, qemu_irq *pic, DeviceState **ls7a_dev)
+{
+ DeviceState *dev;
+ PCIHostState *phb;
+ LS7APCIState *pbs;
+ PCIDevice *pcid;
+ PCIBus *pci_bus;
+ PCIExpressHost *e;
+
+ /*1. init the HT PCI CFG*/
+ DPRINTF("------ %d\n", __LINE__);
+ dev = qdev_new(TYPE_LS7A_PCIE_HOST_BRIDGE);
+ e = PCIE_HOST_BRIDGE(dev);
+ phb = PCI_HOST_BRIDGE(e);
+
+ DPRINTF("------ %d\n", __LINE__);
+ pci_bus = pci_ls7a_init(machine, dev, pic);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+ phb->bus = pci_bus;
+ /* set the pcihost pointer after rs780_pcihost_initfn is called */
+ DPRINTF("------ %d\n", __LINE__);
+ pcid = pci_new(PCI_DEVFN(0, 0), TYPE_PCIE_LS7A);
+ pbs = PCIE_LS7A(pcid);
+ pbs->pciehost = LS7A_PCIE_HOST_BRIDGE(dev);
+ pbs->pciehost->pci_dev = pbs;
+
+ if (ls7a_dev) {
+ *ls7a_dev = DEVICE(pcid);
+ }
+
+ pci_realize_and_unref(pcid, phb->bus, &error_fatal);
+
+ /* IOMMU */
+ pci_setup_iommu(phb->bus, ls7a_pci_dma_iommu, NULL);
+
+ ls7a_pm_init(&pbs->pm, pic);
+ DPRINTF("------ %d\n", __LINE__);
+ /*3. init the north bridge VGA,not do now*/
+ return pci_bus;
+}
+
+LS7APCIState *get_ls7a_type(Object *obj)
+{
+ LS7APCIState *pbs;
+
+ pbs = PCIE_LS7A(obj);
+ return pbs;
+}
+
+static void ls7a_pcie_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+ HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass);
+ AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(klass);
+
+ k->realize = ls7a_pcie_realize;
+ k->vendor_id = 0x0014;
+ k->device_id = 0x7a00;
+ k->revision = 0x00;
+ k->class_id = PCI_CLASS_BRIDGE_HOST;
+ dc->desc = "LS7A1000 PCIE Host bridge";
+ dc->vmsd = &vmstate_ls7a_pcie;
+ /*
+ * PCI-facing part of the host bridge, not usable without the
+ * host-facing part, which can't be device_add'ed, yet.
+ */
+ dc->user_creatable = false;
+ hc->plug = ls7a_pm_device_plug_cb;
+ hc->unplug_request = ls7a_pm_device_unplug_request_cb;
+ hc->unplug = ls7a_pm_device_unplug_cb;
+ adevc->ospm_status = ls7a_pm_ospm_status;
+ adevc->send_event = ls7a_send_gpe;
+ adevc->madt_cpu = ls7a_madt_cpu_entry;
+}
+
+static void ls7a_pci_add_properties(LS7APCIState *ls7a)
+{
+ ls7a_pm_add_properties(OBJECT(ls7a), &ls7a->pm, NULL);
+}
+
+static void ls7a_pci_initfn(Object *obj)
+{
+ LS7APCIState *ls7a = get_ls7a_type(obj);
+
+ ls7a_pci_add_properties(ls7a);
+}
+
+static const TypeInfo ls7a_pcie_device_info = {
+ .name = TYPE_PCIE_LS7A,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(LS7APCIState),
+ .class_init = ls7a_pcie_class_init,
+ .instance_init = ls7a_pci_initfn,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_HOTPLUG_HANDLER },
+ { TYPE_ACPI_DEVICE_IF },
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { },
+ },
+};
+
+static void ls7a_pciehost_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+ k->parent_class.fw_name = "pci";
+}
+
+static const TypeInfo ls7a_pciehost_info = {
+ .name = TYPE_LS7A_PCIE_HOST_BRIDGE,
+ .parent = TYPE_PCIE_HOST_BRIDGE,
+ .instance_size = sizeof(LS7APCIEHost),
+ .class_init = ls7a_pciehost_class_init,
+};
+
+static void ls7a_register_types(void)
+{
+ type_register_static(&ls7a_pciehost_info);
+ type_register_static(&ls7a_pcie_device_info);
+}
+
+type_init(ls7a_register_types)
diff --git a/hw/loongarch/meson.build b/hw/loongarch/meson.build
new file mode 100644
index 000000000..81ee99a02
--- /dev/null
+++ b/hw/loongarch/meson.build
@@ -0,0 +1,15 @@
+loongarch_ss = ss.source_set()
+loongarch_ss.add(files('larch_3a.c'), fdt)
+loongarch_ss.add(files(
+ 'larch_int.c',
+ 'larch_hotplug.c',
+ 'ls7a_nb.c',
+ 'ioapic.c',
+ 'acpi-build.c',
+ 'ipi.c',
+ 'apic.c',
+ 'iocsr.c',
+))
+
+hw_arch += {'loongarch64': loongarch_ss}
+
diff --git a/include/hw/loongarch/bios.h b/include/hw/loongarch/bios.h
new file mode 100644
index 000000000..3677303bf
--- /dev/null
+++ b/include/hw/loongarch/bios.h
@@ -0,0 +1,5 @@
+#include "qemu/units.h"
+#include "cpu.h"
+
+#define BIOS_SIZE (4 * MiB)
+#define BIOS_FILENAME "loongarch_bios.bin"
diff --git a/include/hw/loongarch/cpudevs.h b/include/hw/loongarch/cpudevs.h
new file mode 100644
index 000000000..c05ae7a7f
--- /dev/null
+++ b/include/hw/loongarch/cpudevs.h
@@ -0,0 +1,53 @@
+#ifndef HW_LOONGARCH_CPUDEVS_H
+#define HW_LOONGARCH_CPUDEVS_H
+
+#include "target/loongarch64/cpu-qom.h"
+
+/* Definitions for LOONGARCH CPU internal devices. */
+#define MAX_GIPI_CORE_NUM 256
+#define MAX_GIPI_MBX_NUM 4
+
+#define LS3A_INTC_IP 8
+#define MAX_CORES 256
+#define EXTIOI_IRQS (256)
+#define EXTIOI_IRQS_BITMAP_SIZE (256 / 8)
+/* map to ipnum per 32 irqs */
+#define EXTIOI_IRQS_IPMAP_SIZE (256 / 32)
+
+typedef struct gipi_core {
+ uint32_t status;
+ uint32_t en;
+ uint32_t set;
+ uint32_t clear;
+ uint64_t buf[MAX_GIPI_MBX_NUM];
+ qemu_irq irq;
+} gipi_core;
+
+typedef struct gipiState {
+ gipi_core core[MAX_GIPI_CORE_NUM];
+} gipiState;
+
+typedef struct apicState {
+ /* hardware state */
+ uint8_t ext_en[EXTIOI_IRQS_BITMAP_SIZE];
+ uint8_t ext_bounce[EXTIOI_IRQS_BITMAP_SIZE];
+ uint8_t ext_isr[EXTIOI_IRQS_BITMAP_SIZE];
+ uint8_t ext_coreisr[MAX_CORES][EXTIOI_IRQS_BITMAP_SIZE];
+ uint8_t ext_ipmap[EXTIOI_IRQS_IPMAP_SIZE];
+ uint8_t ext_coremap[EXTIOI_IRQS];
+ uint16_t ext_nodetype[16];
+ uint64_t ext_control;
+
+ /* software state */
+ uint8_t ext_sw_ipmap[EXTIOI_IRQS];
+ uint8_t ext_sw_coremap[EXTIOI_IRQS];
+ uint8_t ext_ipisr[MAX_CORES * LS3A_INTC_IP][EXTIOI_IRQS_BITMAP_SIZE];
+
+ qemu_irq parent_irq[MAX_CORES][LS3A_INTC_IP];
+ qemu_irq *irq;
+} apicState;
+
+void cpu_init_irq(LOONGARCHCPU *cpu);
+void cpu_loongarch_clock_init(LOONGARCHCPU *cpu);
+
+#endif
diff --git a/include/hw/loongarch/larch.h b/include/hw/loongarch/larch.h
new file mode 100644
index 000000000..0886ed52a
--- /dev/null
+++ b/include/hw/loongarch/larch.h
@@ -0,0 +1,163 @@
+/*
+ * Hotplug emulation on Loongarch system.
+ *
+ * Copyright (c) 2018 Loongarch Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef HW_LOONGARCH_H
+#define HW_LOONGARCH_H
+
+#include "target/loongarch64/cpu.h"
+#include "qemu-common.h"
+#include "exec/memory.h"
+#include "hw/mem/pc-dimm.h"
+#include "hw/hotplug.h"
+#include "hw/boards.h"
+#include "hw/acpi/acpi.h"
+#include "qemu/notify.h"
+#include "qemu/error-report.h"
+#include "qemu/queue.h"
+#include "hw/acpi/memory_hotplug.h"
+#include "hw/loongarch/cpudevs.h"
+#include "hw/block/flash.h"
+
+#define LOONGARCH_MAX_VCPUS 256
+#define LOONGARCH_MAX_PFLASH 2
+/* 256MB alignment for hotplug memory region */
+#define LOONGARCH_HOTPLUG_MEM_ALIGN (1ULL << 28)
+#define LOONGARCH_MAX_RAM_SLOTS 10
+
+/* Memory types: */
+#define SYSTEM_RAM 1
+#define SYSTEM_RAM_RESERVED 2
+#define ACPI_TABLE 3
+#define ACPI_NVS 4
+#define SYSTEM_PMEM 5
+
+#define MAX_MEM_MAP 128
+
+typedef struct LoongarchMachineClass {
+ /*< private >*/
+ MachineClass parent_class;
+
+ /* Methods: */
+ HotplugHandler *(*get_hotplug_handler)(MachineState *machine,
+ DeviceState *dev);
+
+ bool has_acpi_build;
+
+ /* save different cpu address*/
+ uint64_t isa_io_base;
+ uint64_t ht_control_regs_base;
+ uint64_t hpet_mmio_addr;
+ uint64_t smbus_cfg_base;
+ uint64_t ht1lo_pcicfg_base;
+ uint64_t pciecfg_base;
+ uint64_t ls7a_ioapic_reg_base;
+ uint32_t node_shift;
+ char cpu_name[40];
+ char bridge_name[16];
+
+} LoongarchMachineClass;
+
+typedef struct ResetData {
+ LOONGARCHCPU *cpu;
+ uint64_t vector;
+} ResetData;
+
+typedef struct LoongarchMachineState {
+ /*< private >*/
+ MachineState parent_obj;
+
+ /* <public> */
+ ram_addr_t hotplug_memory_size;
+
+ /* State for other subsystems/APIs: */
+ Notifier machine_done;
+ /* Pointers to devices and objects: */
+ HotplugHandler *acpi_dev;
+ int ram_slots;
+ ResetData *reset_info[LOONGARCH_MAX_VCPUS];
+ DeviceState *rtc;
+ gipiState *gipi;
+ apicState *apic;
+
+ FWCfgState *fw_cfg;
+ bool acpi_build_enabled;
+ bool apic_xrupt_override;
+ CPUArchIdList *possible_cpus;
+ PFlashCFI01 *flash[LOONGARCH_MAX_PFLASH];
+ void *fdt;
+ int fdt_size;
+ unsigned int hotpluged_cpu_num;
+ OnOffAuto acpi;
+ char *oem_id;
+ char *oem_table_id;
+} LoongarchMachineState;
+
+#define LOONGARCH_MACHINE_ACPI_DEVICE_PROP "loongarch-acpi-device"
+#define TYPE_LOONGARCH_MACHINE "loongarch-machine"
+
+#define LoongarchMACHINE(obj) \
+ OBJECT_CHECK(LoongarchMachineState, (obj), TYPE_LOONGARCH_MACHINE)
+#define LoongarchMACHINE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(LoongarchMachineClass, (obj), TYPE_LOONGARCH_MACHINE)
+#define LoongarchMACHINE_CLASS(klass) \
+ OBJECT_CLASS_CHECK(LoongarchMachineClass, (klass), TYPE_LOONGARCH_MACHINE)
+
+#define DEFINE_LOONGARCH_MACHINE(suffix, namestr, initfn, optsfn) \
+ static void loongarch_machine_##suffix##_class_init(ObjectClass *oc, void *data) \
+ { \
+ MachineClass *mc = MACHINE_CLASS(oc); \
+ optsfn(mc); \
+ mc->init = initfn; \
+ } \
+ static const TypeInfo loongarch_machine_type_##suffix = { \
+ .name = namestr TYPE_MACHINE_SUFFIX, \
+ .parent = TYPE_LOONGARCH_MACHINE, \
+ .class_init = loongarch_machine_##suffix##_class_init, \
+ }; \
+ static void loongarch_machine_init_##suffix(void) \
+ { \
+ type_register(&loongarch_machine_type_##suffix); \
+ } \
+ type_init(loongarch_machine_init_##suffix)
+
+void loongarch_machine_device_unplug_request(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp);
+void longson_machine_device_unplug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp);
+HotplugHandler *loongarch_get_hotpug_handler(MachineState *machine,
+ DeviceState *dev);
+void loongarch_machine_device_pre_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp);
+void loongarch_machine_device_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp);
+
+LOONGARCHCPU *loongarch_cpu_create(MachineState *machine, LOONGARCHCPU *cpu,
+ Error **errp);
+void loongarch_cpu_destroy(MachineState *machine, LOONGARCHCPU *cpu);
+int cpu_init_ipi(LoongarchMachineState *ms, qemu_irq parent, int cpu);
+int cpu_init_apic(LoongarchMachineState *ms, CPULOONGARCHState *env, int cpu);
+int la_memmap_add_entry(uint64_t address, uint64_t length, uint32_t type);
+bool loongarch_is_acpi_enabled(LoongarchMachineState *vms);
+
+/* acpi-build.c */
+void ls7a_madt_cpu_entry(AcpiDeviceIf *adev, int uid,
+ const CPUArchIdList *apic_ids, GArray *entry, bool force_enabled);
+void slave_cpu_reset(void *opaque);
+extern uint64_t host_cpufreq;
+#endif
diff --git a/include/hw/loongarch/ls7a.h b/include/hw/loongarch/ls7a.h
new file mode 100644
index 000000000..686af763a
--- /dev/null
+++ b/include/hw/loongarch/ls7a.h
@@ -0,0 +1,152 @@
+#ifndef HW_LS7A_H
+#define HW_LS7A_H
+
+#include "hw/hw.h"
+#include "hw/isa/isa.h"
+#include "hw/sysbus.h"
+#include "hw/isa/apm.h"
+#include "hw/pci/pci.h"
+#include "hw/pci/pcie_host.h"
+#include "hw/pci/pci_bridge.h"
+#include "hw/acpi/acpi.h"
+#include "hw/acpi/ls7a.h"
+#include "hw/pci/pci_bus.h"
+
+/* LS7A PCH Registers (Misc, Confreg) */
+#define LS7A_PCH_REG_BASE 0x10000000UL
+#define LS3A5K_LS7A_IOAPIC_REG_BASE (LS7A_PCH_REG_BASE)
+#define LS7A_MISC_REG_BASE (LS7A_PCH_REG_BASE + 0x00080000)
+#define LS7A_ACPI_REG_BASE (LS7A_MISC_REG_BASE + 0x00050000)
+
+#define LOONGARCH_PCH_IRQ_BASE 64
+#define LS7A_UART_IRQ (LOONGARCH_PCH_IRQ_BASE + 2)
+#define LS7A_RTC_IRQ (LOONGARCH_PCH_IRQ_BASE + 3)
+#define LS7A_SCI_IRQ (LOONGARCH_PCH_IRQ_BASE + 4)
+#define LS7A_ACPI_IO_BASE 0x800
+#define LS7A_ACPI_IO_SIZE 0x100
+#define LS7A_PM_EVT_BLK (0x0C) /* 4 bytes */
+#define LS7A_PM_CNT_BLK (0x14) /* 2 bytes */
+#define LS7A_GPE0_STS_REG (0x28) /* 4 bytes */
+#define LS7A_GPE0_ENA_REG (0x2C) /* 4 bytes */
+#define LS7A_GPE0_RESET_REG (0x30) /* 4 bytes */
+#define LS7A_PM_TMR_BLK (0x18) /* 4 bytes */
+#define LS7A_GPE0_LEN (8)
+#define LS7A_RTC_REG_BASE (LS7A_MISC_REG_BASE + 0x00050100)
+#define LS7A_RTC_LEN (0x100)
+
+#define ACPI_IO_BASE (LS7A_ACPI_REG_BASE)
+#define ACPI_GPE0_LEN (LS7A_GPE0_LEN)
+#define ACPI_IO_SIZE (LS7A_ACPI_IO_SIZE)
+#define ACPI_SCI_IRQ (LS7A_SCI_IRQ)
+
+#define LS3A5K_ISA_IO_BASE 0x18000000UL
+#define LS_ISA_MEM_BASE 0x40000000
+#define LS3A5K_HT1LO_PCICFG_BASE 0x1a000000
+#define HT1LO_PCICFG_SIZE 0x02000000
+#define LS_BIOS_BASE 0x1c000000
+#define LS_BIOS_VAR_BASE 0x1c3a0000
+#define LS_BIOS_SIZE (4 * 1024 * 1024)
+
+#define FW_CFG_ADDR 0x1e020000
+#define LS7A_REG_BASE 0x1FE00000
+#define LS7A_PCICONFIG_BASE (LS7A_REG_BASE + 0x30)
+#define LS7A_PCICONFIG_SIZE (0x100)
+#define LS7A_INTERNAL_REG_BASE (LS7A_REG_BASE + 0x100)
+#define LS7A_INTERNAL_REG_SIZE (0xE0)
+#define LS7A_REGS (0xE0 >> 2)
+#define LS7A_UART_BASE 0x1fe001e0
+#define LS7A_UART_LEN 0x8
+
+#define LS_FDT_BASE 0x1c400000
+#define LS_FDT_SIZE 0x100000
+
+#define LS_PCIECFG_BASE 0x20000000
+#define LS_PCIECFG_SIZE 0x08000000
+#define MSI_ADDR_LOW 0x2FF00000
+#define MSI_ADDR_HI 0x0
+
+#define SMP_GIPI_MAILBOX 0x1f000000ULL
+#define CORE0_STATUS_OFF 0x000
+#define CORE0_EN_OFF 0x004
+#define CORE0_SET_OFF 0x008
+#define CORE0_CLEAR_OFF 0x00c
+#define CORE0_BUF_20 0x020
+#define CORE0_BUF_28 0x028
+#define CORE0_BUF_30 0x030
+#define CORE0_BUF_38 0x038
+#define CORE0_IPI_SEND 0x040
+#define CORE0_MAIL_SEND 0x048
+
+#define INT_ROUTER_REGS_BASE 0x1fe01400UL
+#define INT_ROUTER_REGS_SIZE 0x100
+#define INT_ROUTER_REGS_SYS_INT0 0x00
+#define INT_ROUTER_REGS_SYS_INT1 0x01
+#define INT_ROUTER_REGS_SYS_INT2 0x02
+#define INT_ROUTER_REGS_SYS_INT3 0x03
+#define INT_ROUTER_REGS_PCI_INT0 0x04
+#define INT_ROUTER_REGS_PCI_INT1 0x05
+#define INT_ROUTER_REGS_PCI_INT2 0x06
+#define INT_ROUTER_REGS_PCI_INT3 0x07
+#define INT_ROUTER_REGS_MATRIX_INT0 0x08
+#define INT_ROUTER_REGS_MATRIX_INT1 0x09
+#define INT_ROUTER_REGS_LPC_INT 0x0a
+#define INT_ROUTER_REGS_MC0 0x0b
+#define INT_ROUTER_REGS_MC1 0x0c
+#define INT_ROUTER_REGS_BARRIER 0x0d
+#define INT_ROUTER_REGS_THSENS_INT 0x0e
+#define INT_ROUTER_REGS_PCI_PERR 0x0f
+#define INT_ROUTER_REGS_HT0_INT0 0x10
+#define INT_ROUTER_REGS_HT0_INT1 0x11
+#define INT_ROUTER_REGS_HT0_INT2 0x12
+#define INT_ROUTER_REGS_HT0_INT3 0x13
+#define INT_ROUTER_REGS_HT0_INT4 0x14
+#define INT_ROUTER_REGS_HT0_INT5 0x15
+#define INT_ROUTER_REGS_HT0_INT6 0x16
+#define INT_ROUTER_REGS_HT0_INT7 0x17
+#define INT_ROUTER_REGS_HT1_INT0 0x18
+#define INT_ROUTER_REGS_HT1_INT1 0x19
+#define INT_ROUTER_REGS_HT1_INT2 0x1a
+#define INT_ROUTER_REGS_HT1_INT3 0x1b
+#define INT_ROUTER_REGS_HT1_INT4 0x1c
+#define INT_ROUTER_REGS_HT1_INT5 0x1d
+#define INT_ROUTER_REGS_HT1_INT6 0x1e
+#define INT_ROUTER_REGS_HT1_INT7 0x1f
+#define INT_ROUTER_REGS_ISR 0x20
+#define INT_ROUTER_REGS_EN 0x24
+#define INT_ROUTER_REGS_EN_SET 0x28
+#define INT_ROUTER_REGS_EN_CLR 0x2c
+#define INT_ROUTER_REGS_EDGE 0x38
+#define INT_ROUTER_REGS_CORE0_INTISR 0x40
+#define INT_ROUTER_REGS_CORE1_INTISR 0x48
+#define INT_ROUTER_REGS_CORE2_INTISR 0x50
+#define INT_ROUTER_REGS_CORE3_INTISR 0x58
+
+typedef struct LS7APCIState LS7APCIState;
+typedef struct LS7APCIEHost {
+ PCIExpressHost parent_obj;
+ LS7APCIState *pci_dev;
+} LS7APCIEHost;
+
+struct LS7APCIState {
+ PCIDevice dev;
+
+ LS7APCIEHost *pciehost;
+ uint32_t regs[LS7A_REGS];
+
+ /* LS7A registers */
+ MemoryRegion iomem;
+ LS7APCIPMRegs pm;
+};
+
+#define TYPE_LS7A_PCIE_HOST_BRIDGE "ls7a1000-pciehost"
+#define LS7A_PCIE_HOST_BRIDGE(obj) \
+ OBJECT_CHECK(LS7APCIEHost, (obj), TYPE_LS7A_PCIE_HOST_BRIDGE)
+
+#define TYPE_PCIE_LS7A "ls7a1000_pcie"
+#define PCIE_LS7A(obj) \
+ OBJECT_CHECK(LS7APCIState, (obj), TYPE_PCIE_LS7A)
+
+PCIBus *ls7a_init(MachineState *machine, qemu_irq *irq, DeviceState **ls7a_dev);
+LS7APCIState *get_ls7a_type(Object *obj);
+
+#endif /* HW_LS7A_H */
--
2.43.5
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/xisme/qemu-kvm_src-anolis.git
git@gitee.com:xisme/qemu-kvm_src-anolis.git
xisme
qemu-kvm_src-anolis
qemu-kvm_src-anolis
vtkm_support_csv

搜索帮助

371d5123 14472233 46e8bd33 14472233