1 Star 0 Fork 109

panchenbo / qemu

forked from src-openEuler / qemu 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
Add-loongarch-machine.patch 203.02 KB
一键复制 编辑 原始数据 按行查看 历史
huiyingC 提交于 2023-03-28 20:53 . QEMU update to viersion 6.2.0-67(master)
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181
From 810369434538d64a4118c3ec70d4c3daf479bd52 Mon Sep 17 00:00:00 2001
From: lixianglai <lixianglai@loongson.cn>
Date: Tue, 7 Feb 2023 07:14:21 -0500
Subject: [PATCH] Add loongarch machine.
1.Add loongarch ACPI table support.
2.Add loongarch apic support.
3.Add loongarch ipi support.
4.Add loongarch hotplug support.
5.Add loongarch board simulation.
6.Add loongarch interrupt support.
7.Add loongarch north bridge simulation.
8.Add loongarch iocsr device simulation.
9.Add loongarch system bus support.
Signed-off-by: lixianglai <lixianglai@loongson.cn>
---
hw/loongarch/Kconfig | 17 +
hw/loongarch/acpi-build.c | 827 ++++++++++++
hw/loongarch/acpi-build.h | 32 +
hw/loongarch/apic.c | 689 ++++++++++
hw/loongarch/ioapic.c | 419 ++++++
hw/loongarch/iocsr.c | 227 ++++
hw/loongarch/ipi.c | 284 ++++
hw/loongarch/larch_3a.c | 2063 +++++++++++++++++++++++++++++
hw/loongarch/larch_hotplug.c | 377 ++++++
hw/loongarch/larch_int.c | 87 ++
hw/loongarch/ls7a_nb.c | 289 ++++
hw/loongarch/meson.build | 15 +
hw/loongarch/sysbus-fdt.c | 178 +++
include/disas/dis-asm.h | 3 +-
include/elf.h | 2 +
include/hw/loongarch/bios.h | 24 +
include/hw/loongarch/cpudevs.h | 71 +
include/hw/loongarch/larch.h | 164 +++
include/hw/loongarch/ls7a.h | 167 +++
include/hw/loongarch/sysbus-fdt.h | 33 +
include/qemu/osdep.h | 4 +
21 files changed, 5971 insertions(+), 1 deletion(-)
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 hw/loongarch/sysbus-fdt.c
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
create mode 100644 include/hw/loongarch/sysbus-fdt.h
diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig
new file mode 100644
index 0000000000..3fe2677fda
--- /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 0000000000..4dd128a05e
--- /dev/null
+++ b/hw/loongarch/acpi-build.c
@@ -0,0 +1,827 @@
+/*
+ * Support for generating ACPI tables and passing them to Guests
+ *
+ * Copyright (c) 2023 Loongarch Technology
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/platform-bus.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),
+ /* C2 state not supported */
+ .plvl2_lat = 0xfff,
+ /* C3 state not supported */
+ .plvl3_lat = 0xfff,
+ .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 : 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, 1); /* all processors */
+ build_append_int_noprefix(table_data, 0, 2); /* Flags */
+ /* Local APIC INTI# */
+ build_append_int_noprefix(table_data, 1, 1); /* ACPI_LINT1 */
+
+ /* 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, 1); /* all processors */
+ build_append_int_noprefix(table_data, 0, 2); /* Flags */
+ /* Local APIC INTI# */
+ build_append_int_noprefix(table_data, 1, 1); /* ACPI_LINT1 */
+
+ 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);
+}
+
+#ifdef CONFIG_TPM
+static void acpi_dsdt_add_tpm(Aml *scope, LoongarchMachineState *vms)
+{
+ PlatformBusDevice *pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev);
+ hwaddr pbus_base = VIRT_PLATFORM_BUS_BASEADDRESS;
+ SysBusDevice *sbdev = SYS_BUS_DEVICE(tpm_find());
+ MemoryRegion *sbdev_mr;
+ hwaddr tpm_base;
+
+ if (!sbdev) {
+ return;
+ }
+
+ tpm_base = platform_bus_get_mmio_addr(pbus, sbdev, 0);
+ assert(tpm_base != -1);
+
+ tpm_base += pbus_base;
+
+ sbdev_mr = sysbus_mmio_get_region(sbdev, 0);
+
+ Aml *dev = aml_device("TPM0");
+ aml_append(dev, aml_name_decl("_HID", aml_string("MSFT0101")));
+ aml_append(dev, aml_name_decl("_STR", aml_string("TPM 2.0 Device")));
+ aml_append(dev, aml_name_decl("_UID", aml_int(0)));
+
+ Aml *crs = aml_resource_template();
+ aml_append(crs, aml_memory32_fixed(tpm_base,
+ (uint32_t)memory_region_size(sbdev_mr),
+ AML_READ_WRITE));
+ aml_append(dev, aml_name_decl("_CRS", crs));
+ aml_append(scope, dev);
+}
+#endif
+
+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);
+
+#ifdef CONFIG_TPM
+ acpi_dsdt_add_tpm(sb_scope, lsms);
+#endif
+ 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", AML_SYSTEM_MEMORY);
+
+ 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, sizeof(uint32_t)); /* clear */
+ ACPI_BUILD_DPRINTF("init ACPI tables\n");
+
+ bios_linker_loader_alloc(tables->linker, ACPI_BUILD_TABLE_FILE,
+ tables_blob, 64, 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);
+ }
+
+ if (tpm_get_version(tpm_find()) == TPM_VERSION_2_0) {
+ acpi_add_table(table_offsets, tables_blob);
+ build_tpm2(tables_blob, tables->linker, tables->tcpalog, 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 0000000000..97d53a9258
--- /dev/null
+++ b/hw/loongarch/acpi-build.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2023 Loongarch Technology
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ *
+ */
+
+#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 0000000000..9e762cf0fe
--- /dev/null
+++ b/hw/loongarch/apic.c
@@ -0,0 +1,689 @@
+/*
+ * Loongarch 3A5000 interrupt controller emulation
+ *
+ * Copyright (c) 2023 Loongarch Technology
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 "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_enabled()) || (!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_enabled()) || (!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 0000000000..102102781f
--- /dev/null
+++ b/hw/loongarch/ioapic.c
@@ -0,0 +1,419 @@
+/*
+ * LS7A1000 Northbridge IOAPIC support
+ *
+ * Copyright (c) 2023 Loongarch Technology
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 "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 */
+ /*
+ * 0x3e0 interrupt level polarity
+ * selection register 0 for high level tirgger
+ */
+ uint64_t int_polarity;
+ 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_enabled()) || (!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_enabled()) || (!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 0000000000..a1eb54bdd2
--- /dev/null
+++ b/hw/loongarch/iocsr.c
@@ -0,0 +1,227 @@
+/*
+ * LOONGARCH IOCSR support
+ *
+ * Copyright (c) 2023 Loongarch Technology
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 "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
+};
+
+#ifdef CONFIG_KVM
+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,
+};
+#endif
+
+#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;
+
+ if ((!kvm_enabled())) {
+ return 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;
+ LoongarchMachineClass *lsmc;
+ Object *machine = qdev_get_machine();
+ ObjectClass *mc = object_get_class(machine);
+
+ /* 'lams' should be initialized */
+ if (!strcmp(MACHINE_CLASS(mc)->name, "none")) {
+ return;
+ }
+
+ lsms = LoongarchMACHINE(machine);
+ 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 0000000000..affa97392e
--- /dev/null
+++ b/hw/loongarch/ipi.c
@@ -0,0 +1,284 @@
+/*
+ * LOONGARCH IPI support
+ *
+ * Copyright (c) 2023 Loongarch Technology
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 "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_enabled()) || (!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_enabled()) || (!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 0000000000..fe786008ac
--- /dev/null
+++ b/hw/loongarch/larch_3a.c
@@ -0,0 +1,2063 @@
+/*
+ * QEMU loongarch 3a develop board emulation
+ *
+ * Copyright (c) 2023 Loongarch Technology
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 "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/platform-bus.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 "sysemu/tpm.h"
+#include "hw/loongarch/sysbus-fdt.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
+#else
+#define LS_ISA_IO_SIZE 0x00010000
+#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 */
+ /*
+ * see arch/loongarch/include/
+ * asm/mach-loongarch/loongarch_hwmon.h
+ */
+ uint32_t fan_policy;
+ 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");
+ if (!buf_p) {
+ buf_p = strstr(buf, "name");
+ }
+ if (!buf_p) {
+ fprintf(stderr, "Can't find cpu name\n");
+ return 0;
+ }
+
+ 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 = PCIE_MEMORY_BASE;
+ irq_info->pci_mem_end_addr =
+ irq_info->pci_mem_start_addr + PCIE_MEMORY_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 / MiB);
+ /*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;
+#ifdef CONFIG_KVM
+ int i;
+#endif
+
+ 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";
+ }
+
+ smbios_set_defaults("Loongson", product, lsmc->cpu_name, false, true,
+ 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);
+
+ platform_bus_add_all_fdt_nodes(
+ lsms->fdt, NULL, VIRT_PLATFORM_BUS_BASEADDRESS, VIRT_PLATFORM_BUS_SIZE,
+ VIRT_PLATFORM_BUS_IRQ);
+
+ qemu_fdt_dumpdtb(lsms->fdt, lsms->fdt_size);
+ /* 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);
+
+ 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();
+ }
+ }
+ }
+
+ 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 = PCIE_MEMORY_BASE;
+ hwaddr size_mmio = PCIE_MEMORY_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);
+}
+
+static void create_platform_bus(LoongarchMachineState *s, qemu_irq *pic)
+{
+ DeviceState *dev;
+ SysBusDevice *sysbus;
+ int i;
+ MemoryRegion *sysmem = get_system_memory();
+
+ dev = qdev_new(TYPE_PLATFORM_BUS_DEVICE);
+ dev->id = g_strdup(TYPE_PLATFORM_BUS_DEVICE);
+ qdev_prop_set_uint32(dev, "num_irqs", VIRT_PLATFORM_BUS_NUM_IRQS);
+ qdev_prop_set_uint32(dev, "mmio_size", VIRT_PLATFORM_BUS_SIZE);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+ s->platform_bus_dev = dev;
+
+ sysbus = SYS_BUS_DEVICE(dev);
+ for (i = 0; i < VIRT_PLATFORM_BUS_NUM_IRQS; i++) {
+ int irq = VIRT_PLATFORM_BUS_IRQ + i;
+ sysbus_connect_irq(sysbus, i, pic[irq - LOONGARCH_PCH_IRQ_BASE]);
+ }
+
+ memory_region_add_subregion(sysmem, VIRT_PLATFORM_BUS_BASEADDRESS,
+ sysbus_mmio_get_region(sysbus, 0));
+}
+
+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("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 - 256 * MiB;
+ if (numa_info[0].node_mem > GiB) {
+ memmap_size = numa_info[0].node_mem - GiB;
+ la_memmap_add_entry(0xc0000000ULL, memmap_size, SYSTEM_RAM);
+ }
+ } else {
+ highram_size = ram_size - 256 * MiB;
+ if (ram_size > GiB) {
+ memmap_size = ram_size - GiB;
+ 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", PCIE_MEMORY_SIZE);
+ memory_region_add_subregion(get_system_memory(), lsmc->isa_io_base,
+ isa_io);
+ memory_region_add_subregion(get_system_memory(), PCIE_MEMORY_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);
+
+ create_platform_bus(lsms, ls7a_apic);
+
+#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);
+}
+
+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->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";
+
+#ifdef CONFIG_TPM
+ machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS);
+#endif
+
+ 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 0000000000..52f13af7b3
--- /dev/null
+++ b/hw/loongarch/larch_hotplug.c
@@ -0,0 +1,377 @@
+/*
+ * Hotplug emulation on Loongarch system.
+ *
+ * Copyright (c) 2023 Loongarch Technology
+ *
+ * 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"
+#include "hw/platform-bus.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) ||
+ object_dynamic_cast(OBJECT(dev), TYPE_SYS_BUS_DEVICE)) {
+ 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)
+{
+ LoongarchMachineState *lsms = LoongarchMACHINE(hotplug_dev);
+
+ if (lsms->platform_bus_dev) {
+ MachineClass *mc = MACHINE_GET_CLASS(lsms);
+
+ if (device_is_dynamic_sysbus(mc, dev)) {
+ platform_bus_link_device(
+ PLATFORM_BUS_DEVICE(lsms->platform_bus_dev),
+ SYS_BUS_DEVICE(dev));
+ }
+ }
+
+ 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 0000000000..ff3750e982
--- /dev/null
+++ b/hw/loongarch/larch_int.c
@@ -0,0 +1,87 @@
+/*
+ * QEMU LOONGARCH interrupt support
+ *
+ * Copyright (c) 2023 Loongarch Technology
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 "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 0000000000..933b3f2869
--- /dev/null
+++ b/hw/loongarch/ls7a_nb.c
@@ -0,0 +1,289 @@
+/*
+ * Loongarch 7A1000 north bridge support
+ *
+ * Copyright (c) 2023 Loongarch Technology
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 "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_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 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 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;
+ PCIHostState *phb;
+
+ e = PCIE_HOST_BRIDGE(dev);
+ 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);
+
+ 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 0000000000..ca4d5567b5
--- /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',
+ 'sysbus-fdt.c',
+))
+
+hw_arch += {'loongarch64': loongarch_ss}
diff --git a/hw/loongarch/sysbus-fdt.c b/hw/loongarch/sysbus-fdt.c
new file mode 100644
index 0000000000..05b4dda33a
--- /dev/null
+++ b/hw/loongarch/sysbus-fdt.c
@@ -0,0 +1,178 @@
+/*
+ * Loongarch Platform Bus device tree generation helpers
+ *
+ * Copyright (c) 2023 Loongarch Technology
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 <libfdt.h>
+#include "qemu/error-report.h"
+#include "sysemu/device_tree.h"
+#include "hw/platform-bus.h"
+#include "hw/display/ramfb.h"
+#include "hw/loongarch/sysbus-fdt.h"
+#include "sysemu/tpm.h"
+
+/*
+ * internal struct that contains the information to create dynamic
+ * sysbus device node
+ */
+typedef struct PlatformBusFDTData {
+ void *fdt; /* device tree handle */
+ int irq_start; /* index of the first IRQ usable by platform bus devices */
+ const char *pbus_node_name; /* name of the platform bus node */
+ PlatformBusDevice *pbus;
+} PlatformBusFDTData;
+
+/* struct that allows to match a device and create its FDT node */
+typedef struct BindingEntry {
+ const char *typename;
+ const char *compat;
+ int (*add_fn)(SysBusDevice *sbdev, void *opaque);
+ bool (*match_fn)(SysBusDevice *sbdev, const struct BindingEntry *combo);
+} BindingEntry;
+
+static int no_fdt_node(SysBusDevice *sbdev, void *opaque)
+{
+ return 0;
+}
+
+/* Device type based matching */
+static bool type_match(SysBusDevice *sbdev, const BindingEntry *entry)
+{
+ return !strcmp(object_get_typename(OBJECT(sbdev)), entry->typename);
+}
+
+#define TYPE_BINDING(type, add_fn) \
+ { \
+ (type), NULL, (add_fn), NULL \
+ }
+
+#ifdef CONFIG_TPM
+/*
+ * add_tpm_tis_fdt_node: Create a DT node for TPM TIS
+ *
+ * See kernel documentation:
+ * Documentation/devicetree/bindings/security/tpm/tpm_tis_mmio.txt
+ * Optional interrupt for command completion is not exposed
+ */
+static int add_tpm_tis_fdt_node(SysBusDevice *sbdev, void *opaque)
+{
+ PlatformBusFDTData *data = opaque;
+ PlatformBusDevice *pbus = data->pbus;
+ void *fdt = data->fdt;
+ const char *parent_node = data->pbus_node_name;
+ char *nodename;
+ uint32_t reg_attr[2];
+ uint64_t mmio_base;
+
+ mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0);
+ nodename = g_strdup_printf("%s/tpm_tis@%" PRIx64, parent_node, mmio_base);
+ qemu_fdt_add_subnode(fdt, nodename);
+
+ qemu_fdt_setprop_string(fdt, nodename, "compatible", "tcg,tpm-tis-mmio");
+
+ reg_attr[0] = cpu_to_be32(mmio_base);
+ reg_attr[1] = cpu_to_be32(0x5000);
+ qemu_fdt_setprop(fdt, nodename, "reg", reg_attr, 2 * sizeof(uint32_t));
+
+ g_free(nodename);
+
+ return 0;
+}
+#endif
+
+/* list of supported dynamic sysbus bindings */
+static const BindingEntry bindings[] = {
+#ifdef CONFIG_TPM
+ TYPE_BINDING(TYPE_TPM_TIS_SYSBUS, add_tpm_tis_fdt_node),
+#endif
+ TYPE_BINDING(TYPE_RAMFB_DEVICE, no_fdt_node),
+ TYPE_BINDING("", NULL), /* last element */
+};
+
+/**
+ * add_fdt_node - add the device tree node of a dynamic sysbus device
+ *
+ * @sbdev: handle to the sysbus device
+ * @opaque: handle to the PlatformBusFDTData
+ *
+ * Checks the sysbus type belongs to the list of device types that
+ * are dynamically instantiable and if so call the node creation
+ * function.
+ */
+static void add_fdt_node(SysBusDevice *sbdev, void *opaque)
+{
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(bindings); i++) {
+ const BindingEntry *iter = &bindings[i];
+
+ if (type_match(sbdev, iter)) {
+ if (!iter->match_fn || iter->match_fn(sbdev, iter)) {
+ ret = iter->add_fn(sbdev, opaque);
+ assert(!ret);
+ return;
+ }
+ }
+ }
+ error_report("Device %s can not be dynamically instantiated",
+ qdev_fw_name(DEVICE(sbdev)));
+ exit(1);
+}
+
+void platform_bus_add_all_fdt_nodes(void *fdt, const char *intc, hwaddr addr,
+ hwaddr bus_size, int irq_start)
+{
+ const char platcomp[] = "qemu,platform\0simple-bus";
+ PlatformBusDevice *pbus;
+ DeviceState *dev;
+ gchar *node;
+
+ assert(fdt);
+
+ node = g_strdup_printf("/platform@%" PRIx64, addr);
+
+ /* Create a /platform node that we can put all devices into */
+ qemu_fdt_add_subnode(fdt, node);
+ qemu_fdt_setprop(fdt, node, "compatible", platcomp, sizeof(platcomp));
+
+ /*
+ * Our platform bus region is less than 32bits, so 1 cell is enough for
+ * address and size
+ */
+ qemu_fdt_setprop_cells(fdt, node, "#size-cells", 1);
+ qemu_fdt_setprop_cells(fdt, node, "#address-cells", 1);
+ qemu_fdt_setprop_cells(fdt, node, "ranges", 0, addr >> 32, addr, bus_size);
+ if (intc != NULL) {
+ qemu_fdt_setprop_phandle(fdt, node, "interrupt-parent", intc);
+ }
+ dev = qdev_find_recursive(sysbus_get_default(), TYPE_PLATFORM_BUS_DEVICE);
+ pbus = PLATFORM_BUS_DEVICE(dev);
+
+ PlatformBusFDTData data = {
+ .fdt = fdt,
+ .irq_start = irq_start,
+ .pbus_node_name = node,
+ .pbus = pbus,
+ };
+
+ /* Loop through all dynamic sysbus devices and create their node */
+ foreach_dynamic_sysbus_device(add_fdt_node, &data);
+
+ g_free(node);
+}
diff --git a/include/disas/dis-asm.h b/include/disas/dis-asm.h
index 4590bcc968..b165453fa1 100644
--- a/include/disas/dis-asm.h
+++ b/include/disas/dis-asm.h
@@ -336,7 +336,7 @@ typedef struct disassemble_info {
Returns an errno value or 0 for success. */
int (*read_memory_func)
(bfd_vma memaddr, bfd_byte *myaddr, int length,
- struct disassemble_info *info);
+ struct disassemble_info *info);
/* Function which should be called if we get an error that we can't
recover from. STATUS is the errno value from read_memory_func and
@@ -465,6 +465,7 @@ int print_insn_riscv32 (bfd_vma, disassemble_info*);
int print_insn_riscv64 (bfd_vma, disassemble_info*);
int print_insn_rx(bfd_vma, disassemble_info *);
int print_insn_hexagon(bfd_vma, disassemble_info *);
+int print_insn_loongarch(bfd_vma, disassemble_info*);
#ifdef CONFIG_CAPSTONE
bool cap_disas_target(disassemble_info *info, uint64_t pc, size_t size);
diff --git a/include/elf.h b/include/elf.h
index 79c188b62f..cd7808f37a 100644
--- a/include/elf.h
+++ b/include/elf.h
@@ -182,6 +182,8 @@ typedef struct mips_elf_abiflags_v0 {
#define EM_NANOMIPS 249 /* Wave Computing nanoMIPS */
+#define EM_LOONGARCH 258 /* Loongarch */
+
/*
* This is an interim value that we will use until the committee comes
* up with a final number.
diff --git a/include/hw/loongarch/bios.h b/include/hw/loongarch/bios.h
new file mode 100644
index 0000000000..8e0f6c7d64
--- /dev/null
+++ b/include/hw/loongarch/bios.h
@@ -0,0 +1,24 @@
+/*
+ * bios on Loongarch system.
+ *
+ * Copyright (c) 2023 Loongarch Technology
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/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 0000000000..ea4007f8fa
--- /dev/null
+++ b/include/hw/loongarch/cpudevs.h
@@ -0,0 +1,71 @@
+/*
+ * cpu device emulation on Loongarch system.
+ *
+ * Copyright (c) 2023 Loongarch Technology
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ *
+ */
+
+#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 0000000000..a9f8dea9f5
--- /dev/null
+++ b/include/hw/loongarch/larch.h
@@ -0,0 +1,164 @@
+/*
+ * Hotplug emulation on Loongarch system.
+ *
+ * Copyright (c) 2023 Loongarch Technology
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ *
+ */
+
+#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 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;
+ DeviceState *platform_bus_dev;
+ 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);
+#endif
diff --git a/include/hw/loongarch/ls7a.h b/include/hw/loongarch/ls7a.h
new file mode 100644
index 0000000000..e1f3c8d032
--- /dev/null
+++ b/include/hw/loongarch/ls7a.h
@@ -0,0 +1,167 @@
+/*
+ * Acpi emulation on Loongarch system.
+ *
+ * Copyright (c) 2023 Loongarch Technology
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ *
+ */
+
+#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 VIRT_PLATFORM_BUS_BASEADDRESS 0x16000000
+#define VIRT_PLATFORM_BUS_SIZE 0x02000000
+#define VIRT_PLATFORM_BUS_NUM_IRQS 2
+#define VIRT_PLATFORM_BUS_IRQ (LOONGARCH_PCH_IRQ_BASE + 5)
+
+#define LS3A5K_ISA_IO_BASE 0x18000000UL
+#define LS_BIOS_BASE 0x1c000000
+#define LS_BIOS_VAR_BASE 0x1c3a0000
+#define LS_BIOS_SIZE (4 * 1024 * 1024)
+#define LS_FDT_BASE 0x1c400000
+#define LS_FDT_SIZE 0x00100000
+
+#define FW_CFG_ADDR 0x1e020000
+#define LS7A_REG_BASE 0x1FE00000
+#define LS7A_UART_BASE 0x1fe001e0
+#define LS7A_UART_LEN 0x8
+#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
+
+#define LS_PCIECFG_BASE 0x20000000
+#define LS_PCIECFG_SIZE 0x08000000
+#define MSI_ADDR_LOW 0x2FF00000
+#define MSI_ADDR_HI 0x0
+
+#define PCIE_MEMORY_BASE 0x40000000
+#define PCIE_MEMORY_SIZE 0x40000000
+
+typedef struct LS7APCIState LS7APCIState;
+typedef struct LS7APCIEHost {
+ PCIExpressHost parent_obj;
+ LS7APCIState *pci_dev;
+} LS7APCIEHost;
+
+struct LS7APCIState {
+ PCIDevice dev;
+
+ LS7APCIEHost *pciehost;
+
+ /* 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 */
diff --git a/include/hw/loongarch/sysbus-fdt.h b/include/hw/loongarch/sysbus-fdt.h
new file mode 100644
index 0000000000..6bf53097e1
--- /dev/null
+++ b/include/hw/loongarch/sysbus-fdt.h
@@ -0,0 +1,33 @@
+/*
+ * Dynamic sysbus device tree node generation API
+ *
+ * Copyright (c) 2023 Loongarch Technology
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ *
+ */
+
+#ifndef HW_ARM_SYSBUS_FDT_H
+#define HW_ARM_SYSBUS_FDT_H
+
+#include "exec/hwaddr.h"
+
+/**
+ * platform_bus_add_all_fdt_nodes - create all the platform bus nodes
+ *
+ * builds the parent platform bus node and all the nodes of dynamic
+ * sysbus devices attached to it.
+ */
+void platform_bus_add_all_fdt_nodes(void *fdt, const char *intc, hwaddr addr,
+ hwaddr bus_size, int irq_start);
+#endif
diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h
index 60718fc342..fd9e53f623 100644
--- a/include/qemu/osdep.h
+++ b/include/qemu/osdep.h
@@ -533,6 +533,10 @@ static inline void qemu_cleanup_generic_vfree(void *p)
Valgrind does not support alignments larger than 1 MiB,
therefore we need special code which handles running on Valgrind. */
# define QEMU_VMALLOC_ALIGN (512 * 4096)
+#elif defined(__linux__) && defined(__loongarch__)
+ /* Use 32 MiB alignment so transparent hugepages can be used by KVM. */
+#define QEMU_VMALLOC_ALIGN (qemu_real_host_page_size * \
+ qemu_real_host_page_size / 8)
#elif defined(__linux__) && defined(__s390x__)
/* Use 1 MiB (segment size) alignment so gmap can be used by KVM. */
# define QEMU_VMALLOC_ALIGN (256 * 4096)
--
2.27.0
1
https://gitee.com/panchenbo/qemu.git
git@gitee.com:panchenbo/qemu.git
panchenbo
qemu
qemu
master

搜索帮助