I386.Machine.Mod 117 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718
  1. MODULE Machine; (** AUTHOR "pjm"; PURPOSE "Bootstrapping, configuration and machine interface"; *)
  2. (* The code of this module body must be the first in the statically linked boot file. *)
  3. IMPORT SYSTEM, Trace;
  4. CONST
  5. Version = "A2 Revision 5387 (23.08.2013)";
  6. MaxCPU* = 32; (** maximum number of processors (up to 256) *)
  7. DefaultObjectFileExtension* = ".Obx";
  8. (** bits in features variable *)
  9. MTTR* = 12; MMX* = 23; HTT* = 28;
  10. MaxDisks = 2; (* maximum number of disks with BIOS parameters *)
  11. HeapAdr = 100000H;
  12. MaxMemTop = 80000000H;
  13. DefaultDMASize = 20; (* default size of ISA DMA area in KB *)
  14. CONST
  15. StrongChecks = FALSE; (* perform strong checks *)
  16. Stats* = FALSE; (* acquire statistics *)
  17. TimeCount = 0 (* 100000 *); (* number of lock tries before checking timeout - 0 to disable *)
  18. (** standard lock levels (in order) *) (* also refer to Traps.Show *)
  19. TraceOutput* = 0; (* Trace output *)
  20. Memory* = 1; (* Virtual memory management, stack and page allocation *)
  21. Heaps* = 2; (* Storage allocation and Garbage collection *)
  22. Interrupts* = 3 ; (* Interrupt handling. *)
  23. Modules* = 4; (* Module list *)
  24. Objects* = 5; (* Ready queue *)
  25. Processors* = 6; (* Interprocessor interrupts *)
  26. KernelLog* = 7; (* Atomic output *)
  27. (** highest level is all object locks *)
  28. Preemption* = 31; (** flag for BreakAll *)
  29. MaxLocks = 8; (* { <= 32 } *)
  30. LowestLock = 0; HighestLock = MaxLocks-1;
  31. CONST
  32. TraceVerbose = FALSE; (* write out verbose trace info *)
  33. AddressSize = SIZEOF(ADDRESS);
  34. SetSize = MAX (SET) + 1;
  35. (** error codes *)
  36. Ok* = 0;
  37. (* standard multipliers *)
  38. K = 1024; M = 100000H; (* 1K, 1M *)
  39. (* paging sizes *)
  40. PS = 4096; (* page size in bytes *)
  41. PSlog2 = 12; (* ASH(1, PSlog2) = PS *)
  42. RS = 4*M; (* region covered by a page table in bytes *)
  43. PTEs = RS DIV PS; (* number of page table/directory entries *)
  44. ReservedPages = 8; (* pages reserved on page heap (not for normal heap use) *)
  45. NilAdr* = -1; (** nil value for addresses (not same as pointer NIL value) *)
  46. (* free page stack page node layout *)
  47. NodeSP = 0;
  48. NodeNext = AddressSize;
  49. NodePrev = AddressSize*2;
  50. MinSP = AddressSize*3; MaxSP = PS;
  51. (*
  52. 0 sp
  53. AddressSize nextAdr
  54. AddressSize*2 prevAdr
  55. AddressSize*3 first entry
  56. 4092 last entry
  57. *)
  58. (* virtual memory layout. no area will cross the 2G boundary, to avoid LONGINT sign problems. *)
  59. MapAreaAdr = 80000000H; (* dynamic mappings: bottom part of 2G..4G *)
  60. MapAreaSize = 64*M;
  61. IntelAreaAdr = 0FEE00000H; (* reserved by Intel for APIC: 4G-18M..4G-18M+4K *)
  62. IntelAreaSize = 00001000H;
  63. StackAreaAdr = MapAreaAdr+MapAreaSize; (* stacks: middle part of 2G..4G *)
  64. StackAreaSize = IntelAreaAdr-StackAreaAdr;
  65. (* stack sizes *)
  66. KernelStackSize = 2*PS; (* multiple of PS *)
  67. MaxUserStackSize = 128*K; (* multiple of PS *)
  68. InitUserStackSize = PS; (* must be PS (or change NewStack) *)
  69. UserStackGuardSize = PS; (* multiple of PS left unallocated at bottom of stack virtual area *)
  70. MaxUserStacks = StackAreaSize DIV MaxUserStackSize;
  71. (* physical memory layout *)
  72. LowAdr = PS; (* lowest physical address used *)
  73. LinkAdr = M; (* address where kernel is linked, also address where heap begins *)
  74. StaticBlockSize = 32; (* static heap block size *)
  75. BlockHeaderSize = 2 * AddressSize;
  76. RecordDescSize = 4 * AddressSize; (* needs to be adapted in case Heaps.RecordDesc is changed *)
  77. (* gdt indices *)
  78. TSSOfs = 6; (* offset in GDT of TSSs *)
  79. StackOfs = TSSOfs + MaxCPU; (* offset in GDT of stacks *)
  80. GDTSize = StackOfs + MaxCPU;
  81. (* gdt selectors *)
  82. KernelCodeSel = 1*8; (* selector 1 in gdt, RPL 0 *)
  83. KernelStackSel = 2*8; (* selector 2 in gdt, RPL 0 *)
  84. UserCodeSel = 3*8 + 3; (* selector 3 in gdt, RPL 3 *)
  85. DataSel = 4*8; (* selector 4 in gdt, RPL 0 *)
  86. UserStackSel = 5*8 + 3; (* selector 5 in gdt, RPL 3 *)
  87. KernelTR = TSSOfs*8; (* selector in gdt, RPL 0 *)
  88. (* paging flags *)
  89. PageNotPresent = 0; (* not present page *)
  90. KernelPage = 3; (* supervisor, present, r/w *)
  91. UserPage = 7; (* user, present, r/w *)
  92. HeapMin = 50; (* "minimum" heap size as percentage of total memory size (used for heap expansion in scope of GC ) *)
  93. HeapMax = 95; (* "maximum" heap size as percentage of total memory size (used for heap expansion in scope of GC) *)
  94. ExpandRate = 1; (* always extend heap with at least this percentage of total memory size *)
  95. Threshold = 10; (* periodic GC initiated when this percentage of total memory size bytes has "passed through" NewBlock *)
  96. InitialHeapIncrement = 4096;
  97. HeaderSize = 40H; (* cf. Linker0 *)
  98. EndBlockOfs = 38H; (* cf. Linker0 *)
  99. MemoryBlockOfs = BlockHeaderSize + RecordDescSize + BlockHeaderSize; (* memory block (including header) starts at offset HeaderSize *)
  100. CONST
  101. (** pre-defined interrupts 0-31, used with InstallHandler *)
  102. DE* = 0; DB* = 1; NMI* = 2; BP* = 3; OVF* = 4; BR* = 5; UD* = 6; NM* = 7;
  103. DF* = 8; TS* = 10; NP* = 11; SSF* = 12; GP* = 13; PF* = 14; MF*= 16; AC*= 17; MC* = 18;
  104. IRQ0* = 32; (* {IRQ0 MOD 8 = 0} *)
  105. IRQ2 = IRQ0 + 2;
  106. IRQ7 = IRQ0 + 7;
  107. IRQ8 = IRQ0 + 8;
  108. IRQ15 = 47;
  109. MaxIRQ* = IRQ15; (** hardware interrupt numbers *)
  110. MPKC* = 49; (** SMP: kernel call *)
  111. SoftInt* = 58; (** temporary software interrupt *)
  112. MPIPCLocal* = 59; (** SMP: local interprocessor interrupt *)
  113. MPTMR* = 60; (** SMP: timer interrupt *)
  114. MPIPC* = 61; (** SMP: interprocessor interrupt *)
  115. MPERR* = 62; (** SMP: error interrupt *)
  116. MPSPU* = 63; (** SMP: spurious interrupt {MOD 16 = 15} *)
  117. IDTSize = 64;
  118. MaxNumHandlers = 16;
  119. TraceSpurious = FALSE; (* no message on spurious hardware interrupts *)
  120. HandleSpurious = TRUE OR TraceSpurious; (* do not trap on spurious interrupts *)
  121. IntA0 = 020H; IntA1 = 021H; (* Interrupt Controller 1 *)
  122. IntB0 = 0A0H; IntB1 = 0A1H; (* Interrupt Controller 2 *)
  123. (** EFLAGS bits *)
  124. IFBit* = 9; VMBit* = 17;
  125. KernelLevel* = 0; UserLevel* = 3; (** CS MOD 4 *)
  126. Second* = 1000; (* frequency of ticks increments in Hz *)
  127. CONST
  128. Self* = 0; FrontBarrier* = 1; BackBarrier* = 2; (** Broadcast flags. *)
  129. TraceApic = FALSE;
  130. TraceProcessor = FALSE; (* remove this hack! *)
  131. ClockRateDelay = 50; (* ms - delay when timing bus clock rate *)
  132. TimerClock = 1193180; (* timer clock is 1.19318 MHz *)
  133. CONST
  134. (* low level tracing *)
  135. TraceV24 = 2; TraceScreen = 0;
  136. TraceWidth = 80; TraceHeight = 25;
  137. TraceLen = TraceWidth * SIZEOF (INTEGER);
  138. TraceSize = TraceLen * TraceHeight;
  139. TYPE
  140. Vendor* = ARRAY 13 OF CHAR;
  141. IDMap* = ARRAY 256 OF SHORTINT;
  142. TYPE
  143. Stack* = RECORD (** values are read-only *)
  144. low: ADDRESS; (* lowest virtual address that may be allocated for stack *)
  145. adr*: ADDRESS; (* lowest address on allocated stack *) (* exported for Objects only *)
  146. high*: ADDRESS; (* next virtual address after stack *) (* exported for Objects only *)
  147. END;
  148. (* task state segment *)
  149. TSSDesc = RECORD (* 1, p. 485 and p. 612 for required fields *)
  150. Link: LONGINT; (* lower 16 bits significant *)
  151. ESP0: LONGINT;
  152. ESS0: LONGINT; (* lower 16 bits significant *)
  153. ESP1: LONGINT;
  154. ESS1: LONGINT; (* lower 16 bits significant *)
  155. ESP2: LONGINT;
  156. ESS2: LONGINT; (* lower 16 bits significant *)
  157. CR3: LONGINT;
  158. EIP: LONGINT;
  159. EFLAGS: SET;
  160. EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI: LONGINT;
  161. ES, CS, SS, DS, FS, GS: LONGINT; (* lower 16 bits significant *)
  162. LDT: LONGINT; (* lower 16 bits significant *)
  163. TaskAttributes: INTEGER;
  164. IOBitmapOffset: INTEGER
  165. (* Implicit: IOBitmap: ARRAY 8192 DIV 4 OF SET *)
  166. END;
  167. Startup* = PROCEDURE; (** can not be a method *)
  168. (* global descriptor table *)
  169. SegDesc = RECORD
  170. low, high: LONGINT
  171. END;
  172. GDT = ARRAY GDTSize OF SegDesc;
  173. Range* = RECORD
  174. adr*: ADDRESS; size*: SIZE;
  175. END;
  176. TYPE
  177. (** processor state, ordering of record fields is predefined! *)
  178. State* = RECORD (* offsets used in FieldInterrupt, FieldIRQ and Objects.RestoreState *)
  179. EDI*, ESI*, ERR*, ESP0*, EBX*, EDX*, ECX*, EAX*: LONGINT; (** ESP0 = ADR(s.INT) *)
  180. INT*, BP*, PC*, CS*: LONGINT; (* BP and ERR are exchanged by glue code, for procedure link *)
  181. FLAGS*: SET;
  182. SP*, SS*: LONGINT; (** only valid if (VMBit IN s.EFLAGS) OR (CS MOD 4 < s.CS MOD 4) *)
  183. ES*, DS*, FS*, GS*: LONGINT; (** only valid if (VMBit IN s.FLAGS) *)
  184. END;
  185. (** exception state, ordering of record fields is predefined! *)
  186. ExceptionState* = RECORD
  187. halt*: ADDRESS; (** halt code *)
  188. pf*: ADDRESS; (** page fault address *)
  189. locks*: SET; (** active locks *)
  190. SP*: ADDRESS; (** actual ESP value at time of interrupt *)
  191. SS*, ES*, DS*, FS*, GS*: LONGINT; (** segment registers *)
  192. CR*: ARRAY 5 OF LONGINT; (** control registers *)
  193. DR*: ARRAY 8 OF LONGINT; (** debug registers *)
  194. FPU*: ARRAY 7 OF SET (** floating-point state *)
  195. END;
  196. Handler* = PROCEDURE {DELEGATE} (VAR state: State);
  197. HandlerRec = RECORD
  198. valid: BOOLEAN; (* offset 0 *)
  199. handler: Handler (* offset 4 *)
  200. END;
  201. GateDescriptor = RECORD
  202. offsetBits0to15: INTEGER;
  203. selector: INTEGER;
  204. gateType: INTEGER;
  205. offsetBits16to31: INTEGER
  206. END;
  207. IDT = ARRAY IDTSize OF GateDescriptor;
  208. SSEState* = ARRAY (512+16) OF CHAR;
  209. TYPE
  210. MemoryBlock* = POINTER TO MemoryBlockDesc;
  211. MemoryBlockDesc* = RECORD
  212. next- {UNTRACED}: MemoryBlock;
  213. startAdr-: ADDRESS; (* unused field for I386 *)
  214. size-: SIZE; (* unused field for I386 *)
  215. beginBlockAdr-, endBlockAdr-: ADDRESS
  216. END;
  217. TYPE
  218. EventHandler = PROCEDURE (id: LONGINT; CONST state: State);
  219. Message* = POINTER TO RECORD END; (** Broadcast message. *)
  220. BroadcastHandler = PROCEDURE (id: LONGINT; CONST state: State; msg: Message);
  221. TimeArray = ARRAY MaxCPU OF HUGEINT;
  222. Address32* = LONGINT;
  223. VAR
  224. lowTop*: ADDRESS; (** top of low memory *)
  225. memTop*: ADDRESS; (** top of memory *)
  226. dmaSize*: SIZE; (** size of ISA dma area, above lowTop (for use in Aos.Diskettes) *)
  227. configMP: ADDRESS; (** MP spec config table physical address (outside reported RAM) *)
  228. revMP: CHAR; (** MP spec revision *)
  229. featureMP: ARRAY 5 OF CHAR; (** MP spec feature bytes 1-5 *)
  230. version-: ARRAY 64 OF CHAR; (** Aos version *)
  231. SSESupport-: BOOLEAN;
  232. SSE2Support-: BOOLEAN;
  233. SSE3Support-: BOOLEAN; (* PH 04/11*)
  234. SSSE3Support-: BOOLEAN;
  235. SSE41Support-: BOOLEAN;
  236. SSE42Support-: BOOLEAN;
  237. SSE5Support-: BOOLEAN;
  238. AVXSupport-: BOOLEAN;
  239. features-, features2-: SET; (** processor features *)
  240. fcr*: SET; (** default floating-point control register value (default rounding mode is towards -infinity, for ENTIER) *)
  241. mhz*: HUGEINT; (** clock rate of GetTimer in MHz, or 0 if not known *)
  242. chs: ARRAY MaxDisks OF RECORD cyls, hds, spt: LONGINT END;
  243. initRegs: ARRAY 2 OF LONGINT; (* kernel parameters *)
  244. config: ARRAY 2048 OF CHAR; (* config strings *)
  245. bootFlag: ADDRESS;
  246. idAdr: ADDRESS; (* address of processor ID register *)
  247. map: IDMap;
  248. bootID: LONGINT; (* ID of boot processor (0) *)
  249. numberOfProcessors: LONGINT; (* number of processors installed during start up *)
  250. coresPerProcessor : LONGINT; (* number of cores per physical package *)
  251. threadsPerCore : LONGINT; (* number of threads per core *)
  252. CONST
  253. CacheLineSize = 128;
  254. TYPE
  255. (* Synchronization variables should reside in own cache line. This data structure should be aligned to CacheLineSize. *)
  256. Lock = RECORD
  257. locked : BOOLEAN;
  258. filler : ARRAY CacheLineSize - 1 OF CHAR;
  259. END;
  260. VAR
  261. lock: ARRAY MaxLocks OF Lock; (** all locks *)
  262. (*
  263. Every element in the proc array belongs to one processor. It is therefore sufficient to disable interrupts to protect the consistency of these elements. Race conditions with interrupts handled on the same processor are avoided by disabling interrupts for the entire time that a lock is held (using locksHeld & state). The data structures are padded to CacheLineSize to separate the locks out on cache lines of their own, to avoid false sharing.
  264. *)
  265. proc-, trapState-: ARRAY MaxCPU OF RECORD
  266. locksHeld-: SET; (** locks held by a processor *)
  267. state-: SET; (** processor flags (interrupt state) at entry to its first lock *)
  268. preemptCount-: LONGINT; (** if 0, preemption is allowed *)
  269. padding : ARRAY CacheLineSize - 12 OF CHAR;
  270. END;
  271. (* the data structures above should be aligned to CacheLineSize *)
  272. padding : ARRAY 92 OF CHAR;
  273. trapLocksBusy-: SET;
  274. maxTime: LONGINT;
  275. VAR
  276. gdt: GDT; (* global descriptor table *)
  277. procm: ARRAY MaxCPU OF RECORD (* indexed by ID () *)
  278. tss: TSSDesc;
  279. sp: ADDRESS; (* snapshot for GC *)
  280. stack: Stack
  281. END;
  282. kernelPD: ADDRESS; (* physical address of page directory *)
  283. freeLowPage: ADDRESS; (* free low page stack pointer (link at offset 0 in page). All addresses physical. NIL = -1 *)
  284. freeLowPages, freeHighPages, totalPages: LONGINT; (* number of free pages and total number of pages *)
  285. mapTop: ADDRESS; (* virtual address of end of memory mapping area *)
  286. heapEndAdr: ADDRESS; (* virtual address of end of heap (page aligned) *)
  287. topPageNum: LONGINT; (* page containing byte memTop-1 *)
  288. pageHeapAdr: ADDRESS; (* address (physical and virtual) of bottom of page heap area *)
  289. pageStackAdr: ADDRESS; (* virtual address of top page of free page stack *)
  290. freeStack: ARRAY (MaxUserStacks+SetSize-1) DIV SetSize OF SET; (* free stack bitmap *)
  291. freeStackIndex: LONGINT; (* current position in bitmap (rotates) *)
  292. Nbigskips-: LONGINT; (* number of times a stack was extended leaving a hole *)
  293. Nfilled-: LONGINT; (* number of times a "hole" in a stack was filled *)
  294. NnewStacks-, NnewStackLoops-, NnewStackInnerLoops-, NdisposeStacks-,
  295. NlostPages-, NreservePagesUsed-, NmaxUserStacks-: LONGINT;
  296. VAR
  297. idt: IDT; (* interrupt descriptor table *)
  298. glue: ARRAY IDTSize OF ARRAY 15 OF CHAR; (* code *)
  299. intHandler: ARRAY IDTSize, MaxNumHandlers OF HandlerRec; (* array of handlers for interrupts, the table is only filled up to MaxNumHandlers - 1, the last element in each row acts as a sentinel *)
  300. stateTag: ADDRESS;
  301. default: HandlerRec;
  302. ticks*: LONGINT; (** timer ticks. Use Kernel.GetTicks() to read, don't write *)
  303. VAR
  304. ipcBusy, ipcFlags, ipcFrontBarrier, ipcBackBarrier: SET;
  305. ipcHandler: BroadcastHandler;
  306. ipcMessage: Message;
  307. numProcessors-: LONGINT; (* number of processors we attempted to boot (some may have failed) *)
  308. maxProcessors: LONGINT; (* max number of processors we are allowed to boot (-1 for uni) *)
  309. allProcessors-: SET; (* IDs of all successfully booted processors *)
  310. localAPIC: ADDRESS; (* address of local APIC, 0 if not present *)
  311. apicVer: ARRAY MaxCPU OF LONGINT; (* APIC version *)
  312. started: ARRAY MaxCPU OF BOOLEAN; (* CPU started successfully / CPU halted *)
  313. busHz0, busHz1: ARRAY MaxCPU OF LONGINT; (* unrounded and rounded bus speed in Hz *)
  314. timer: EventHandler;
  315. timerRate: LONGINT; (* Hz - rate at which CPU timers run - for timeslicing and profiling *)
  316. stopped: BOOLEAN; (* StopAll was called *)
  317. idMap: IDMap;
  318. revIDmap: ARRAY MaxCPU OF SHORTINT;
  319. time: TimeArray;
  320. eventCount, eventMax: LONGINT;
  321. event: Handler;
  322. expandMin, heapMinKB, heapMaxKB : SIZE;
  323. gcThreshold-: SIZE;
  324. memBlockHead-{UNTRACED}, memBlockTail-{UNTRACED}: MemoryBlock; (* refer to the same memory block for I386, not traced by GC *)
  325. traceProcessorProc*: EventHandler; (** temp tracing *)
  326. traceProcessor: BOOLEAN;
  327. Timeslice*: Handler;
  328. start*: PROCEDURE;
  329. VAR
  330. traceMode: SET; (* tracing mode: Screen or V24 *)
  331. traceBase: ADDRESS; (* screen buffer base address *)
  332. tracePos: SIZE; (* current screen cursor *)
  333. tracePort: LONGINT; (* serial base port *)
  334. traceColor: SHORTINT; (* current screen tracing color *)
  335. (** -- Processor identification -- *)
  336. (** Return current processor ID (0 to MaxNum-1). *)
  337. PROCEDURE ID* (): LONGINT;
  338. CODE {SYSTEM.i386}
  339. MOV EAX, idAdr
  340. LEA EBX, map
  341. MOV EAX, [EAX]
  342. SHR EAX, 24
  343. MOV AL, [EBX+EAX]
  344. END ID;
  345. (** -- Miscellaneous -- *)
  346. (** This procedure should be called in all spin loops as a hint to the processor (e.g. Pentium 4). *)
  347. PROCEDURE -SpinHint*;
  348. CODE {SYSTEM.i386}
  349. XOR ECX, ECX ; just in case some processor interprets REP this way
  350. REP NOP ; PAUSE instruction; NOP on pre-P4 processors, Spin Loop Hint on P4 and after
  351. END SpinHint;
  352. (** Fill "size" bytes at "destAdr" with "filler". "size" must be multiple of 4. *)
  353. PROCEDURE Fill32* (destAdr: ADDRESS; size: SIZE; filler: ADDRESS);
  354. CODE {SYSTEM.i386}
  355. MOV EDI, [EBP+destAdr]
  356. MOV ECX, [EBP+size]
  357. MOV EAX, [EBP+filler]
  358. TEST ECX, 3
  359. JZ ok
  360. PUSH 8 ; ASSERT failure
  361. INT 3
  362. ok:
  363. SHR ECX, 2
  364. CLD
  365. REP STOSD
  366. END Fill32;
  367. (** Return timer value of the current processor, or 0 if not available. *)
  368. (* e.g. ARM does not have a fine-grained timer *)
  369. PROCEDURE -GetTimer* (): HUGEINT;
  370. CODE {SYSTEM.Pentium}
  371. RDTSC ; set EDX:EAX
  372. END GetTimer;
  373. (** Disable interrupts and return old interrupt state. *)
  374. PROCEDURE -DisableInterrupts* (): SET;
  375. CODE {SYSTEM.i386}
  376. PUSHFD
  377. CLI
  378. POP EAX
  379. END DisableInterrupts;
  380. (** Restore interrupt state. Parameter s must be return value of earlier DisableInterrupts call on same processor. *)
  381. PROCEDURE -RestoreInterrupts* (s: SET);
  382. CODE {SYSTEM.i386}
  383. POPFD
  384. END RestoreInterrupts;
  385. (** Return TRUE iff interrupts are enabled on the current processor. *)
  386. PROCEDURE -InterruptsEnabled* (): BOOLEAN;
  387. CODE {SYSTEM.i386}
  388. PUSHFD
  389. POP EAX
  390. SHR EAX, 9
  391. AND AL, 1
  392. END InterruptsEnabled;
  393. (** -- Processor initialization -- *)
  394. PROCEDURE -SetFCR (s: SET);
  395. CODE {SYSTEM.i386, SYSTEM.FPU}
  396. FLDCW [ESP] ; parameter s
  397. POP EAX
  398. END SetFCR;
  399. PROCEDURE -FCR (): SET;
  400. CODE {SYSTEM.i386, SYSTEM.FPU}
  401. PUSH 0
  402. FNSTCW [ESP]
  403. FWAIT
  404. POP EAX
  405. END FCR;
  406. PROCEDURE -InitFPU;
  407. CODE {SYSTEM.i386, SYSTEM.FPU}
  408. FNINIT
  409. END InitFPU;
  410. (** Setup FPU control word of current processor. *)
  411. PROCEDURE SetupFPU*;
  412. BEGIN
  413. InitFPU; SetFCR(fcr)
  414. END SetupFPU;
  415. (* Set up flags (3, p. 20)
  416. Bit
  417. 1,3,5,15,19..31 - no change
  418. 0,2,4,6..7,11 - CF,PF,AF,ZF,SF,OF off
  419. 8 - TF off
  420. 9 - IF off (no interrupts)
  421. 10 - DF off
  422. 12..13 - IOPL = 3
  423. 14 - NT off (no Windows)
  424. 16 - RF off (no Interference)
  425. 17- VM off (no virtual 8086 mode)
  426. 18 - AC off (no 486 alignment checks) *)
  427. PROCEDURE -SetupFlags;
  428. CODE {SYSTEM.i386}
  429. PUSHFD
  430. AND DWORD [ESP], 0FFF8802AH
  431. OR DWORD [ESP], 3000H
  432. POPFD
  433. END SetupFlags;
  434. (* Set up various 486-specific flags (3, p. 23)
  435. 1. Enable exception 16 on math errors.
  436. 2. Disable supervisor mode faults on write to read-only pages
  437. (386-compatible for stack checking).
  438. 3. Enable the Alignment Check field in EFLAGS *)
  439. PROCEDURE -Setup486Flags;
  440. CODE {SYSTEM.i386, SYSTEM.Privileged}
  441. MOV EAX, CR0
  442. OR EAX, 00040020H
  443. AND EAX, 0FFFEFFFFH
  444. MOV CR0, EAX
  445. END Setup486Flags;
  446. (* Set up 586-specific things *)
  447. PROCEDURE -Setup586Flags;
  448. CODE {SYSTEM.Pentium, SYSTEM.Privileged}
  449. MOV EAX, CR4
  450. BTR EAX, 2 ; clear TSD
  451. MOV CR4, EAX
  452. END Setup586Flags;
  453. (* setup SSE and SSE2 extension *)
  454. PROCEDURE SetupSSE2Ext;
  455. CONST
  456. FXSRFlag = 24; (*IN features from EBX*)
  457. SSEFlag = 25;
  458. SSE2Flag = 26;
  459. SSE3Flag = 0; (*IN features2 from ECX*) (*PH 04/11*)
  460. SSSE3Flag =9;
  461. SSE41Flag =19;
  462. SSE42Flag =20;
  463. SSE5Flag = 11;
  464. AVXFlag = 28;
  465. BEGIN
  466. SSE2Support := FALSE;
  467. SSE3Support := FALSE;
  468. SSSE3Support := FALSE;
  469. SSE41Support := FALSE;
  470. SSE42Support := FALSE;
  471. SSE5Support := FALSE;
  472. AVXSupport := FALSE;
  473. (* checking for SSE support *)
  474. IF SSEFlag IN features THEN
  475. SSESupport := TRUE;
  476. (* checking for SSE2 support *)
  477. IF SSE2Flag IN features THEN SSE2Support := TRUE;
  478. (* checking for SSE3... support*)(*PH 04/11*)
  479. IF SSE3Flag IN features2 THEN SSE3Support := TRUE;
  480. IF SSSE3Flag IN features2 THEN SSSE3Support := TRUE END;
  481. IF SSE41Flag IN features2 THEN SSE41Support := TRUE;
  482. IF SSE42Flag IN features2 THEN SSE42Support := TRUE END;
  483. END;
  484. IF SSE5Flag IN features2 THEN SSE5Support := TRUE END;
  485. IF AVXFlag IN features2 THEN AVXSupport := TRUE END;
  486. END;
  487. END;
  488. (* checking for support for the FXSAVE and FXRSTOR instruction *)
  489. IF FXSRFlag IN features THEN InitSSE END;
  490. END;
  491. END SetupSSE2Ext;
  492. PROCEDURE -InitSSE;
  493. CODE {SYSTEM.Pentium, SYSTEM.Privileged}
  494. MOV EAX, CR4
  495. OR EAX, 00000200H ; set bit 9 (OSFXSR)
  496. AND EAX, 0FFFFFBFFH ; delete bit 10 (OSXMMEXCPT)
  497. MOV CR4, EAX
  498. END InitSSE;
  499. (* Disable exceptions caused by math in new task. (1, p. 479) *)
  500. PROCEDURE -DisableMathTaskEx;
  501. CODE {SYSTEM.i386, SYSTEM.Privileged}
  502. MOV EAX,CR0
  503. AND AL, 0F5H
  504. MOV CR0, EAX;
  505. END DisableMathTaskEx;
  506. (* Disable math emulation (1, p. 479) , bit 2 of CR0 *)
  507. PROCEDURE -DisableEmulation;
  508. CODE {SYSTEM.i386, SYSTEM.Privileged}
  509. MOV EAX, CR0
  510. AND AL, 0FBH
  511. MOV CR0, EAX
  512. END DisableEmulation;
  513. (** CPU identification *)
  514. PROCEDURE CPUID*(function : ADDRESS; VAR eax, ebx, ecx, edx : SET);
  515. CODE {SYSTEM.i386, SYSTEM.Pentium}
  516. MOV EAX, [EBP+function] ; CPUID function parameter
  517. MOV ESI, [EBP+ecx] ; copy ecx into ECX (sometimes used as input parameter)
  518. MOV ECX, [ESI]
  519. CPUID ; execute CPUID
  520. MOV ESI, [EBP+eax] ; copy EAX into eax;
  521. MOV [ESI], EAX
  522. MOV ESI, [EBP+ebx] ; copy EBX into ebx
  523. MOV [ESI], EBX
  524. MOV ESI, [EBP+ecx] ; copy ECX into ecx
  525. MOV [ESI], ECX
  526. MOV ESI, [EBP+edx] ; copy EDX into edx
  527. MOV [ESI], EDX
  528. END CPUID;
  529. (* If the CPUID instruction is supported, the ID flag (bit 21) of the EFLAGS register is r/w *)
  530. PROCEDURE CpuIdSupported*() : BOOLEAN;
  531. CODE {SYSTEM.i386}
  532. PUSHFD ; save EFLAGS
  533. POP EAX ; store EFLAGS in EAX
  534. MOV EBX, EAX ; save EBX for later testing
  535. XOR EAX, 00200000H ; toggle bit 21
  536. PUSH EAX ; push to stack
  537. POPFD ; save changed EAX to EFLAGS
  538. PUSHFD ; push EFLAGS to TOS
  539. POP EAX ; store EFLAGS in EAX
  540. CMP EAX, EBX ; see if bit 21 has changed
  541. SETNE AL; ; return TRUE if bit 21 has changed, FALSE otherwise
  542. END CpuIdSupported;
  543. (** Initialise current processor. Must be called by every processor. *)
  544. PROCEDURE InitProcessor*;
  545. BEGIN
  546. SetupFlags;
  547. Setup486Flags;
  548. Setup586Flags;
  549. DisableMathTaskEx;
  550. DisableEmulation;
  551. SetupFPU;
  552. SetupSSE2Ext
  553. END InitProcessor;
  554. (** Initialize APIC ID address. *)
  555. PROCEDURE InitAPICIDAdr* (adr: ADDRESS; CONST m: IDMap);
  556. VAR s: SET;
  557. BEGIN
  558. s := DisableInterrupts ();
  559. idAdr := adr; map := m;
  560. RestoreInterrupts (s)
  561. END InitAPICIDAdr;
  562. PROCEDURE InitBoot;
  563. VAR
  564. largestFunction, i: LONGINT;
  565. eax, ebx, ecx, edx : SET;
  566. logicalProcessorCount : LONGINT;
  567. u: ARRAY 8 OF CHAR; vendor : Vendor;
  568. PROCEDURE GetString(VAR string : ARRAY OF CHAR; offset : LONGINT; register : SET);
  569. BEGIN
  570. string[offset] :=CHR(SYSTEM.VAL(LONGINT, register * {0..7}));
  571. string[offset+1] := CHR(SYSTEM.VAL(LONGINT, LSH(register * {8..15}, -8)));
  572. string[offset+2] := CHR(SYSTEM.VAL(LONGINT, LSH(register * {16..23}, -16)));
  573. string[offset+3] := CHR(SYSTEM.VAL(LONGINT, LSH(register * {24..31}, -24)));
  574. END GetString;
  575. BEGIN
  576. vendor := "Unknown"; features := {}; features2 := {};
  577. coresPerProcessor := 1; threadsPerCore := 1;
  578. IF CpuIdSupported() THEN
  579. (* Assume that all processors are the same *)
  580. (* CPUID standard function 0 returns: eax: largest CPUID standard function supported, ebx, edx, ecx: vendor string *)
  581. CPUID(0, eax, ebx, ecx, edx);
  582. largestFunction := SYSTEM.VAL(LONGINT, eax);
  583. ASSERT(LEN(vendor) >= 13);
  584. GetString(vendor, 0, ebx); GetString(vendor, 4, edx); GetString(vendor, 8, ecx); vendor[12] := 0X;
  585. IF (largestFunction >= 1) THEN
  586. (* CPUID standard function 1 returns: CPU features in ecx & edx *)
  587. CPUID(1, eax, ebx, ecx, edx);
  588. features := SYSTEM.VAL(SET, edx);
  589. features2 := SYSTEM.VAL(SET, ecx);
  590. (* The code below is used to determine the number of threads per processor core (hyperthreading). This is required
  591. since processors supporting hyperthreading are listed only once in the MP tables, so we need to know the
  592. exact number of threads per processor to start the processor correctly *)
  593. IF (HTT IN features) THEN (* multithreading supported by CPU *)
  594. (* logical processor count = number of cores * number of threads per core = total number of threads supported *)
  595. logicalProcessorCount := SYSTEM.VAL(LONGINT, LSH(ebx * {16..23}, -16));
  596. IF (vendor = "GenuineIntel") THEN
  597. IF (largestFunction >= 4) THEN
  598. (* CPUID standard function 4 returns: number of processor cores -1 on this die eax[26.31] *)
  599. ecx := SYSTEM.VAL(SET, 0); (* input parameter - must be set to 0 *)
  600. CPUID(4, eax, ebx, ecx, edx);
  601. coresPerProcessor := SYSTEM.VAL(LONGINT, LSH(eax * {26..31}, -26)) + 1;
  602. threadsPerCore := logicalProcessorCount DIV coresPerProcessor;
  603. ELSE
  604. threadsPerCore := logicalProcessorCount;
  605. END;
  606. ELSIF (vendor = "AuthenticAMD") THEN
  607. (* CPUID extended function 1 returns: largest extended function *)
  608. CPUID(80000000H, eax, ebx, ecx, edx);
  609. largestFunction := SYSTEM.VAL(LONGINT, eax - {31}); (* remove sign *)
  610. IF (largestFunction >= 8) THEN
  611. (* CPUID extended function 8 returns: *)
  612. CPUID(80000008H, eax, ebx, ecx, edx);
  613. coresPerProcessor := SYSTEM.VAL(LONGINT, ecx * {0..7}) + 1;
  614. threadsPerCore := logicalProcessorCount DIV coresPerProcessor;
  615. ELSIF (largestFunction >= 1) THEN
  616. (* CPUID extended function 1 returns CmpLegacy bit in ecx *)
  617. CPUID(80000001H, eax, ebx, ecx, edx);
  618. IF 1 IN ecx THEN (* CmpLegacy bit set -> no hyperthreading *)
  619. coresPerProcessor := logicalProcessorCount;
  620. threadsPerCore := 1;
  621. END;
  622. ELSE
  623. (* single-core, single-thread *)
  624. END;
  625. ELSE
  626. Trace.String("Machine: "); Trace.Yellow; Trace.String("Warning: Cannot detect hyperthreading, unknown CPU vendor ");
  627. Trace.String(vendor); Trace.Ln; Trace.Default;
  628. END;
  629. END;
  630. END;
  631. END;
  632. Trace.String("Machine: "); Trace.Int(coresPerProcessor, 0); Trace.String(" cores per physical package, ");
  633. Trace.Int(threadsPerCore, 0); Trace.String(" threads per core.");
  634. Trace.Ln;
  635. InitFPU;
  636. fcr := (FCR () - {0, 2, 3, 10, 11}) + {0 .. 5, 8, 9}; (* default FCR RC=00B *)
  637. bootID := 0; map[0] := 0;
  638. idAdr := ADDRESSOF (bootID);
  639. (* allow user to specify GetTimer rate, for tracing purposes *)
  640. GetConfig ("MHz", u);
  641. i := 0; mhz := StrToInt (i, u);
  642. END InitBoot;
  643. (** -- Configuration and bootstrapping -- *)
  644. (** Return the value of the configuration string specified by parameter name in parameter val. Returns val = "" if the string was not found, or has an empty value. *)
  645. PROCEDURE GetConfig* (CONST name: ARRAY OF CHAR; VAR val: ARRAY OF CHAR);
  646. VAR i, src: LONGINT; ch: CHAR;
  647. BEGIN
  648. ASSERT (name[0] # "="); (* no longer supported, use GetInit instead *)
  649. src := 0;
  650. LOOP
  651. ch := config[src];
  652. IF ch = 0X THEN EXIT END;
  653. i := 0;
  654. LOOP
  655. ch := config[src];
  656. IF (ch # name[i]) OR (name[i] = 0X) THEN EXIT END;
  657. INC (i); INC (src)
  658. END;
  659. IF (ch = 0X) & (name[i] = 0X) THEN (* found: (src^ = 0X) & (name[i] = 0X) *)
  660. i := 0;
  661. REPEAT
  662. INC (src); ch := config[src]; val[i] := ch; INC (i);
  663. IF i = LEN(val) THEN val[i - 1] := 0X; RETURN END (* val too short *)
  664. UNTIL ch = 0X;
  665. val[i] := 0X; RETURN
  666. ELSE
  667. WHILE ch # 0X DO (* skip to end of name *)
  668. INC (src); ch := config[src]
  669. END;
  670. INC (src);
  671. REPEAT (* skip to end of value *)
  672. ch := config[src]; INC (src)
  673. UNTIL ch = 0X
  674. END
  675. END;
  676. val[0] := 0X
  677. END GetConfig;
  678. (** Get CHS parameters of first two BIOS-supported hard disks. *)
  679. PROCEDURE GetDiskCHS* (d: LONGINT; VAR cyls, hds, spt: LONGINT);
  680. BEGIN
  681. cyls := chs[d].cyls; hds := chs[d].hds; spt := chs[d].spt
  682. END GetDiskCHS;
  683. (** Get parameter values from Init string. If n = 0, return val = ASH(bx, 16) + ax, and if n = 1, return val = ASH(dx, 16) + cx, where ax, bx, cx, dx are the register values after the OBL boot loader or noboot.exe have executed the 16-bit x86 code in the Init string. *)
  684. PROCEDURE GetInit* (n: LONGINT; VAR val: LONGINT);
  685. BEGIN
  686. val := initRegs[n]
  687. END GetInit;
  688. (** Convert a string to an integer. Parameter i specifies where in the string scanning should begin (usually 0 in the first call). Scanning stops at the first non-valid character, and i returns the updated position. Parameter s is the string to be scanned. The value is returned as result, or 0 if not valid. Syntax: number = ["-"] digit {digit} ["H" | "h"] . digit = "0" | ... "9" | "A" .. "F" | "a" .. "f" . If the number contains any hexdecimal letter, or if it ends in "H" or "h", it is interpreted as hexadecimal. *)
  689. PROCEDURE StrToInt* (VAR i: LONGINT; CONST s: ARRAY OF CHAR): LONGINT;
  690. VAR vd, vh, sgn, d: LONGINT; hex: BOOLEAN;
  691. BEGIN
  692. vd := 0; vh := 0; hex := FALSE;
  693. IF s[i] = "-" THEN sgn := -1; INC (i) ELSE sgn := 1 END;
  694. LOOP
  695. IF (s[i] >= "0") & (s[i] <= "9") THEN d := ORD (s[i])-ORD ("0")
  696. ELSIF (CAP (s[i]) >= "A") & (CAP (s[i]) <= "F") THEN d := ORD (CAP (s[i]))-ORD ("A") + 10; hex := TRUE
  697. ELSE EXIT
  698. END;
  699. vd := 10*vd + d; vh := 16*vh + d;
  700. INC (i)
  701. END;
  702. IF CAP (s[i]) = "H" THEN hex := TRUE; INC (i) END; (* optional H *)
  703. IF hex THEN vd := vh END;
  704. RETURN sgn * vd
  705. END StrToInt;
  706. (* Delay for IO *)
  707. PROCEDURE -Wait*;
  708. CODE {SYSTEM.i386}
  709. JMP 0
  710. JMP 0
  711. JMP 0
  712. END Wait;
  713. (* Reset processor by causing a double fault. *)
  714. PROCEDURE Reboot;
  715. CODE {SYSTEM.i386, SYSTEM.Privileged}
  716. PUSH 0
  717. PUSH 0
  718. LIDT [ESP]
  719. INT 3
  720. END Reboot;
  721. PROCEDURE -Cli*;
  722. CODE{SYSTEM.i386}
  723. CLI
  724. END Cli;
  725. PROCEDURE -Sti*;
  726. CODE{SYSTEM.i386}
  727. STI
  728. END Sti;
  729. (** Shut down the system. If parameter reboot is set, attempt to reboot the system. *)
  730. PROCEDURE Shutdown* (reboot: BOOLEAN);
  731. VAR i: LONGINT;
  732. BEGIN
  733. Cli;
  734. IF reboot THEN (* attempt reboot *)
  735. Portout8 (70H, 8FX); (* Reset type: p. 5-37 AT Tech. Ref. *)
  736. Wait; Portout8 (71H, 0X); (* Note: soft boot flag was set in InitMemory *)
  737. Wait; Portout8 (70H, 0DX);
  738. Wait; Portout8 (64H, 0FEX); (* reset CPU *)
  739. FOR i := 1 TO 10000 DO END;
  740. Reboot
  741. END;
  742. LOOP END
  743. END Shutdown;
  744. (* Get hard disk parameters. *)
  745. PROCEDURE GetPar (p: ADDRESS; ofs: LONGINT): LONGINT;
  746. VAR ch: CHAR;
  747. BEGIN
  748. SYSTEM.GET (p + 12 + ofs, ch);
  749. RETURN ORD (ch)
  750. END GetPar;
  751. (* Read boot table. *)
  752. PROCEDURE ReadBootTable (bt: ADDRESS);
  753. VAR i, p: ADDRESS; j, d, type, addr, size, heapSize: LONGINT; ch: CHAR;
  754. BEGIN
  755. heapSize := 0; lowTop := 0;
  756. p := bt; d := 0;
  757. LOOP
  758. SYSTEM.GET (p, type);
  759. IF type = -1 THEN
  760. EXIT (* end *)
  761. ELSIF type = 3 THEN (* boot memory/top of low memory *)
  762. SYSTEM.GET (p + 8, addr); SYSTEM.GET (p + 12, size);
  763. lowTop := addr + size
  764. ELSIF type = 4 THEN (* free memory/extended memory size *)
  765. SYSTEM.GET (p + 8, addr); SYSTEM.GET (p + 12, size);
  766. IF addr = HeapAdr THEN heapSize := size END
  767. ELSIF type = 5 THEN (* HD config *)
  768. IF d < MaxDisks THEN
  769. chs[d].cyls := GetPar (p, 0) + 100H * GetPar (p, 1);
  770. chs[d].hds := GetPar (p, 2); chs[d].spt := GetPar (p, 14);
  771. INC (d)
  772. END
  773. ELSIF type = 8 THEN (* config strings *)
  774. i := p + 8; j := 0; (* copy the config strings over *)
  775. LOOP
  776. SYSTEM.GET (i, ch); config[j] := ch; INC (i); INC (j);
  777. IF ch = 0X THEN EXIT END;
  778. REPEAT SYSTEM.GET (i, ch); config[j] := ch; INC (i); INC (j) UNTIL ch = 0X; (* end of name *)
  779. REPEAT SYSTEM.GET (i, ch); config[j] := ch; INC (i); INC (j) UNTIL ch = 0X (* end of value *)
  780. END
  781. END;
  782. SYSTEM.GET (p + 4, size); INC (p, size)
  783. END;
  784. ASSERT((heapSize # 0) & (lowTop # 0));
  785. memTop := HeapAdr + heapSize
  786. END ReadBootTable;
  787. (** Read a byte from the non-volatile setup memory. *)
  788. PROCEDURE GetNVByte* (ofs: LONGINT): CHAR;
  789. VAR c: CHAR;
  790. BEGIN
  791. Portout8 (70H, CHR(ofs)); Wait; Portin8(71H, c);
  792. RETURN c
  793. END GetNVByte;
  794. (** Write a byte to the non-volatile setup memory. *)
  795. PROCEDURE PutNVByte* (ofs: LONGINT; val: CHAR);
  796. BEGIN
  797. Portout8 (70H, CHR(ofs)); Wait; Portout8 (71H, val)
  798. END PutNVByte;
  799. (** Compute a checksum for the Intel SMP spec floating pointer structure. *)
  800. PROCEDURE ChecksumMP* (adr: ADDRESS; size: SIZE): LONGINT;
  801. VAR sum: LONGINT; x: ADDRESS; ch: CHAR;
  802. BEGIN
  803. sum := 0;
  804. FOR x := adr TO adr + size-1 DO
  805. SYSTEM.GET (x, ch);
  806. sum := (sum + ORD(ch)) MOD 256
  807. END;
  808. RETURN sum
  809. END ChecksumMP;
  810. (* Search for MP floating pointer structure. *)
  811. PROCEDURE SearchMem (adr: ADDRESS; size: SIZE): ADDRESS;
  812. VAR x, len: LONGINT; ch: CHAR;
  813. BEGIN
  814. WHILE size > 0 DO
  815. SYSTEM.GET (adr, x);
  816. IF x = 05F504D5FH THEN (* "_MP_" found *)
  817. SYSTEM.GET (adr + 8, ch); len := ORD(ch)*16;
  818. IF len > 0 THEN
  819. SYSTEM.GET (adr + 9, ch);
  820. IF (ch = 1X) OR (ch >= 4X) THEN (* version 1.1 or 1.4 or higher *)
  821. IF ChecksumMP(adr, len) = 0 THEN
  822. RETURN adr (* found *)
  823. END
  824. END
  825. END
  826. END;
  827. INC (adr, 16); DEC (size, 16)
  828. END;
  829. RETURN NilAdr (* not found *)
  830. END SearchMem;
  831. (* Search for MP spec info. *)
  832. PROCEDURE SearchMP;
  833. VAR adr: ADDRESS;
  834. BEGIN
  835. adr := 0;
  836. SYSTEM.GET (040EH, SYSTEM.VAL (INTEGER, adr)); (* EBDA address *)
  837. adr := adr*16;
  838. IF adr < 100000H THEN adr := SearchMem(adr, 1024) (* 1. look in EBDA *)
  839. ELSE adr := NilAdr
  840. END;
  841. IF adr = NilAdr THEN (* 2. look in last kb of base memory *)
  842. adr := SearchMem(lowTop + (-lowTop) MOD 10000H - 1024, 1024);
  843. IF adr = NilAdr THEN (* 3. look at top of physical memory *)
  844. adr := SearchMem(memTop - 1024, 1024);
  845. IF adr = NilAdr THEN (* 4. look in BIOS ROM space *)
  846. adr := SearchMem(0E0000H, 20000H)
  847. END
  848. END
  849. END;
  850. IF adr = NilAdr THEN
  851. revMP := 0X; configMP := NilAdr
  852. ELSE
  853. SYSTEM.GET (adr + 9, revMP);
  854. SYSTEM.MOVE(adr + 11, ADDRESSOF(featureMP[0]), 5); (* feature bytes *)
  855. configMP := SYSTEM.GET32 (adr + 4); (* physical address outside reported RAM (spec 1.4 p. 4-2) *)
  856. IF configMP = 0 THEN configMP := NilAdr END
  857. END
  858. END SearchMP;
  859. (* Allocate area for ISA DMA. *)
  860. PROCEDURE AllocateDMA;
  861. VAR old: ADDRESS;
  862. BEGIN
  863. old := lowTop;
  864. dmaSize := DefaultDMASize*1024;
  865. ASSERT((dmaSize >= 0) & (dmaSize <= 65536));
  866. IF (lowTop-dmaSize) DIV 65536 # (lowTop-1) DIV 65536 THEN (* crosses 64KB boundary *)
  867. DEC (lowTop, lowTop MOD 65536) (* round down to 64KB boundary *)
  868. END;
  869. DEC (lowTop, dmaSize); (* allocate memory *)
  870. dmaSize := old - lowTop (* how much was allocated (including rounding) *)
  871. END AllocateDMA;
  872. (* Check if the specified address is RAM. *)
  873. PROCEDURE IsRAM(adr: ADDRESS): BOOLEAN;
  874. CONST Pattern1 = 0BEEFC0DEH; Pattern2 = 0AA55FF00H;
  875. VAR save, x: ADDRESS; ok: BOOLEAN;
  876. BEGIN
  877. ok := FALSE;
  878. SYSTEM.GET (adr, save);
  879. SYSTEM.PUT (adr, Pattern1); (* attempt 1st write *)
  880. x := Pattern2; (* write something else *)
  881. SYSTEM.GET (adr, x); (* attempt 1st read *)
  882. IF x = Pattern1 THEN (* first test passed *)
  883. SYSTEM.PUT (adr, Pattern2); (* attempt 2nd write *)
  884. x := Pattern1; (* write something else *)
  885. SYSTEM.GET (adr, x); (* attempt 2nd read *)
  886. ok := (x = Pattern2)
  887. END;
  888. SYSTEM.PUT (adr, save);
  889. RETURN ok
  890. END IsRAM;
  891. (* Check amount of memory available and update memTop. *)
  892. PROCEDURE CheckMemory;
  893. CONST M = 100000H; ExtMemAdr = M; Step = M;
  894. VAR s: ARRAY 16 OF CHAR; adr: ADDRESS; i: LONGINT;
  895. BEGIN
  896. GetConfig("ExtMemSize", s); (* in MB *)
  897. IF s[0] # 0X THEN (* override detection *)
  898. i := 0; memTop := ExtMemAdr + StrToInt(i, s) * M;
  899. Trace.String("Machine: Memory: ");
  900. ELSE
  901. Trace.String("Machine: Detecting memory... ");
  902. IF memTop >= 15*M THEN (* search for more memory (ignore aliasing) *)
  903. adr := memTop-4;
  904. WHILE (LSH(memTop, -12) < LSH(MaxMemTop, -12)) & IsRAM(adr) DO
  905. memTop := adr + 4;
  906. INC (adr, Step)
  907. END;
  908. IF (memTop <= 0) THEN memTop := 2047 * M ; END;
  909. END
  910. END;
  911. Trace.Green; Trace.IntSuffix(memTop, 0, "B"); Trace.Ln; Trace.Default;
  912. END CheckMemory;
  913. (* Initialize locks. *)
  914. PROCEDURE InitLocks;
  915. VAR i: LONGINT; s: ARRAY 12 OF CHAR;
  916. BEGIN
  917. IF TimeCount # 0 THEN
  918. GetConfig("LockTimeout", s);
  919. i := 0; maxTime := StrToInt(i, s);
  920. IF maxTime > MAX(LONGINT) DIV 1000000 THEN
  921. maxTime := MAX(LONGINT)
  922. ELSE
  923. maxTime := maxTime * 1000000
  924. END
  925. END;
  926. FOR i := 0 TO MaxCPU-1 DO
  927. proc[i].locksHeld := {}; proc[i].preemptCount := 0
  928. END;
  929. FOR i := 0 TO MaxLocks-1 DO
  930. lock[i].locked := FALSE
  931. END
  932. END InitLocks;
  933. (* Return flags state. *)
  934. PROCEDURE -GetFlags (): SET;
  935. CODE {SYSTEM.i386}
  936. PUSHFD
  937. POP EAX
  938. END GetFlags;
  939. (* Set flags state. *)
  940. PROCEDURE -SetFlags (s: SET);
  941. CODE {SYSTEM.i386}
  942. POPFD
  943. END SetFlags;
  944. PROCEDURE -PushFlags*;
  945. CODE {SYSTEM.i386}
  946. PUSHFD
  947. END PushFlags;
  948. PROCEDURE -PopFlags*;
  949. CODE {SYSTEM.i386}
  950. POPFD
  951. END PopFlags;
  952. (** Disable preemption on the current processor (increment the preemption counter). Returns the current processor ID as side effect. *)
  953. PROCEDURE AcquirePreemption* (): LONGINT;
  954. VAR id: LONGINT;
  955. BEGIN
  956. PushFlags; Cli;
  957. id := ID ();
  958. INC (proc[id].preemptCount);
  959. PopFlags;
  960. RETURN id
  961. END AcquirePreemption;
  962. (** Enable preemption on the current processor (decrement the preemption counter). *)
  963. PROCEDURE ReleasePreemption*;
  964. VAR id: LONGINT;
  965. BEGIN
  966. PushFlags; Cli;
  967. id := ID ();
  968. IF StrongChecks THEN
  969. ASSERT(proc[id].preemptCount > 0)
  970. END;
  971. DEC (proc[id].preemptCount);
  972. PopFlags
  973. END ReleasePreemption;
  974. (** Return the preemption counter of the current processor (specified in parameter). *)
  975. PROCEDURE PreemptCount* (id: LONGINT): LONGINT;
  976. BEGIN
  977. IF StrongChecks THEN
  978. (*ASSERT(~(9 IN GetFlags ()));*) (* interrupts off *) (* commented out because check is too strong *)
  979. ASSERT(id = ID ()) (* caller must specify current processor *)
  980. END;
  981. RETURN proc[id].preemptCount
  982. END PreemptCount;
  983. (* Spin waiting for a lock. Return AL = 1X iff timed out. *)
  984. PROCEDURE AcquireSpinTimeout(VAR locked: BOOLEAN; count: LONGINT; flags: SET): CHAR;
  985. CODE {SYSTEM.i386}
  986. MOV ESI, [EBP+flags] ; ESI := flags
  987. MOV EDI, [EBP+count] ; EDI := count
  988. MOV EBX, [EBP+locked] ; EBX := ADR(locked)
  989. MOV AL, 1 ; AL := 1
  990. CLI ; switch interrupts off before acquiring lock
  991. test:
  992. CMP [EBX], AL ; locked? { AL = 1 }
  993. JE wait ; yes, go wait
  994. XCHG [EBX], AL ; set and read the lock atomically. LOCK prefix implicit.
  995. CMP AL, 1 ; was locked?
  996. JNE exit ; no, we have it now, interrupts are off, and AL # 1
  997. wait:
  998. ; ASSERT(AL = 1)
  999. XOR ECX, ECX ; just in case some processor interprets REP this way
  1000. REP NOP ; PAUSE instruction; see SpinHint
  1001. TEST ESI, 200H ; bit 9 - IF
  1002. JZ intoff
  1003. STI ; restore interrupt state quickly to allow pending interrupts (e.g. Processors.StopAll/Broadcast)
  1004. NOP ; NOP required, otherwise STI; CLI not interruptable
  1005. CLI ; disable interrupts
  1006. intoff:
  1007. DEC EDI ; counter
  1008. JNZ test ; not timed out yet
  1009. OR EDI, [EBP+count] ; re-fetch original value & set flags
  1010. JZ test ; if count = 0, retry forever
  1011. ; timed out (AL = 1)
  1012. exit:
  1013. END AcquireSpinTimeout;
  1014. (** Acquire a spin-lock and disable interrupts. *)
  1015. PROCEDURE Acquire* (level: LONGINT);
  1016. VAR id, i: LONGINT; flags: SET; start: HUGEINT;
  1017. BEGIN
  1018. id := AcquirePreemption ();
  1019. flags := GetFlags (); (* store state of interrupt flag *)
  1020. IF StrongChecks THEN
  1021. ASSERT(~(9 IN flags) OR (proc[id].locksHeld = {})); (* interrupts enabled => no locks held *)
  1022. ASSERT(~(level IN proc[id].locksHeld)) (* recursive locks not allowed *)
  1023. END;
  1024. IF (TimeCount = 0) OR (maxTime = 0) THEN
  1025. IF AcquireSpinTimeout(lock[level].locked, 0, flags) = 0X THEN END; (* {interrupts off} *)
  1026. ELSE
  1027. start := GetTimer ();
  1028. WHILE AcquireSpinTimeout(lock[level].locked, TimeCount, flags) = 1X DO
  1029. IF GetTimer () - start > maxTime THEN
  1030. trapState := proc;
  1031. trapLocksBusy := {};
  1032. FOR i := 0 TO MaxLocks-1 DO
  1033. IF lock[i].locked THEN INCL(trapLocksBusy, i) END
  1034. END;
  1035. HALT(1301) (* Lock timeout - see Traps *)
  1036. END
  1037. END
  1038. END;
  1039. IF proc[id].locksHeld = {} THEN
  1040. proc[id].state := flags
  1041. END;
  1042. INCL(proc[id].locksHeld, level); (* we now hold the lock *)
  1043. IF StrongChecks THEN (* no lower-level locks currently held by this processor *)
  1044. ASSERT((level = 0) OR (proc[id].locksHeld * {0..level-1} = {}))
  1045. END
  1046. END Acquire;
  1047. (** Release a spin-lock. Switch on interrupts when last lock released. *)
  1048. PROCEDURE Release* (level: LONGINT);
  1049. VAR id: LONGINT; flags: SET;
  1050. BEGIN (* {interrupts off} *)
  1051. id := ID ();
  1052. IF StrongChecks THEN
  1053. ASSERT(~(9 IN GetFlags ())); (* {interrupts off} *)
  1054. ASSERT(lock[level].locked);
  1055. ASSERT(level IN proc[id].locksHeld)
  1056. END;
  1057. EXCL(proc[id].locksHeld, level);
  1058. IF proc[id].locksHeld = {} THEN
  1059. flags := proc[id].state ELSE flags := GetFlags ()
  1060. END;
  1061. lock[level].locked := FALSE;
  1062. SetFlags(flags);
  1063. ReleasePreemption
  1064. END Release;
  1065. (** Acquire all locks. Only for exceptional cases. *)
  1066. PROCEDURE AcquireAll*;
  1067. VAR lock: LONGINT;
  1068. BEGIN
  1069. FOR lock := HighestLock TO LowestLock BY -1 DO Acquire(lock) END
  1070. END AcquireAll;
  1071. (** Release all locks. Reverse of AcquireAll. *)
  1072. PROCEDURE ReleaseAll*;
  1073. VAR lock: LONGINT;
  1074. BEGIN
  1075. FOR lock := LowestLock TO HighestLock DO Release(lock) END
  1076. END ReleaseAll;
  1077. (** Break all locks held by current processor (for exception handling). Returns levels released. *)
  1078. PROCEDURE BreakAll* (): SET;
  1079. VAR id, level: LONGINT; released: SET;
  1080. BEGIN
  1081. id := AcquirePreemption ();
  1082. PushFlags; Cli;
  1083. released := {};
  1084. FOR level := 0 TO MaxLocks-1 DO
  1085. IF level IN proc[id].locksHeld THEN
  1086. lock[level].locked := FALSE; (* break the lock *)
  1087. EXCL(proc[id].locksHeld, level);
  1088. INCL(released, level)
  1089. END
  1090. END;
  1091. IF proc[id].preemptCount > 1 THEN INCL(released, Preemption) END;
  1092. proc[id].preemptCount := 0; (* clear preemption flag *)
  1093. PopFlags;
  1094. RETURN released
  1095. END BreakAll;
  1096. (** Acquire a fine-grained lock on an active object. *)
  1097. PROCEDURE AcquireObject* (VAR locked: BOOLEAN);
  1098. CODE {SYSTEM.i386}
  1099. PUSHFD
  1100. MOV EBX, [EBP+locked] ; EBX := ADR(locked)
  1101. MOV AL, 1
  1102. test:
  1103. CMP [EBX], AL ; locked? { AL = 1 }
  1104. JNE try
  1105. XOR ECX, ECX ; just in case some processor interprets REP this way
  1106. STI
  1107. REP NOP ; PAUSE instruction; see SpinHint
  1108. CLI
  1109. JMP test
  1110. try:
  1111. XCHG [EBX], AL ; set and read the lock atomically. LOCK prefix implicit.
  1112. CMP AL, 1 ; was locked?
  1113. JE test ; yes, try again
  1114. POPFD
  1115. END AcquireObject;
  1116. (** Release an active object lock. *)
  1117. PROCEDURE ReleaseObject* (VAR locked: BOOLEAN);
  1118. CODE {SYSTEM.i386}
  1119. MOV EBX, [EBP+locked] ; EBX := ADR(locked)
  1120. MOV BYTE [EBX], 0
  1121. END ReleaseObject;
  1122. (* Load global descriptor table *)
  1123. PROCEDURE LoadGDT(base: ADDRESS; size: SIZE);
  1124. CODE {SYSTEM.i386, SYSTEM.Privileged}
  1125. SHL DWORD [EBP+size], 16
  1126. MOV EBX, 2
  1127. LGDT [EBP+EBX+size]
  1128. END LoadGDT;
  1129. (* Load segment registers *)
  1130. PROCEDURE LoadSegRegs(data: LONGINT);
  1131. CODE {SYSTEM.i386}
  1132. MOV EAX, [EBP+data]
  1133. MOV DS, AX
  1134. MOV ES, AX
  1135. XOR EAX, EAX
  1136. MOV FS, AX
  1137. MOV GS, AX
  1138. END LoadSegRegs;
  1139. (* Return CS. *)
  1140. PROCEDURE -CS* (): LONGINT;
  1141. CODE {SYSTEM.i386}
  1142. XOR EAX, EAX
  1143. MOV AX, CS
  1144. END CS;
  1145. (** -- Memory management -- *)
  1146. (* Allocate a physical page below 1M. Parameter adr returns physical and virtual address (or NilAdr).*)
  1147. PROCEDURE NewLowPage(VAR adr: ADDRESS);
  1148. BEGIN
  1149. adr := freeLowPage;
  1150. IF freeLowPage # NilAdr THEN
  1151. SYSTEM.GET (freeLowPage, freeLowPage); (* freeLowPage := freeLowPage.next *)
  1152. DEC(freeLowPages)
  1153. END
  1154. END NewLowPage;
  1155. (* Allocate a directly-mapped page. Parameter adr returns physical and virtual address (or NilAdr). *)
  1156. PROCEDURE NewDirectPage(VAR adr: ADDRESS);
  1157. BEGIN
  1158. IF pageHeapAdr # heapEndAdr THEN
  1159. DEC(pageHeapAdr, PS); adr := pageHeapAdr;
  1160. DEC(freeHighPages)
  1161. ELSE
  1162. adr := NilAdr
  1163. END
  1164. END NewDirectPage;
  1165. (* Allocate a physical page. *)
  1166. PROCEDURE NewPage(VAR physAdr: ADDRESS);
  1167. VAR sp, prev: ADDRESS;
  1168. BEGIN
  1169. SYSTEM.GET(pageStackAdr + NodeSP, sp);
  1170. ASSERT((sp >= MinSP) & (sp <= MaxSP) & (sp MOD AddressSize = 0)); (* index check *)
  1171. IF sp > MinSP THEN (* stack not empty, pop entry *)
  1172. DEC(sp, AddressSize);
  1173. SYSTEM.GET (pageStackAdr+sp, physAdr);
  1174. SYSTEM.PUT (pageStackAdr+NodeSP, sp);
  1175. SYSTEM.GET (pageStackAdr+NodePrev, prev);
  1176. IF (sp = MinSP) & (prev # NilAdr) THEN
  1177. pageStackAdr := prev
  1178. END;
  1179. DEC(freeHighPages)
  1180. ELSE
  1181. NewDirectPage(physAdr)
  1182. END
  1183. END NewPage;
  1184. (* Deallocate a physical page. *)
  1185. PROCEDURE DisposePage(physAdr: ADDRESS);
  1186. VAR sp, next, newAdr: ADDRESS;
  1187. BEGIN
  1188. SYSTEM.GET (pageStackAdr + NodeSP, sp);
  1189. ASSERT((sp >= MinSP) & (sp <= MaxSP) & (sp MOD AddressSize = 0)); (* index check *)
  1190. IF sp = MaxSP THEN (* current stack full *)
  1191. SYSTEM.GET (pageStackAdr + NodeNext, next);
  1192. IF next # NilAdr THEN (* next stack exists, make it current *)
  1193. pageStackAdr := next;
  1194. SYSTEM.GET (pageStackAdr+NodeSP, sp);
  1195. ASSERT(sp = MinSP) (* must be empty *)
  1196. ELSE (* allocate next stack *)
  1197. NewDirectPage(newAdr);
  1198. IF newAdr = NilAdr THEN
  1199. NewLowPage(newAdr); (* try again from reserve *)
  1200. IF newAdr = NilAdr THEN
  1201. IF Stats THEN INC(NlostPages) END;
  1202. RETURN (* give up (the disposed page is lost) *)
  1203. ELSE
  1204. IF Stats THEN INC(NreservePagesUsed) END
  1205. END
  1206. END;
  1207. sp := MinSP; (* will be written to NodeSP below *)
  1208. SYSTEM.PUT (newAdr + NodeNext, next);
  1209. SYSTEM.PUT (newAdr + NodePrev, pageStackAdr);
  1210. pageStackAdr := newAdr
  1211. END
  1212. END;
  1213. (* push entry on current stack *)
  1214. SYSTEM.PUT (pageStackAdr + sp, physAdr);
  1215. SYSTEM.PUT (pageStackAdr + NodeSP, sp + AddressSize);
  1216. INC(freeHighPages)
  1217. END DisposePage;
  1218. (* Allocate virtual address space for mapping. Parameter size must be multiple of page size. Parameter virtAdr returns virtual address or NilAdr on failure. *)
  1219. PROCEDURE NewVirtual(VAR virtAdr: ADDRESS; size: SIZE);
  1220. BEGIN
  1221. ASSERT(size MOD PS = 0);
  1222. IF mapTop+size > MapAreaAdr+MapAreaSize THEN
  1223. virtAdr := NilAdr (* out of virtual space *)
  1224. ELSE
  1225. virtAdr := mapTop;
  1226. INC(mapTop, size)
  1227. END
  1228. END NewVirtual;
  1229. PROCEDURE DisposeVirtual(virtAdr: ADDRESS; size: SIZE);
  1230. (* to do *)
  1231. END DisposeVirtual;
  1232. (* Map a physical page into the virtual address space. Parameter virtAdr is mapped address and phys is mapping value. Returns TRUE iff mapping successful. *)
  1233. PROCEDURE MapPage(virtAdr, phys: ADDRESS): BOOLEAN;
  1234. VAR i, pt: ADDRESS;
  1235. BEGIN
  1236. i := virtAdr DIV RS MOD PTEs;
  1237. SYSTEM.GET (kernelPD + AddressSize*i, pt);
  1238. IF ODD(pt) THEN (* pt present *)
  1239. DEC(pt, pt MOD PS)
  1240. ELSE
  1241. NewPage(pt);
  1242. IF pt = NilAdr THEN RETURN FALSE END;
  1243. SYSTEM.PUT (kernelPD + AddressSize*i, pt + UserPage);
  1244. Fill32(pt, PTEs*AddressSize, PageNotPresent)
  1245. END;
  1246. SYSTEM.PUT (pt + AddressSize*(virtAdr DIV PS MOD PTEs), phys);
  1247. RETURN TRUE
  1248. END MapPage;
  1249. (* Return mapped page address for a given virtual address (ODD if mapped) *)
  1250. PROCEDURE MappedPage(virtAdr: ADDRESS): ADDRESS;
  1251. VAR pt: ADDRESS;
  1252. BEGIN
  1253. SYSTEM.GET (kernelPD + AddressSize*(virtAdr DIV RS MOD PTEs), pt);
  1254. IF ODD(pt) THEN (* pt present *)
  1255. SYSTEM.GET (pt - pt MOD PS + AddressSize*(virtAdr DIV PS MOD PTEs), pt);
  1256. RETURN pt
  1257. ELSE
  1258. RETURN 0 (* ~ODD *)
  1259. END
  1260. END MappedPage;
  1261. (* Unmap a page and return the previous mapping, like MappedPage (). Caller must flush TLB. *)
  1262. PROCEDURE UnmapPage(virtAdr: ADDRESS): ADDRESS;
  1263. VAR t, pt: ADDRESS;
  1264. BEGIN
  1265. SYSTEM.GET (kernelPD + AddressSize*(virtAdr DIV RS MOD PTEs), pt);
  1266. IF ODD(pt) THEN (* pt present *)
  1267. pt := pt - pt MOD PS + AddressSize*(virtAdr DIV PS MOD PTEs);
  1268. SYSTEM.GET (pt, t);
  1269. SYSTEM.PUT (pt, NIL); (* unmap *)
  1270. (* could use INVLPG here, but it is not supported equally on all processors *)
  1271. RETURN t
  1272. ELSE
  1273. RETURN 0 (* ~ODD *)
  1274. END
  1275. END UnmapPage;
  1276. (* Map area [virtAdr..virtAdr+size) directly to area [Adr(phys)..Adr(phys)+size). Returns TRUE iff successful. *)
  1277. PROCEDURE MapDirect(virtAdr: ADDRESS; size: SIZE; phys: ADDRESS): BOOLEAN;
  1278. BEGIN
  1279. (*
  1280. Trace.String("MapDirect "); Trace.Address (virtAdr); Trace.Char(' '); Trace.Address (phys); Trace.Char (' '); Trace.Int (size, 0);
  1281. Trace.Int(size DIV PS, 8); Trace.Ln;
  1282. *)
  1283. ASSERT((virtAdr MOD PS = 0) & (size MOD PS = 0));
  1284. WHILE size # 0 DO
  1285. IF ~ODD(MappedPage(virtAdr)) THEN
  1286. IF ~MapPage(virtAdr, phys) THEN RETURN FALSE END
  1287. END;
  1288. INC(virtAdr, PS); INC(phys, PS); DEC(size, PS)
  1289. END;
  1290. RETURN TRUE
  1291. END MapDirect;
  1292. (* Policy decision for heap expansion. NewBlock for the same block has failed try times. *)
  1293. PROCEDURE ExpandNow(try: LONGINT): BOOLEAN;
  1294. VAR size: SIZE;
  1295. BEGIN
  1296. size := LSH(memBlockTail.endBlockAdr - memBlockHead.beginBlockAdr, -10); (* heap size in KB *)
  1297. RETURN (~ODD(try) OR (size < heapMinKB)) & (size < heapMaxKB)
  1298. END ExpandNow;
  1299. (* Try to expand the heap by at least "size" bytes *)
  1300. PROCEDURE ExpandHeap*(try: LONGINT; size: SIZE; VAR memBlock: MemoryBlock; VAR beginBlockAdr, endBlockAdr: ADDRESS);
  1301. BEGIN
  1302. IF ExpandNow(try) THEN
  1303. IF size < expandMin THEN size := expandMin END;
  1304. beginBlockAdr := memBlockHead.endBlockAdr;
  1305. endBlockAdr := beginBlockAdr;
  1306. INC(endBlockAdr, size);
  1307. SetHeapEndAdr(endBlockAdr); (* in/out parameter *)
  1308. memBlock := memBlockHead;
  1309. (* endBlockAdr of memory block is set by caller after free block has been set in memory block - this process is part of lock-free heap expansion *)
  1310. ELSE
  1311. beginBlockAdr := memBlockHead.endBlockAdr;
  1312. endBlockAdr := memBlockHead.endBlockAdr;
  1313. memBlock := NIL
  1314. END
  1315. END ExpandHeap;
  1316. (* Set memory block end address *)
  1317. PROCEDURE SetMemoryBlockEndAddress*(memBlock: MemoryBlock; endBlockAdr: ADDRESS);
  1318. BEGIN
  1319. ASSERT(endBlockAdr >= memBlock.beginBlockAdr);
  1320. memBlock.endBlockAdr := endBlockAdr
  1321. END SetMemoryBlockEndAddress;
  1322. (* Free unused memory block *)
  1323. PROCEDURE FreeMemBlock*(memBlock: MemoryBlock);
  1324. BEGIN
  1325. HALT(515) (* impossible to free heap in I386 native A2 version *)
  1326. END FreeMemBlock;
  1327. (** Attempt to set the heap end address to the specified address. The returned value is the actual new end address (never smaller than previous value). *)
  1328. PROCEDURE SetHeapEndAdr(VAR endAdr: ADDRESS);
  1329. VAR n, m: SIZE;
  1330. BEGIN
  1331. Acquire(Memory);
  1332. n := LSH(endAdr+(PS-1), -PSlog2) - LSH(heapEndAdr, -PSlog2); (* pages requested *)
  1333. m := LSH(pageHeapAdr, -PSlog2) - LSH(heapEndAdr, -PSlog2) - ReservedPages; (* max pages *)
  1334. IF n > m THEN n := m END;
  1335. IF n > 0 THEN INC(heapEndAdr, n*PS); DEC(freeHighPages, n) END;
  1336. endAdr := heapEndAdr;
  1337. Release(Memory)
  1338. END SetHeapEndAdr;
  1339. (** Map a physical memory area (physAdr..physAdr+size-1) into the virtual address space. Parameter virtAdr returns the virtual address of mapped region, or NilAdr on failure. *)
  1340. PROCEDURE MapPhysical*(physAdr: ADDRESS; size: SIZE; VAR virtAdr: ADDRESS);
  1341. VAR ofs: ADDRESS;
  1342. BEGIN
  1343. IF (LSH(physAdr, -PSlog2) <= topPageNum) &
  1344. (LSH(physAdr+size-1, -PSlog2) <= topPageNum) &
  1345. (LSH(physAdr, -PSlog2) >= LSH(LowAdr, -PSlog2)) THEN
  1346. virtAdr := physAdr (* directly mapped *)
  1347. ELSE
  1348. ofs := physAdr MOD PS;
  1349. DEC(physAdr, ofs); INC(size, ofs); (* align start to page boundary *)
  1350. INC(size, (-size) MOD PS); (* align end to page boundary *)
  1351. Acquire(Memory);
  1352. NewVirtual(virtAdr, size);
  1353. IF virtAdr # NilAdr THEN
  1354. IF ~MapDirect(virtAdr, size, physAdr + UserPage) THEN
  1355. DisposeVirtual(virtAdr, size);
  1356. virtAdr := NilAdr
  1357. END
  1358. END;
  1359. Release(Memory);
  1360. IF TraceVerbose THEN
  1361. Acquire (TraceOutput);
  1362. Trace.String("Mapping ");
  1363. Trace.IntSuffix(size, 1, "B"); Trace.String(" at ");
  1364. Trace.Address (physAdr); Trace.String (" - "); Trace.Address (physAdr+size-1);
  1365. IF virtAdr = NilAdr THEN
  1366. Trace.String(" failed")
  1367. ELSE
  1368. Trace.String (" to "); Trace.Address (virtAdr);
  1369. IF ofs # 0 THEN Trace.String (", offset "); Trace.Int(ofs, 0) END
  1370. END;
  1371. Trace.Ln;
  1372. Release (TraceOutput);
  1373. END;
  1374. IF virtAdr # NilAdr THEN INC(virtAdr, ofs) END (* adapt virtual address to correct offset *)
  1375. END
  1376. END MapPhysical;
  1377. (** Unmap an area previously mapped with MapPhysical. *)
  1378. PROCEDURE UnmapPhysical*(virtAdr: ADDRESS; size: SIZE);
  1379. (* to do *)
  1380. END UnmapPhysical;
  1381. (** Return the physical address of the specified range of memory, or NilAdr if the range is not contiguous. It is the caller's responsibility to assure the range remains allocated during the time it is in use. *)
  1382. PROCEDURE PhysicalAdr*(adr: ADDRESS; size: SIZE): ADDRESS;
  1383. VAR physAdr, mapped, expected: ADDRESS;
  1384. BEGIN
  1385. IF (LSH(adr, -PSlog2) <= topPageNum) & (LSH(adr+size-1, -PSlog2) <= topPageNum) THEN
  1386. RETURN adr (* directly mapped *)
  1387. ELSE
  1388. Acquire(Memory);
  1389. mapped := MappedPage(adr);
  1390. Release(Memory);
  1391. IF ODD(mapped) & (size > 0) THEN (* mapped, and range not empty or too big *)
  1392. physAdr := mapped - mapped MOD PS + adr MOD PS; (* strip paging bits and add page offset *)
  1393. (* now check if whole range is physically contiguous *)
  1394. DEC(size, PS - adr MOD PS); (* subtract distance to current page end *)
  1395. IF size > 0 THEN (* range crosses current page end *)
  1396. expected := LSH(mapped, -PSlog2)+1; (* expected physical page *)
  1397. LOOP
  1398. INC(adr, PS); (* step to next page *)
  1399. Acquire(Memory);
  1400. mapped := MappedPage(adr);
  1401. Release(Memory);
  1402. IF ~ODD(mapped) OR (LSH(mapped, -PSlog2) # expected) THEN
  1403. physAdr := NilAdr; EXIT
  1404. END;
  1405. DEC(size, PS);
  1406. IF size <= 0 THEN EXIT END; (* ok *)
  1407. INC(expected)
  1408. END
  1409. ELSE
  1410. (* ok, skip *)
  1411. END
  1412. ELSE
  1413. physAdr := NilAdr
  1414. END;
  1415. RETURN physAdr
  1416. END
  1417. END PhysicalAdr;
  1418. (** Translate a virtual address range to num ranges of physical address. num returns 0 on error. *)
  1419. PROCEDURE TranslateVirtual*(virtAdr: ADDRESS; size: SIZE; VAR num: LONGINT; VAR physAdr: ARRAY OF Range);
  1420. VAR ofs, phys1: ADDRESS; size1: SIZE;
  1421. BEGIN
  1422. Acquire(Memory);
  1423. num := 0;
  1424. LOOP
  1425. IF size = 0 THEN EXIT END;
  1426. IF num = LEN(physAdr) THEN num := 0; EXIT END; (* index check *)
  1427. ofs := virtAdr MOD PS; (* offset in page *)
  1428. size1 := PS - ofs; (* distance to next page boundary *)
  1429. IF size1 > size THEN size1 := size END;
  1430. phys1 := MappedPage(virtAdr);
  1431. IF ~ODD(phys1) THEN num := 0; EXIT END; (* page not present *)
  1432. physAdr[num].adr := phys1 - phys1 MOD PS + ofs;
  1433. physAdr[num].size := size1; INC(num);
  1434. INC(virtAdr, size1); DEC(size, size1)
  1435. END;
  1436. IF num = 0 THEN physAdr[0].adr := NilAdr; physAdr[0].size := 0 END;
  1437. Release(Memory)
  1438. END TranslateVirtual;
  1439. (** Return information on free memory in Kbytes. *)
  1440. PROCEDURE GetFreeK*(VAR total, lowFree, highFree: SIZE);
  1441. CONST KperPage = PS DIV 1024;
  1442. BEGIN
  1443. Acquire(Memory);
  1444. total := totalPages * KperPage;
  1445. lowFree := freeLowPages * KperPage;
  1446. highFree := freeHighPages * KperPage;
  1447. Release(Memory)
  1448. END GetFreeK;
  1449. (** -- Stack -- *)
  1450. (** Extend the stack to include the specified address, if possible. Returns TRUE iff ok. *)
  1451. PROCEDURE ExtendStack*(VAR s: Stack; virtAdr: ADDRESS): BOOLEAN;
  1452. VAR phys: ADDRESS; ok: BOOLEAN;
  1453. BEGIN
  1454. Acquire(Memory);
  1455. ok := FALSE;
  1456. IF (virtAdr < s.high) & (virtAdr >= s.low) THEN
  1457. DEC(virtAdr, virtAdr MOD PS); (* round down to page boundary *)
  1458. IF Stats & (virtAdr < s.adr-PS) THEN INC(Nbigskips) END;
  1459. IF ODD(MappedPage(virtAdr)) THEN (* already mapped *)
  1460. ok := TRUE
  1461. ELSE
  1462. NewPage(phys);
  1463. IF phys # NilAdr THEN
  1464. IF MapPage(virtAdr, phys + UserPage) THEN
  1465. IF virtAdr < s.adr THEN
  1466. s.adr := virtAdr
  1467. ELSE
  1468. IF Stats THEN INC(Nfilled) END
  1469. END;
  1470. ok := TRUE
  1471. ELSE
  1472. DisposePage(phys)
  1473. END
  1474. END
  1475. END
  1476. END;
  1477. Release(Memory);
  1478. RETURN ok
  1479. END ExtendStack;
  1480. (** Allocate a stack. Parameter initSP returns initial stack pointer value. *)
  1481. PROCEDURE NewStack*(VAR s: Stack; process: ANY; VAR initSP: ADDRESS);
  1482. VAR adr, phys: ADDRESS; old: LONGINT; free: SET;
  1483. BEGIN
  1484. ASSERT(InitUserStackSize = PS); (* for now *)
  1485. Acquire(Memory);
  1486. IF Stats THEN INC(NnewStacks) END;
  1487. old := freeStackIndex;
  1488. LOOP
  1489. IF Stats THEN INC(NnewStackLoops) END;
  1490. free := freeStack[freeStackIndex];
  1491. IF free # {} THEN
  1492. adr := 0; WHILE ~(adr IN free) DO INC(adr) END; (* BTW: BSF instruction is not faster *)
  1493. IF Stats THEN INC(NnewStackInnerLoops, adr+1) END;
  1494. EXCL(freeStack[freeStackIndex], SIZE (adr));
  1495. adr := StackAreaAdr + (freeStackIndex*SetSize + adr)*MaxUserStackSize;
  1496. EXIT
  1497. END;
  1498. INC(freeStackIndex);
  1499. IF freeStackIndex = LEN(freeStack) THEN freeStackIndex := 0 END;
  1500. IF freeStackIndex = old THEN HALT(1503) END (* out of stack space *)
  1501. END;
  1502. NewPage(phys); ASSERT(phys # NilAdr); (* allocate one physical page at first *)
  1503. s.high := adr + MaxUserStackSize; s.low := adr + UserStackGuardSize;
  1504. s.adr := s.high - InitUserStackSize; (* at the top of the virtual area *)
  1505. initSP := s.high-AddressSize;
  1506. IF ~MapPage(s.adr, phys + UserPage) THEN HALT(99) END;
  1507. SYSTEM.PUT (initSP, process);
  1508. Release(Memory)
  1509. END NewStack;
  1510. (** Return the process pointer set when the current user stack was created (must be running on user stack). *)
  1511. PROCEDURE -GetProcessPtr* (): ANY;
  1512. CONST Mask = -MaxUserStackSize; Ofs = MaxUserStackSize-4;
  1513. CODE {SYSTEM.i386}
  1514. MOV EAX, Mask
  1515. AND EAX, ESP
  1516. MOV EAX, [EAX+Ofs]
  1517. POP EBX; pointer returned via stack!
  1518. MOV [EBX], EAX
  1519. END GetProcessPtr;
  1520. (** True iff current process works on a kernel stack *)
  1521. PROCEDURE WorkingOnKernelStack* (): BOOLEAN;
  1522. VAR id: LONGINT; sp: ADDRESS;
  1523. BEGIN
  1524. ASSERT(KernelStackSize # MaxUserStackSize - UserStackGuardSize); (* detection does only work with this assumption *)
  1525. sp := CurrentSP ();
  1526. id := ID ();
  1527. RETURN (sp >= procm[id].stack.low) & (sp <= procm[id].stack.high)
  1528. END WorkingOnKernelStack;
  1529. (** Deallocate a stack. Current thread should not dispose its own stack. Uses privileged instructions. *)
  1530. PROCEDURE DisposeStack*(CONST s: Stack);
  1531. VAR adr, phys: ADDRESS;
  1532. BEGIN
  1533. (* First make sure there are no references to virtual addresses of the old stack in the TLBs. This is required because we are freeing the pages, and they could be remapped later at different virtual addresses. DisposeStack will only be called from the thread finalizer, which ensures that the user will no longer be referencing this memory. Therefore we can make this upcall from outside the locked region, avoiding potential deadlock. *)
  1534. GlobalFlushTLB; (* finalizers are only called after Processors has initialized this upcall *)
  1535. Acquire(Memory);
  1536. IF Stats THEN INC(NdisposeStacks) END;
  1537. adr := s.adr; (* unmap and deallocate all pages of stack *)
  1538. REPEAT
  1539. phys := UnmapPage(adr); (* TLB was flushed and no intermediate references possible to unreachable stack *)
  1540. IF ODD(phys) THEN DisposePage(phys - phys MOD PS) END;
  1541. INC(adr, PS)
  1542. UNTIL adr = s.high;
  1543. adr := (adr - MaxUserStackSize - StackAreaAdr) DIV MaxUserStackSize;
  1544. INCL(freeStack[adr DIV 32], SIZE(adr MOD 32));
  1545. Release(Memory)
  1546. END DisposeStack;
  1547. (** Check if the specified stack is valid. *)
  1548. PROCEDURE ValidStack*(CONST s: Stack; sp: ADDRESS): BOOLEAN;
  1549. VAR valid: BOOLEAN;
  1550. BEGIN
  1551. Acquire(Memory);
  1552. valid := (sp MOD 4 = 0) & (sp >= s.adr) & (sp <= s.high);
  1553. WHILE valid & (sp < s.high) DO
  1554. valid := ODD(MappedPage(sp));
  1555. INC(sp, PS)
  1556. END;
  1557. Release(Memory);
  1558. RETURN valid
  1559. END ValidStack;
  1560. (** Update the stack snapshot of the current processor. (for Processors) *)
  1561. PROCEDURE UpdateState*;
  1562. VAR id: LONGINT;
  1563. BEGIN
  1564. ASSERT(CS () MOD 4 = 0); (* to get kernel stack pointer *)
  1565. id := ID ();
  1566. ASSERT(procm[id].stack.high # 0); (* current processor stack has been assigned *)
  1567. procm[id].sp := CurrentBP () (* instead of ESP, just fetch EBP of current procedure (does not contain pointers) *)
  1568. END UpdateState;
  1569. (** Get kernel stack regions for garbage collection. (for Heaps) *)
  1570. PROCEDURE GetKernelStacks*(VAR stack: ARRAY OF Stack);
  1571. VAR i: LONGINT;
  1572. BEGIN (* {UpdateState has been called by each processor} *)
  1573. FOR i := 0 TO MaxCPU-1 DO
  1574. stack[i].adr := procm[i].sp;
  1575. stack[i].high := procm[i].stack.high
  1576. END
  1577. END GetKernelStacks;
  1578. (* Init page tables (paging still disabled until EnableMM is called). *)
  1579. PROCEDURE InitPages;
  1580. VAR i, j, phys, lTop, mTop: ADDRESS;
  1581. BEGIN
  1582. (* get top of high and low memory *)
  1583. mTop := memTop;
  1584. DEC(mTop, mTop MOD PS); (* mTop MOD PS = 0 *)
  1585. topPageNum := LSH(mTop-1, -PSlog2);
  1586. lTop := lowTop;
  1587. DEC(lTop, lTop MOD PS); (* lTop MOD PS = 0 *)
  1588. (* initialize NewDirectPage and SetHeapEndAdr (get kernel range) *)
  1589. SYSTEM.GET (LinkAdr + EndBlockOfs, heapEndAdr);
  1590. (* ug *) (*
  1591. SYSTEM.PUT (heapEndAdr, NIL); (* set tag to NIL *)
  1592. INC(heapEndAdr, AddressSize); (* space for NIL *)
  1593. *)
  1594. (* ug: not needed, extension of heap done in GetStaticHeap anyway
  1595. INC(heapEndAdr, K); (* space for free heap block descriptor of type Heaps.HeapBlockDesc at heapEndAdr, initialization is done in Heaps *)
  1596. INC(heapEndAdr, (-heapEndAdr) MOD PS); (* round up to page size *)
  1597. *)
  1598. pageHeapAdr := mTop;
  1599. freeHighPages := LSH(pageHeapAdr, -PSlog2) - LSH(heapEndAdr, -PSlog2);
  1600. IF TraceVerbose THEN
  1601. Trace.String("Kernel: "); Trace.Address (LinkAdr); Trace.String(" .. ");
  1602. Trace.Address (heapEndAdr-1); Trace.Ln;
  1603. Trace.String ("High: "); Trace.Address (heapEndAdr); Trace.String(" .. ");
  1604. Trace.Address (pageHeapAdr-1); Trace.String(" = "); Trace.Int (freeHighPages, 0);
  1605. Trace.StringLn (" free pages")
  1606. END;
  1607. (* initialize empty free page stack *)
  1608. NewDirectPage(pageStackAdr); ASSERT(pageStackAdr # NilAdr);
  1609. SYSTEM.PUT (pageStackAdr+NodeSP, SYSTEM.VAL (ADDRESS, MinSP));
  1610. SYSTEM.PUT (pageStackAdr+NodeNext, SYSTEM.VAL (ADDRESS, NilAdr));
  1611. SYSTEM.PUT (pageStackAdr+NodePrev, SYSTEM.VAL (ADDRESS, NilAdr));
  1612. (* free low pages *)
  1613. freeLowPage := NilAdr; freeLowPages := 0;
  1614. i := lTop DIV PS; j := LowAdr DIV PS;
  1615. IF TraceVerbose THEN
  1616. Trace.String("Low: "); Trace.Address (j*PS); Trace.String (".."); Trace.Address (i*PS-1)
  1617. END;
  1618. REPEAT
  1619. DEC(i); phys := i*PS;
  1620. SYSTEM.PUT (phys, freeLowPage); (* phys.next := freeLowPage *)
  1621. freeLowPage := phys; INC(freeLowPages)
  1622. UNTIL i = j;
  1623. IF TraceVerbose THEN
  1624. Trace.String(" = "); Trace.Int(freeLowPages, 1); Trace.StringLn (" free pages")
  1625. END;
  1626. totalPages := LSH(memTop - M + lowTop + dmaSize + PS, -PSlog2); (* what BIOS gave us *)
  1627. (* stacks *)
  1628. ASSERT((StackAreaAdr MOD MaxUserStackSize = 0) & (StackAreaSize MOD MaxUserStackSize = 0));
  1629. FOR i := 0 TO LEN(freeStack)-1 DO freeStack[i] := {0..SetSize-1} END;
  1630. FOR i := MaxUserStacks TO LEN(freeStack)*SetSize-1 DO EXCL(freeStack[i DIV SetSize], SIZE (i MOD SetSize)) END;
  1631. freeStackIndex := 0;
  1632. (* mappings *)
  1633. mapTop := MapAreaAdr;
  1634. (* create the address space *)
  1635. NewPage(kernelPD); ASSERT(kernelPD # NilAdr);
  1636. Fill32(kernelPD, PTEs*4, PageNotPresent);
  1637. IF ~MapDirect(LowAdr, mTop-LowAdr, LowAdr + UserPage) THEN HALT(99) END (* map heap direct *)
  1638. END InitPages;
  1639. (* Generate a memory segment descriptor. type IN {0..7} & dpl IN {0..3}.
  1640. type
  1641. 0 data, expand-up, read-only
  1642. 1 data, expand-up, read-write
  1643. 2 data, expand-down, read-only
  1644. 3 data, expand-down, read-write
  1645. 4 code, non-conforming, execute-only
  1646. 5 code, non-conforming, execute-read
  1647. 6 code, conforming, execute-only
  1648. 7 code, conforming, execute-read
  1649. *)
  1650. PROCEDURE GenMemSegDesc(type, base, limit, dpl: LONGINT; page: BOOLEAN; VAR sd: SegDesc);
  1651. VAR s: SET;
  1652. BEGIN
  1653. sd.low := ASH(base MOD 10000H, 16) + limit MOD 10000H;
  1654. s := SYSTEM.VAL (SET, ASH(ASH(base, -24), 24) + ASH(ASH(limit, -16), 16) +
  1655. ASH(dpl, 13) + ASH(type, 9) + ASH(base, -16) MOD 100H);
  1656. s := s + {12, 15, 22}; (* code/data=1, present=1, 32-bit=1, A=0, AVL=0 *)
  1657. IF page THEN INCL(s, 23) END; (* page granularity *)
  1658. sd.high := SYSTEM.VAL (LONGINT, s)
  1659. END GenMemSegDesc;
  1660. (* Generate a TSS descriptor. *)
  1661. PROCEDURE GenTSSDesc(base: ADDRESS; limit, dpl: LONGINT; VAR sd: SegDesc);
  1662. VAR s: SET;
  1663. BEGIN
  1664. sd.low := ASH(base MOD 10000H, 16) + limit MOD 10000H;
  1665. s := SYSTEM.VAL (SET, ASH(ASH(base, -24), 24) + ASH(ASH(limit, -16), 16) +
  1666. ASH(dpl, 13) + ASH(base, -16) MOD 100H);
  1667. s := s + {8, 11, 15}; (* type=non-busy TSS, present=1, AVL=0, 32-bit=0 *)
  1668. sd.high := SYSTEM.VAL (LONGINT, s)
  1669. END GenTSSDesc;
  1670. (* Initialize segmentation. *)
  1671. PROCEDURE InitSegments;
  1672. VAR i: LONGINT;
  1673. BEGIN
  1674. (* GDT 0: Null segment *)
  1675. gdt[0].low := 0; gdt[0].high := 0;
  1676. (* GDT 1: Kernel code: non-conforming, execute-read, base 0, limit 4G, PL 0 *)
  1677. GenMemSegDesc(5, 0, M-1, 0, TRUE, gdt[1]);
  1678. (* GDT 2: Kernel stack: expand-up, read-write, base 0, limit 4G, PL 0 *)
  1679. GenMemSegDesc(1, 0, M-1, 0, TRUE, gdt[2]);
  1680. (* GDT 3: User code: conforming, execute-read, base 0, limit 4G, PL 0 *)
  1681. GenMemSegDesc(7, 0, M-1, 0, TRUE, gdt[3]);
  1682. (* GDT 4: User/Kernel data: expand-up, read-write, base 0, limit 4G, PL 3 *)
  1683. GenMemSegDesc(1, 0, M-1, 3, TRUE, gdt[4]);
  1684. (* GDT 5: User stack: expand-down, read-write, base 0, limit 1M, PL 3 *)
  1685. GenMemSegDesc(3, 0, M DIV PS, 3, TRUE, gdt[5]);
  1686. (* GDT TSSOfs..n: Kernel TSS *)
  1687. FOR i := 0 TO MaxCPU-1 DO
  1688. GenTSSDesc(ADDRESSOF(procm[i].tss), SIZEOF(TSSDesc)-1, 0, gdt[TSSOfs+i]);
  1689. procm[i].sp := 0; procm[i].stack.high := 0
  1690. END
  1691. END InitSegments;
  1692. (* Enable segmentation on the current processor. *)
  1693. PROCEDURE EnableSegments;
  1694. BEGIN
  1695. LoadGDT(ADDRESSOF(gdt[0]), SIZEOF(GDT)-1);
  1696. LoadSegRegs(DataSel)
  1697. END EnableSegments;
  1698. (* Allocate a kernel stack. *)
  1699. PROCEDURE NewKernelStack(VAR stack: Stack);
  1700. VAR phys, virt: ADDRESS; size: SIZE;
  1701. BEGIN
  1702. size := KernelStackSize;
  1703. NewVirtual(virt, size + PS); (* add one page for overflow protection *)
  1704. ASSERT(virt # NilAdr, 1502);
  1705. INC(virt, PS); (* leave page open at bottom *)
  1706. stack.low := virt;
  1707. stack.adr := virt; (* return stack *)
  1708. REPEAT
  1709. NewPage(phys); ASSERT(phys # NilAdr);
  1710. IF ~MapPage(virt, phys + KernelPage) THEN HALT(99) END;
  1711. DEC(size, PS); INC(virt, PS)
  1712. UNTIL size = 0;
  1713. stack.high := virt
  1714. END NewKernelStack;
  1715. (* Set task register *)
  1716. PROCEDURE -SetTR(tr: ADDRESS);
  1717. CODE {SYSTEM.i386, SYSTEM.Privileged}
  1718. POP EAX
  1719. LTR AX
  1720. END SetTR;
  1721. (* Enable memory management and switch to new stack in virtual space.
  1722. Stack layout:
  1723. caller1 return
  1724. caller1 EBP <-- caller0 EBP
  1725. [caller0 locals]
  1726. 04 caller0 return
  1727. 00 caller0 EBP <-- EBP
  1728. locals <-- ESP
  1729. *)
  1730. PROCEDURE -EnableMM(pd, esp: ADDRESS);
  1731. CODE {SYSTEM.Pentium, SYSTEM.Privileged}
  1732. POP EBX ; esp
  1733. POP EAX ; pd
  1734. MOV CR3, EAX ; page directory ptr
  1735. MOV ECX, [EBP+4] ; caller0 return
  1736. MOV EDX, [EBP] ; caller0 EBP
  1737. MOV EDX, [EDX+4] ; caller1 return
  1738. MOV EAX, CR0
  1739. OR EAX, 80000000H ; set PG bit
  1740. MOV CR0, EAX ; enable virtual addressing (old stack no longer usable)
  1741. JMP 0 ; flush queue
  1742. WBINVD
  1743. MOV DWORD [EBX-4], 0 ; not UserStackSel (cf. GetUserStack)
  1744. MOV [EBX-8], EDX ; caller1 return on new stack
  1745. MOV DWORD [EBX-12], 0 ; caller1 EBP on new stack
  1746. LEA EBP, [EBX-12] ; new stack top
  1747. MOV ESP, EBP
  1748. JMP ECX ; back to caller0 (whose locals are now inaccessible!)
  1749. END EnableMM;
  1750. (** -- Initialization -- *)
  1751. (** Initialize memory management.
  1752. o every processor calls this once during initialization
  1753. o mutual exclusion with other processors must be guaranteed by the caller
  1754. o interrupts must be off
  1755. o segmentation and paging is enabled
  1756. o return is on the new stack => caller must have no local variables
  1757. *)
  1758. PROCEDURE InitMemory*;
  1759. VAR id: LONGINT;
  1760. BEGIN
  1761. EnableSegments;
  1762. (* allocate stack *)
  1763. id := ID ();
  1764. NewKernelStack(procm[id].stack);
  1765. procm[id].sp := 0;
  1766. (* initialize TSS *)
  1767. Fill32(ADDRESSOF(procm[id].tss), SIZEOF(TSSDesc), 0);
  1768. procm[id].tss.ESP0 := procm[id].stack.high; (* kernel stack org *)
  1769. procm[id].tss.ESS0 := KernelStackSel;
  1770. procm[id].tss.IOBitmapOffset := -1; (* no bitmap *)
  1771. (* enable paging and switch stack *)
  1772. SetTR(KernelTR + id*8);
  1773. EnableMM(kernelPD, procm[id].tss.ESP0)
  1774. END InitMemory;
  1775. (** Initialize a boot page for MP booting. Parameter physAdr returns the physical address of a low page. *)
  1776. PROCEDURE InitBootPage*(start: Startup; VAR physAdr: ADDRESS);
  1777. CONST BootOfs = 800H;
  1778. VAR adr, a: ADDRESS;
  1779. BEGIN
  1780. Acquire(Memory);
  1781. NewLowPage(physAdr);
  1782. Release(Memory);
  1783. ASSERT((physAdr # NilAdr) & (physAdr >= 0) & (physAdr < M) & (physAdr MOD PS = 0));
  1784. adr := physAdr + BootOfs;
  1785. a := adr; (* from SMP.Asm - generated with BinToCode.Kernel smp.bin 800H *)
  1786. SYSTEM.PUT32(a, 0100012EBH); INC(a, 4); SYSTEM.PUT32(a, 000080000H); INC(a, 4);
  1787. SYSTEM.PUT32(a, 000000000H); INC(a, 4); SYSTEM.PUT32(a, 000170000H); INC(a, 4);
  1788. SYSTEM.PUT32(a, 000000000H); INC(a, 4); SYSTEM.PUT32(a, 0010F2EFAH); INC(a, 4);
  1789. SYSTEM.PUT32(a, 02E08081EH); INC(a, 4); SYSTEM.PUT32(a, 00E16010FH); INC(a, 4);
  1790. SYSTEM.PUT32(a, 0E0010F08H); INC(a, 4); SYSTEM.PUT32(a, 0010F010CH); INC(a, 4);
  1791. SYSTEM.PUT32(a, 0B800EBF0H); INC(a, 4); SYSTEM.PUT32(a, 0D08E0010H); INC(a, 4);
  1792. SYSTEM.PUT32(a, 0C08ED88EH); INC(a, 4); SYSTEM.PUT32(a, 00800BC66H); INC(a, 4);
  1793. SYSTEM.PUT32(a, 033660000H); INC(a, 4); SYSTEM.PUT32(a, 0FF2E66C0H); INC(a, 4);
  1794. SYSTEM.PUT32(a, 09008022EH); INC(a, 4);
  1795. (* these offsets are from the last two dwords in SMP.Asm *)
  1796. SYSTEM.PUT32(adr+2, SYSTEM.VAL (LONGINT, start)); (* not a method *)
  1797. SYSTEM.PUT32(adr+16, ADDRESSOF(gdt[0]));
  1798. (* jump at start *)
  1799. SYSTEM.PUT8(physAdr, 0EAX); (* jmp far *)
  1800. SYSTEM.PUT32(physAdr + 1, ASH(physAdr, 16-4) + BootOfs) (* seg:ofs *)
  1801. END InitBootPage;
  1802. (** The BP in a MP system calls this to map the APIC physical address directly. *)
  1803. PROCEDURE InitAPICArea*(adr: ADDRESS; size: SIZE);
  1804. BEGIN
  1805. ASSERT((size = PS) & (adr >= IntelAreaAdr) & (adr+size-1 < IntelAreaAdr+IntelAreaSize));
  1806. IF ~MapDirect(adr, size, adr + UserPage) THEN HALT(99) END
  1807. END InitAPICArea;
  1808. (* Set machine-dependent parameters gcThreshold, expandMin, heapMinKB and heapMaxKB *)
  1809. PROCEDURE SetGCParams*;
  1810. VAR size, t: SIZE;
  1811. BEGIN
  1812. GetFreeK(size, t, t); (* size is total memory size in KB *)
  1813. heapMinKB := size * HeapMin DIV 100;
  1814. heapMaxKB := size * HeapMax DIV 100;
  1815. expandMin := size * ExpandRate DIV 100 * 1024;
  1816. IF expandMin < 0 THEN expandMin := MAX(LONGINT) END;
  1817. gcThreshold := size * Threshold DIV 100 * 1024;
  1818. IF gcThreshold < 0 THEN gcThreshold := MAX(LONGINT) END
  1819. END SetGCParams;
  1820. (** Get first memory block and first free address, heap area in first memory block is automatically expanded to account for the first
  1821. few calls to NEW *)
  1822. PROCEDURE GetStaticHeap*(VAR beginBlockAdr, endBlockAdr, freeBlockAdr: ADDRESS);
  1823. VAR memBlockAdr, endAdr: ADDRESS;
  1824. BEGIN
  1825. SYSTEM.GET(LinkAdr + EndBlockOfs, freeBlockAdr);
  1826. ASSERT(freeBlockAdr MOD StaticBlockSize = 0);
  1827. memBlockAdr := LinkAdr + HeaderSize + MemoryBlockOfs;
  1828. memBlockHead := SYSTEM.VAL(MemoryBlock, memBlockAdr);
  1829. ASSERT(memBlockHead.beginBlockAdr MOD StaticBlockSize = 0);
  1830. endAdr := memBlockHead.endBlockAdr;
  1831. INC(endAdr, InitialHeapIncrement);
  1832. SetHeapEndAdr(endAdr); (* in/out parameter *)
  1833. memBlockHead.endBlockAdr := endAdr;
  1834. ASSERT((memBlockHead.endBlockAdr - memBlockHead.beginBlockAdr) MOD StaticBlockSize = 0);
  1835. memBlockTail := memBlockHead;
  1836. beginBlockAdr := memBlockHead.beginBlockAdr;
  1837. endBlockAdr := memBlockHead.endBlockAdr
  1838. END GetStaticHeap;
  1839. (* returns if an address is a currently allocated heap address *)
  1840. PROCEDURE ValidHeapAddress*(p: ADDRESS): BOOLEAN;
  1841. BEGIN
  1842. RETURN (p >= memBlockHead.beginBlockAdr) & (p <= memBlockTail.endBlockAdr)
  1843. END ValidHeapAddress;
  1844. (** Jump from kernel to user mode. Every processor calls this during initialization. *)
  1845. PROCEDURE JumpToUserLevel*(userEBP: ADDRESS);
  1846. CODE {SYSTEM.i386}
  1847. PUSH UserStackSel ; SS3
  1848. PUSH DWORD [EBP+userEBP] ; ESP3
  1849. PUSHFD ; EFLAGS3
  1850. PUSH UserCodeSel ; CS3
  1851. CALL L1 ; PUSH L1 (EIP3)
  1852. L1:
  1853. ADD DWORD [ESP], BYTE 5; adjust EIP3 to L2 (L2-L1 = 5)
  1854. IRETD ; switch to level 3 and continue at following instruction
  1855. L2:
  1856. POP EBP ; from level 3 stack (refer to Objects.NewProcess)
  1857. RET ; jump to body of first active object
  1858. END JumpToUserLevel;
  1859. PROCEDURE Ensure32BitAddress*(adr: ADDRESS): LONGINT;
  1860. BEGIN
  1861. RETURN adr
  1862. END Ensure32BitAddress;
  1863. PROCEDURE Is32BitAddress*(adr: ADDRESS): BOOLEAN;
  1864. BEGIN RETURN SYSTEM.VAL (Address32, adr) = adr;
  1865. END Is32BitAddress;
  1866. (**
  1867. * Flush Data Cache for the specified virtual address range. If len is negative, flushes the whole cache.
  1868. * This is used on some architecture to interact with DMA hardware (e.g. Ethernet and USB. It can be
  1869. * left empty on Intel architecture.
  1870. *)
  1871. PROCEDURE FlushDCacheRange * (adr: ADDRESS; len: LONGINT);
  1872. END FlushDCacheRange;
  1873. (**
  1874. * Invalidate Data Cache for the specified virtual address range. If len is negative, flushes the whole cache.
  1875. * This is used on some architecture to interact with DMA hardware (e.g. Ethernet and USB. It can be
  1876. * left empty on Intel architecture.
  1877. *)
  1878. PROCEDURE InvalidateDCacheRange * (adr: ADDRESS; len: LONGINT);
  1879. END InvalidateDCacheRange;
  1880. PROCEDURE DisableDCacheRange * (adr: ADDRESS; len: LONGINT);
  1881. END DisableDCacheRange;
  1882. PROCEDURE EnableDCacheRange * (adr: ADDRESS; len: LONGINT);
  1883. END EnableDCacheRange;
  1884. (**
  1885. * Invalidate Instruction Cache for the specified virtual address range. If len is negative, flushes the whole cache.
  1886. * This is used on some architecture to interact with DMA hardware (e.g. Ethernet and USB. It can be
  1887. * left empty on Intel architecture.
  1888. *)
  1889. PROCEDURE InvalidateICacheRange * (adr: ADDRESS; len: LONGINT);
  1890. END InvalidateICacheRange;
  1891. (* Unexpected - Default interrupt handler *)
  1892. PROCEDURE Unexpected(VAR state: State);
  1893. VAR int: LONGINT; isr, irr: CHAR;
  1894. BEGIN
  1895. int := state.INT;
  1896. IF HandleSpurious & ((int >= IRQ0) & (int <= MaxIRQ) OR (int = MPSPU)) THEN (* unexpected IRQ, get more info *)
  1897. IF (int >= IRQ8) & (int <= IRQ15) THEN
  1898. Portout8 (IntB0, 0BX); Portin8(IntB0, isr);
  1899. Portout8 (IntB0, 0AX); Portin8(IntB0, irr)
  1900. ELSIF (int >= IRQ0) & (int <= IRQ7) THEN
  1901. Portout8 (IntA0, 0BX); Portin8(IntA0, isr);
  1902. Portout8 (IntA0, 0AX); Portin8(IntA0, irr)
  1903. ELSE
  1904. isr := 0X; irr := 0X
  1905. END;
  1906. IF TraceSpurious THEN
  1907. Acquire (TraceOutput);
  1908. Trace.String("INT"); Trace.Int(int, 1);
  1909. Trace.Hex(ORD(isr), -3); Trace.Hex(ORD(irr), -2); Trace.Ln;
  1910. Release (TraceOutput);
  1911. END
  1912. ELSE
  1913. Acquire (TraceOutput);
  1914. Trace.StringLn ("Unexpected interrupt");
  1915. Trace.Memory(ADDRESSOF(state), SIZEOF(State)-4*4); (* exclude last 4 fields *)
  1916. IF int = 3 THEN (* was a HALT or ASSERT *)
  1917. (* It seems that no trap handler is installed (Traps not linked), so wait endlessly, while holding trace lock. This should quiten down the system, although other processors may possibly still run processes. *)
  1918. LOOP END
  1919. ELSE
  1920. Release (TraceOutput);
  1921. SetEAX(int);
  1922. HALT(1801) (* unexpected interrupt *)
  1923. END
  1924. END
  1925. END Unexpected;
  1926. (* InEnableIRQ - Enable a hardware interrupt (caller must hold module lock). *)
  1927. PROCEDURE -InEnableIRQ (int: LONGINT);
  1928. CODE {SYSTEM.i386}
  1929. POP EBX
  1930. CMP EBX, IRQ7
  1931. JG cont2
  1932. IN AL, IntA1
  1933. SUB EBX, IRQ0
  1934. BTR EAX, EBX
  1935. OUT IntA1, AL
  1936. JMP end
  1937. cont2:
  1938. IN AL, IntB1
  1939. SUB EBX, IRQ8
  1940. BTR EAX, EBX
  1941. OUT IntB1, AL
  1942. end:
  1943. END InEnableIRQ;
  1944. (* InDisableIRQ - Disable a hardware interrupt (caller must hold module lock). *)
  1945. PROCEDURE -InDisableIRQ (int: LONGINT);
  1946. CODE {SYSTEM.i386}
  1947. POP EBX
  1948. CMP EBX, IRQ7
  1949. JG cont2
  1950. IN AL, IntA1
  1951. SUB EBX, IRQ0
  1952. BTS EAX, EBX
  1953. OUT IntA1, AL
  1954. JMP end
  1955. cont2:
  1956. IN AL, IntB1
  1957. SUB EBX, IRQ8
  1958. BTS EAX, EBX
  1959. OUT IntB1, AL
  1960. end:
  1961. END InDisableIRQ;
  1962. (** EnableIRQ - Enable a hardware interrupt (also done automatically by InstallHandler). *)
  1963. PROCEDURE EnableIRQ* (int: LONGINT);
  1964. BEGIN
  1965. ASSERT((int >= IRQ0) & (int <= IRQ15) & (int # IRQ2));
  1966. Acquire(Interrupts); (* protect interrupt mask register *)
  1967. InEnableIRQ(int);
  1968. Release(Interrupts)
  1969. END EnableIRQ;
  1970. (** DisableIRQ - Disable a hardware interrupt. *)
  1971. PROCEDURE DisableIRQ* (int: LONGINT);
  1972. BEGIN
  1973. ASSERT((int >= IRQ0) & (int <= IRQ15) & (int # IRQ2));
  1974. Acquire(Interrupts); (* protect interrupt mask register *)
  1975. InDisableIRQ(int);
  1976. Release(Interrupts)
  1977. END DisableIRQ;
  1978. (** InstallHandler - Install interrupt handler & enable IRQ if necessary.
  1979. On entry to h interrupts are disabled and may be enabled with Sti. After handling the interrupt
  1980. the state of interrupts are restored. The acknowledgement of a hardware interrupt is done automatically.
  1981. IRQs are mapped from IRQ0 to MaxIRQ. *)
  1982. PROCEDURE InstallHandler* (h: Handler; int: LONGINT);
  1983. VAR (* n: HandlerList; *) i: LONGINT; unexpected: Handler;
  1984. BEGIN
  1985. ASSERT(default.valid); (* initialized *)
  1986. ASSERT(int # IRQ2); (* IRQ2 is used for cascading and remapped to IRQ9 *)
  1987. Acquire(Interrupts);
  1988. (* FieldInterrupt may traverse list while it is being modified *)
  1989. i := 0;
  1990. unexpected := Unexpected;
  1991. IF intHandler[int, 0].handler # unexpected THEN
  1992. WHILE (i < MaxNumHandlers - 1) & intHandler[int, i].valid DO
  1993. INC(i)
  1994. END;
  1995. IF i < MaxNumHandlers - 1 THEN
  1996. intHandler[int, i].valid := TRUE;
  1997. intHandler[int, i].handler := h;
  1998. ELSE
  1999. Acquire(TraceOutput);
  2000. Trace.String("Machine.InstallHandler: handler could not be installed for interrupt "); Trace.Int(int, 0);
  2001. Trace.String(" - too many handlers per interrupt number"); Trace.Ln;
  2002. Release(TraceOutput)
  2003. END
  2004. ELSE
  2005. intHandler[int, 0].handler := h;
  2006. IF (int >= IRQ0) & (int <= IRQ15) THEN InEnableIRQ(int) END
  2007. END;
  2008. Release(Interrupts)
  2009. END InstallHandler;
  2010. (** RemoveHandler - Uninstall interrupt handler & disable IRQ if necessary *)
  2011. PROCEDURE RemoveHandler* (h: Handler; int: LONGINT);
  2012. VAR (* p, c: HandlerList; *) i, j, foundIndex: LONGINT;
  2013. BEGIN
  2014. ASSERT(default.valid); (* initialized *)
  2015. Acquire(Interrupts);
  2016. (* find h *)
  2017. i := 0;
  2018. foundIndex := -1;
  2019. WHILE (i < MaxNumHandlers - 1) & intHandler[int, i].valid DO
  2020. IF intHandler[int, i].handler = h THEN foundIndex := i END;
  2021. INC(i)
  2022. END;
  2023. IF foundIndex # -1 THEN
  2024. (* h found -> copy interrupt handlers higher than foundIndex *)
  2025. FOR j := foundIndex TO i - 2 DO
  2026. intHandler[int, j] := intHandler[int, j + 1]
  2027. END
  2028. END;
  2029. IF ~intHandler[int, 0].valid THEN
  2030. (* handler h was the only interrupt handler for interrupt int -> install the default handler *)
  2031. intHandler[int, 0] := default;
  2032. IF (int >= IRQ0) & (int <= IRQ15) THEN DisableIRQ(int) END
  2033. END;
  2034. Release(Interrupts)
  2035. END RemoveHandler;
  2036. (*
  2037. PROCEDURE ListIntHandlers*;
  2038. VAR i, j, highest: LONGINT; handler: Handler;
  2039. BEGIN
  2040. highest := 0;
  2041. FOR i := 0 TO IDTSize - 1 DO
  2042. j := 0;
  2043. WHILE (j < MaxNumHandlers - 1) & intHandler[i, j].valid DO INC(j) END;
  2044. Trace.String("int = "); Trace.Int(i, 3); Trace.String(" # installed handlers = "); Trace.Int(j, 0);
  2045. IF j = 1 THEN
  2046. handler := Unexpected;
  2047. IF intHandler[i, 0].handler = handler THEN
  2048. Trace.String(" default handler installed")
  2049. END
  2050. END;
  2051. Trace.Ln;
  2052. IF j > highest THEN highest := j END;
  2053. END;
  2054. Trace.String("highest # installed handlers = "); Trace.Int(highest, 0); Trace.Ln
  2055. END ListIntHandlers;
  2056. *)
  2057. (* Get control registers. *)
  2058. PROCEDURE GetCR0to4(VAR cr: ARRAY OF LONGINT);
  2059. CODE {SYSTEM.Pentium, SYSTEM.Privileged}
  2060. MOV EDI, [EBP+cr]
  2061. MOV EAX, CR0
  2062. XOR EBX, EBX ; CR1 is not documented
  2063. MOV ECX, CR2
  2064. MOV EDX, CR3
  2065. MOV [EDI], EAX
  2066. MOV [EDI+4], EBX
  2067. MOV [EDI+8], ECX
  2068. MOV [EDI+12], EDX
  2069. MOV EAX, CR4 ; Pentium only
  2070. MOV [EDI+16], EAX
  2071. END GetCR0to4;
  2072. (* GetDR0to7 - Get debug registers. *)
  2073. PROCEDURE GetDR0to7(VAR dr: ARRAY OF LONGINT);
  2074. CODE {SYSTEM.i386, SYSTEM.Privileged}
  2075. MOV EDI, [EBP+dr]
  2076. MOV EAX, DR0
  2077. MOV EBX, DR1
  2078. MOV ECX, DR2
  2079. MOV EDX, DR3
  2080. MOV [EDI], EAX
  2081. MOV [EDI+4], EBX
  2082. MOV [EDI+8], ECX
  2083. MOV [EDI+12], EDX
  2084. XOR EAX, EAX ; DR4 is not documented
  2085. XOR EBX, EBX ; DR5 is not documented
  2086. MOV ECX, DR6
  2087. MOV EDX, DR7
  2088. MOV [EDI+16], EAX
  2089. MOV [EDI+20], EBX
  2090. MOV [EDI+24], ECX
  2091. MOV [EDI+28], EDX
  2092. END GetDR0to7;
  2093. (* GetSegments - Get segment registers. *)
  2094. PROCEDURE GetSegments(VAR ss, es, ds, fs, gs: LONGINT);
  2095. CODE {SYSTEM.i386}
  2096. XOR EAX, EAX
  2097. MOV EBX, [EBP+ss]
  2098. MOV AX, SS
  2099. MOV [EBX], EAX
  2100. MOV EBX, [EBP+es]
  2101. MOV AX, ES
  2102. MOV [EBX], EAX
  2103. MOV EBX, [EBP+ds]
  2104. MOV AX, DS
  2105. MOV [EBX], EAX
  2106. MOV EBX, [EBP+fs]
  2107. MOV AX, FS
  2108. MOV [EBX], EAX
  2109. MOV EBX, [EBP+gs]
  2110. MOV AX, GS
  2111. MOV [EBX], EAX
  2112. END GetSegments;
  2113. (* CLTS - Clear task-switched flag. *)
  2114. PROCEDURE -CLTS;
  2115. CODE {SYSTEM.i386, SYSTEM.Privileged}
  2116. CLTS
  2117. END CLTS;
  2118. (* GetFPU - Store floating-point environment (28 bytes) and mask all floating-point exceptions. *)
  2119. PROCEDURE -GetFPU(adr: ADDRESS);
  2120. CODE {SYSTEM.i386, SYSTEM.FPU}
  2121. POP EBX
  2122. FNSTENV [EBX] ; also masks all exceptions
  2123. FWAIT
  2124. END GetFPU;
  2125. (* CR2 - Get page fault address. *)
  2126. PROCEDURE -CR2* (): ADDRESS;
  2127. CODE {SYSTEM.i386, SYSTEM.Privileged}
  2128. MOV EAX, CR2
  2129. END CR2;
  2130. (** GetExceptionState - Get exception state from interrupt state (and switch on interrupts). *)
  2131. PROCEDURE GetExceptionState* (VAR int: State; VAR exc: ExceptionState);
  2132. VAR id: LONGINT; level0: BOOLEAN;
  2133. BEGIN
  2134. (* save all state information while interrupts are still disabled *)
  2135. exc.halt := -int.INT; id := ID ();
  2136. IF int.INT = PF THEN exc.pf := CR2 () ELSE exc.pf := 0 END;
  2137. GetCR0to4(exc.CR);
  2138. GetDR0to7(exc.DR);
  2139. CLTS; (* ignore task switch flag *)
  2140. IF int.INT = MF THEN
  2141. GetFPU(ADDRESSOF(exc.FPU[0]));
  2142. int.PC := SYSTEM.VAL (ADDRESS, exc.FPU[3]); (* modify PC according to FPU info *)
  2143. (* set halt code according to FPU info *)
  2144. IF 2 IN exc.FPU[1] THEN exc.halt := -32 (* division by 0 *)
  2145. ELSIF 3 IN exc.FPU[1] THEN exc.halt := -33 (* overflow *)
  2146. ELSIF 0 IN exc.FPU[1] THEN exc.halt := -34 (* operation invalid *)
  2147. ELSIF 6 IN exc.FPU[1] THEN exc.halt := -35 (* stack fault *)
  2148. ELSIF 1 IN exc.FPU[1] THEN exc.halt := -36 (* denormalized *)
  2149. ELSIF 4 IN exc.FPU[1] THEN exc.halt := -37 (* underflow *)
  2150. ELSIF 5 IN exc.FPU[1] THEN exc.halt := -38 (* precision loss *)
  2151. ELSE (* {exc.halt = -16} *)
  2152. END
  2153. ELSE
  2154. Fill32(ADDRESSOF(exc.FPU[0]), LEN(exc.FPU)*SIZEOF(SET), 0)
  2155. END;
  2156. SetupFPU;
  2157. level0 := (int.CS MOD 4 = KernelLevel);
  2158. IF int.INT = BP THEN (* breakpoint (HALT) *)
  2159. IF level0 THEN
  2160. exc.halt := int.SP (* get halt code *)
  2161. (* if HALT(MAX(INTEGER)), leave halt code on stack when returning, but not serious problem.*)
  2162. ELSE
  2163. SYSTEM.GET (int.SP, exc.halt); (* get halt code from outer stack *)
  2164. IF exc.halt >= MAX(INTEGER) THEN INC (int.SP, AddressSize) END (* pop halt code from outer stack *)
  2165. END;
  2166. IF exc.halt < MAX(INTEGER) THEN DEC (int.PC) END; (* point to the INT 3 instruction (assume 0CCX, not 0CDX 3X) *)
  2167. ELSIF int.INT = OVF THEN (* overflow *)
  2168. DEC (int.PC) (* point to the INTO instruction (assume 0CEX, not 0CDX 4X) *)
  2169. ELSIF int.INT = PF THEN (* page fault *)
  2170. IF int.PC = 0 THEN (* reset PC to return address of indirect CALL to 0 *)
  2171. IF level0 THEN int.PC := int.SP (* ret adr *) ELSE SYSTEM.GET (int.SP, int.PC) END
  2172. END
  2173. END;
  2174. (* get segment registers *)
  2175. GetSegments(exc.SS, exc.ES, exc.DS, exc.FS, exc.GS);
  2176. IF level0 THEN (* from same level, no ESP, SS etc. on stack *)
  2177. exc.SP := ADDRESSOF(int.SP) (* stack was here when interrupt happened *)
  2178. ELSE (* from outer level *)
  2179. exc.SP := int.SP; exc.SS := int.SS
  2180. END
  2181. END GetExceptionState;
  2182. (* FieldInterrupt and FieldIRQ *)
  2183. (*
  2184. At entry to a Handler procedure the stack is as follows:
  2185. 72 -- .GS
  2186. 68 -- .FS
  2187. 64 -- .DS
  2188. 60 -- .ES ; or haltcode
  2189. -- if (VMBit IN .FLAGS) --
  2190. 56 -- .SS
  2191. 52 -- .ESP ; or haltcode
  2192. -- (VMBit IN .EFLAGS) OR (CS MOD 4 < .CS MOD 4) --
  2193. 48 -- .EFLAGS
  2194. 44 -- .CS
  2195. 40 -- .EIP ; rest popped by IRETD
  2196. 36 -- .ERR/EBP ; pushed by processor or glue code, popped by POP EBP
  2197. 32 -- .INT <-- .ESP0 ; pushed by glue code, popped by POP EBP
  2198. 28 -- .EAX
  2199. 24 -- .ECX
  2200. 20 -- .EDX
  2201. 16 -- .EBX
  2202. 12 -- .ESP0
  2203. 08 -- .BP/ERR ; exchanged by glue code
  2204. 04 -- .ESI
  2205. 00 24 .EDI <--- state: State
  2206. -- 20 ptr
  2207. -- 16 object pointer for DELEGATE
  2208. -- 12 TAG(state)
  2209. -- 08 ADR(state)
  2210. -- 04 EIP' (RET to FieldInterrupt)
  2211. -- 00 EBP' <-- EBP
  2212. -- -- locals <-- ESP
  2213. *)
  2214. PROCEDURE FieldInterrupt;
  2215. CODE {SYSTEM.i386} ; 3 bytes implicit code skipped: PUSH EBP; MOV EBP, ESP
  2216. entry:
  2217. PUSHAD ; save all registers (EBP = error code)
  2218. LEA EBP, [ESP+36] ; procedure link (for correct tracing of interrupt procedures)
  2219. MOV EBX, [ESP+32] ; EBX = int number
  2220. IMUL EBX, EBX, MaxNumHandlers
  2221. IMUL EBX, EBX, 12
  2222. LEA EAX, intHandler
  2223. ADD EAX, EBX ; address of intHandler[int, 0]
  2224. loop: ; call all handlers for the interrupt
  2225. MOV ECX, ESP
  2226. PUSH EAX ; save ptr for table
  2227. PUSH DWORD [EAX+8] ; delegate
  2228. PUSH stateTag ; TAG(state)
  2229. PUSH ECX ; ADR(state)
  2230. CALL DWORD [EAX+4] ; call handler
  2231. ADD ESP, 12
  2232. CLI ; handler may have re-enabled interrupts
  2233. POP EAX
  2234. ADD EAX, 12
  2235. MOV EBX, [EAX]
  2236. CMP EBX, 0
  2237. JNE loop
  2238. POPAD ; now EBP = error code
  2239. POP EBP ; now EBP = INT
  2240. POP EBP ; now EBP = caller EBP
  2241. IRETD
  2242. END FieldInterrupt;
  2243. PROCEDURE FieldIRQ;
  2244. CODE {SYSTEM.i386} ; 3 bytes implicit code skipped: PUSH EBP; MOV EBP, ESP
  2245. entry:
  2246. PUSHAD ; save all registers (EBP = error code)
  2247. LEA EBP, [ESP+36] ; procedure link (for correct tracing of interrupt procedures)
  2248. ; PUSH [ESP+32] ; int number
  2249. ; CALL traceInterruptIn
  2250. MOV EBX, [ESP+32] ; EBX = int number
  2251. CMP BL, IRQ0 + 7 ; if irq=7 then check for spurious interrupt on master
  2252. JNE skip1
  2253. MOV AL, 0BH
  2254. OUT IntA0, AL
  2255. IN AL, IntA0
  2256. BT AX, 7
  2257. JNC end
  2258. skip1:
  2259. CMP BL, IRQ8 + 7 ; if irq=15 then check for spurious interrupt on slave
  2260. JNE skip2
  2261. MOV AL, 0BH
  2262. OUT IntB0, AL
  2263. IN AL, IntB0
  2264. BT AX, 7
  2265. MOV AL, 20H
  2266. JNC irq0 ; acknowledge IRQ on master
  2267. skip2:
  2268. IMUL EBX, EBX, MaxNumHandlers
  2269. IMUL EBX, EBX, 12
  2270. LEA EAX, intHandler
  2271. ADD EAX, EBX ; address of intHandler[int, 0]
  2272. loop: ; call all handlers for the interrupt
  2273. MOV ECX, ESP
  2274. PUSH EAX ; save ptr for linked list
  2275. PUSH DWORD [EAX+8] ; delegate
  2276. PUSH stateTag ; TAG(state)
  2277. PUSH ECX ; ADR(state)
  2278. CALL DWORD [EAX+4] ; call handler
  2279. ADD ESP, 12
  2280. CLI ; handler may have re-enabled interrupts
  2281. POP EAX
  2282. ADD EAX, 12
  2283. MOV EBX, [EAX]
  2284. CMP EBX, 0
  2285. JNE loop
  2286. ; PUSH [ESP+32] ; int number
  2287. ; CALL traceInterruptOut
  2288. ; ack interrupt
  2289. MOV AL, 20H ; undoc PC ed. 2 p. 1018
  2290. CMP BYTE [ESP+32], IRQ8
  2291. JB irq0
  2292. OUT IntB0, AL ; 2nd controller
  2293. irq0:
  2294. OUT IntA0, AL ; 1st controller
  2295. end:
  2296. POPAD ; now EBP = error code
  2297. POP EBP ; now EBP = INT
  2298. POP EBP ; now EBP = caller EBP
  2299. IRETD
  2300. END FieldIRQ;
  2301. (* LoadIDT - Load interrupt descriptor table *)
  2302. PROCEDURE LoadIDT(base: ADDRESS; size: SIZE);
  2303. CODE {SYSTEM.i386, SYSTEM.Privileged}
  2304. SHL DWORD [EBP+size], 16
  2305. MOV EBX, 2
  2306. LIDT [EBP+EBX+size]
  2307. END LoadIDT;
  2308. (** Init - Initialize interrupt handling. Called once during initialization. Uses NEW. *)
  2309. (*
  2310. The glue code is:
  2311. entry0: ; entry point for interrupts without error code
  2312. PUSH 0 ; fake error code
  2313. entry1: ; entry point for interrupts with error code
  2314. XCHG [ESP], EBP ; exchange error code and caller EBP
  2315. PUSH int ; interrupt number
  2316. JMP FieldInterrupt:entry
  2317. *)
  2318. PROCEDURE InitInterrupts*;
  2319. VAR a: ADDRESS; o, i: LONGINT; p: PROCEDURE; mask: SET;
  2320. BEGIN
  2321. stateTag := SYSTEM.TYPECODE(State);
  2322. (* initialise 8259 interrupt controller chips *)
  2323. Portout8 (IntA0, 11X); Portout8 (IntA1, CHR(IRQ0));
  2324. Portout8 (IntA1, 4X); Portout8 (IntA1, 1X); Portout8 (IntA1, 0FFX);
  2325. Portout8 (IntB0, 11X); Portout8 (IntB1, CHR(IRQ8));
  2326. Portout8 (IntB1, 2X); Portout8 (IntB1, 1X); Portout8 (IntB1, 0FFX);
  2327. (* enable interrupts from second interrupt controller, chained to line 2 of controller 1 *)
  2328. Portin8(IntA1, SYSTEM.VAL (CHAR, mask));
  2329. EXCL(mask, IRQ2-IRQ0);
  2330. Portout8 (IntA1, SYSTEM.VAL (CHAR, mask));
  2331. (*
  2332. NEW(default); default.next := NIL; default.handler := Unexpected;
  2333. *)
  2334. (*
  2335. newrec (SYSTEM.VAL (ANY, default), SYSTEM.TYPECODE (HandlerList));
  2336. *)
  2337. (* default.next := NIL; default.handler := Unexpected; *)
  2338. default.valid := TRUE; default.handler := Unexpected;
  2339. FOR i := 0 TO IDTSize-1 DO (* set up glue code *)
  2340. intHandler[i, 0] := default; o := 0;
  2341. (* PUSH error code, int num & regs *)
  2342. glue[i][o] := 6AX; INC (o); glue[i][o] := 0X; INC (o); (* PUSH 0 ; {o = 2} *)
  2343. glue[i][o] := 87X; INC (o); glue[i][o] := 2CX; INC (o); glue[i][o] := 24X; INC (o); (* XCHG [ESP], EBP *)
  2344. glue[i][o] := 6AX; INC (o); glue[i][o] := CHR(i); INC (o); (* PUSH i *)
  2345. IF (i >= IRQ0) & (i <= IRQ15) THEN p := FieldIRQ ELSE p := FieldInterrupt END;
  2346. a := SYSTEM.VAL (ADDRESS, p) + 3 - (ADDRESSOF(glue[i][o]) + 5);
  2347. glue[i][o] := 0E9X; INC (o); (* JMP FieldInterrupt.entry *)
  2348. SYSTEM.PUT32 (ADDRESSOF(glue[i][o]), a);
  2349. (* set up IDT entry *)
  2350. IF (i > 31) OR ~(i IN {8, 10..14, 17}) THEN a := ADDRESSOF(glue[i][0]) (* include PUSH 0 *)
  2351. ELSE a := ADDRESSOF(glue[i][2]) (* skip PUSH 0, processor supplies error code *)
  2352. END;
  2353. idt[i].offsetBits0to15 := INTEGER (a MOD 10000H);
  2354. (* IRQ0 must be at level 0 because time slicing in Objects needs to set interrupted process' ESP *)
  2355. (* all irq's are handled at level 0, because of priority experiment in Objects.FieldIRQ *)
  2356. IF TRUE (* (i < IRQ0) OR (i > IRQ15) OR (i = IRQ0) OR (i = IRQ0 + 1)*) THEN
  2357. idt[i].selector := KernelCodeSel; (* gdt[1] -> non-conformant segment => level 0 *)
  2358. idt[i].gateType := SYSTEM.VAL (INTEGER, 0EE00H) (* present, DPL 3, system, 386 interrupt *)
  2359. ELSE (* {IRQ0..IRQ15} - {IRQ0 + 1} *)
  2360. idt[i].selector := UserCodeSel; (* gdt[3] -> conformant segment => level 0 or 3 *)
  2361. idt[i].gateType := SYSTEM.VAL (INTEGER, 08E00H) (* present, DPL 0, system, 386 interrupt *)
  2362. END;
  2363. idt[i].offsetBits16to31 := INTEGER(a DIV 10000H)
  2364. END
  2365. END InitInterrupts;
  2366. (** Start - Start handling interrupts. Every processor calls this once during initialization. *)
  2367. PROCEDURE Start*;
  2368. BEGIN
  2369. ASSERT(default.valid); (* initialized *)
  2370. LoadIDT(ADDRESSOF(idt[0]), SIZEOF(IDT)-1);
  2371. Sti
  2372. END Start;
  2373. (* Return current instruction pointer *)
  2374. PROCEDURE CurrentPC* (): ADDRESS;
  2375. CODE {SYSTEM.i386}
  2376. MOV EAX, [EBP+4]
  2377. END CurrentPC;
  2378. (* Return current frame pointer *)
  2379. PROCEDURE -CurrentBP* (): ADDRESS;
  2380. CODE {SYSTEM.i386}
  2381. MOV EAX, EBP
  2382. END CurrentBP;
  2383. (* Set current frame pointer *)
  2384. PROCEDURE -SetBP* (bp: ADDRESS);
  2385. CODE {SYSTEM.i386}
  2386. POP EBP
  2387. END SetBP;
  2388. (* Return current stack pointer *)
  2389. PROCEDURE -CurrentSP* (): ADDRESS;
  2390. CODE {SYSTEM.i386}
  2391. MOV EAX, ESP
  2392. END CurrentSP;
  2393. (* Set current stack pointer *)
  2394. PROCEDURE -SetSP* (sp: ADDRESS);
  2395. CODE {SYSTEM.i386}
  2396. POP ESP
  2397. END SetSP;
  2398. (* Save minimal FPU state (for synchronous process switches). *)
  2399. (* saving FPU state takes 108 bytes memory space, no alignment required *)
  2400. PROCEDURE -FPUSaveMin* (VAR state: SSEState);
  2401. CODE {SYSTEM.i386, SYSTEM.FPU}
  2402. POP EAX
  2403. FNSTCW [EAX] ; control word is at state[0]
  2404. FWAIT
  2405. END FPUSaveMin;
  2406. (* Restore minimal FPU state. *)
  2407. PROCEDURE -FPURestoreMin* (VAR state: SSEState);
  2408. CODE {SYSTEM.i386, SYSTEM.FPU}
  2409. POP EAX
  2410. FLDCW [EAX] ; control word is at state[0]
  2411. END FPURestoreMin;
  2412. (* Save full FPU state (for asynchronous process switches). *)
  2413. PROCEDURE -FPUSaveFull* (VAR state: SSEState);
  2414. CODE {SYSTEM.i386, SYSTEM.FPU}
  2415. POP EAX
  2416. FSAVE [EAX]
  2417. END FPUSaveFull;
  2418. (* Restore full FPU state. *)
  2419. PROCEDURE -FPURestoreFull* (VAR state: SSEState);
  2420. CODE {SYSTEM.i386, SYSTEM.FPU}
  2421. POP EAX
  2422. FRSTOR [EAX]
  2423. END FPURestoreFull;
  2424. (* stateAdr must be the address of a 16-byte aligned memory area of at least 512 bytes *)
  2425. PROCEDURE -SSESaveFull* (stateAdr: ADDRESS);
  2426. CODE {SYSTEM.P2, SYSTEM.FPU, SYSTEM.SSE2}
  2427. POP EAX
  2428. FXSAVE [EAX]
  2429. FWAIT
  2430. FNINIT
  2431. END SSESaveFull;
  2432. PROCEDURE -SSERestoreFull* (stateAdr: ADDRESS);
  2433. CODE {SYSTEM.P2, SYSTEM.FPU, SYSTEM.SSE2}
  2434. POP EAX
  2435. FXRSTOR [EAX]
  2436. END SSERestoreFull;
  2437. PROCEDURE -SSESaveMin* (stateAdr: ADDRESS);
  2438. CODE {SYSTEM.i386, SYSTEM.FPU, SYSTEM.SSE2}
  2439. POP EAX
  2440. FNSTCW [EAX]
  2441. FWAIT
  2442. STMXCSR [EAX+24]
  2443. END SSESaveMin;
  2444. PROCEDURE -SSERestoreMin* (stateAdr: ADDRESS);
  2445. CODE {SYSTEM.i386, SYSTEM.FPU, SYSTEM.SSE2}
  2446. POP EAX
  2447. FLDCW [EAX]
  2448. LDMXCSR [EAX+24]
  2449. END SSERestoreMin;
  2450. (* Helper functions for SwitchTo. *)
  2451. PROCEDURE -PushState* (CONST state: State);
  2452. CODE {SYSTEM.i386}
  2453. POP EAX ; ADR (state)
  2454. POP EBX ; TYPECODE (state), ignored
  2455. PUSH DWORD [EAX+48] ; FLAGS
  2456. PUSH DWORD [EAX+44] ; CS
  2457. PUSH DWORD [EAX+40] ; PC
  2458. PUSH DWORD [EAX+28] ; EAX
  2459. PUSH DWORD [EAX+24] ; ECX
  2460. PUSH DWORD [EAX+20] ; EDX
  2461. PUSH DWORD [EAX+16] ; EBX
  2462. PUSH DWORD 0 ; ignored
  2463. PUSH DWORD [EAX+36] ; BP
  2464. PUSH DWORD [EAX+4] ; ESI
  2465. PUSH DWORD [EAX+0] ; EDI
  2466. END PushState;
  2467. PROCEDURE -JumpState*;
  2468. CODE {SYSTEM.i386}
  2469. POPAD
  2470. IRETD
  2471. END JumpState;
  2472. PROCEDURE -CallLocalIPC*;
  2473. CODE {SYSTEM.i386}
  2474. INT MPIPCLocal
  2475. END CallLocalIPC;
  2476. PROCEDURE -HLT*;
  2477. CODE {SYSTEM.i386, SYSTEM.Privileged}
  2478. STI ; (* required according to ACPI 2.0 spec section 8.2.2 *)
  2479. HLT
  2480. END HLT;
  2481. PROCEDURE -GetEAX*(): LONGINT;
  2482. CODE{SYSTEM.i386}
  2483. END GetEAX;
  2484. PROCEDURE -GetECX*(): LONGINT;
  2485. CODE{SYSTEM.i386}
  2486. MOV EAX,ECX
  2487. END GetECX;
  2488. PROCEDURE -GetESI*(): LONGINT;
  2489. CODE{SYSTEM.i386}
  2490. MOV EAX,ESI
  2491. END GetESI;
  2492. PROCEDURE -GetEDI*(): LONGINT;
  2493. CODE{SYSTEM.i386}
  2494. MOV EAX,EDI
  2495. END GetEDI;
  2496. PROCEDURE -SetEAX*(n: LONGINT);
  2497. CODE{SYSTEM.i386} POP EAX
  2498. END SetEAX;
  2499. PROCEDURE -SetEBX*(n: LONGINT);
  2500. CODE{SYSTEM.i386}
  2501. POP EBX
  2502. END SetEBX;
  2503. PROCEDURE -SetECX*(n: LONGINT);
  2504. CODE{SYSTEM.i386}
  2505. POP ECX
  2506. END SetECX;
  2507. PROCEDURE -SetEDX*(n: LONGINT);
  2508. CODE{SYSTEM.i386}
  2509. POP EDX
  2510. END SetEDX;
  2511. PROCEDURE -SetESI*(n: LONGINT);
  2512. CODE{SYSTEM.i386}
  2513. POP ESI
  2514. END SetESI;
  2515. PROCEDURE -SetEDI*(n: LONGINT);
  2516. CODE{SYSTEM.i386}
  2517. POP EDI
  2518. END SetEDI;
  2519. PROCEDURE Portin8*(port: LONGINT; VAR val: CHAR);
  2520. CODE{SYSTEM.i386}
  2521. MOV EDX,[EBP+port]
  2522. IN AL, DX
  2523. MOV ECX, [EBP+val]
  2524. MOV [ECX], AL
  2525. END Portin8;
  2526. PROCEDURE Portin16*(port: LONGINT; VAR val: INTEGER);
  2527. CODE{SYSTEM.i386}
  2528. MOV EDX,[EBP+port]
  2529. IN AX, DX
  2530. MOV ECX, [EBP+val]
  2531. MOV [ECX], AX
  2532. END Portin16;
  2533. PROCEDURE Portin32*(port: LONGINT; VAR val: LONGINT);
  2534. CODE{SYSTEM.i386}
  2535. MOV EDX,[EBP+port]
  2536. IN EAX, DX
  2537. MOV ECX, [EBP+val]
  2538. MOV [ECX], EAX
  2539. END Portin32;
  2540. PROCEDURE Portout8*(port: LONGINT; val: CHAR);
  2541. CODE{SYSTEM.i386}
  2542. MOV AL,[EBP+val]
  2543. MOV EDX,[EBP+port]
  2544. OUT DX,AL
  2545. END Portout8;
  2546. PROCEDURE Portout16*(port: LONGINT; val: INTEGER);
  2547. CODE{SYSTEM.i386}
  2548. MOV AX,[EBP+val]
  2549. MOV EDX,[EBP+port]
  2550. OUT DX,AX
  2551. END Portout16;
  2552. PROCEDURE Portout32*(port: LONGINT; val: LONGINT);
  2553. CODE{SYSTEM.i386}
  2554. MOV EAX,[EBP+val]
  2555. MOV EDX,[EBP+port]
  2556. OUT DX,EAX
  2557. END Portout32;
  2558. (* Kernel mode upcall to perform global processor halt. *)
  2559. PROCEDURE KernelCallHLT*;
  2560. CODE {SYSTEM.i386}
  2561. MOV EAX, 2
  2562. INT MPKC
  2563. END KernelCallHLT;
  2564. (* Parse processor entry in MP config table. *)
  2565. PROCEDURE CPUID1*(): LONGINT;
  2566. CODE {SYSTEM.i386, SYSTEM.Pentium}
  2567. MOV EAX, 1
  2568. CPUID
  2569. MOV EAX, EBX
  2570. END CPUID1;
  2571. (** -- Atomic operations -- *)
  2572. (** Atomic INC(x). *)
  2573. PROCEDURE -AtomicInc*(VAR x: LONGINT);
  2574. CODE {SYSTEM.i386}
  2575. POP EAX
  2576. LOCK
  2577. INC DWORD [EAX]
  2578. END AtomicInc;
  2579. (** Atomic DEC(x). *)
  2580. PROCEDURE -AtomicDec*(VAR x: LONGINT);
  2581. CODE {SYSTEM.i386}
  2582. POP EAX
  2583. LOCK
  2584. DEC DWORD [EAX]
  2585. END AtomicDec;
  2586. (** Atomic EXCL. *)
  2587. PROCEDURE AtomicExcl* (VAR s: SET; bit: LONGINT);
  2588. CODE {SYSTEM.i386}
  2589. MOV EAX, [EBP+bit]
  2590. MOV EBX, [EBP+s]
  2591. LOCK
  2592. BTR [EBX], EAX
  2593. END AtomicExcl;
  2594. (** Atomic INC(x, y). *)
  2595. PROCEDURE -AtomicAdd*(VAR x: LONGINT; y: LONGINT);
  2596. CODE {SYSTEM.i386}
  2597. POP EBX
  2598. POP EAX
  2599. LOCK
  2600. ADD DWORD [EAX], EBX
  2601. END AtomicAdd;
  2602. (** Atomic test-and-set. Set x = TRUE and return old value of x. *)
  2603. PROCEDURE -AtomicTestSet*(VAR x: BOOLEAN): BOOLEAN;
  2604. CODE {SYSTEM.i386}
  2605. POP EBX
  2606. MOV AL, 1
  2607. XCHG [EBX], AL
  2608. END AtomicTestSet;
  2609. (* Atomic compare-and-swap. Set x = new if x = old and return old value of x *)
  2610. PROCEDURE -AtomicCAS* (VAR x: LONGINT; old, new: LONGINT): LONGINT;
  2611. CODE {SYSTEM.i386}
  2612. POP EBX ; new
  2613. POP EAX ; old
  2614. POP ECX ; address of x
  2615. DB 0F0X, 00FX, 0B1X, 019X ; LOCK CMPXCHG [ECX], EBX; atomicly compare x with old and set it to new if equal
  2616. END AtomicCAS;
  2617. PROCEDURE CopyState* (CONST from: State; VAR to: State);
  2618. BEGIN
  2619. to.EDI := from.EDI; to.ESI := from.ESI;
  2620. to.EBX := from.EBX; to.EDX := from.EDX;
  2621. to.ECX := from.ECX; to.EAX := from.EAX;
  2622. to.BP := from.BP; to.PC := from.PC;
  2623. to.CS := from.CS; to.FLAGS := from.FLAGS;
  2624. to.SP := from.SP
  2625. END CopyState;
  2626. (* function returning the number of processors that are available to Aos *)
  2627. PROCEDURE NumberOfProcessors*( ): LONGINT;
  2628. BEGIN
  2629. RETURN numberOfProcessors
  2630. END NumberOfProcessors;
  2631. (*! non portable code, for native Aos only *)
  2632. PROCEDURE SetNumberOfProcessors*(num: LONGINT);
  2633. BEGIN
  2634. numberOfProcessors := num;
  2635. END SetNumberOfProcessors;
  2636. (* function for changing byte order *)
  2637. PROCEDURE ChangeByteOrder* (n: LONGINT): LONGINT;
  2638. CODE { SYSTEM.Pentium }
  2639. MOV EAX, [EBP+n] ; load n in eax
  2640. BSWAP EAX ; swap byte order
  2641. END ChangeByteOrder;
  2642. (* Write a value to the APIC. *)
  2643. PROCEDURE ApicPut(ofs: SIZE; val: SET);
  2644. BEGIN
  2645. IF TraceApic THEN
  2646. Acquire(TraceOutput);
  2647. Trace.Hex(ofs, SIZEOF(SIZE)*2); Trace.String(" := "); Trace.Hex(SYSTEM.VAL (LONGINT, val), 9); Trace.Ln;
  2648. Release(TraceOutput);
  2649. END;
  2650. SYSTEM.PUT(localAPIC+ofs, SYSTEM.VAL (LONGINT, val))
  2651. END ApicPut;
  2652. (* Read a value from the APIC. *)
  2653. PROCEDURE ApicGet(ofs: SIZE): SET;
  2654. VAR val: SET;
  2655. BEGIN
  2656. SYSTEM.GET(localAPIC+ofs, SYSTEM.VAL (LONGINT, val));
  2657. IF TraceApic THEN
  2658. Acquire(TraceOutput);
  2659. Trace.String(" ("); Trace.Hex(ofs, SIZEOF(SIZE)*2); Trace.String(" = ");
  2660. Trace.Hex(SYSTEM.VAL(LONGINT, val), 9); Trace.StringLn (")");
  2661. Release(TraceOutput);
  2662. END;
  2663. RETURN val
  2664. END ApicGet;
  2665. (* Handle interprocessor interrupt. During upcall interrupts are off and processor is at kernel level. *)
  2666. PROCEDURE HandleIPC(VAR state: State);
  2667. VAR id: LONGINT;
  2668. BEGIN
  2669. id := ID();
  2670. IF ~TraceProcessor OR (id IN allProcessors) THEN
  2671. IF FrontBarrier IN ipcFlags THEN
  2672. AtomicExcl(ipcFrontBarrier, id);
  2673. WHILE ipcFrontBarrier # {} DO SpinHint END (* wait for all *)
  2674. END;
  2675. ipcHandler(id, state, ipcMessage); (* interrupts off and at kernel level *)
  2676. IF BackBarrier IN ipcFlags THEN
  2677. AtomicExcl(ipcBackBarrier, id);
  2678. WHILE ipcBackBarrier # {} DO SpinHint END (* wait for all *)
  2679. END;
  2680. AtomicExcl(ipcBusy, id) (* ack - after this point we do not access shared variables for this broadcast *)
  2681. END;
  2682. IF state.INT = MPIPC THEN
  2683. ApicPut(0B0H, {}) (* EOI (not needed for NMI or local call, see 7.4.10.6) *)
  2684. END
  2685. END HandleIPC;
  2686. (* Handle MP error interrupt. *)
  2687. PROCEDURE HandleError(VAR state: State);
  2688. VAR esr: SET; (* int: LONGINT; *)
  2689. BEGIN
  2690. (* int := state.INT; *) esr := ApicGet(280H);
  2691. ApicPut(0B0H, {}); (* EOI *)
  2692. HALT(2302) (* SMP error *)
  2693. END HandleError;
  2694. (* Interprocessor broadcasting. Lock level SMP. *)
  2695. PROCEDURE LocalBroadcast(h: BroadcastHandler; msg: Message; flags: SET);
  2696. BEGIN
  2697. IF Self IN flags THEN ipcBusy := allProcessors
  2698. ELSE ipcBusy := allProcessors - {ID()}
  2699. END;
  2700. ipcFrontBarrier := ipcBusy; ipcBackBarrier := ipcBusy;
  2701. ipcHandler := h; ipcMessage := msg; ipcFlags := flags;
  2702. IF numProcessors > 1 THEN (* ICR: Fixed, Physical, Edge, All Excl. Self, INT IPC *)
  2703. ApicPut(300H, {18..19} + SYSTEM.VAL (SET, MPIPC));
  2704. (*REPEAT UNTIL ~(12 IN ApicGet(300H))*) (* wait for send to finish *)
  2705. END;
  2706. IF Self IN flags THEN CallLocalIPC END; (* "send" to self also *)
  2707. WHILE ipcBusy # {} DO SpinHint END; (* wait for all to ack before we release locks *)
  2708. ipcHandler := NIL; ipcMessage := NIL (* no race, because we have IPC lock *)
  2709. END LocalBroadcast;
  2710. (** Broadcast an operation to all processors. *)
  2711. PROCEDURE Broadcast* (h: BroadcastHandler; msg: Message; flags: SET);
  2712. BEGIN
  2713. Acquire(Processors);
  2714. LocalBroadcast(h, msg, flags);
  2715. Release(Processors)
  2716. END Broadcast;
  2717. (* Start all halted processors. *) (* Lock level Processors. *)
  2718. PROCEDURE StartAll*;
  2719. BEGIN
  2720. Acquire(Processors); (* wait for any pending Stops to finish, and disallow further Stops *)
  2721. ASSERT(stopped & (ipcBusy = {}));
  2722. ipcBusy := allProcessors - {ID()};
  2723. stopped := FALSE;
  2724. WHILE ipcBusy # {} DO SpinHint END; (* wait for all to ack *)
  2725. Release(Processors)
  2726. END StartAll;
  2727. PROCEDURE HandleFlushTLB(id: LONGINT; CONST state: State; msg: Message);
  2728. CODE {SYSTEM.i386, SYSTEM.Privileged}
  2729. MOV EAX, CR3
  2730. MOV CR3, EAX
  2731. END HandleFlushTLB;
  2732. (** Flush the TLBs on all processors (multiprocessor-safe). *)
  2733. PROCEDURE GlobalFlushTLB;
  2734. BEGIN
  2735. Acquire(Processors);
  2736. LocalBroadcast(HandleFlushTLB, NIL, {Self, FrontBarrier, BackBarrier});
  2737. Release(Processors)
  2738. END GlobalFlushTLB;
  2739. PROCEDURE HandleFlushCache(id: LONGINT; CONST state: State; msg: Message);
  2740. CODE {SYSTEM.Pentium, SYSTEM.Privileged}
  2741. WBINVD ; write back and invalidate internal cache and initiate write back and invalidation of external caches
  2742. END HandleFlushCache;
  2743. (** Flush the caches on all processors (multiprocessor-safe). *)
  2744. PROCEDURE GlobalFlushCache;
  2745. BEGIN
  2746. Acquire(Processors);
  2747. LocalBroadcast(HandleFlushCache, NIL, {Self, FrontBarrier, BackBarrier});
  2748. Release(Processors)
  2749. END GlobalFlushCache;
  2750. (* Activate the garbage collector in single-processor mode. Lock level ALL. *)
  2751. PROCEDURE HandleKernelCall(VAR state: State);
  2752. BEGIN (* level 0 *)
  2753. IF IFBit IN state.FLAGS THEN
  2754. Sti (* re-enable interrupts *)
  2755. END;
  2756. CASE state.EAX OF (* see KernelCall* *)
  2757. |2: (* HLT *)
  2758. IF IFBit IN state.FLAGS THEN
  2759. HLT
  2760. END
  2761. END
  2762. END HandleKernelCall;
  2763. (*
  2764. (** Activate the garbage collector immediately (multiprocessor-safe). *)
  2765. PROCEDURE GlobalGC*;
  2766. BEGIN
  2767. Acquire(Processors);
  2768. gcBarrier := allProcessors;
  2769. LocalBroadcast(HandleGC, NIL, {Self, BackBarrier});
  2770. Release(Processors);
  2771. END GlobalGC;
  2772. *)
  2773. PROCEDURE HandleGetTimestamp(id: LONGINT; CONST state: State; msg: Message);
  2774. BEGIN
  2775. time[id] := GetTimer()
  2776. END HandleGetTimestamp;
  2777. (** Get timestamp on all processors (for testing). *)
  2778. PROCEDURE GlobalGetTimestamp;
  2779. VAR t: TimeArray; i: LONGINT; mean, var, n: HUGEINT;
  2780. BEGIN
  2781. Acquire(Processors);
  2782. LocalBroadcast(HandleGetTimestamp, NIL, {Self, FrontBarrier});
  2783. LocalBroadcast(HandleGetTimestamp, NIL, {Self, FrontBarrier});
  2784. t := time;
  2785. Release(Processors);
  2786. Acquire (TraceOutput);
  2787. FOR i := 0 TO numProcessors-1 DO Trace.HIntHex(t[i], 17) END;
  2788. IF numProcessors > 1 THEN
  2789. mean := 0;
  2790. n := numProcessors;
  2791. FOR i := 0 TO numProcessors-1 DO
  2792. INC (mean, t[i])
  2793. END;
  2794. mean := mean DIV n;
  2795. var := 0;
  2796. FOR i := 0 TO numProcessors-1 DO
  2797. n := t[i] - mean;
  2798. INC (var, n * n)
  2799. END;
  2800. var := var DIV (numProcessors - 1);
  2801. Trace.String(" mean="); Trace.HIntHex(mean, 16);
  2802. Trace.String(" var="); Trace.HIntHex(var, 16);
  2803. Trace.String(" var="); Trace.Int(SHORT (var), 1);
  2804. Trace.String(" diff:");
  2805. FOR i := 0 TO numProcessors-1 DO
  2806. Trace.Int(SHORT (t[i] - mean), 1); Trace.Char(" ")
  2807. END
  2808. END;
  2809. Release (TraceOutput);
  2810. END GlobalGetTimestamp;
  2811. PROCEDURE ParseProcessor(adr: ADDRESS);
  2812. CONST Enabled = 24; BootProcessor = 25;
  2813. VAR id, idx, signature, family, feat, ver, log: LONGINT; flags: SET; string : ARRAY 8 OF CHAR;
  2814. BEGIN
  2815. SYSTEM.GET(adr, SYSTEM.VAL (LONGINT, flags));
  2816. id := ASH(SYSTEM.VAL (LONGINT, flags * {8..15}), -8);
  2817. ver := ASH(SYSTEM.VAL (LONGINT, flags * {16..23}), -16);
  2818. SYSTEM.GET (adr+4, signature);
  2819. family := ASH(signature, -8) MOD 10H;
  2820. SYSTEM.GET (adr+8, feat);
  2821. idx := -1;
  2822. IF (family # 0) & (signature MOD 1000H # 0FFFH) & (Enabled IN flags) & (id < LEN(idMap)) & (idMap[id] = -1) THEN
  2823. IF BootProcessor IN flags THEN idx := 0 (* boot processor *)
  2824. ELSIF numProcessors < maxProcessors THEN idx := numProcessors; INC(numProcessors)
  2825. ELSE (* skip *)
  2826. END
  2827. END;
  2828. IF idx # -1 THEN apicVer[idx] := ver; idMap[id] := SHORT(SHORT(idx)) END;
  2829. IF TraceVerbose THEN
  2830. Trace.String(" Processor "); Trace.Int(id, 1);
  2831. Trace.String(", APIC"); Trace.Hex(ver, -3);
  2832. Trace.String(", ver "); Trace.Int(family, 1);
  2833. Trace.Char("."); Trace.Int(ASH(signature, -4) MOD 10H, 1);
  2834. Trace.Char("."); Trace.Int(signature MOD 10H, 1);
  2835. Trace.String(", features "); Trace.Hex(feat, 9);
  2836. Trace.String(", ID "); Trace.Int(idx, 1);
  2837. IF (threadsPerCore > 1) THEN Trace.String(" ("); Trace.Int(threadsPerCore, 0); Trace.String(" threads)"); END;
  2838. Trace.Ln;
  2839. END;
  2840. IF (threadsPerCore > 1) & (Enabled IN flags) THEN
  2841. GetConfig("DisableHyperthreading", string);
  2842. IF (string = "1") THEN
  2843. Trace.String("Machine: Hyperthreading disabled."); Trace.Ln;
  2844. RETURN;
  2845. END;
  2846. log := (LSH(CPUID1(), -16) MOD 256) DIV coresPerProcessor;
  2847. WHILE log > 1 DO
  2848. INC(id); DEC(log);
  2849. IF numProcessors < maxProcessors THEN
  2850. idx := numProcessors; INC(numProcessors);
  2851. apicVer[idx] := ver; idMap[id] := SHORT(SHORT(idx))
  2852. END
  2853. END
  2854. END
  2855. END ParseProcessor;
  2856. (* Parse MP configuration table. *)
  2857. PROCEDURE ParseMPConfig;
  2858. VAR adr, x: ADDRESS; i: LONGINT; entries: INTEGER; ch: CHAR; s: SET; str: ARRAY 8 OF CHAR;
  2859. BEGIN
  2860. localAPIC := 0; numProcessors := 1; allProcessors := {0};
  2861. FOR i := 0 TO LEN(idMap)-1 DO idMap[i] := -1 END; (* all unassigned *)
  2862. FOR i := 0 TO MaxCPU-1 DO started[i] := FALSE END;
  2863. adr := configMP;
  2864. GetConfig("MaxProcs", str);
  2865. i := 0; maxProcessors := StrToInt(i, str);
  2866. IF maxProcessors = 0 THEN maxProcessors := MaxCPU END;
  2867. IF (maxProcessors > 0) & (adr # NilAdr) THEN (* MP config table present, possible multi-processor *)
  2868. Trace.String("Machine: Intel MP Spec "); Trace.Int(ORD(revMP) DIV 10H + 1, 1);
  2869. Trace.Char("."); Trace.Int(ORD(revMP) MOD 10H, 1); Trace.Ln;
  2870. IF TraceVerbose THEN
  2871. IF ODD(ASH(ORD(featureMP[1]), -7)) THEN
  2872. Trace.StringLn (" PIC mode");
  2873. (* to do: enable SymIO *)
  2874. ELSE
  2875. Trace.StringLn (" Virtual wire mode");
  2876. END
  2877. END;
  2878. IF featureMP[0] # 0X THEN (* pre-defined configuration *)
  2879. Trace.String(" Default config "); Trace.Int(ORD(featureMP[0]), 1); Trace.Ln;
  2880. localAPIC := 0FEE00000H;
  2881. apicVer[0] := 0; apicVer[1] := 0
  2882. ELSE (* configuration defined in table *)
  2883. MapPhysical(adr, 68*1024, adr); (* 64K + 4K header *)
  2884. SYSTEM.GET (adr, x); ASSERT(x = 504D4350H); (* check signature *)
  2885. SYSTEM.GET (adr+4, x); (* length *)
  2886. ASSERT(ChecksumMP(adr, x MOD 10000H) = 0);
  2887. IF TraceVerbose THEN
  2888. Trace.String(" ID: ");
  2889. FOR x := adr+8 TO adr+27 DO
  2890. SYSTEM.GET (x, ch); Trace.Char(ch);
  2891. IF x = adr+15 THEN Trace.Char(" ") END
  2892. END;
  2893. Trace.Ln
  2894. END;
  2895. localAPIC := 0; SYSTEM.GET(adr+36, SYSTEM.VAL (LONGINT, localAPIC));
  2896. IF TraceVerbose THEN Trace.String(" Local APIC:"); Trace.Address (localAPIC); Trace.Ln END;
  2897. SYSTEM.GET (adr+34, entries);
  2898. INC(adr, 44); (* skip header *)
  2899. WHILE entries > 0 DO
  2900. SYSTEM.GET (adr, ch); (* type *)
  2901. CASE ORD(ch) OF
  2902. 0: (* processor *)
  2903. ParseProcessor(adr);
  2904. INC(adr, 20)
  2905. |1: (* bus *)
  2906. IF TraceVerbose THEN
  2907. SYSTEM.GET (adr+1, ch);
  2908. Trace.String(" Bus "); Trace.Int(ORD(ch), 1); Trace.String(": ");
  2909. FOR x := adr+2 TO adr+7 DO SYSTEM.GET (x, ch); Trace.Char(ch) END;
  2910. Trace.Ln
  2911. END;
  2912. INC(adr, 8)
  2913. |2: (* IO APIC *)
  2914. IF TraceVerbose THEN
  2915. SYSTEM.GET (adr+1, ch); Trace.String(" IO APIC ID:"); Trace.Hex(ORD(ch), -3);
  2916. SYSTEM.GET (adr+2, ch); Trace.String(", version "); Trace.Int(ORD(ch), 1);
  2917. SYSTEM.GET(adr, SYSTEM.VAL (LONGINT, s)); IF ~(24 IN s) THEN Trace.String(" (disabled)") END;
  2918. Trace.Ln
  2919. END;
  2920. INC(adr, 8)
  2921. |3: (* IO interrupt assignment *)
  2922. INC(adr, 8)
  2923. |4: (* Local interrupt assignment *)
  2924. INC(adr, 8)
  2925. END; (* CASE *)
  2926. DEC(entries)
  2927. END
  2928. END
  2929. END;
  2930. IF localAPIC = 0 THEN (* single processor *)
  2931. Trace.StringLn ("Machine: Single-processor");
  2932. apicVer[0] := 0
  2933. END;
  2934. started[0] := TRUE;
  2935. FOR i := 0 TO MaxCPU-1 DO revIDmap[i] := -1 END;
  2936. FOR i := 0 TO LEN(idMap)-1 DO
  2937. x := idMap[i];
  2938. IF x # -1 THEN
  2939. ASSERT(revIDmap[x] = -1); (* no duplicate APIC ids *)
  2940. revIDmap[x] := SHORT(SHORT(i))
  2941. END
  2942. END;
  2943. (* timer configuration *)
  2944. GetConfig("TimerRate", str);
  2945. i := 0; timerRate := StrToInt(i, str);
  2946. IF timerRate = 0 THEN timerRate := 1000 END;
  2947. IF TraceProcessor THEN
  2948. GetConfig("TraceProc", str);
  2949. i := 0; traceProcessor := StrToInt(i, str) # 0
  2950. END
  2951. END ParseMPConfig;
  2952. (* Return the current average measured bus clock speed in Hz. *)
  2953. PROCEDURE GetBusClockRate(): LONGINT;
  2954. VAR timer: LONGINT; t: LONGINT;
  2955. BEGIN
  2956. t := ticks;
  2957. REPEAT UNTIL ticks # t; (* wait for edge *)
  2958. timer := ticks + ClockRateDelay;
  2959. ApicPut(380H, SYSTEM.VAL (SET, MAX(LONGINT))); (* initial count *)
  2960. REPEAT UNTIL timer - ticks <= 0;
  2961. t := MAX(LONGINT) - SYSTEM.VAL (LONGINT, ApicGet(390H)); (* current count *)
  2962. IF t <= MAX(LONGINT) DIV 1000 THEN
  2963. RETURN 1000 * t DIV ClockRateDelay
  2964. ELSE
  2965. RETURN t DIV ClockRateDelay * 1000
  2966. END
  2967. END GetBusClockRate;
  2968. (* Initialize APIC timer for timeslicing. *)
  2969. PROCEDURE InitMPTimer;
  2970. VAR rate: LONGINT;
  2971. BEGIN
  2972. IF timerRate > 0 THEN
  2973. ApicPut(3E0H, {0,1,3}); (* divide by 1 *)
  2974. ApicPut(320H, {16} + SYSTEM.VAL (SET, MPTMR)); (* masked, one-shot *)
  2975. rate := GetBusClockRate();
  2976. busHz0[ID()] := rate;
  2977. rate := (rate+500000) DIV 1000000 * 1000000; (* round to nearest MHz *)
  2978. busHz1[ID()] := rate;
  2979. ApicPut(320H, {17} + SYSTEM.VAL (SET, MPTMR)); (* unmasked, periodic *)
  2980. ApicPut(380H, SYSTEM.VAL (SET, rate DIV timerRate)) (* initial count *)
  2981. END
  2982. END InitMPTimer;
  2983. (* Handle multiprocessor timer interrupt. *)
  2984. PROCEDURE HandleMPTimer(VAR state: State);
  2985. BEGIN (* {interrupts off} *)
  2986. timer(ID(), state);
  2987. Sti; (* enable interrupts before acquiring locks below - to avoid deadlock with StopAll. *)
  2988. Timeslice(state);
  2989. ApicPut(0B0H, {}); (* EOI *)
  2990. END HandleMPTimer;
  2991. (* Handle uniprocessor timer interrupt. *)
  2992. PROCEDURE HandleUPTimer(VAR state: State);
  2993. BEGIN (* {interrupts off} *)
  2994. timer(0, state);
  2995. Sti; (* enable interrupts before acquiring locks below - to avoid deadlock with StopAll. *)
  2996. Timeslice(state)
  2997. END HandleUPTimer;
  2998. PROCEDURE DummyEvent(id: LONGINT; CONST state: State);
  2999. END DummyEvent;
  3000. (** Install a processor timer event handler. *)
  3001. PROCEDURE InstallEventHandler* (h: EventHandler);
  3002. BEGIN
  3003. IF h # NIL THEN timer := h ELSE timer := DummyEvent END
  3004. END InstallEventHandler;
  3005. (* Initialize APIC for current processor. *)
  3006. PROCEDURE InitAPIC;
  3007. BEGIN
  3008. (* enable APIC, set focus checking & set spurious interrupt handler *)
  3009. ASSERT(MPSPU MOD 16 = 15); (* low 4 bits set, p. 7-29 *)
  3010. ApicPut(0F0H, {8} + SYSTEM.VAL (SET, MPSPU));
  3011. (* set error interrupt handler *)
  3012. ApicPut(370H, SYSTEM.VAL (SET, MPERR));
  3013. InitMPTimer
  3014. END InitAPIC;
  3015. (* Start processor activity. *)
  3016. PROCEDURE StartMP;
  3017. VAR id: LONGINT; state: State;
  3018. BEGIN (* running at kernel level with interrupts on *)
  3019. InitAPIC;
  3020. id := ID(); (* timeslicing is disabled, as we are running at kernel level *)
  3021. IF TraceVerbose THEN
  3022. Acquire (TraceOutput);
  3023. Trace.String (" P"); Trace.Int(id, 1); Trace.StringLn (" running");
  3024. Release (TraceOutput);
  3025. END;
  3026. IF TraceProcessor & traceProcessor & (id = numProcessors-1) THEN
  3027. DEC(numProcessors) (* exclude from rest of activity *)
  3028. ELSE
  3029. INCL(allProcessors, id)
  3030. END;
  3031. (* synchronize with boot processor - end of mutual exclusion *)
  3032. started[id] := TRUE;
  3033. IF TraceProcessor & ~(id IN allProcessors) THEN
  3034. IF TraceVerbose THEN
  3035. Acquire (TraceOutput);
  3036. Trace.String (" P"); Trace.Int(id, 1); Trace.StringLn (" tracing");
  3037. Release (TraceOutput);
  3038. END;
  3039. LOOP
  3040. IF traceProcessorProc # NIL THEN traceProcessorProc(id, state) END;
  3041. SpinHint
  3042. END
  3043. END;
  3044. (* wait until woken up *)
  3045. WHILE stopped DO SpinHint END;
  3046. (* now fully functional, including storage allocation *)
  3047. AtomicExcl(ipcBusy, id); (* ack *)
  3048. IF TraceVerbose THEN
  3049. Acquire (TraceOutput);
  3050. Trace.String (" P"); Trace.Int(id, 1); Trace.StringLn(" scheduling");
  3051. Release (TraceOutput);
  3052. END;
  3053. ASSERT(id = ID()); (* still running on same processor *)
  3054. start;
  3055. END StartMP;
  3056. (* Subsequent processors start executing here. *)
  3057. PROCEDURE EnterMP;
  3058. (* no local variables allowed, because stack is switched. *)
  3059. BEGIN (* running at kernel level with interrupts off *)
  3060. InitProcessor;
  3061. InitMemory; (* switch stack *)
  3062. Start;
  3063. StartMP
  3064. END EnterMP;
  3065. (* Start another processor. *)
  3066. PROCEDURE StartProcessor(phys: ADDRESS; apicid: LONGINT; startup: BOOLEAN);
  3067. VAR j, k: LONGINT; s: SET; timer: LONGINT;
  3068. BEGIN
  3069. (* clear APIC errors *)
  3070. ApicPut(280H, {}); s := ApicGet(280H);
  3071. (* assert INIT *)
  3072. ApicPut(310H, SYSTEM.VAL (SET, ASH(apicid, 24))); (* set destination *)
  3073. ApicPut(300H, {8, 10, 14, 15}); (* set Dest, INIT, Phys, Assert, Level *)
  3074. timer := ticks + 5; (* > 200us *)
  3075. REPEAT UNTIL timer - ticks <= 0;
  3076. (* deassert INIT *)
  3077. ApicPut(310H, SYSTEM.VAL (SET, ASH(apicid, 24))); (* set destination *)
  3078. ApicPut(300H, {8, 10, 15}); (* set Dest, INIT, Deassert, Phys, Level *)
  3079. IF startup THEN (* send STARTUP if required *)
  3080. j := 0; k := 2;
  3081. WHILE j # k DO
  3082. ApicPut(280H, {});
  3083. ApicPut(310H, SYSTEM.VAL (SET, ASH(apicid, 24))); (* set destination *)
  3084. (* set Dest, Startup, Deassert, Phys, Edge *)
  3085. ApicPut(300H, {9, 10} + SYSTEM.VAL (SET, phys DIV 4096 MOD 256));
  3086. timer := ticks + 10; (* ~10ms *)
  3087. REPEAT UNTIL timer - ticks <= 0;
  3088. IF ~(12 IN ApicGet(300H)) THEN (* idle *)
  3089. IF ApicGet(280H) * {0..3, 5..7} = {} THEN k := j (* ESR success, exit *)
  3090. ELSE INC(j) (* retry *)
  3091. END
  3092. ELSE INC(j) (* retry *)
  3093. END
  3094. END
  3095. END
  3096. END StartProcessor;
  3097. (* Boot other processors, one at a time. *)
  3098. PROCEDURE BootMP;
  3099. VAR phys, page0Adr: ADDRESS; i: LONGINT; timer: LONGINT;
  3100. BEGIN
  3101. stopped := TRUE; ipcBusy := {}; (* other processors can be woken with StartAll *)
  3102. InitBootPage(EnterMP, phys);
  3103. MapPhysical(0, 4096, page0Adr); (* map in BIOS data area *)
  3104. Acquire(TraceOutput); Trace.String("Machine: Booting processors... "); Trace.Ln; Release(TraceOutput);
  3105. FOR i := 1 TO numProcessors-1 DO
  3106. (* set up booting for old processor types that reset on INIT & don't understand STARTUP *)
  3107. SYSTEM.PUT (page0Adr + 467H, ASH(phys, 16-4));
  3108. PutNVByte(15, 0AX); (* shutdown status byte *)
  3109. (* attempt to start another processor *)
  3110. IF TraceVerbose THEN
  3111. Acquire(TraceOutput); Trace.String(" P0 starting P"); Trace.Int(i, 1); Trace.Ln; Release(TraceOutput);
  3112. END;
  3113. StartProcessor(phys, revIDmap[i], apicVer[i] >= 10H); (* try booting processor i *)
  3114. (* wait for CPU to become active *)
  3115. timer := ticks + 5000; (* ~5s timeout *)
  3116. REPEAT SpinHint UNTIL started[i] OR (timer - ticks <= 0);
  3117. (* end of mutual exclusion *)
  3118. Acquire(TraceOutput);
  3119. IF started[i] THEN
  3120. IF TraceVerbose THEN
  3121. Trace.String(" P0 recognized P"); Trace.Int(i, 1); Trace.Ln;
  3122. END;
  3123. ELSE
  3124. Trace.String(" P0 timeout on P"); Trace.Int(i, 1); Trace.Ln;
  3125. END;
  3126. Release(TraceOutput);
  3127. END;
  3128. SYSTEM.PUT (page0Adr + 467H, SYSTEM.VAL (LONGINT, 0));
  3129. UnmapPhysical(page0Adr, 4096);
  3130. PutNVByte(15, 0X) (* restore shutdown status *)
  3131. END BootMP;
  3132. (* Timer interrupt handler. *)
  3133. PROCEDURE TimerInterruptHandler(VAR state: State);
  3134. BEGIN
  3135. INC(ticks);
  3136. DEC(eventCount);
  3137. IF eventCount = 0 THEN
  3138. eventCount := eventMax; event(state)
  3139. END
  3140. END TimerInterruptHandler;
  3141. PROCEDURE Dummy(VAR state: State);
  3142. END Dummy;
  3143. PROCEDURE InitTicks;
  3144. CONST Div = (2*TimerClock + Second) DIV (2*Second); (* timer clock divisor *)
  3145. BEGIN
  3146. eventCount := 0; eventMax := 0; event := Dummy;
  3147. (* initialize timer hardware *)
  3148. ASSERT(Div <= 65535);
  3149. Portout8(43H, 34X); Wait; (* mode 2, rate generator *)
  3150. Portout8(40H, CHR(Div MOD 100H)); Wait;
  3151. Portout8(40H, CHR(ASH(Div, -8)));
  3152. InstallHandler(TimerInterruptHandler, IRQ0)
  3153. END InitTicks;
  3154. (* Set timer upcall. The handler procedure will be called at a rate of Second/divisor Hz. *)
  3155. PROCEDURE InstallTickHandler(handler: Handler; divisor: LONGINT);
  3156. BEGIN
  3157. eventMax := divisor; event := handler;
  3158. eventCount := eventMax
  3159. END InstallTickHandler;
  3160. (* Initialize processors *)
  3161. PROCEDURE InitProcessors*;
  3162. BEGIN
  3163. traceProcessor := FALSE; traceProcessorProc := NIL;
  3164. ASSERT(Second = 1000); (* use of Machine.ticks *)
  3165. InitTicks;
  3166. timer := DummyEvent;
  3167. ParseMPConfig;
  3168. InstallHandler(HandleIPC, MPIPCLocal);
  3169. IF localAPIC # 0 THEN (* APIC present *)
  3170. InitAPICArea(localAPIC, 4096);
  3171. InitAPICIDAdr(localAPIC+20H, idMap);
  3172. ASSERT(MPSPU MOD 16 = 15); (* use default handler (see 7.4.11.1) *)
  3173. InstallHandler(HandleError, MPERR);
  3174. InstallHandler(HandleMPTimer, MPTMR);
  3175. InstallHandler(HandleIPC, MPIPC);
  3176. InitAPIC;
  3177. IF numProcessors > 1 THEN BootMP END
  3178. ELSE
  3179. IF timerRate > 0 THEN
  3180. InstallTickHandler(HandleUPTimer, Second DIV timerRate)
  3181. END
  3182. END;
  3183. InstallHandler(HandleKernelCall, MPKC);
  3184. END InitProcessors;
  3185. VAR scrollLines: LONGINT;
  3186. (* Send and print character *)
  3187. PROCEDURE TraceChar (c: CHAR);
  3188. VAR status: SHORTINT;
  3189. (* Scroll the screen by one line. *)
  3190. PROCEDURE Scroll;
  3191. VAR adr: ADDRESS; off: SIZE; i,j: LONGINT;
  3192. BEGIN
  3193. IF (traceDelay > 0) & (scrollLines MOD TraceHeight = 0) THEN
  3194. FOR i := 0 TO traceDelay-1 DO
  3195. FOR j := 0 TO 1000000 DO END;
  3196. END;
  3197. END;
  3198. INC(scrollLines);
  3199. adr := traceBase + TraceLen;
  3200. SYSTEM.MOVE (adr, adr - TraceLen, TraceSize - TraceLen);
  3201. adr := traceBase + TraceSize - TraceLen;
  3202. FOR off := 0 TO TraceLen - SIZEOF(INTEGER) BY SIZEOF(INTEGER) DO SYSTEM.PUT16 (adr + off, 100H * 7H + 32) END
  3203. END Scroll;
  3204. BEGIN
  3205. IF TraceV24 IN traceMode THEN
  3206. REPEAT (* wait until port is ready to accept a character *)
  3207. Portin8 (SHORT(tracePort + 5), SYSTEM.VAL(CHAR,status))
  3208. UNTIL ODD (status DIV 20H); (* THR empty *)
  3209. Portout8 (SHORT(tracePort), c);
  3210. END;
  3211. IF TraceScreen IN traceMode THEN
  3212. IF c = 9X THEN c := 20X END;
  3213. IF c = 0DX THEN (* CR *)
  3214. DEC (tracePos, tracePos MOD TraceLen)
  3215. ELSIF c = 0AX THEN (* LF *)
  3216. IF tracePos < TraceSize THEN
  3217. INC (tracePos, TraceLen) (* down to next line *)
  3218. ELSE
  3219. Scroll
  3220. END
  3221. ELSE
  3222. IF tracePos >= TraceSize THEN
  3223. Scroll;
  3224. DEC (tracePos, TraceLen)
  3225. END;
  3226. SYSTEM.PUT16 (traceBase + tracePos, 100H * traceColor + ORD (c));
  3227. INC (tracePos, SIZEOF(INTEGER))
  3228. END
  3229. END
  3230. END TraceChar;
  3231. (* Change color *)
  3232. PROCEDURE TraceColor (c: SHORTINT);
  3233. BEGIN traceColor := c;
  3234. END TraceColor;
  3235. VAR traceDelay: LONGINT;
  3236. (* Initialise tracing. *)
  3237. PROCEDURE InitTrace;
  3238. CONST MaxPorts = 8;
  3239. VAR i, p, bps: LONGINT; off: SIZE; s, name: ARRAY 32 OF CHAR;
  3240. baselist: ARRAY MaxPorts OF LONGINT;
  3241. BEGIN
  3242. GetConfig ("TraceMode", s);
  3243. p := 0; traceMode := SYSTEM.VAL (SET, StrToInt (p, s));
  3244. IF TraceScreen IN traceMode THEN
  3245. GetConfig ("TraceMem", s);
  3246. p := 0; traceBase := SYSTEM.VAL (ADDRESS, StrToInt (p, s));
  3247. IF traceBase = 0 THEN traceBase := 0B8000H END; (* default screen buffer *)
  3248. FOR off := 0 TO TraceSize - SIZEOF(INTEGER) BY SIZEOF(INTEGER) DO SYSTEM.PUT16 (traceBase + off, 100H * 7H + 32) END;
  3249. tracePos := 0;
  3250. Portout8(3D4H, 0EX);
  3251. Portout8(3D5H, CHR((TraceWidth*TraceHeight) DIV 100H));
  3252. Portout8(3D4H, 0FX);
  3253. Portout8(3D5H, CHR((TraceWidth*TraceHeight) MOD 100H));
  3254. GetConfig("TraceDelay", s);
  3255. p := 0; traceDelay := StrToInt(p, s);
  3256. END;
  3257. IF TraceV24 IN traceMode THEN
  3258. FOR i := 0 TO MaxPorts - 1 DO
  3259. COPY ("COMx", name); name[3] := CHR (ORD ("1") + i);
  3260. GetConfig (name, s); p := 0; baselist[i] := StrToInt (p, s);
  3261. END;
  3262. IF baselist[0] = 0 THEN baselist[0] := 3F8H END; (* COM1 port default values *)
  3263. IF baselist[1] = 0 THEN baselist[1] := 2F8H END; (* COM2 port default values *)
  3264. GetConfig("TracePort", s); p := 0; p := StrToInt(p, s); DEC(p);
  3265. IF (p >= 0) & (p < MaxPorts) THEN tracePort := baselist[p] ELSE tracePort := baselist[0] END;
  3266. ASSERT(tracePort > 0);
  3267. GetConfig("TraceBPS", s); p := 0; bps := StrToInt(p, s);
  3268. IF bps <= 0 THEN bps := 38400 END;
  3269. Portout8 (SHORT(tracePort + 3), 80X); (* Set the Divisor Latch Bit - DLAB = 1 *)
  3270. bps := 115200 DIV bps; (* compiler DIV/PORTOUT bug workaround *)
  3271. Portout8 (SHORT(tracePort + 1), CHR (bps DIV 100H)); (* Set the Divisor Latch MSB *)
  3272. Portout8 (SHORT(tracePort), CHR (bps MOD 100H)); (* Set the Divisor Latch LSB *)
  3273. Portout8 (SHORT(tracePort + 3), 3X); (* 8N1 *)
  3274. Portout8 (SHORT(tracePort + 4), 3X); (* Set DTR, RTS on in the MCR *)
  3275. Portout8 (SHORT(tracePort + 1), 0X); (* Disable receive interrupts *)
  3276. END;
  3277. traceColor := 7; Trace.Char := TraceChar; Trace.Color := TraceColor;
  3278. END InitTrace;
  3279. PROCEDURE InitHandlers;
  3280. VAR i,j: LONGINT;
  3281. BEGIN
  3282. FOR i := 0 TO IDTSize - 1 DO
  3283. FOR j := 0 TO MaxNumHandlers - 1 DO
  3284. intHandler[i, j].valid := FALSE;
  3285. intHandler[i, j].handler := NIL
  3286. END
  3287. END;
  3288. END InitHandlers;
  3289. PROCEDURE - PushBootValues;
  3290. CODE{SYSTEM.i386}
  3291. PUSH EDI
  3292. PUSH ESI
  3293. PUSH EAX
  3294. END PushBootValues;
  3295. PROCEDURE - GetBootValue(): LONGINT;
  3296. CODE{SYSTEM.i386}
  3297. POP EAX
  3298. END GetBootValue;
  3299. (* Init code called from OBL. EAX = boot table offset. ESI, EDI=initRegs. 2k stack is available. No trap handling. *)
  3300. BEGIN
  3301. PushBootValues;
  3302. bootFlag := GetBootValue();
  3303. initRegs[0] := GetBootValue();
  3304. initRegs[1] := GetBootValue();
  3305. (* registers 6 and 7 get converted to 32 bit, cf. PCB.Assigne
  3306. SYSTEM.GETREG(6, initRegs[0]); SYSTEM.GETREG(7, initRegs[1]); (* initRegs0 & initRegs1 *)
  3307. *)
  3308. SYSTEM.PUT16(0472H, 01234H); (* soft boot flag, for when we reboot *)
  3309. ReadBootTable(bootFlag);
  3310. InitTrace;
  3311. Trace.String("Machine: "); Trace.Blue;Trace.StringLn (Version); Trace.Default;
  3312. CheckMemory;
  3313. SearchMP;
  3314. AllocateDMA; (* must be called after SearchMP, as lowTop is modified *)
  3315. version := Version;
  3316. InitBoot;
  3317. InitProcessor;
  3318. InitLocks;
  3319. NmaxUserStacks := MaxUserStacks;
  3320. ASSERT(ASH(1, PSlog2) = PS);
  3321. Trace.String("Machine: Enabling MMU... ");
  3322. InitSegments; (* enable flat segments *)
  3323. InitPages; (* create page tables *)
  3324. InitMemory; (* switch on segmentation, paging and switch stack *)
  3325. Trace.Green; Trace.StringLn("Ok"); Trace.Default;
  3326. InitHandlers; (* no FOR loops here as they create local variables, collision with GetEAX in first statement *)
  3327. default.valid := FALSE; (* initialized later *)
  3328. END Machine.
  3329. (*
  3330. 03.03.1998 pjm First version
  3331. 30.06.1999 pjm ProcessorID moved to AosProcessor
  3332. *)
  3333. (**
  3334. Notes
  3335. This module defines an interface to the boot environment of the system. The facilities provided here are only intended for the lowest levels of the system, and should never be directly imported by user modules (exceptions are noted below). They are highly specific to the system hardware and firmware architecture.
  3336. Typically a machine has some type of firmware that performs initial testing and setup of the system. The firmware initiates the operating system bootstrap loader, which loads the boot file. This module is the first module in the statically linked boot file that gets control.
  3337. There are two more-or-less general procedures in this module: GetConfig and StrToInt. GetConfig is used to query low-level system settings, e.g., the location of the boot file system. StrToInt is a utility procedure that parses numeric strings.
  3338. Config strings:
  3339. ExtMemSize Specifies size of extended memory (above 1MB) in MB. This value is not checked for validity. Setting it false may cause the system to fail, possible after running for some time. The memory size is usually detected automatically, but if the detection does not work for some reason, or if you want to limit the amount of memory detected, this string can be set. For example, if the machine has 64MB of memory, this value can be set as ExtMemSize="63".
  3340. *)